Skip to content

feat(kernel-platforms): fs paths become segment arrays - #1057

Draft
ci-belphegor wants to merge 1 commit into
grypez/narrowing-9b-fs-exofrom
grypez/narrowing-9c-fs-segments
Draft

ci-belphegor wants to merge 1 commit into
grypez/narrowing-9b-fs-exofrom
grypez/narrowing-9c-fs-segments

Conversation

@ci-belphegor

Copy link
Copy Markdown

Explanation

fs methods now take absolute segment arraysreadFile(['srv', 'data', 'x'], 'utf8') — guarded by M.arrayOf(M.string()). rootDir: string becomes root: string[], makeRootCaveat becomes an element-wise segment prefix check, and makeNoSymlinksCaveat joins segments back into a path for lstatSync.

Absolute rather than root-relative, because re-basing a narrowed holder's coordinates means rewriting arguments, and a rewritten argument is not an unaltered forward. The accepted price is that a narrowed holder sees prefix segments it has no authority over.

The prefix check is now element-wise and platform-agnostic, so it moved to shared.ts and no longer needs node:path. ['srv','data'] cannot admit ['srv','database','x'] — the string-prefix false positive the old relative()-based check had to defend against is structurally impossible.

This is the subtle PR of the chain, which is why it lands last: a focused diff against an already-exo-shaped file.

Notes for reviewers

"With segments there is nothing to normalize" is false, and this PR is where that was found. A guard can only require strings, so ['srv', 'data/../../etc'] passes M.arrayOf(M.string()) intact, satisfies a ['srv'] prefix check, and then resolves to /etc:

path.posix.resolve('/', 'srv', 'data/../../etc', 'passwd')  →  /etc/passwd

No pattern can catch this — @endo/patterns has no regex, glob, or prefix matcher — so it has to be a caveat. assertPlainSegments rejects ., .., empty, and any segment containing / or \ (both separators regardless of platform, so a POSIX-authored config cannot be exploited on Windows). It runs inside makeCaveatedFsOperation, one chokepoint every platform shares, before the platform caveat and before segments are joined. Rejecting these is what makes a prefix check sufficient.

toPath uses resolve(sep, ...segments) rather than '/' + segments.join('/'), because the design makes a platform prefix a leading segment and only resolve gets ['C:','srv']C:\srv right. That does not reintroduce a normalization concern: assertPlainSegments has already rejected everything resolve could collapse, so it runs on input that provably needs none.

The caveat chain is reordered to withinRootnoSymlinks so validation precedes the lstatSync. No test depended on the old order — the two pre-existing error cases were mutually exclusive on exactly that axis.

Carries the chain's single **BREAKING:** changelog entry, covering 9a + 9b + 9c: sync operation and existsSync gone, fs vended as an exo with a flat method namespace, config flattened to methods with rootDir becoming segment root, paths becoming segment arrays, and the Passable-only second-argument tightening.

Stack

PR 11 of 12. Base: #1056.

Methods take absolute segment arrays, guarded as `M.arrayOf(M.string())`,
and `rootDir` becomes a segment `root` checked as a prefix. `makeRootCaveat`
moves to the shared module now that it needs no path APIs.

A guard can only require strings, so `['srv', 'data/../../etc']` would arrive
intact and resolve to `/etc`. Segments carrying a separator, a traversal, or
nothing at all are therefore rejected before the prefix check, which is what
makes that check sufficient. An empty `root` is a config error.

Carries the one `**BREAKING:**` changelog entry for the whole 9a/9b/9c shape
change, since the chain squashes to a single commit on main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ci-belphegor
ci-belphegor force-pushed the grypez/narrowing-9c-fs-segments branch from c034a5d to 3e6a90d Compare September 11, 2026 11:42
@ci-belphegor
ci-belphegor added this pull request to stack #1060 September 11, 2026 11:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant