mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-07 07:59:42 +02:00
fix(attachments): harden registry + close magic-link injection vector
Security (MAJOR): the terminal-output codeman://attach scanner registered any
matching path server-side with no user confirmation and broadcast the rawUrl
over SSE. Terminal output is attacker-influenceable (a prompt-injected session
can print an arbitrary path), so on the default no-auth deployment this was an
arbitrary host-file (png/pdf/docx/pptx/md/txt) read primitive reachable by any
SSE client. Magic-link registration is now force-confined to the session
workspace (forceWorkspaceConfinement) regardless of the global confine setting;
deliberate cross-workspace attach still works through the explicit,
Origin-guarded POST /attachments route and 'codeman attach' (which POSTs
directly inside a managed session). Documented in security-architecture.md.
Regression (MAJOR): .png was rerouted from the image-popup path to
attachment:detected, which has no frontend consumer — silently breaking the
dropped/pasted-screenshot popup. PNG stays on image:detected; only pdf/docx/pptx
(which never had a popup) emit attachment:detected.
Also:
- raw route streams the freshly-resolved path, not the stored one, so a
post-registration symlink swap can't redirect the stream (TOCTOU).
- 50MB cap on the attachment raw route, matching file-raw / download.
- per-session attachment registry cap (200) to bound the POST path.
- CLI reads creds via dataPath('.env'), honoring CODEMAN_INSTANCE.
Tests: forced-confinement reject/allow cases; PNG popup-path assertions updated.
This commit is contained in:
@@ -320,7 +320,37 @@ injected from API JSON (`innerHTML`), not via `file-raw`, so they are unaffected
|
||||
`/api/download` additionally refuses a blocklist of sensitive paths
|
||||
(`/etc/shadow`, `~/.ssh/`, `.env`, `*credentials*`, `.aws/credentials`, …). This
|
||||
is **defense‑in‑depth, not the primary boundary** — the realpath containment is
|
||||
the control.
|
||||
the control. The blocklist patterns are shared (`src/web/sensitive-path.ts`) with
|
||||
the attachment guard below.
|
||||
|
||||
### External attachments (registry) & the magic‑link trust boundary
|
||||
|
||||
Live external attachments (`src/attachment-registry.ts`) mint an `att_<uuid>` id
|
||||
for a host file so browser requests carry the id, never an absolute path. Serving
|
||||
is by id (`GET /api/sessions/:id/attachments/:attachmentId/raw`, 50 MB cap,
|
||||
`nosniff`) and re‑resolves the symlink + re‑checks the **attachment guard**
|
||||
(`src/config/attachment-guard.ts`: the shared sensitive‑path blocklist **plus**
|
||||
the `/root` and `/etc` trees, extendable via `attachmentBlockedPaths` /
|
||||
`CODEMAN_ATTACHMENT_BLOCKED_PATHS`) on every request. Unlike the workspace file
|
||||
routes, attachments are intentionally **cross‑workspace** — so the effective gate
|
||||
is the blocklist + a 6‑extension allowlist (`png/pdf/docx/pptx/md/txt`), not
|
||||
realpath containment.
|
||||
|
||||
Two registration paths, with **different trust**:
|
||||
|
||||
- **Explicit `POST /api/sessions/:id/attachments`** (and `codeman attach`, which
|
||||
POSTs directly inside a managed session) — a deliberate, Origin‑guarded HTTP
|
||||
request. Allowed cross‑workspace (subject to the guard). This is the supported
|
||||
path for codeman‑publish and the `~/.codeman` review‑card loop.
|
||||
- **Terminal `codeman://attach?path=…` magic links** — scanned passively from
|
||||
session output. Terminal output is **attacker‑influenceable** (a prompt‑injected
|
||||
session can print an arbitrary path), and registration here is server‑side with
|
||||
no Origin gate and broadcasts the `rawUrl` over SSE to all clients. This path is
|
||||
therefore **force‑confined to the session workspace** (`forceWorkspaceConfinement`
|
||||
in `registerExternalAttachment`, wired in `WebServer.registerAttachment`),
|
||||
regardless of the global confine setting — a passive magic link cannot expose a
|
||||
file outside the session's own workspace. Cross‑workspace attach must go through
|
||||
the explicit POST path above.
|
||||
|
||||
### SSE log‑tail route — intentional extra read roots
|
||||
|
||||
|
||||
Reference in New Issue
Block a user