Skip to content

policy: nothing is granted below a file (#686) - #704

Merged
vyskocilm merged 2 commits into
mainfrom
fix/686-file-cover
Oct 26, 2026
Merged

vyskocilm merged 2 commits into
mainfrom
fix/686-file-cover

Conversation

@vyskocilm

Copy link
Copy Markdown
Contributor

Fixes #686.

walkLinks reported walkLanded for path below a file. Kernel answers ENOTDIR inside. Effect: no ← not granted on --dry-run, sanitise kept element, coupling accepted value.

Change:

  • walkLinks: below a file ends walkNowhere (GrantsGuestPath false, keepHostElement DropNoGrant, Shadow NotSlot). Three arms: KindData with path left; host ENOTDIR under a bind (only at a bind root that is a file, a mountpoint); host non-dir with path left.
  • Exceptions stay landed/slot, file replaceable by a directory: file inside rw or host-aliased directory bind; AccessRW KindData (bwrap --file, plain file, not mountpoint) on writable ground. The last was NotSlot before this branch too.
  • Coupling: file grant covers own path only. Bind of host file still covers downward: coupling is text-only, file-vs-dir is host state; screen mark + sanitise handle it.
  • fakeEnv.Lstat answers ENOTDIR below fixture file, like lstat(2).

Measured, bwrap, tmpfs parent: --file then rm && mkdir -> REPLACED; --ro-bind-data -> Device or resource busy; rw bind of a file -> rm/mv Device or resource busy, mkdir below Not a directory.

Issue repro, old vs new build:

  • file at /srv/cfile, KUBECONFIG=/srv/cfile/sub/x: rc=0 -> rc=77 "which it does not grant"
  • ro = ["/etc/hostname:/srv/hn"], KUBECONFIG=/srv/hn/sub/x: no mark -> ← not granted
  • host PATH=/opt/tool/bin:..., file bind at /opt/tool, sanitise PATH: kept -> dropped "nothing grants that path: /opt/tool/bin"

Tests: TestNothingIsGrantedBelowAFile, TestAFileInsideAWritableDirectoryStillAnswersBelowIt, TestAWritableGeneratedFileOnWritableGroundIsASlotBelowIt, TestCouplingFileGrantCoversOnlyItsOwnPath; each made to fail against code with its arm reverted. TestEarlyLandingNeverMatchesAnExactPath: file case moved to rw bind, generated-file case dropped (now walkNowhere).

Redteam, two rounds before PR. Round 1 stopped early, unmeasured; its lead gave the --file fix. Round 2, live sandbox: no mismatch in the dangerous direction (ro bind + rw alias at other guest path, profile file in tmpfs, host links to a file — each writable case marked and SHADOW-RAN; each unmarked case ENOTDIR inside). Safe-direction over-mark noted: rw bind of the file itself marked writable, cannot be taken over (EROFS/ENOTDIR).

make gate green. make integration / make verify not run (policy-pure change).

Book facts: #700 comment §6.

🤖 Generated with Claude Code

vyskocilm and others added 2 commits October 26, 2026 18:54
walkLinks reported walkLanded for a path below a generated file and below a
bind whose host side is a file, so GrantsGuestPath printed no `← not granted`
mark and sanitise kept the element, while the kernel answers ENOTDIR inside.
Those arms now end walkNowhere. A file inside a writable (or aliased)
directory bind still lands: the payload can rm it and mkdir its own.

Coupling: a `file` grant covers only its own path. A bind of a host file
still covers downward there, because coupling is computed over profile text
and file-or-directory is host state; --dry-run marks the value and sanitise
drops it instead.

fakeEnv.Lstat answers ENOTDIR below a fixture file, as lstat(2) does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AccessRW KindData is bwrap's --file: a plain file written into its parent,
not a mountpoint. On a writable tmpfs or rw bind the payload can rm it and
mkdir its own directory (measured: --file REPLACED, --ro-bind-data EBUSY),
so a path below it lands replaceable instead of walkNowhere. The old walk
called it NotSlot too; the #686 change made that arm a stated claim, so it
now has to be true.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@vyskocilm
vyskocilm merged commit c9f9480 into main Oct 26, 2026
5 checks passed
@vyskocilm
vyskocilm deleted the fix/686-file-cover branch October 26, 2026 18:45
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.

env coupling and not-granted mark count a path below a single-file grant as granted

1 participant