Repository navigation
epic: contain host execution and external I/O #189
Description
Completion status — 2026-10-15
Completed and merged into main via #279, commit b360c4ee50090b8bd0ddd7ba7ab0b17ea2311197. The observable-result checklist below is updated to reflect the delivered scope and recorded decisions. Final integrated main aaca138d7c3773be662941b4081c8d97ea40019c passed post-merge CI. Final local verification passed pnpm validate, 5,895 frontend/API tests, and five policy-record tests. Browser E2E was skipped.
Decision issue #207 was completed under its recorded scope; this closure does not claim that every possible CSP or capability restriction was enabled.
Epic branch: epic/189-containment. Plan checkpoint 2 of 7.
Why
The review found a default-mode HTTP listener that can reach host execution, shell interpolation in file and sandbox routes, untrusted URLs fetched without the existing network policy, a lexical file-containment check, and an unverified native binary download in the sidecar build. This epic closes those boundaries without removing legitimate desktop, WebUI, or local-integration behavior.
Evidence is in 01-findings.md. The sequencing is in 02-development-plan.md. The lessons behind it are in LESSONS.md and 03-lessons-and-contributors.md.
Observable result
- Default launch binds loopback. Remote mode is explicit and authenticates before privileged routes (S1).
- File names and package names stay literal. No shell command is built from a controlled value (S2, S3).
- File containment is checked on the resolved path, including symlinks and not-yet-existing write targets.
- Untrusted URLs go through
safeFetchwith redirect, resolved-address, timeout, and byte limits (S7). - External MCP auth fails closed on missing peer metadata (S8). Downloaded native code is digest-verified (S9).
- No exploit payload or credential is committed to public documentation.
Sub-issues
- fix(security): bind the daemon to loopback by default and require auth for remote mode (S1, P0) #200 fix(security): bind the daemon to loopback by default and require auth for remote mode (S1, P0)
- fix(security): open files and editors without a shell command string (S2) #201 fix(security): open files and editors without a shell command string (S2)
- fix(security): validate and isolate sandbox package installation (S3) #202 fix(security): validate and isolate sandbox package installation (S3)
- fix(security): check file containment on the resolved path, including symlinks #203 fix(security): check file containment on the resolved path, including symlinks (blocked by fix(security): open files and editors without a shell command string (S2) #201)
- fix(security): route untrusted URL fetches through safeFetch (S7) #204 fix(security): route untrusted URL fetches through safeFetch (S7)
- fix(security): external MCP auth fails closed on missing peer address (S8) #205 fix(security): external MCP auth fails closed on missing peer address (S8)
- fix(build): verify the native SQLite addon digest and key the cache by version (S9) #206 fix(build): verify the native SQLite addon digest and key the cache by version (S9)
- decide: Tauri CSP and capability narrowing, compatibility-tested #207 decide: Tauri CSP and capability narrowing, compatibility-tested
Sub-issues without a blocker can run in parallel, each in its own worktree: #200, #201, #202, #204, #205, #206, #207.
How this epic is worked
- The maintainer creates
epic/189-containmentfrommainand pushes it. Nobody commits to it directly. - Each sub-issue is implemented by one agent in its own git worktree on
issue/<n>-<slug>, opened as a PR whose base isepic/189-containmentand whose body containsCloses #<sub-issue>. - A merged sub-issue PR closes its issue through
.github/workflows/epic-close-issues.yml. Sub-issues merge by squash, one commit each. - When every sub-issue above is closed, open one epic PR from
epic/189-containmentintomainwithCloses #189. Runpnpm validate,pnpm test:fast(naming the knownresolve.test.tsfailure until it is fixed), and the surface checks for this epic on the final head, and write that head sha in the PR. - The maintainer reviews and merges the epic PR. No sub-issue PR targets
main.
Shared files that two streams must not edit at once are package.json, pnpm-lock.yaml, locale modules, component-size-allowlist.json, http-daemon.ts, video-edit-server.ts, and the shared video IR schemas. Each sub-issue names the files it owns and what it is blocked by. If you need a file another open sub-issue owns, comment on both issues and wait.
Suggested merge order into main across epics is E1 first, so later epic PRs run the repaired gate, then the rest by priority. Rebase an epic branch onto main before its final PR.
Activity
bravew commentedon Oct 15, 2026
Completed in #279 and merged into main as
b360c4ee50090b8bd0ddd7ba7ab0b17ea2311197. All seven epic changes are integrated ataaca138d7c3773be662941b4081c8d97ea40019c, with successful post-merge CI: https://github.com/bravew/Neumar/actions/runs/38678829734. Updated the completion checklist and status above. Closing as completed.Decision issue #207 was completed under its recorded scope; this closure does not claim that every possible CSP or capability restriction was enabled.