Also corrects the Changed fragment: it named three containment exports as the only ones, which stopped being true when the lexical family was added to close DW1/DW9. It now describes one decision resolved two ways, and says why the lexical pair exists rather than leaving a reader to assume it is a weaker alternative to the realpath form. scripts/lint-docs-required.cjs now passes (ok_docs_updated) — it could not evaluate against the mandated pr:0 placeholder. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6 lines
1.4 KiB
Markdown
6 lines
1.4 KiB
Markdown
---
|
|
type: Changed
|
|
pr: 4672
|
|
---
|
|
**The path-containment predicate is now a single exported seam** — `security.cjs` no longer exports `validatePath`. Containment is decided in exactly one place and resolved two ways: `assertWithinRoot` (throws) and `tryWithinRoot` (returns null) resolve symlinks, while `assertWithinRootLexical` and `tryWithinRootLexical` use string resolution alone and never touch the filesystem, for the few callers that must preserve a symlink rather than resolve it or that validate a destination before it exists. `requireSafePath` is preserved as an alias of the throwing form. All of them return a branded `ContainedPath` so a validated path cannot be silently swapped for an unvalidated one. The per-call-site `{ allowAbsolute: true }` flag is replaced by the named `PathAcceptance` policy, which states what it actually permits: an absolute path outside the root was always rejected and still is. The traversal rejection text `Path escapes allowed directory: <resolved> is outside <base>` is preserved verbatim, and no command changes what it accepts or rejects. Three rejection MESSAGES are reworded, none of which now reveals a host path it previously hid: `state.cts`'s `<label> path rejected: …` becomes `<label> path validation failed: …`, and the sub-repo and agent-skills warnings name the condition instead of echoing the predicate's error string. (#4653)
|