Sandbox CLI output paths (Fixes #124) - #125
Conversation
Require CLI artifacts to stay inside cwd with a safe extension, and keep compare receipts as .json under the same boundary. Fixes tt-a1i#124. Co-authored-by: Cursor <cursoragent@cursor.com>
chiang21fcb
left a comment
There was a problem hiding this comment.
Thanks for the PR @alivirgo. This is a clean, well-scoped fix.
What I reviewed
output-path.mjs: ThesandboxSourceunification is elegant. All three safety gates (extension, resolved-extension, cwd containment) now apply to bothmetaandclipaths. TherequiredExtensionparameter keeps the contract flexible for compare receipts.archify.mjs: The tworequiredExtension: '.json'additions for receipt paths are exactly right.output-path.test.mjs: Three new regression tests cover the exact scenarios from #124. The two updated existing tests correctly adapt to the new check order (extension gate runs before alias gate).
Confirmed
- The
../marker.envoverwrite from #124 is blocked by the extension check - Compare receipts are gated by
.jsonextension independently - Stable diagnostic codes follow the existing
output/meta-*pattern - All existing tests are updated to match the new behavior
One note
The cwd containment for CLI paths is a reasonable safety choice. If maintainers want to allow absolute CLI paths for power users, that can be relaxed later without changing the extension gate, which is the critical fix.
No blockers. This is ready for maintainer review.
tt-a1i
left a comment
There was a problem hiding this comment.
Thanks for the focused sandboxing fix. The source-level direction is sound, but the distributable is stale on this exact head. I compared the committed files with the corresponding entries in archify.zip: both archify/renderers/shared/output-path.mjs and archify/bin/archify.mjs differ from the packaged copies. As a result, users installing the release artifact would not receive the output-path protections this PR adds, even though the repository source has them. Please rebuild archify.zip with the repository's canonical Node 22 build, verify byte-for-byte freshness, and run the current package-smoke/zip-freshness gates after syncing with main.
tt-a1i
left a comment
There was a problem hiding this comment.
Additional review of unchanged head e9653ba: the prior stale-ZIP finding still applies, but this is a newly reproduced public CLI regression, not a duplicate package reminder.
[P1] Separate trusted internal renderer targets from user-supplied output paths (archify/renderers/shared/output-path.mjs:268-273,288-315). From archify/, node bin/archify.mjs validate architecture examples/web-app.architecture.json --json now fails output/cli-outside-cwd because commandValidate renders into os.tmpdir while retaining the project cwd. node bin/archify.mjs inspect architecture examples/web-app.architecture.json fails output/cli-extension because its internal renderer target is /dev/null. Parent-versus-head resolver comparison confirms the regression. This blocks ordinary validation/inspection of valid diagrams, not an attempted unsafe user write.
Preserve the external sandbox while giving internal temporary/no-output calls an explicit safe seam, and add successful public validate/inspect regressions. Focused output-path tests: 17 passed, 5 failed; the latter also include fixtures not updated for the extension restriction, so they are not presented as proof of remaining overwrite vulnerabilities. No source edits or merge.
Summary
meta.outputto CLI output paths, sodeliver/render/comparecannot overwrite arbitrary files (e.g.../marker.env)..jsonviarequiredExtension: '.json'while HTML artifacts still require.html..htmldeliver targets, and non-.jsonreceipts.Fixes #124
Test plan
node --test test/output-path.test.mjs(non-symlink cases; symlink tests need Windows Developer Mode / elevated privileges)archify deliver workflow <input> ../outside.envfails and leaves the outside file untouchedarchify deliver workflow <input> ../escaped.htmlfails withoutput/cli-outside-cwdarchify compare ... --receipt foo.htmlfails withoutput/cli-extension.html/.receipt.jsonoutputs still succeedMade with Cursor