Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions plugins/Wzdhehe/mcode-webui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

# v2026-08-28 modacker: webui runtime artifacts (server.err + sessions json)
.server.err
.webui-sessions.json
126 changes: 126 additions & 0 deletions plugins/Wzdhehe/mcode-webui/BASELINE-2026-09-04.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# BASELINE — mcode-webui pre-fix snapshot

> 采集日期:2026-09-04 20:55 CST
> 工作目录:`/Users/moc/workspaces/Mcode-webui-sync/upstream` 分支 `fix/cve-csrf-token-leak-2026-09`
> 起点 commit:`92cae0c` (== `Wzdhehe/Mcode-webui` PR #23 head `091dec5` "Token Plan key integration end-to-end")
> 环境:macOS Darwin, Node v25.9.0, sqlite3 系统默认, mcode 0.2.4, mavis 0.1.0+
> 工具:npm 11.x, `node --experimental-test-module-mocks --test test/*.test.js`

---

## 1. npm test

```
ℹ tests 413
ℹ suites 105
ℹ pass 408
ℹ fail 3
ℹ cancelled 0
ℹ skipped 2
ℹ todo 0
ℹ duration_ms 2313.78
```

### 3 个 fail 全部来自 `test/csrf-token-disclosure.test.js`

| # | 测试 | 行 | 状态 | 现象 |
|---|---|---|---|---|
| blocker#1 | `GET /api/settings` 跨源不得返回 200 带 token | L160 | ❌ FAIL | status=200, 响应体含 `currentToken: 8a520aae769fbf4ea6eac2227fa59ba6` |
| blocker#2 | `POST /api/settings` 跨源不得返回 200 带 token | L172 | ❌ FAIL | status=200, 响应体含 `currentToken` |
| blocker#3 | 跨源响应 `Access-Control-Allow-Origin` 不得为 `*` | L182 | ❌ FAIL | header = `*`(裸星号) |
| blocker#4 | `DELETE /api/sessions/:id` 跨源须 401/403/404 | L193 | ✅ PASS | status=404(session 不存在;token gate 旁路了但 404 兜底) |

> 注释:blocker#4 在测试用例里 PASS 是因为路径不存在返回 404 —— 但**真实删除路径**上仍会被跨源执行。是 PoC 没覆盖到的盲点,第二段顺带修。

---

## 2. 独立 PoC 复现(hetaoBackend 2026-09-01 01:25Z 报告)

跑 `node ~/workspaces/Mcode-webui-sync/poc-csrf.mjs <plugin-dir>`(脚本在 modacker 本地):

```
== CSRF / token-disclosure PoC ==
plugin dir: /Users/moc/workspaces/Mcode-webui-sync/upstream/plugins/Wzdhehe/mcode-webui
settings dir: /var/folders/xp/.../poc-csrf-QqSVKt
port: 18080

bootstrap token (from operator-only settings file or first GET):
3801ed9d...725a

=== TEST 1: GET /api/settings, Origin: https://evil.example ===
status: 200
access-control-allow-origin: *
access-control-allow-methods: GET, POST, OPTIONS, DELETE
access-control-allow-headers: Content-Type, Authorization
content-type: application/json; charset=utf-8
body[0..400]: {"ok":true,"lanBroadcast":true,"readOnly":false,"tokenEnabled":true,
"tokenAcknowledged":false,"currentToken":"3801ed9d7aa8fd1d4846f7472efb725a",...}
[LEAKED] bootstrap token in response body: true

=== TEST 2: POST /api/settings, Origin: https://evil.example ===
status: 200, body 含 currentToken: 3801ed9d...725a
[LEAKED] bootstrap token in response body: true

=== TEST 3: DELETE /api/sessions/nonexistent, Origin: https://evil.example ===
status: 404, body {"ok":false,"error":"session not found"}(DELETE 路径盲点)

=== SUMMARY ===
CORS allows cross-origin read: YES (vulnerable)
Local bypass on cross-origin: YES (vulnerable)
Bootstrap token leak count: 2/2

>>> CSRF / token-disclosure blocker CONFIRMED
>>> hetaoBackend report (2026-09-01 01:25Z) reproduced
```

PoC `process.exitCode = 2`(vulnerability confirmed),但被 `tail` 截断后看到 shell `$?` = 0;真实 node 退出码 = 2。

---

## 3. npm run lint

```
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@eslint/js' imported from
/Users/moc/workspaces/Mcode-webui-sync/upstream/eslint.config.mjs
```

**根因**:`eslint.config.mjs` 位于 `upstream/` 根目录,但 `@eslint/js` / `globals` 的 `devDependencies` 在 `upstream/plugins/Wzdhehe/mcode-webui/package.json` 里。`npm install` 在子目录跑,不会把 devDeps 装到根的 `node_modules/`,ESLint 找不到包。

**严重性**:lint 完全不可跑。这跟 CSRF 修复**无关**,是 pre-existing 路径错位。

**处置**:列入第二段同步修的列表(不阻塞 CSRF 修,但同 PR 改掉)。

---

## 4. 根因定位(4 个文件)

| 文件 | 行 | 问题 |
|---|---|---|
| `server/router.js` | 279 | `res.setHeader("Access-Control-Allow-Origin", "*");`(裸星号) |
| `server/lib/auth.js` | Gate 3 入口 | `isLocalRequest(req)` 命中即绕过 token 校验(跨源 127.0.0.1 同样命中) |
| `server/routes/settings.js` | (待 grep) | `GET /api/settings` 响应里塞 `currentToken`(`tokenAcknowledged === false` 时) |
| `server/lib/state-bus.js` | `pushStateFor` (待 grep) | SSE state push 也带 `currentToken`(**PoC 没覆盖**,但同源浏览器 EventSource 也会订阅到) |

---

## 5. 修后判定线(机械可证)

修复后必须满足:
1. `npm test` → `tests 413 / pass 413 / fail 0 / skipped 2`(4 个 blocker 全绿)
2. `poc-csrf.mjs` → exit code 0 + "PoC did not reproduce the blocker"
3. `npm run lint` → 0 errors, 0 warnings(先修路径错位)
4. 人工复测:clean checkout 跑 `node server.js` 仍能正常 listening on / 静态资源 / `/api/health`

---

## 6. 范围声明

- 起点:`92cae0c` 干净 HEAD
- 不动 `MiniMax-Code-Plugins`(marketplace 仓,用户明确禁止)
- 修复落点:`Wzdhehe/Mcode-webui`(用户已加入协作者)
- 工作分支:`fix/cve-csrf-token-leak-2026-09`(已创建,**未推送**)
- 后续 PR 标题(计划):`fix(mcode-webui): round 8 — CORS tightening + cross-origin token-leak fix`

---

*baseline locked 2026-09-04 20:55 CST.*
171 changes: 171 additions & 0 deletions plugins/Wzdhehe/mcode-webui/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Contributing to Mcode Web UI

Thanks for your interest in Mcode Web UI! This document covers
the day-to-day contribution workflow. For the bigger picture (plugin
packaging, release process), see [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md)
and [`plugins/Wzdhehe/mcode-webui/README.md`](plugins/Wzdhehe/mcode-webui/README.md).

## Code of conduct

Be kind. We review for substance, not for style preferences. If a
change makes the webui more correct / faster / easier to use, it's
in scope.

## Development setup

Requirements:

- **Node 22.19+** (uses `node:test`, `URL.parse`, `Blob.stream`)
- **Mcode CLI 0.1.4+** on `PATH` (or `MCODE_CMD` pointing to it)
- A POSIX-like shell on Windows: PowerShell 7+ or Git Bash

Clone and run:

```bash
git clone https://github.com/Wzdhehe/Mcode-webui.git
cd Mcode-webui
npm install # only devDeps (eslint, prettier, c8)
npm test # 382 unit tests + 1 skipped (383 total)
npm run lint # eslint flat config, must be 0 warnings
npm run dev # node server.js
# → http://127.0.0.1:8080/
```

`npm test` and `npm run lint` **must pass** before opening a PR.

## Repository layout

This repo has a **dual layout** — both copies are kept in sync:

```
Mcode-webui/ # ← the development tree (root)
├── server/ public/ test/ # Node + frontend + tests
├── docs/ # ARCHITECTURE, API, CAPABILITIES, …
├── acp.mjs, server.js, package.json
└── plugins/Wzdhehe/mcode-webui/ # ← the plugin artifact
├── server/ public/ test/ # ↑ real copies, not symlinks
├── docs/ references/ skills/
├── plugin.json package.json LICENSE
├── README.md PR_DESCRIPTION.md
└── SKILL.md # lives at skills/mcode-webui/SKILL.md
```

**Why two copies?** The community plugin registry takes the
`plugins/.../Mcode-webui/` tree as the submission. We keep it as a
real directory copy (not a junction or symlink — those break
zip-packaging and confuse `git log`).

`npm run setup:plugin` is a no-op on the current layout (it used to
create junctions; the trees have been expanded since).

## Editing flow

1. **Edit at the repo root** (`server/`, `public/`, `test/`).
2. **Mirror the change to the plugin tree** — copy the changed files
from `<root>/server/...` to `plugins/Wzdhehe/mcode-webui/server/...`,
and the same for `public/`, `test/`, `docs/`.
(The `package:plugin` script does this for you, but a
per-PR manual sync is fine for small changes.)
3. **Run the gate**:
```bash
npm test
npm run lint
npm run validate:plugin
```
4. **Commit** with a conventional message (see below).
5. **Push** to a feature branch and open a PR.

## Commit message format

We loosely follow [Conventional Commits](https://www.conventionalcommits.org/):

```
<type>(<scope>): <subject>

<body — explain WHY, not what>
<footer — refs, BREAKING CHANGE, etc.>
```

Common types:

- `feat:` — new feature
- `fix:` — bug fix
- `refactor:` — internal change, no behavior diff
- `test:` — test-only change
- `docs:` — documentation only
- `chore:` — build / CI / tooling

Scope is the area (`server`, `public`, `plugin`, `acp`, `test`, `docs`).

Example:

```
fix(acp): retry session/fork once on "Method not found"

mcode 0.1.5 returns "Method not found" for session/fork on the
first attempt but accepts it on retry. One retry is enough in
practice; log + continue.
```

## Pull request checklist

- [ ] `npm test` passes (382 + 1 skipped)
- [ ] `npm run lint` is clean (0 warnings)
- [ ] `npm run validate:plugin` is clean (mirrors official gate)
- [ ] Plugin tree (`plugins/.../Mcode-webui/`) is in sync with root
- [ ] No personal data in commit content (no IPs, no usernames, no
real session IDs)
- [ ] New env vars documented in `docs/API.md` and `plugin.json`
- [ ] New endpoints / events documented in `docs/API.md`
- [ ] `CHANGELOG.md` updated under an "Unreleased" section
- [ ] If destructive behavior changes, the security note
`plugins/.../references/SECURITY-NOTES.md` is updated (and
`plugin.json`'s `extensions.securityNotes` summary stays in sync)

## Adding a new route / event / panel

See [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) for recipes. The
short version:

- **Route**: drop a file in `server/routes/<name>.js` exporting
`(req, res, deps) => …`, register in `server/router.js`.
- **SSE event**: emit via `state-bus` in the route; consume in
`public/app/render.js`.
- **UI panel**: add a `state` slice in `public/app/state.js`,
a renderer in `public/app/render.js`, a handler in
`public/app/events.js`, and an i18n key in `public/app/i18n.js`.

## Style guide

- **ESM only** — no CommonJS, no `require()`.
- **No runtime npm deps** — only `devDependencies`. Everything
runtime must be Node 22+ stdlib.
- **No silent failures** — every catch either re-throws, returns
an explicit error response, or logs a warning with a `console.warn`
tag. No `try { … } catch {}` blocks.
- **No fake UI buttons** — if mcode acp doesn't support a method
(see `docs/CAPABILITIES.md`), don't render a button that
pretends to work. Use a toast + skip.
- **i18n first** — every user-visible string in the frontend goes
through `i18n.t()`. No inline English / Chinese literals.
- **Token-aware error messages** — never echo the request URL
or headers into error bodies (token leak risk).

## Release process

1. Bump `version` in `package.json` (root + plugin copy).
2. Move "Unreleased" section in `CHANGELOG.md` to a dated
versioned section.
3. `npm run package:plugin` — produces `dist/Wzdhehe/mcode-webui/`
+ `dist/Wzdhehe/Mcode-webui.zip`.
4. Open a PR to the community registry
[`MiniMax-AI/MiniMax-Code-Plugins`](https://github.com/MiniMax-AI/MiniMax-Code-Plugins)
adding only the `plugins/Wzdhehe/mcode-webui/` tree (per the
"one folder = one plugin" model — see the official README).
5. Tag the release: `git tag v1.X.Y && git push --tags`.

## Questions?

Open an issue. If it's about a plugin-submission process (reviewer
comments, manifest fields, etc.), tag it `plugin-registry`.
21 changes: 21 additions & 0 deletions plugins/Wzdhehe/mcode-webui/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Wzdhehe

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading