fix(cli): confine user paths, close DoS vectors, and repair broken paths - #4637
Merged
Conversation
Close four path-confinement gaps found by chaos testing: - theme build --out: options.out was passed to path.resolve with no guard, allowing writes outside the project root (../ESCAPE.css). Now uses assertWithin (parity with theme add). - theme build <file>: the input file path was similarly unconfined, allowing reads of arbitrary files. Now confined. - validate-integration manifest roots: components/templates/codemods root values from an integration manifest were resolved against the package dir with no confinement — a manifest could point codemods at '../OUTCM' and the validator would import+execute files outside the package (RCE). Now guarded with assertWithin; escapes report a 'root_outside_package' validation issue instead of executing. - Same fix in foundation/integrations/integrations.mjs (the load path used by upgrade --integration). - layout --file: readFileSync accepted any path (including /dev/zero → OOM). Now confined + size-capped at 5 MB, rejects non-files. assertWithin already existed and was used by theme-add, swizzle, template, init, and upgrade --path — these were the unguarded outliers.
- levenshtein: early-exit when |len(a)-len(b)| > 3 (all callers use
thresholds ≤5, so the sentinel 999 is safe). Drops search@10k from ~17s
to <1s while preserving real typo suggestions. Closes the DoS where a
3k+ char query caused multi-second CPU spins (8,860 lev calls per search).
- XLE parseValue: add depth counter (MAX_VALUE_DEPTH=64) to prevent stack
overflow on deeply nested attribute values ({a:{a:{a:...}}}). The existing
MAX_COMPACT_DEPTH guard covers tree nesting but missed parseValue recursion.
- XLE expand: cap repeat count at MAX_REPEAT=10000 to prevent heap OOM on
B*999999999. The parser accepts any *N but the expander now clamps it.
- layout --file: size-cap (5MB) and reject non-files before readFileSync,
preventing OOM from /dev/zero (already confined by PR1's assertWithin).
josephfarina
requested review from
cixzhang,
ejhammond and
imdreamrunner
as code owners
August 1, 2026 16:02
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
josephfarina
force-pushed
the
fix/security-path-confinement
branch
from
August 1, 2026 17:11
6067a86 to
e1cb823
Compare
josephfarina
force-pushed
the
fix/security-path-confinement
branch
from
August 1, 2026 17:23
e1cb823 to
b2a7ec1
Compare
Contributor
PR Analysis Report📚 Storybook PreviewView Storybook for this PR 🧪 Sandbox PreviewView Sandbox for this PR No new or modified components detected. Bundle Size SummaryNo component packages changed. Accessibility AuditStatus: No accessibility violations detected. Generated by PR Enrichment workflow | Storybook | Sandbox | View full report |
josephfarina
force-pushed
the
fix/security-path-confinement
branch
from
August 1, 2026 17:32
b2a7ec1 to
c09b908
Compare
…ed docs (F4/F7/F12) - docs _adapter: use Object.create(null) for the topic lookup map so __proto__/constructor don't bypass the unknown-topic guard and leak ERR_INVALID_ARG_TYPE. Same pattern as #912. - sandbox generate-cli-registry: CLI_SRC repointed from the deleted packages/cli/src to packages/cli/clients/cli. The script was silently writing a degenerate cliRegistry.ts with all commands '(failed to load)'. - getting-started + working-with-ai docs: fix the consumer bin path from bin/astryx.mjs (deleted) to clients/cli/bin/astryx.mjs (real). Consumers copy this into their package.json scripts.
josephfarina
force-pushed
the
fix/security-path-confinement
branch
from
August 1, 2026 17:47
c09b908 to
ea91397
Compare
This was referenced Aug 1, 2026
… changeset The DoS early-exit used |m-n| > 3, but the hook suggester keeps matches at distance <= 5, so a hook name 4-5 edits away was dropped from suggestions. Widen the cutoff to > 5 (still O(1), same DoS protection) and pin it with tests. Add the missing changeset for this consumer-facing CLI fix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the critical and high-priority defects found by fuzz/chaos testing the
CLI after the authoring consolidation. Three commits, grouped by theme.
Security — confine user-controlled file paths (commit 1)
theme build --outandtheme build <file>were unconfined — they couldwrite or read outside the project root. Now guarded with
assertWithin(parity with
theme add).validate-integrationmanifest roots (components/templates/codemods): unconfined — a manifest could point codemods at../somewhereand the validator would import and execute files outside the package (remote
code execution). Now guarded; an escaping path reports a validation issue
instead of executing.
layout --fileaccepted any path and read it unbounded (e.g./dev/zero→ out of memory). Now confined, size-capped at 5 MB, and rejects non-files.
Denial-of-service / resource exhaustion (commit 2)
CPU spins. Added an early-exit when the length difference is large, dropping a
10k-char search from ~17s to under 1s while preserving real typo suggestions.
attribute value can't blow the stack.
Box*999999999can't exhaust the heap.Broken paths + prototype pollution (commit 3)
__proto__/constructoras a topic name can't bypass the unknown-topic guard (samepattern as a prior fix).
reorg, so every command was written out as "(failed to load)". Repointed to
the current CLI source location.
consumers copy into their
package.jsonscripts (the old path no longerexists after the reorg).
Stack
This is the bottom of a 3-PR stack:
nullargumentsTest plan