feat(py): the macOS seatbelt sandbox the worker engages on itself - #355
Draft
jat255 wants to merge 1 commit into
Draft
feat(py): the macOS seatbelt sandbox the worker engages on itself#355jat255 wants to merge 1 commit into
jat255 wants to merge 1 commit into
Conversation
Seatbelt's entry points are plain libSystem symbols, so ctypes reaches them without a compiled extension. The profile follows the same rules and the same order as the Darwin branch of pkg-r/src/sandbox.c: a permissive default, then each filesystem deny immediately followed by its allowlist, since later rules win in SBPL. Roots are arguments rather than computed here. The worker entry point is where the scratch directory and the parent's tmpdir are known, so the root set is assembled there when it lands. Both the original and the symlink-free form of every root are granted, because the sandbox matches resolved paths and macOS /tmp and /var live under /private. A root that is not absolute is rejected, which R does not check: realpath would otherwise resolve it against the worker's cwd and grant some other directory. sandbox_capabilities() now reports a real seatbelt value, looked up as a symbol rather than inferred from the platform. protection_mode() therefore returns "sandbox" on macOS instead of raising; nothing calls it from Commons.__init__ yet, so no construction path changes.
jat255
force-pushed
the
jat255/tpw5-macos-seatbelt-sandbox
branch
from
September 11, 2026 19:47
6f7945c to
456c329
Compare
jat255
added this pull request to stack #364
September 11, 2026 19:48
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.
This PR adds the macOS seatbelt sandbox the execution worker engages on itself, reached through
ctypesso it needs no compiled extension, and it fills in theseatbeltfield of the capability probe from #352.Stacked on #352, so review that one first. Roots are arguments rather than computed here: the worker entry point is where the scratch directory and the parent's tmpdir are known, so it assembles the root set when it lands. Tracked as kata
tpw5.Agent-written detail
The profile follows the rules and the order of the Darwin branch of
pkg-r/src/sandbox.c: a permissive default, then each filesystem deny immediately followed by its allowlist, because later rules win in SBPL. Write roots are granted for reading too,/dev/nullstays a writable literal, andfile-read-metadatastays allowed so path traversal keeps working.Two things differ from the R implementation. A root that is not absolute is rejected, which R does not check, because
realpathwould otherwise resolve it against the worker's cwd and grant some unrelated directory. And the read allow is left out entirely when there is nothing to grant, rather than emitted with no path predicate; an unscoped allow sitting after the deny would hand back every readable file on the host. The local review caught that as a real hole, and there is now a live test that fails without the fix.protection_mode()starts returning"sandbox"on macOS instead of raising. That is the intended direction, and nothing calls it fromCommons.__init__yet (task 12, kata2q9b), so no construction path changes here. The probe looks the symbol up rather than trusting the platform name, so a macOS that finally drops these entry points (deprecated since 10.8) reports no seatbelt instead of promising one the worker cannot engage.No
tests/shared/fixture for this: R builds its profile insidesandbox.cwith nothing reachable from R's test suite, so a shared fixture could not be executed by both suites. The observable behaviour is pinned per language against the same C source of truth.The two tests for the platform check call
_seatbelt_present()rather thansandbox_capabilities(). Claiming to be Linux means patching the sharedplatformmodule, so going through the aggregate would send the seccomp and Landlock probes down their Linux paths on a host that has neither interface.Testing. Profile generation is pure, so its rules run on any host. The live path is exercised for real on macOS and skipped elsewhere: a subprocess engages the seatbelt and then proves a write inside its root succeeds, a write outside raises, a read outside the read roots is denied,
/dev/nullstays writable, and the network rule blocks a connection to a listener in the test process, which keeps the test off the internet. The subprocess imports the module by directory rather than throughcommons, which is how the worker reaches it and proves it needs nothing from the package.ruff check,pyrefly check src tests, and the fullpytestrun are clean; pyrefly's six remaining errors are missing optional OpenTelemetry imports that predate this branch.