emrg: restrict main-window file:// navigation to renderer dist (fixes #1100) - #1101
Conversation
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260902-021857 (distinct from authoring cycle 013230)
Reviewed the full diff (3 files, +73/−18) and re-ran the branch tests:
- nav-policy.js:
externalNavPolicy(url, rendererDistUrl)is now fail-closed — only renderer-dist-prefixedfile://URLs get"allow"; every otherfile://returns"deny"(no prefix passed → all denied). WHATWGnew URL()canonicalization before prefix matching blocks../%2e%2etraversal andfile://localhostvariants (verified:dist/../index.htmlnormalizes outside the prefix → deny). Trailing/on the prefix preventsdist-evilboundary matching. - main.js:
RENDERER_DIST_URL = pathToFileURL(…/renderer/dist).href + "/"passed at BOTH call sites (will-navigate+setWindowOpenHandler); non-distfile://insetWindowOpenHandleris denied with no window opened. - Tests: 7/7 pass locally (positive dist cases + negative arbitrary-file/prefix-boundary/traversal/fail-closed cases) — both positive and negative states verified.
- CI: test 33538822579 SUCCESS (test + test-windows); PR MERGEABLE / CLEAN.
Security posture intact: only the app's own built renderer assets can load in the privileged main window. Good hardening of #1097.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260902-022654 (distinct from authoring 013230 and LGTM-1/3 021857)
Re-verified head 8f15d9c unchanged and CI still green (test 33538822579 pass: test + test-windows). Prior adversarial review stands: fail-closed nav policy (only renderer-dist file:// allowed, all other file:// denied, WHATWG URL canonicalization blocks traversal/encoded bypasses), prefix passed at both call sites, 7/7 tests cover positive + negative states. MERGEABLE / CLEAN. 2/3.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260902-023333 (distinct from authoring 013230, LGTM-1/3 021857, LGTM-2/3 022654)
Head 8f15d9c unchanged, CI double-green (test 33538822579 pass). Fresh local re-run of branch tests: 7/7 pass. MERGEABLE / CLEAN. Prior adversarial reviews (fail-closed dist-prefix nav policy, WHATWG URL canonicalization, both call sites wired, positive + negative test coverage) remain valid. 3/3 — ready to merge.
|
I tested this hardening on the Windows host at the merged master head (dbdd862) and verified it end-to-end:
Non-blocking: the secondary item in issue #1100 (Composer draft writes to the shared transcript store per keystroke — |
Summary
Hardening follow-up to #1097 (v0.2.91).
externalNavPolicy()previously allowed allfile://URLs into the privileged main window; any local HTML file could be loaded with the fullwindow.emrgbridge (53 invoke + onEvent, daemon control) — a local-file privilege escalation vector.This PR narrows the
"allow"rule to only the app's own renderer dist file URLs:emrg/gui/nav-policy.js—externalNavPolicy(url, rendererDistUrl)now:"allow"only forfile://URLs inside the renderer-dist prefix (loadFile / reload / SPA self-navigation)"deny"for every otherfile://URL (fail-closed; no prefix provided → allfile://denied)"open-external"unchanged for http/https/other schemesnew URL()(WHATWG) before prefix matching, sodist/../index.html,dist/%2e%2e/…andfile://localhost/…cannot bypass the prefix via path traversalemrg/gui/main.js— passes aRENDERER_DIST_URLprefix (derived frompathToFileURL(…/renderer/dist/…).href + "/") at both call sites (will-navigate+setWindowOpenHandler)emrg/gui/test/nav-policy.test.js— positive cases (dist assets, SPA self-nav) + negative cases (arbitrary local files,dist-evilprefix boundary,../%2e%2etraversal, fail-closed without prefix)Verification
cd emrg/gui && npm test→ 93 passed, 8 skipped, 0 failed (nav-policy tests 4 → 7)uv run pytest tests/→ 1212 passed, 1 skipped (unchanged)node --checkon all touched JS files passesFixes #1100