Skip to content

feat(sandbox): unix_socket_connects primitive for AF_UNIX socket grants (SEA-636)#620

Closed
mattwilkinsonn wants to merge 4 commits into
mainfrom
sea-636-unix-socket-connects
Closed

feat(sandbox): unix_socket_connects primitive for AF_UNIX socket grants (SEA-636)#620
mattwilkinsonn wants to merge 4 commits into
mainfrom
sea-636-unix-socket-connects

Conversation

@mattwilkinsonn

@mattwilkinsonn mattwilkinsonn commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Pull request

Summary

Adds unix_socket_connects as a first-class primitive on BundleSpec and GrantToolEntry, allowing curated bundles to declare AF_UNIX socket paths their CLI needs to connect(2) to. On macOS, the compile pipeline emits (allow network-outbound (literal "...")) SBPL rules with literal+canonical dual-emission to handle /tmp/private/tmp firmlink resolution. On Linux, each entry emits a --bind-try <path> <path> so the host-side socket inode is reachable inside the bwrap mount namespace.

Related issues

Refs SEA-636

Changes

  • Added unix_socket_connects: &'static [&'static str] to BundleSpec (currently &[] for all existing bundles) and unix_socket_connects: Vec<String> to GrantToolEntry, with skip_serializing_if = "Vec::is_empty" so existing grant hashes don't churn.
  • Added unix_socket_connects: Vec<PathBuf> to KernelParams with the same elision policy.
  • <USER> template substitution in socket paths is resolved at compile() time from the daemon's captured $USER (threaded in as user_env: Option<&str>) so the grant hash stays per-user-agnostic while the compiled kernel params are user-specific.
  • dual_emit_socket_connect canonicalizes the parent directory when the socket file itself is absent (daemon stopped or lazy-created), recovering the /private/tmp/... firmlink form for the SBPL profile even when the socket inode doesn't exist yet.
  • macOS compile_profile emits one (allow network-outbound (literal ...)) rule per resolved socket entry, gated on a non-empty list.
  • Linux compile_bwrap_args emits one --bind-try <path> <path> per entry; collection in apply_bundles is gated on Platform::Macos since the only current consumer (yabai) is macOS-only, keeping the Linux profile clean.
  • Added (allow file-read* (literal "/private")) to the macOS SBPL baseline so realpath/canonicalize calls that lstat("/private") during firmlink traversal don't hit EPERM.
  • sandbox_exec_can_nest() probe added to macOS integration tests so they skip gracefully when running inside an outer sandbox-exec wrapper (e.g. cargo nextest from a seal session) rather than failing with EPERM.
  • Approval TUI display now surfaces unix_socket_connects entries to the reviewer.

Test plan

  • expand_user_placeholder_* unit tests cover passthrough, substitution, missing $USER, path-unsafe values (traversal, empty, NUL, separators), and multiple-occurrence replacement.
  • compile_resolves_and_dual_emits_socket_connects verifies the full compile()expand_unix_socket_connectsKernelParams wiring with a stub that firmlink-maps /tmp/.../private/tmp/....
  • compile_dual_emits_socket_connect_when_socket_file_absent drives the parent-directory fallback branch where the socket file doesn't exist at compile time.
  • compile_socket_connects_track_user_env_not_process_env confirms that two compiles differing only in user_env produce different socket paths and different kernel-params hashes, and that None drops the templated entry.
  • socket_connects_not_collected_on_linux asserts the Linux compile path produces an empty unix_socket_connects.
  • unix_socket_connects_emits_bind_try, _emits_one_bind_per_entry, _empty_*, and _bind_emitted_before_tmpdir cover the Linux bwrap argv shape.
  • unix_socket_connects_emits_network_outbound_literal, _emits_multiple_literals_in_order, _empty_*, and _path_escapes_quotes_and_backslashes cover the macOS SBPL emission.
  • unix_socket_connects_round_trip_and_hash covers KernelParams serialization and hash sensitivity.
  • sandbox_unix_socket_connects_canonical_form_round_trips_and_elides covers GrantToolEntry canonical-form round-trip and elision.
  • unix_socket_connect_succeeds_inside_namespace (bwrap integration): starts a host AF_UNIX listener in a directory outside all fixture binds, sets unix_socket_connects, runs an in-sandbox nc -U / python3 connect, and asserts the marker round-trips to the host listener.
  • unix_socket_connect_fails_inside_namespace_without_bind (negative): same setup without the grant, asserts the in-sandbox connect fails with ENOENT/ECONNREFUSED.

Notes for reviewers

Socket-connect collection in apply_bundles is explicitly gated on Platform::Macos — a deliberate scope cut while yabai is the only declared consumer. A future Linux-daemon bundle (tmux socket, i3-msg, etc.) would lift that guard intentionally; the --bind-try primitive itself is already exercised by the integration tests. The <USER> safety validator rejects empty strings, dot-segments (./..), path separators, backslashes, and NUL bytes to prevent a malformed $USER from escaping the intended /tmp/<daemon>_<USER>.socket shape and widening the connect grant.

