Repository navigation
Read a caller's path as a path where it exists, and let a caller mark one - #608
Closed
ronleizrowice-ant wants to merge 4 commits into
Closed
ronleizrowice-ant wants to merge 4 commits into
ronleizrowice-ant wants to merge 4 commits into
Conversation
… one
Whether an entry of denyRead, allowRead, allowWrite or denyWrite is a
pattern is decided by looking for `*`, `?`, `[` and `]` in what the
caller wrote. A directory may have those characters in its name, and a
caller that lists absolute paths inside `[WIP] project` had every one of
them read as a pattern: on Linux a write entry was dropped and a read
entry expanded to nothing; on macOS the rule compiled to a regex that
covers `W project` and not the folder. The deny was lost on both.
An entry with those characters keeps the pattern reading it always had,
and is now also read as what it names wherever the part of it that
holds the characters exists on disk: as the path it spells when all of
it does, and as the rest of the pattern beneath the folder when a
pattern follows, the folder taken as the name it is. A deny takes every
reading, and counts a name as there when anything has it, a symbolic
link included, or when it cannot be looked at. An allow takes a reading
only for a path that is there as itself: no component from the first
with such a character on may be a symbolic link.
Each of the four lists also takes `{ path, literal: true }`, a path and
never a pattern whether or not it exists.
The decision is made in one new module, path-entries.ts, once per entry
per wrap, and carried with the entry: the restriction configs hold the
names in literalDenyOnly, literalAllowWithinDeny, literalAllowOnly and
literalDenyWithinAllow, the Linux wrapper takes every path it is handed
for a name, and the macOS builder emits a literal filter beside the
regex and escapes the anchor of a pattern beneath a named folder. A lint
rule keeps the sniffing form of normalizePathForSandbox out of the two
Linux files. An entry with no such folder on disk gives what it gave.
How an entry is read, for every shape of spelling, each glob character, a link or a dangling link in the prefix and an unsearchable parent. By real bubblewrap runs inside `[WIP] project`: the project writable when allowed, a denyWrite enforced with the file on the host unchanged, a denyRead file and directory unreadable, a carve-out working, a recursive deny masking `.env` at two depths. An allow never follows a planted link. The macOS profile for the same, with the anchor escaped. Both readings at once. The marker through the schema, the getters, updateConfig, a per-command config in all four lists and the violation monitor. And that entries with no such folder on disk give configurations, bubblewrap arguments and a macOS profile identical to what they gave before.
This was referenced Sep 25, 2026
Closed
Comments only: the JavaScript emitted with comments removed is byte-identical for every file, and the test names are unchanged. Kept: every invariant and what could be done without it, the documentation of exported names, and the reasons for what is not done the obvious way. Cut: how the code came to be as it is, narration of the lines that follow, and explanations repeated at several sites.
This was referenced Sep 28, 2026
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.
Superseded by three smaller stacked pull requests, to be read in this order: #620 (the
anchoroption on the glob walk), #621 (the{ path, literal: true }marker), #622 (an entry is also read as the path it spells). Together they hold this change plus fixes:<bracketed>/**on Windows, tests for each{ literal: true }argument, corrected testing claims, and two more known limits in the README.A path inside a folder whose name holds
[,],*or?was read as a pattern, so the caller's deny or allow was lost. An entry is now also read as the path it spells where that exists, and a caller can mark one as a path outright.What changes
denyRead,allowRead,allowWriteanddenyWritetake{ "path": "...", "literal": true }: never a pattern, existing or not.FilesystemPathEntry; the restriction configs gain optionalliteralDenyOnly,literalAllowWithinDeny,literalAllowOnly,literalDenyWithinAllow.Who sees a difference
FilesystemPathEntry[], so reading an entry as a string stops compiling. Silently: pass the restriction configs on whole; an emptydenyOnlyno longer means nothing is read-denied.main.Known limits
initialize()).credentials.filestakes no marker. More in the README.Merge order
Late: conflicts with #569, #574, #575, #595, #607. A merge will not flag that readers of
allowOnlyadded by #569, #594, #595 must usewriteRootsOf(), nor that #607 givesexpandReadDenyGlobLinuxa different fourth parameter.Testing
New
test/sandbox/literal-path-entries.test.ts; its real bubblewrap runs fail onmain.sandbox-execand Windows cases run in CI only.