Co-Authored-By: seal noreply@sealedsecurity.com
Co-Authored-By: seal noreply@sealedsecurity.com

@linear-code

linear-code Bot commented Jun 20, 2026

Copy link
Copy Markdown

SEA-636

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds unix_socket_connects to BundleSpec and KernelParams for AF_UNIX socket connect permissions. The compile pipeline collects socket-connect paths from bundle grants, expands <USER> placeholders using deterministic shell-env snapshots, and dual-emits literal and canonicalized paths. Linux sandboxes emit --bind-try bwrap arguments; macOS sandboxes emit SBPL network-outbound rules and extend baseline permissions to /private. Corresponding grant audit-hash integration, unit tests, and end-to-end integration tests are included.

Changes

AF_UNIX socket connect surface (SEA-636)

Layer / File(s) Summary
BundleSpec, KernelParams, and GrantToolEntry field definitions
CHANGELOG.md, crates/seal-policy/src/manifest/sandbox.rs, crates/seal-policy/src/grant.rs, crates/seal-sandbox/src/kernel_params.rs
BundleSpec gains unix_socket_connects: &'static [&'static str] with platform-specific documentation; all 16 curated bundle entries are initialized to &[]; GrantToolEntry gains unix_socket_connects: Vec<String> with skip-when-empty serde; KernelParams gains unix_socket_connects: Vec<PathBuf> with serde skip-when-empty to preserve audit-hash stability; changelog entry added.
GrantToolEntry audit-hash integration
crates/seal-policy/src/grant.rs, crates/seal-tui/src/approval.rs
from_manifest_entry resolves socket-connect templates from the curated bundle verbatim into the GrantToolEntry field so canonical JSON changes with the allowlist; doc comments update canonical-field ordering; describe_tool_entry surfaces the field in human-readable output; TUI approval test fixture updated with concrete socket path and display assertion; round-trip test validates serialization and canonical-form behavior.
compile.rs: seed collection, USER expansion, path resolution
crates/seal-sandbox/src/compile.rs
compile() declares new user_env: Option<&str> parameter; apply_bundles accepts a mutable unix_socket_connects accumulator and deduplicates socket-connect tails from active bundles; compile() expands <USER> placeholders (dropping entries when $USER is unset), dual-emits literal + canonicalized PathBuf values via host_fs, and wires them into KernelParams; expand_user_placeholder, expand_unix_socket_connects, and dual_emit_socket_connect helpers and unit tests added.
Linux bwrap --bind-try emission and unit tests
crates/seal-sandbox/src/linux.rs, crates/seal-runtime/src/scope/sandbox_spawn/linux.rs
compile_bwrap_args emits --bind-try <path> <path> triples before $TMPDIR bind; unit tests cover single, multi, empty, and ordering semantics; test fixtures across seal-sandbox and seal-runtime modules updated.
macOS SBPL emission, /private baseline, and unit tests
crates/seal-sandbox/src/macos.rs, crates/seal-runtime/src/scope/sandbox_spawn/macos.rs
BASELINE_SBPL gains file-read* for literal /private; compile_profile conditionally emits ; AF_UNIX socket connects + (allow network-outbound (literal ...)) per path; unit tests cover single, multi, empty, and SBPL escaping; test fixture updated.
macOS sandbox_exec nesting probe and integration test gating
crates/seal-sandbox/tests/sandbox_exec_integration.rs
sandbox_exec_can_nest() probing helper detects whether host sandbox-exec supports nesting by executing a trivial nested profile; all 9 sandbox_exec integration tests replace binary-presence early-exit checks with sandbox_exec_can_nest() skip gates and updated messaging; test fixture updated.
Runtime user_env capture and threading
crates/seal-runtime/src/scope/tool_scope.rs
ToolScope::build_sandboxed_spawn captures $USER from the daemon-wide shell-env snapshot and threads it into seal_sandbox::compile via user_env.as_deref() to keep <USER> substitution consistent with the shell-env snapshot.
KernelParams serialization round-trip and hash validation
crates/seal-sandbox/src/kernel_params.rs, crates/seal-runtime/tests/bwrap_dispatcher_integration.rs
Test fixtures updated to initialize unix_socket_connects; new unix_socket_connects_round_trip_and_hash test verifies field serialization under correct JSON key name, serde deserialization preservation of PathBuf values, and hash changes with non-empty socket-connect targets.
Linux bwrap end-to-end integration tests
crates/seal-sandbox/tests/bwrap_integration.rs
Test fixture updated; two new SEA-636 tests added: unix_socket_connect_succeeds_inside_namespace creates a host AF_UNIX listener outside fixture binds, grants it via unix_socket_connects, and verifies in-sandbox connect succeeds; unix_socket_connect_fails_inside_namespace_without_bind creates a listener outside binds with empty unix_socket_connects and verifies connect fails.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 Hoppity-hop through the socket lane,
A <USER> gets swapped, and paths come plain.
On Linux we bind-try, on macOS we allow,
The firmlink /private gets a read-rule now.
Two tests—one connects, one fails with grace—
AF_UNIX finds its proper sandboxed place! 🔌

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and specifically describes the main change: introducing the unix_socket_connects primitive for AF_UNIX socket grants (SEA-636), which aligns with the comprehensive changeset across policy, sandbox, runtime, and TUI components.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description is directly related to the changeset and comprehensively documents the unix_socket_connects feature additions across multiple crates and platforms.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sea-636-unix-socket-connects

Comment @coderabbitai help to get the list of available commands.

mattwilkinsonn commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • Merge Queue - adds this PR to the back of the merge queue
  • Merge Queue Fast Track - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has required the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@greptile-apps

greptile-apps Bot commented Jun 20, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds unix_socket_connects as a first-class primitive to BundleSpec and GrantToolEntry, allowing curated bundles to declare AF_UNIX socket paths their CLI needs to connect(2). On macOS, the compile pipeline emits (allow network-outbound (literal "...")) SBPL rules with dual-emission for /tmp/private/tmp firmlink resolution; on Linux, each entry emits --bind-try <path> <path> so the host-side socket inode is reachable inside the bwrap namespace.

  • <USER> template substitution in socket paths is resolved at compile() time from the daemon's captured $USER (threaded in as user_env: Option<&str>), keeping the grant hash per-user-agnostic while producing user-specific kernel params.
  • The BASELINE_SBPL gains (allow file-read* (literal \"/private\")) so lstat(\"/private\") succeeds during firmlink traversal inside any macOS sandbox, fixing a latent EPERM for any sandbox process that calls realpath on /tmp/... paths.
  • The sandbox_exec_can_nest() probe gracefully skips macOS integration tests when run inside an outer sandbox-exec wrapper.

Confidence Score: 5/5

Safe to merge — the change is well-scoped, all new code paths are covered by unit and integration tests, and the security-sensitive USER validator correctly guards against path traversal.

The unix_socket_connects primitive is threaded cleanly through the stack with no regressions to existing hash stability or sandbox profiles. The BASELINE_SBPL addition is a narrow, justified change fixing a latent EPERM. The NOCONNECTTOOL listener-thread deadlock flagged in a prior review has been resolved. No correctness or security issues were identified.

No files require special attention.

Important Files Changed

Filename Overview
crates/seal-sandbox/src/compile.rs Adds user_env param to compile(), expand_user_placeholder (with path-traversal guards), expand_unix_socket_connects, dual_emit_socket_connect (parent-dir fallback for absent sockets), and collection in apply_bundles (gated on Platform::Macos). Logic is correct and extensively tested.
crates/seal-sandbox/src/macos.rs Adds (allow network-outbound (literal ...)) emission per unix_socket_connects entry. Also adds (allow file-read* (literal "/private")) to BASELINE_SBPL to unblock lstat("/private") during firmlink traversal inside any macOS sandbox.
crates/seal-sandbox/src/linux.rs Emits --bind-try per unix_socket_connects entry, inserted after fs_write binds and before the tmpdir bind.
crates/seal-sandbox/src/kernel_params.rs Adds unix_socket_connects: Vec with skip_serializing_if so the field elides from the audit hash when empty, preserving existing grant hashes.
crates/seal-policy/src/grant.rs Adds unix_socket_connects: Vec to GrantToolEntry, copies verbatim from BundleSpec in from_manifest_entry, and extends describe_tool_entry for TUI display.
crates/seal-sandbox/tests/bwrap_integration.rs Adds positive and negative integration tests. The NOCONNECTTOOL skip path now correctly wakes the blocked listener thread via a host-side connect before joining.
crates/seal-sandbox/tests/sandbox_exec_integration.rs Adds sandbox_exec_can_nest() probe used by all macOS integration tests to skip gracefully when run inside an outer sandbox-exec wrapper.
crates/seal-policy/src/manifest/sandbox.rs Adds unix_socket_connects: &'static [&'static str] to BundleSpec, initialized to &[] for all 20+ existing bundles with no hash churn.
crates/seal-runtime/src/scope/tool_scope.rs Threads $USER from the daemon's captured shell-env snapshot into compile() as the new user_env argument.

Reviews (11): Last reviewed commit: "test(seal-sandbox): fix macOS flake in s..." | Re-trigger Greptile

Comment thread crates/seal-sandbox/tests/bwrap_integration.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/seal-policy/src/grant.rs`:
- Around line 768-777: The `unix_socket_connects` field is now part of the grant
policy and requires test coverage for the `from_manifest_entry` behavior. Add a
unit test that verifies two behaviors: first, that non-empty values from the
manifest are correctly copied into the `GrantToolEntry.unix_socket_connects`
field, and second, that when `unix_socket_connects` is an empty list, it is
properly elided from the canonical serialization output. This test should ensure
the field integration with the manifest entry parsing logic is robust and
maintains consistency with the grant policy shape.

In `@crates/seal-sandbox/src/compile.rs`:
- Around line 326-336: The expand_unix_socket_connects() function reads the USER
environment variable directly, which means identical compile() arguments can
produce different socket paths depending on the caller's environment. This
violates the replayable-input contract for this hash-bearing function. To fix
this, add USER as an explicit parameter to the compile() function signature,
thread it through to expand_unix_socket_connects() instead of having that
function read from the process environment, and ensure the USER value is
captured in the audit/replay inputs so the hash-bearing behavior remains
deterministic across different invocations with different USER values.
- Around line 7731-7770: Add a new integration test after the existing
expand_user_placeholder tests that creates a GrantToolEntry with
unix_socket_connects field containing a path with the <USER> placeholder, sets
up the USER environment variable and a /tmp to /private/tmp stub mapping, then
calls the compile() function and asserts that the resulting
KernelParams.unix_socket_connects contains both the resolved /tmp and
/private/tmp canonical forms. This will catch regressions in apply_bundles(),
compile() wiring, or the canonical dual-emission logic for the
unix_socket_connects field that the current unit tests of
expand_user_placeholder() alone would not detect.
- Around line 1364-1373: The issue is that dual_emit() fails to emit both the
literal and canonical forms of socket paths when the socket file does not exist
yet (e.g., during daemon startup or restart on macOS). To fix this, modify the
logic in expand_unix_socket_connects to canonicalize the parent directory of the
socket path instead of the full path, since the parent directory always exists
but the socket file may not. Before calling dual_emit() with the path variable,
extract the parent directory, canonicalize it, then reconstruct the full socket
path with the canonicalized parent and the socket filename. This ensures that on
macOS where /tmp firmlinks to /private/tmp, both forms of the socket path will
be properly emitted even if the socket does not yet exist.

In `@crates/seal-sandbox/src/kernel_params.rs`:
- Line 514: The fixture at line 514 initializes `unix_socket_connects` as an
empty vector, but there is no focused test case (parallel to the existing
`dbus_filter_rules_round_trip_and_hash` test) that verifies non-empty
`unix_socket_connects` values serialize and deserialize correctly and affect the
kernel-params hash. Add a new test function similar in structure to
`dbus_filter_rules_round_trip_and_hash` that validates when
`unix_socket_connects` is populated with actual socket connection paths, the
serialization round-trips correctly and produces a different hash compared to
the default empty configuration.

In `@crates/seal-sandbox/tests/bwrap_integration.rs`:
- Around line 1107-1114: The listener thread is blocked on accept() waiting for
a client connection, but when the skip condition for missing connect tools is
triggered, drop(rx) is called followed by listener_handle.join(). Since no
client will ever connect in this scenario, the join() blocks indefinitely and
hangs the test. To fix this, before calling listener_handle.join() in the skip
branch, you need to unblock the listener thread from its accept() call. This can
be done by closing the listener socket itself before joining, which will cause
the accept() call to return with an error that the listener thread can handle
and exit cleanly. Ensure the listener thread is designed to propagate or handle
this socket closure error gracefully instead of panicking.

In `@crates/seal-tui/src/approval.rs`:
- Line 983: The test fixture for `unix_socket_connects` is initialized with an
empty vector, which means the test does not actually verify that AF_UNIX socket
grants are displayed to reviewers. Replace the empty `Vec::new()` initialization
of `unix_socket_connects` with at least one test value representing a Unix
socket connection, then add an assertion that checks this value appears in the
`view.display` output to ensure the functionality is properly tested.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8b37a55-3795-4598-9bc3-333a2b0511a6

📥 Commits

Reviewing files that changed from the base of the PR and between 68888c7 and db32a59.

⛔ Files ignored due to path filters (5)
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_all_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_none_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_permissive_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_system_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_with_proxy_network.snap is excluded by !**/*.snap
📒 Files selected for processing (13)
  • CHANGELOG.md
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest/sandbox.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/linux.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/macos.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/mod.rs
  • crates/seal-sandbox/src/compile.rs
  • crates/seal-sandbox/src/kernel_params.rs
  • crates/seal-sandbox/src/linux.rs
  • crates/seal-sandbox/src/macos.rs
  • crates/seal-sandbox/tests/bwrap_integration.rs
  • crates/seal-sandbox/tests/sandbox_exec_integration.rs
  • crates/seal-tui/src/approval.rs

Comment thread crates/seal-policy/src/grant.rs
Comment thread crates/seal-sandbox/src/compile.rs Outdated
Comment thread crates/seal-sandbox/src/compile.rs Outdated
Comment thread crates/seal-sandbox/src/compile.rs
Comment thread crates/seal-sandbox/src/kernel_params.rs
Comment thread crates/seal-sandbox/tests/bwrap_integration.rs
Comment thread crates/seal-tui/src/approval.rs Outdated
@mattwilkinsonn mattwilkinsonn changed the base branch from graphite-base/620 to sea-636-clippy-base June 20, 2026 19:52
@mattwilkinsonn mattwilkinsonn changed the base branch from sea-636-clippy-base to graphite-base/620 June 20, 2026 19:52
@mattwilkinsonn mattwilkinsonn force-pushed the sea-636-unix-socket-connects branch from b4b5d3a to f85ae92 Compare June 21, 2026 06:13
@mattwilkinsonn mattwilkinsonn changed the base branch from graphite-base/620 to main June 21, 2026 06:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/seal-sandbox/src/compile.rs`:
- Around line 1349-1352: The rustdoc comment for the helper function in the
compile module contains outdated information about how the user parameter is
sourced. The documentation currently states that the expansion source is read
via seal_utils::env::var, but the implementation now accepts user as a
caller-provided parameter instead. Update the rustdoc comment to accurately
reflect that the user is passed by the caller rather than being read from the
daemon's $USER environment variable through seal_utils::env::var.

In `@crates/seal-sandbox/tests/sandbox_exec_integration.rs`:
- Around line 890-894: The keychain sandbox-exec tests need to be gated with a
nesting probe check to prevent hard-failures when running under an outer
sandbox. Add a guard condition using sandbox_exec_can_nest() at the beginning of
the test functions that start around line 881 and line 924 (the ones checking
binary presence), similar to how other sandbox-exec integration tests are
protected. Apply the same gate to the code at lines 933-937. This will skip
these tests when nesting is not available instead of allowing them to fail with
sandbox_apply EPERM errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 01802dde-85da-457b-83a4-10c59904ff24

📥 Commits

Reviewing files that changed from the base of the PR and between b4b5d3a and f85ae92.

⛔ Files ignored due to path filters (5)
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_all_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_none_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_permissive_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_system_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_with_proxy_network.snap is excluded by !**/*.snap
📒 Files selected for processing (15)
  • CHANGELOG.md
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest/sandbox.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/linux.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/macos.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/mod.rs
  • crates/seal-runtime/src/scope/tool_scope.rs
  • crates/seal-runtime/tests/bwrap_dispatcher_integration.rs
  • crates/seal-sandbox/src/compile.rs
  • crates/seal-sandbox/src/kernel_params.rs
  • crates/seal-sandbox/src/linux.rs
  • crates/seal-sandbox/src/macos.rs
  • crates/seal-sandbox/tests/bwrap_integration.rs
  • crates/seal-sandbox/tests/sandbox_exec_integration.rs
  • crates/seal-tui/src/approval.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/seal-sandbox/src/compile.rs`:
- Around line 1349-1352: The rustdoc comment for the helper function in the
compile module contains outdated information about how the user parameter is
sourced. The documentation currently states that the expansion source is read
via seal_utils::env::var, but the implementation now accepts user as a
caller-provided parameter instead. Update the rustdoc comment to accurately
reflect that the user is passed by the caller rather than being read from the
daemon's $USER environment variable through seal_utils::env::var.

In `@crates/seal-sandbox/tests/sandbox_exec_integration.rs`:
- Around line 890-894: The keychain sandbox-exec tests need to be gated with a
nesting probe check to prevent hard-failures when running under an outer
sandbox. Add a guard condition using sandbox_exec_can_nest() at the beginning of
the test functions that start around line 881 and line 924 (the ones checking
binary presence), similar to how other sandbox-exec integration tests are
protected. Apply the same gate to the code at lines 933-937. This will skip
these tests when nesting is not available instead of allowing them to fail with
sandbox_apply EPERM errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 01802dde-85da-457b-83a4-10c59904ff24

📥 Commits

Reviewing files that changed from the base of the PR and between b4b5d3a and f85ae92.

⛔ Files ignored due to path filters (5)
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_all_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_none_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_permissive_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_system_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_with_proxy_network.snap is excluded by !**/*.snap
📒 Files selected for processing (15)
  • CHANGELOG.md
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest/sandbox.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/linux.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/macos.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/mod.rs
  • crates/seal-runtime/src/scope/tool_scope.rs
  • crates/seal-runtime/tests/bwrap_dispatcher_integration.rs
  • crates/seal-sandbox/src/compile.rs
  • crates/seal-sandbox/src/kernel_params.rs
  • crates/seal-sandbox/src/linux.rs
  • crates/seal-sandbox/src/macos.rs
  • crates/seal-sandbox/tests/bwrap_integration.rs
  • crates/seal-sandbox/tests/sandbox_exec_integration.rs
  • crates/seal-tui/src/approval.rs
🛑 Comments failed to post (2)
crates/seal-sandbox/src/compile.rs (1)

1349-1352: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the stale $USER source docs.

The helper no longer reads seal_utils::env::var; it uses the caller-provided user, so the rustdoc currently contradicts the implementation.

📝 Proposed doc fix
-/// `$USER`, read via `seal_utils::env::var` so the test harness
-/// can override it through the same scoped-env mechanism the
-/// rest of the compile path uses.
+/// `$USER`, supplied by the caller as `user` so `compile()`
+/// remains pure on its explicit inputs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/seal-sandbox/src/compile.rs` around lines 1349 - 1352, The rustdoc
comment for the helper function in the compile module contains outdated
information about how the user parameter is sourced. The documentation currently
states that the expansion source is read via seal_utils::env::var, but the
implementation now accepts user as a caller-provided parameter instead. Update
the rustdoc comment to accurately reflect that the user is passed by the caller
rather than being read from the daemon's $USER environment variable through
seal_utils::env::var.
crates/seal-sandbox/tests/sandbox_exec_integration.rs (1)

890-894: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply the nesting probe gate to the keychain sandbox-exec tests.

These tests still only check binary presence (functions starting at Line 881 and Line 924) and can hard-fail with sandbox_apply EPERM under an outer sandbox. Gate them with sandbox_exec_can_nest() like the other sandbox-exec integration tests.

Suggested patch
 fn keychain_granted_spawn_can_read_keychains_dir() {
-    let Some(_) = find_sandbox_exec() else {
-        eprintln!("sandbox-exec not present — skipping");
+    if !sandbox_exec_can_nest() {
+        eprintln!(
+            "sandbox-exec can't nest (either missing or running inside an outer \
+             sandbox-exec that EPERMs sandbox_apply) — skipping"
+        );
         return;
-    };
+    }
     let Some(sh) = resolve_on_path("sh") else {
         eprintln!("no sh on PATH — skipping");
         return;
@@
 fn keychain_ungranted_spawn_cannot_read_keychains_dir() {
-    let Some(_) = find_sandbox_exec() else {
-        eprintln!("sandbox-exec not present — skipping");
+    if !sandbox_exec_can_nest() {
+        eprintln!(
+            "sandbox-exec can't nest (either missing or running inside an outer \
+             sandbox-exec that EPERMs sandbox_apply) — skipping"
+        );
         return;
-    };
+    }
     let Some(sh) = resolve_on_path("sh") else {
         eprintln!("no sh on PATH — skipping");
         return;

Also applies to: 933-937

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/seal-sandbox/tests/sandbox_exec_integration.rs` around lines 890 -
894, The keychain sandbox-exec tests need to be gated with a nesting probe check
to prevent hard-failures when running under an outer sandbox. Add a guard
condition using sandbox_exec_can_nest() at the beginning of the test functions
that start around line 881 and line 924 (the ones checking binary presence),
similar to how other sandbox-exec integration tests are protected. Apply the
same gate to the code at lines 933-937. This will skip these tests when nesting
is not available instead of allowing them to fail with sandbox_apply EPERM
errors.

@mattwilkinsonn mattwilkinsonn force-pushed the sea-636-unix-socket-connects branch from f85ae92 to e036454 Compare June 21, 2026 21:35

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/seal-sandbox/src/compile.rs`:
- Around line 1390-1393: The validation check at line 1390 in the `user_env`
function is incomplete and allows dot-segments (`.` and `..`) to pass through,
which could enable path traversal attacks. Extend the existing condition that
checks for `/`, `\\`, and `\0` characters to also reject the strings `.` and
`..` before the substitution occurs in `tail.replace("<USER>", u)`. Add checks
to ensure the user variable `u` is not equal to `.` or `..` as part of the same
validation condition.
- Around line 8093-8100: The stub_canon closure in the test currently
canonicalizes the full socket path directly, which prevents the absent-socket
parent fallback code path from being exercised. Modify the closure to return
None when the path contains a socket file (such as paths ending with .socket),
and only canonicalize the parent directory path `/tmp`. This ensures that when
the closure is invoked with a socket path, it falls back to canonicalizing the
parent directory instead, properly exercising the absent-socket parent fallback
logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: eb707db8-c8a3-4a13-9682-ee679e9f5251

📥 Commits

Reviewing files that changed from the base of the PR and between f85ae92 and e036454.

⛔ Files ignored due to path filters (5)
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_all_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_none_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_permissive_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_system_mask_on.snap is excluded by !**/*.snap
  • crates/seal-sandbox/src/snapshots/seal_sandbox__macos__tests__insta_profile_with_proxy_network.snap is excluded by !**/*.snap
📒 Files selected for processing (15)
  • CHANGELOG.md
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest/sandbox.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/linux.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/macos.rs
  • crates/seal-runtime/src/scope/sandbox_spawn/mod.rs
  • crates/seal-runtime/src/scope/tool_scope.rs
  • crates/seal-runtime/tests/bwrap_dispatcher_integration.rs
  • crates/seal-sandbox/src/compile.rs
  • crates/seal-sandbox/src/kernel_params.rs
  • crates/seal-sandbox/src/linux.rs
  • crates/seal-sandbox/src/macos.rs
  • crates/seal-sandbox/tests/bwrap_integration.rs
  • crates/seal-sandbox/tests/sandbox_exec_integration.rs
  • crates/seal-tui/src/approval.rs

Comment thread crates/seal-sandbox/src/compile.rs Outdated
Comment thread crates/seal-sandbox/src/compile.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/seal-sandbox/src/compile.rs (1)

8295-8302: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not drop unix_socket_connects on Linux.

Line 8300 makes compile intentionally skip socket grants on Linux, but the Linux backend consumes KernelParams.unix_socket_connects to emit --bind-try. With the compile-side macOS gate, Linux AF_UNIX grants never reach bwrap, so the new primitive is ineffective on Linux. Remove the Platform::Macos collection gate and update this test to assert the resolved socket path is present.

Suggested test expectation update
-    fn socket_connects_not_collected_on_linux() {
+    fn socket_connects_collected_on_linux_for_bind_try() {
@@
-        // AF_UNIX socket-connect grants are macOS-only in effect:
-        // the load-bearing emission is the sandbox-exec
-        // `network-outbound` literal rule, and the only bundle that
-        // declares sockets (yabai) is macOS-only. `apply_bundles`
-        // gates the collection on `Platform::Macos`, so the same
-        // grant compiled for Linux yields no socket connects (no
-        // `--bind-try` of a host inode into the namespace).
+        // AF_UNIX socket-connect grants must reach KernelParams on
+        // Linux so the bwrap backend can emit `--bind-try`.
@@
-        assert!(
-            p.unix_socket_connects.is_empty(),
-            "socket connects must not be collected on Linux; got {:?}",
-            p.unix_socket_connects
-        );
+        assert_eq!(
+            p.unix_socket_connects,
+            vec![PathBuf::from("/tmp/wm_alice.socket")],
+            "socket connects must be collected on Linux for bwrap --bind-try"
+        );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/seal-sandbox/src/compile.rs` around lines 8295 - 8302, The test
`socket_connects_not_collected_on_linux` and the corresponding compile-side code
incorrectly gate `unix_socket_connects` collection to macOS only via
`Platform::Macos`, but the Linux backend actually needs these socket connects to
emit `--bind-try` parameters to bwrap. Remove the `Platform::Macos` conditional
gate that prevents socket grant collection on Linux, then update the
`socket_connects_not_collected_on_linux` test to assert that the resolved socket
path is present in the compiled output for Linux instead of asserting that no
socket connects were collected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/seal-sandbox/src/compile.rs`:
- Around line 8295-8302: The test `socket_connects_not_collected_on_linux` and
the corresponding compile-side code incorrectly gate `unix_socket_connects`
collection to macOS only via `Platform::Macos`, but the Linux backend actually
needs these socket connects to emit `--bind-try` parameters to bwrap. Remove the
`Platform::Macos` conditional gate that prevents socket grant collection on
Linux, then update the `socket_connects_not_collected_on_linux` test to assert
that the resolved socket path is present in the compiled output for Linux
instead of asserting that no socket connects were collected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6d858f57-6bfc-4be2-bd5f-c9dbdcf37581

📥 Commits

Reviewing files that changed from the base of the PR and between e036454 and ec47141.

📒 Files selected for processing (2)
  • crates/seal-policy/src/grant.rs
  • crates/seal-sandbox/src/compile.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 22, 2026
@mattwilkinsonn mattwilkinsonn force-pushed the sea-636-unix-socket-connects branch from ec47141 to 1716de4 Compare June 22, 2026 03:29
@graphite-app

graphite-app Bot commented Jun 22, 2026

Copy link
Copy Markdown

Merge activity

  • Jun 22, 3:51 AM UTC: mattwilkinsonn added this pull request to the Graphite merge queue.
  • Jun 22, 3:57 AM UTC: CI is running for this pull request on a draft pull request (#657) due to your merge queue CI optimization settings.
  • Jun 22, 3:58 AM UTC: This pull request has been added to the Graphite merge queue failure handling queue because the batch it was included in failed to pass CI.
  • Jun 22, 3:59 AM UTC: CI is running for this pull request on a draft pull request (#658) due to batch failure handling.
  • Jun 22, 4:02 AM UTC: CI is running for this pull request on a draft pull request (#659) due to batch failure handling.
  • Jun 22, 4:10 AM UTC: This pull request was re-added to the Graphite merge queue after passing CI during failure handling.
  • Jun 22, 4:11 AM UTC: CI is running for this pull request on a draft pull request (#661) due to your merge queue CI optimization settings.
  • Jun 22, 4:15 AM UTC: The Graphite merge queue couldn't merge this PR because it was not satisfying all requirements (Failed CI (buildkite/seal-ci-orchestrator)).
  • Jun 22, 5:32 AM UTC: mattwilkinsonn added this pull request to the Graphite merge queue.
  • Jun 22, 5:38 AM UTC: CI is running for this pull request on a draft pull request (#664) due to your merge queue CI optimization settings.
  • Jun 22, 5:41 AM UTC: The Graphite merge queue couldn't merge this PR because it was not satisfying all requirements (Failed CI (buildkite/seal-ci-orchestrator)).
  • Jun 22, 8:44 PM UTC: mattwilkinsonn added this pull request to the Graphite merge queue.
  • Jun 22, 8:50 PM UTC: CI is running for this pull request on a draft pull request (#669) due to your merge queue CI optimization settings.
  • Jun 22, 8:51 PM UTC: The Graphite merge queue couldn't merge this PR because it was not satisfying all requirements (Failed CI (buildkite/seal-ci-orchestrator)).
  • Jun 22, 8:57 PM UTC: mattwilkinsonn added this pull request to the Graphite merge queue.
  • Jun 22, 8:59 PM UTC: CI is running for this pull request on a draft pull request (#670) due to your merge queue CI optimization settings.
  • Jun 22, 9:02 PM UTC: This pull request has been added to the Graphite merge queue failure handling queue because the batch it was included in failed to pass CI.
  • Jun 22, 9:15 PM UTC: CI is running for this pull request on a draft pull request (#674) due to batch failure handling.
  • Jun 22, 9:19 PM UTC: The Graphite merge queue couldn't merge this PR because it was not satisfying all requirements (Failed CI (buildkite/seal-ci-orchestrator)).
  • Jun 22, 9:19 PM UTC: PR #620 has been removed from the merge queue after undergoing failure handling due to failing CI checks: buildkite/seal-ci-orchestrator. Check for failures in draft pull request (#674).
  • Jun 24, 1:37 AM UTC: mattwilkinsonn added this pull request to the Graphite merge queue.
  • Jun 24, 1:42 AM UTC: The Graphite merge queue couldn't merge this PR because it had merge conflicts.
  • Jun 24, 2:32 AM UTC: mattwilkinsonn added this pull request to the Graphite merge queue.
  • Jun 24, 2:37 AM UTC: CI is running for this pull request on a draft pull request (#688) due to your merge queue CI optimization settings.
  • Jun 24, 2:45 AM UTC: Merged by the Graphite merge queue via draft PR: #688.

@mattwilkinsonn mattwilkinsonn force-pushed the sea-636-unix-socket-connects branch 3 times, most recently from 4ee0460 to 9eb9ac2 Compare June 23, 2026 03:45
…ts (SEA-636)

Adds the unix_socket_connects BundleSpec field + per-platform
emission machinery so a curated bundle can authorize AF_UNIX
connect(2) to a host daemon socket from inside the sandbox.

- macOS: emits (allow network-outbound (literal <path>)) SBPL
  rules in compile_profile, dual-emitted through host_fs
  canonicalize so /tmp <-> /private/tmp firmlink variants both
  land on a matching rule.
- Linux: emits --bind-try <path> <path> in compile_into so the
  host socket inode is bind-mounted into the bwrap namespace.

<USER> placeholder expands against the daemon's $USER at
compile() time. Also lands the macOS SBPL (/private) literal
read allow + sandbox_exec_can_nest() probe so the integration
tests run/skip cleanly in-session.

Co-Authored-By: seal <noreply@sealedsecurity.com>
mattwilkinsonn and others added 3 commits June 23, 2026 21:56
Reject `.` / `..` dot-segments in the `<USER>` placeholder so a
template like `/tmp/<USER>/sock` can't be widened to `/tmp/../sock`.
Add compile-level coverage for the absent-socket parent fallback (the
`None` canonicalize branch) and a grant-level test pinning that
`unix_socket_connects` round-trips in the canonical form when
populated and elides when empty.

Refs SEA-636

Co-Authored-By: seal <noreply@sealedsecurity.com>
`sandboxed_curl_reaches_loopback_proxy_with_bridge_rules` flaked on
the macOS CI runner (`curl-exit=56`, `connect_seen` never set). The
stub proxy's accept loop read the accepted socket once with
`read().unwrap_or(0)` — but on macOS the accepted socket inherits the
listener's non-blocking mode (BSD semantics), so the read can return
`WouldBlock` before curl's CONNECT bytes arrive, miss the request
line, and leave the flag false even though the connect reached the
listener. Force the accepted socket to blocking mode with a bounded
read timeout and loop until the request line arrives.

Refs SEA-636

Co-Authored-By: seal <noreply@sealedsecurity.com>
@mattwilkinsonn mattwilkinsonn force-pushed the sea-636-unix-socket-connects branch from 9eb9ac2 to b90ce29 Compare June 24, 2026 02:16
@graphite-app graphite-app Bot closed this Jun 24, 2026
@graphite-app graphite-app Bot deleted the sea-636-unix-socket-connects branch June 24, 2026 02:45
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 24, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant