Skip to content

feat(seal-policy): add DbusFilterRule typed D-Bus filter rules + dbus_filter field on BundleSpec/GrantToolEntry/KernelParams (SEA-780 slice 1)#511

Open
mattwilkinsonn wants to merge 3 commits into
mainfrom
sea-780-dbus-proxy-slice-1--athena
Open

feat(seal-policy): add DbusFilterRule typed D-Bus filter rules + dbus_filter field on BundleSpec/GrantToolEntry/KernelParams (SEA-780 slice 1)#511
mattwilkinsonn wants to merge 3 commits into
mainfrom
sea-780-dbus-proxy-slice-1--athena

Conversation

@mattwilkinsonn

@mattwilkinsonn mattwilkinsonn commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Pull request

Summary

Introduces typed D-Bus session-bus filter rules (DbusFilterRule, BusName, InterfaceMatch, PathMatch) for the curated bundle catalog, wires them through BundleSpecGrantToolEntryKernelParams as rendered xdg-dbus-proxy CLI flag strings, and adds the compile-time resolver that aggregates, dedupes, and sorts the rules across all bundles applicable to a spawn. This is SEA-780 slice 1 — the typed shape and audit-chain plumbing that later slices build the dispatcher integration and per-bundle defaults on top of.

Related issues

Refs SEA-780, SEA-571, SEA-769

Changes

  • Adds crates/seal-policy/src/manifest/dbus_filter.rs with DbusFilterRule (Talk, See, Call variants), BusName, InterfaceMatch, and PathMatch newtypes. Each has new_unchecked for const-constructible catalog entries and TryFrom<String> / TryFrom<&str> for future user-bundle deserialization. to_flag() renders each rule into its exact xdg-dbus-proxy CLI flag form.
  • Adds dbus_filter: &'static [DbusFilterRule] to BundleSpec (all existing bundles default to &[]).
  • Propagates rendered flag strings into GrantToolEntry::dbus_filter (elided from canonical JSON when empty, so existing grants don't re-sign).
  • Adds dbus_filter_rules: Vec<String> to KernelParams (elided from canonical JSON when empty). Changing the rule set flips the audit hash, identical to the domains / psl_exemptions closure.
  • Adds resolve_dbus_filter_rules in seal-sandbox/src/compile.rs that mirrors apply_bundles's applicability predicate — including the SEA-571/SEA-769 direnv-wrap and gh credential-helper widenings — then dedupes and sorts the collected flag strings.
  • Adds every_bundle_dbus_filter_rule_validates catalog test that walks every BundleSpec::dbus_filter entry and calls rule.validate(), catching typos that new_unchecked skips at construction time.

Test plan

  • New unit tests in dbus_filter.rs cover BusName, InterfaceMatch, and PathMatch validation (well-formed, empty, invalid chars, length cap, leading-hyphen, trailing-slash, empty-segment, round-trip TryFrom), and DbusFilterRule::to_flag rendering for all three variants including wildcard members.
  • New compile-module tests cover: empty rules when no bundle requests D-Bus access; aggregation when a bundle applies; deduplication across two bundles sharing a rule; non-aggregation when the bundle doesn't apply on the spawn; and hash-flip when dbus_filter_rules changes.
  • every_bundle_dbus_filter_rule_validates catalog test guards against malformed rule strings in the static BUNDLES table.
  • All existing test fixtures updated with dbus_filter_rules: Vec::new() to keep the struct exhaustive.

Notes for reviewers

All bundles carry dbus_filter: &[] in this slice — no behavioral change to any existing spawn. The gh_dbus_disabled bypass (SEA-571/SEA-769) is untouched here; slice 4 retires it once the proxy is the only path. The rendered-string-rather-than-typed-enum choice in KernelParams and GrantToolEntry is intentional: it decouples audit-chain stability from future DbusFilterRule variant additions and lets the dispatcher (slice 2) push strings directly onto the proxy argv without re-parsing.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

@linear-code

linear-code Bot commented Jun 11, 2026

Copy link
Copy Markdown

SEA-780

SEA-571

SEA-769

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8628a4ac-5c7b-479f-b2ab-7096197603ba

📥 Commits

Reviewing files that changed from the base of the PR and between b5957be and 7aa0145.

📒 Files selected for processing (14)
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest.rs
  • crates/seal-policy/src/manifest/dbus_filter.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/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

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Sandbox policies now support curated D-Bus session-bus filter rules aggregated from applicable bundles, deduplicated, sorted, and rendered as xdg-dbus-proxy flags; empty lists are omitted so only non-empty filters affect canonical hashes and runtime parameters.
    • Kernel/launch parameters now include an explicit D-Bus filter rule list for deterministic ordering and hashing.
  • Tests

    • Added parsing, validation, flag-rendering, aggregation, deduplication, and hash-regression tests.

Walkthrough

Adds typed D-Bus session-bus filter rules to the manifest, validates them, renders rules to xdg-dbus-proxy flag strings into grants, aggregates sorted/deduped rules into KernelParams during compile, and updates tests/fixtures.

Changes

D-Bus Session-Bus Filtering for Sandbox Bundles

Layer / File(s) Summary
D-Bus filter rule type model and validation
crates/seal-policy/src/manifest/dbus_filter.rs, crates/seal-policy/src/manifest.rs
Adds BusName, InterfaceMatch, PathMatch, DbusFilterRule, validation and error types, to_flag() rendering, unit tests, and re-exports.
Bundle specification with D-Bus filter rules
crates/seal-policy/src/manifest/sandbox.rs
Extends BundleSpec with dbus_filter: &'static [DbusFilterRule], documents aggregation semantics, initializes curated bundles with &[], and adds a validation test iterating bundles' rules.
Grant payload synthesis with D-Bus filters
crates/seal-policy/src/grant.rs
Adds dbus_filter: Vec<String> to GrantToolEntry (serde elided when empty); synthesizes flags from manifest rules via rule.to_flag(), sorts and dedupes, and includes field in grant canonical JSON/hash.
Kernel parameters aggregation and compilation
crates/seal-sandbox/src/kernel_params.rs, crates/seal-sandbox/src/compile.rs
Adds dbus_filter_rules: Vec<String> to KernelParams (serde elision); adds resolve_dbus_filter_rules() to aggregate/dedupe/sort applicable bundles' rendered flags (including indirect direnv/gh cases); wires resolver into compile() and adds tests for empty/aggregated/deduped/skipped/hash-change cases.
Test fixture updates
crates/seal-runtime/src/scope/sandbox_spawn/*.rs, crates/seal-runtime/tests/bwrap_dispatcher_integration.rs, crates/seal-sandbox/src/*.rs, crates/seal-sandbox/tests/*.rs
Updates test helpers/fixtures to initialize KernelParams.dbus_filter_rules: Vec::new() so tests compile and snapshots remain deterministic.
sequenceDiagram
  participant Manifest as manifest::BundleSpec
  participant Validator as DbusFilterRule::validate
  participant GrantSynth as GrantToolEntry::from_manifest_entry
  participant Compile as seal_sandbox::compile
  participant Kernel as KernelParams
  Manifest->>Validator: validate each rule (rule.validate())
  Manifest->>GrantSynth: provide DbusFilterRule items
  GrantSynth->>GrantSynth: rule.to_flag(), sort, dedupe
  GrantSynth-->>Compile: include dbus_filter strings
  Compile->>Compile: resolve_dbus_filter_rules()
  Compile-->>Kernel: set dbus_filter_rules
Loading

🎯 4 (Complex) | ⏱️ ~45 minutes

🐰 The bundles now speak with care,
D-Bus rules float through the air,
Aggregated, sorted, clean,
A tiny proxy rule machine,
Hop, render, guard with flair!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main change: adding typed DbusFilterRule with dbus_filter fields to key structures (BundleSpec/GrantToolEntry/KernelParams) as part of SEA-780 slice 1.
Description check ✅ Passed The PR description is comprehensive and clearly related to the changeset, detailing the motivation, implementation approach, test coverage, and design rationale for the D-Bus filter rules feature.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sea-780-dbus-proxy-slice-1--athena

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

mattwilkinsonn commented Jun 11, 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.

@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-policy/src/grant.rs`:
- Around line 713-725: The dbus_filter Vec built from bundle.dbus_filter
currently preserves catalog order and duplicates, causing different grant hashes
for semantically identical policies; after mapping with rule.to_flag() but
before assigning into the grant payload (the local dbus_filter variable used by
GrantToolEntry), sort_unstable the Vec and then dedup it so the rendered flags
are canonicalized (stable order, no duplicates) to match the downstream
KernelParams normalization.

In `@crates/seal-policy/src/manifest/dbus_filter.rs`:
- Around line 53-80: Update the validation logic to match the xdg-dbus-proxy
NAME/RULE grammar: in validate_bus_name (affecting BusName) allow
segment-leading '-' per the file's documented regex (i.e., accept [A-Za-z_-] as
first char), in validate_interface_match (affecting InterfaceMatch) permit '.'
in names and support the interface wildcard form '.*' while still rejecting
member names that start with a digit (so enforce member-leading char ≠ digit),
and in validate_path_match (affecting PathMatch) prohibit '*' in arbitrary path
segments and only accept the documented subtree wildcard as a trailing '/*'
suffix; keep error returns using DbusFilterRuleError variants already used so
callers (e.g., try_from) continue to get DbusFilterRuleError::Invalid... on
malformed input.
🪄 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: c0b77c7f-dbbd-4d7a-b582-c5dd072826e4

📥 Commits

Reviewing files that changed from the base of the PR and between 57c3358 and 0581535.

📒 Files selected for processing (14)
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest.rs
  • crates/seal-policy/src/manifest/dbus_filter.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/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

Comment thread crates/seal-policy/src/grant.rs
Comment thread crates/seal-policy/src/manifest/dbus_filter.rs
@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown

Greptile Summary

Introduces typed D-Bus session-bus filter rules and threads them through the full audit chain — BundleSpecGrantToolEntryKernelParams — as rendered xdg-dbus-proxy CLI flag strings. This is slice 1 of SEA-780: all existing bundles carry dbus_filter: &[] so there is no behavioral change to any existing spawn.

  • Adds dbus_filter.rs with DbusFilterRule (Talk/See/Call), BusName, InterfaceMatch, and PathMatch newtypes; each has a const-safe new_unchecked constructor for the curated catalog and TryFrom with full validation for future user-bundle deserialization.
  • Wires the rendered flag strings into GrantToolEntry::dbus_filter and KernelParams::dbus_filter_rules (both elide from canonical JSON when empty, keeping existing grant hashes stable); adds resolve_dbus_filter_rules in compile.rs that mirrors apply_bundles's applicability predicate exactly — including the SEA-571/SEA-769 direnv-wrap and gh credential-helper widenings — then deduplicates via HashSet<&str> and sorts for a deterministic canonical form.
  • Adds every_bundle_dbus_filter_rule_validates catalog test as the runtime safety net for new_unchecked call sites, plus five focused compile-module tests covering the empty/aggregate/dedup/non-apply/hash-flip paths.

Confidence Score: 5/5

All existing bundles carry dbus_filter: &[] so there is zero behavioral change to any live spawn; the new plumbing is inert until slice 3 populates real rules.

The typed shape, validators, rendering, aggregation, dedup, sort, and audit-hash integration are all correct and well-tested. The applicability predicate in resolve_dbus_filter_rules is an exact copy of apply_bundles's inner-loop guard, including both widening paths. Serde elision on empty Vecs keeps all existing grant hashes stable.

No files require special attention.

Important Files Changed

Filename Overview
crates/seal-policy/src/manifest/dbus_filter.rs New file: well-structured typed D-Bus filter rules (BusName, InterfaceMatch, PathMatch, DbusFilterRule) with validators, const constructors, TryFrom impls, and thorough test coverage for all validation paths and rendering.
crates/seal-sandbox/src/compile.rs Adds resolve_dbus_filter_rules that correctly mirrors apply_bundles's applicability predicate (including direnv-wrap and gh-credential-helper widenings); uses HashSet<&str> for O(1) cross-bundle dedup then sorts for deterministic canonical hash; tests cover empty, aggregation, dedup, non-apply, and hash-flip scenarios.
crates/seal-sandbox/src/kernel_params.rs Adds dbus_filter_rules: Vec field with serde default + skip_serializing_if = "Vec::is_empty" so existing grants don't re-sign; field correctly participates in the audit hash when non-empty.
crates/seal-policy/src/grant.rs Adds dbus_filter: Vec to GrantToolEntry with matching serde elision; from_bundle renders, sorts (sort_unstable), and deduplicates per-bundle rules correctly.
crates/seal-policy/src/manifest/sandbox.rs Adds dbus_filter: &'static [DbusFilterRule] to BundleSpec (all existing bundles default to &[]); adds every_bundle_dbus_filter_rule_validates catalog test to catch typos in future non-empty entries.

Reviews (5): Last reviewed commit: "docs(seal-policy): address PR #511 round..." | Re-trigger Greptile

Comment thread crates/seal-policy/src/manifest/dbus_filter.rs
Comment thread crates/seal-sandbox/src/compile.rs
Comment thread crates/seal-policy/src/manifest/dbus_filter.rs Outdated
mattwilkinsonn added a commit that referenced this pull request Jun 11, 2026
…ilter validators with xdg-dbus-proxy grammar, canonicalize grant dbus_filter, HashSet dedup (SEA-780)
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 11, 2026
mattwilkinsonn added a commit that referenced this pull request Jun 11, 2026
…ilter validators with xdg-dbus-proxy grammar, canonicalize grant dbus_filter, HashSet dedup (SEA-780)
@mattwilkinsonn mattwilkinsonn force-pushed the sea-780-dbus-proxy-slice-1--athena branch from 288fa51 to 9c3e96c Compare June 11, 2026 15:47

@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-policy/src/manifest/sandbox.rs (1)

3333-3352: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove duplicated doc comment block.

Lines 3333-3352 contain the same doc comment twice. The second copy (starting at line 3343) duplicates the first. Only one copy is needed.

🧹 Suggested fix
-    /// Every entry in a bundle's `psl_exemptions` list must
-    /// match at least one entry in the same bundle's `domains`
-    /// list. An exemption for a pattern the bundle doesn't
-    /// actually use is dead config — and worse, it could mask
-    /// a typo: a future engineer adding `*.foo.bar.com` to
-    /// `domains` (which is a PSL-rejected pattern if
-    /// `bar.com` is a public suffix) might leave the unrelated
-    /// `*.baz.qux.com` in `psl_exemptions` and assume that
-    /// covers the new entry. The 1:1 check forces exemptions
-    /// to be explicitly tied to the domains they unblock.
     /// Every entry in a bundle's `psl_exemptions` list must
     /// match at least one entry in the same bundle's `domains`
     /// list. An exemption for a pattern the bundle doesn't
🤖 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-policy/src/manifest/sandbox.rs` around lines 3333 - 3352, Remove
the duplicated doc comment block describing the psl_exemptions/domains 1:1 check
so only a single copy remains immediately above the item it documents; locate
the repeated paragraph that starts "Every entry in a bundle's `psl_exemptions`
list must match at least one entry in the same bundle's `domains` list" and
delete the second occurrence, preserving the first intact.
🤖 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-policy/src/manifest/sandbox.rs`:
- Around line 3333-3352: Remove the duplicated doc comment block describing the
psl_exemptions/domains 1:1 check so only a single copy remains immediately above
the item it documents; locate the repeated paragraph that starts "Every entry in
a bundle's `psl_exemptions` list must match at least one entry in the same
bundle's `domains` list" and delete the second occurrence, preserving the first
intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 10f7052a-e0ad-4d2d-a959-2a88b6637600

📥 Commits

Reviewing files that changed from the base of the PR and between 288fa51 and 9c3e96c.

📒 Files selected for processing (14)
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest.rs
  • crates/seal-policy/src/manifest/dbus_filter.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/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

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 11, 2026
mattwilkinsonn added a commit that referenced this pull request Jun 11, 2026
…ption doc block (SEA-780)

Co-Authored-By: seal <noreply@sealedsecurity.com>
@graphite-app

graphite-app Bot commented Jun 11, 2026

Copy link
Copy Markdown

Merge activity

mattwilkinsonn and others added 3 commits June 11, 2026 17:41
…KernelParams::dbus_filter_rules — slice 1 of SEA-780 D-Bus / Secret Service proxy support
…ilter validators with xdg-dbus-proxy grammar, canonicalize grant dbus_filter, HashSet dedup (SEA-780)
…ption doc block (SEA-780)

Co-Authored-By: seal <noreply@sealedsecurity.com>
@mattwilkinsonn mattwilkinsonn force-pushed the sea-780-dbus-proxy-slice-1--athena branch from b5957be to 7aa0145 Compare June 12, 2026 00:40

@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: 3

🤖 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 706-726: Extract the sort+dedup normalization of dbus_filter into
a small helper (e.g., fn normalize_dbus_filter(rules: impl
IntoIterator<Item=&Rule>) -> Vec<String>) and update the existing code that
builds dbus_filter to call that helper (use bundle.dbus_filter.iter().map(|r|
r.to_flag()) as input); add a focused unit test for normalize_dbus_filter that
verifies canonicalization (sorted and deduplicated) for a variety of input
orders/duplicates and exercises Rule::to_flag behavior so the canonicalization
contract is pinned before non-empty bundles are used.

In `@crates/seal-sandbox/src/compile.rs`:
- Around line 526-543: resolve_dbus_filter_rules duplicates the "widened
bundle-activation" logic that's implemented in apply_bundles, causing the D-Bus
rule set to drift when that predicate changes; extract the predicate into a
shared helper (e.g., bundle_applies_widened or bundle_matches_with_wrappers) and
update resolve_dbus_filter_rules and apply_bundles to call it instead of
re-implementing logic. The helper should accept the same inputs used here (a
&GrantToolEntry or entry.name, section_wrappers, matched_pattern) plus the
direnv_wrap_active and gh_credential_helper flags and return the boolean;
replace the inline checks (is_direnv_wrap_target /
is_gh_credential_helper_target and bundle_applies call) with a single call to
the new helper so both code paths stay consistent and add the two specific cases
(direnv_wrap_active and gh_credential_helper) inside that helper.
- Around line 1029-1043: The Linux branch currently hardcodes tail =
"~/.cache/sccache" which can miss sccache's actual cache location when
SCCACHE_DIR or XDG_CACHE_HOME is used; update the resolution before calling
bundle_path_to_bind so that for Platform::Linux you first check
std::env::var("SCCACHE_DIR") and use that if present, otherwise use
XDG_CACHE_HOME (or fall back to ~/.cache) and append "sccache" (matching
sccache's ProjectDirs logic), then pass the resulting path to
bundle_path_to_bind and push_unique into fs_write.paths (same call-site where
tail was used), ensuring the resolved path mirrors sccache cache-dir resolution
instead of the hardcoded "~/.cache/sccache".
🪄 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: efa49fab-a2ba-4b4c-9ca4-a9c7c4801473

📥 Commits

Reviewing files that changed from the base of the PR and between b5957be and 7aa0145.

📒 Files selected for processing (14)
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest.rs
  • crates/seal-policy/src/manifest/dbus_filter.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/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

@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: 3

🤖 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 706-726: Extract the sort+dedup normalization of dbus_filter into
a small helper (e.g., fn normalize_dbus_filter(rules: impl
IntoIterator<Item=&Rule>) -> Vec<String>) and update the existing code that
builds dbus_filter to call that helper (use bundle.dbus_filter.iter().map(|r|
r.to_flag()) as input); add a focused unit test for normalize_dbus_filter that
verifies canonicalization (sorted and deduplicated) for a variety of input
orders/duplicates and exercises Rule::to_flag behavior so the canonicalization
contract is pinned before non-empty bundles are used.

In `@crates/seal-sandbox/src/compile.rs`:
- Around line 526-543: resolve_dbus_filter_rules duplicates the "widened
bundle-activation" logic that's implemented in apply_bundles, causing the D-Bus
rule set to drift when that predicate changes; extract the predicate into a
shared helper (e.g., bundle_applies_widened or bundle_matches_with_wrappers) and
update resolve_dbus_filter_rules and apply_bundles to call it instead of
re-implementing logic. The helper should accept the same inputs used here (a
&GrantToolEntry or entry.name, section_wrappers, matched_pattern) plus the
direnv_wrap_active and gh_credential_helper flags and return the boolean;
replace the inline checks (is_direnv_wrap_target /
is_gh_credential_helper_target and bundle_applies call) with a single call to
the new helper so both code paths stay consistent and add the two specific cases
(direnv_wrap_active and gh_credential_helper) inside that helper.
- Around line 1029-1043: The Linux branch currently hardcodes tail =
"~/.cache/sccache" which can miss sccache's actual cache location when
SCCACHE_DIR or XDG_CACHE_HOME is used; update the resolution before calling
bundle_path_to_bind so that for Platform::Linux you first check
std::env::var("SCCACHE_DIR") and use that if present, otherwise use
XDG_CACHE_HOME (or fall back to ~/.cache) and append "sccache" (matching
sccache's ProjectDirs logic), then pass the resulting path to
bundle_path_to_bind and push_unique into fs_write.paths (same call-site where
tail was used), ensuring the resolved path mirrors sccache cache-dir resolution
instead of the hardcoded "~/.cache/sccache".
🪄 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: efa49fab-a2ba-4b4c-9ca4-a9c7c4801473

📥 Commits

Reviewing files that changed from the base of the PR and between b5957be and 7aa0145.

📒 Files selected for processing (14)
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest.rs
  • crates/seal-policy/src/manifest/dbus_filter.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/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
🛑 Comments failed to post (3)
crates/seal-policy/src/grant.rs (1)

706-726: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Extract and pin dbus_filter normalization with a focused unit test.

This path now feeds the signed grant payload, but in this slice it is only exercised through the all-empty catalog. Pulling the sort+dedup into a tiny helper and adding a direct test would lock in the canonicalization contract before slice 3 starts shipping non-empty rules.

🤖 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-policy/src/grant.rs` around lines 706 - 726, Extract the
sort+dedup normalization of dbus_filter into a small helper (e.g., fn
normalize_dbus_filter(rules: impl IntoIterator<Item=&Rule>) -> Vec<String>) and
update the existing code that builds dbus_filter to call that helper (use
bundle.dbus_filter.iter().map(|r| r.to_flag()) as input); add a focused unit
test for normalize_dbus_filter that verifies canonicalization (sorted and
deduplicated) for a variety of input orders/duplicates and exercises
Rule::to_flag behavior so the canonicalization contract is pinned before
non-empty bundles are used.
crates/seal-sandbox/src/compile.rs (2)

526-543: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Extract the widened bundle-activation predicate.

resolve_dbus_filter_rules() now re-implements the same direnv-wrap / gh-helper widening that apply_bundles() owns, but the new tests only pin direct-prefix applicability. Pulling this into one shared helper and adding one direnv_wrap_active case plus one gh_credential_helper case would keep the D-Bus surface from drifting away from bind/env/network behavior on the next widening tweak.

Suggested shape
+fn bundle_applies_with_widenings(
+    entry: &GrantToolEntry,
+    matched_pattern: &str,
+    section_wrappers: &[String],
+    direnv_wrap_active: bool,
+    gh_credential_helper: bool,
+) -> bool {
+    let is_direnv_wrap_target = direnv_wrap_active && entry.name == "direnv";
+    let is_gh_credential_helper_target = gh_credential_helper && entry.name == "gh";
+    is_direnv_wrap_target
+        || is_gh_credential_helper_target
+        || bundle_applies(entry, section_wrappers, matched_pattern)
+}
+
 fn resolve_dbus_filter_rules(
     entries: &[GrantToolEntry],
     matched_pattern: &str,
     section_wrappers: &[String],
     direnv_wrap_active: bool,
     gh_credential_helper: bool,
 ) -> Vec<String> {
@@
     let mut rules: Vec<String> = Vec::new();
     for entry in entries {
-        let is_direnv_wrap_target = direnv_wrap_active && entry.name == "direnv";
-        let is_gh_credential_helper_target = gh_credential_helper && entry.name == "gh";
-        if !is_direnv_wrap_target
-            && !is_gh_credential_helper_target
-            && !bundle_applies(entry, section_wrappers, matched_pattern)
-        {
+        if !bundle_applies_with_widenings(
+            entry,
+            matched_pattern,
+            section_wrappers,
+            direnv_wrap_active,
+            gh_credential_helper,
+        ) {
             continue;
         }

Also applies to: 6470-6668

🤖 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 526 - 543,
resolve_dbus_filter_rules duplicates the "widened bundle-activation" logic
that's implemented in apply_bundles, causing the D-Bus rule set to drift when
that predicate changes; extract the predicate into a shared helper (e.g.,
bundle_applies_widened or bundle_matches_with_wrappers) and update
resolve_dbus_filter_rules and apply_bundles to call it instead of
re-implementing logic. The helper should accept the same inputs used here (a
&GrantToolEntry or entry.name, section_wrappers, matched_pattern) plus the
direnv_wrap_active and gh_credential_helper flags and return the boolean;
replace the inline checks (is_direnv_wrap_target /
is_gh_credential_helper_target and bundle_applies call) with a single call to
the new helper so both code paths stay consistent and add the two specific cases
(direnv_wrap_active and gh_credential_helper) inside that helper.

1029-1043: ⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For current sccache releases, what cache directory does sccache use on Linux by default, and does it honor XDG_CACHE_HOME?

💡 Result:

On Linux, sccache uses ~/.cache/sccache as its default directory for local disk storage [1][2][3]. Regarding XDG_CACHE_HOME, sccache's default implementation for determining the cache directory utilizes the directories crate (ProjectDirs) [4]. While sccache explicitly defaults to ~/.cache/sccache on Linux [1][2], it generally relies on the standard behaviors provided by the underlying Rust libraries to resolve platform-specific cache directories [4]. You can override this location entirely by setting the SCCACHE_DIR environment variable to a path of your choosing [1][2][5].

Citations:


Fix Linux sccache bind to follow sccache’s actual cache-dir resolution (XDG_CACHE_HOME / SCCACHE_DIR)

In crates/seal-sandbox/src/compile.rs:1029-1043, the Linux branch hardcodes ~/.cache/sccache. However, sccache determines its Linux disk cache directory via standard platform cache-dir logic (ProjectDirs) and may vary with XDG_CACHE_HOME; it can also be overridden entirely via SCCACHE_DIR. If the sandbox sets either, binding ~/.cache/sccache may miss the real sccache cache dir and make the write bind ineffective.
Also applies to: 4091-4158

🤖 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 1029 - 1043, The Linux
branch currently hardcodes tail = "~/.cache/sccache" which can miss sccache's
actual cache location when SCCACHE_DIR or XDG_CACHE_HOME is used; update the
resolution before calling bundle_path_to_bind so that for Platform::Linux you
first check std::env::var("SCCACHE_DIR") and use that if present, otherwise use
XDG_CACHE_HOME (or fall back to ~/.cache) and append "sccache" (matching
sccache's ProjectDirs logic), then pass the resulting path to
bundle_path_to_bind and push_unique into fs_write.paths (same call-site where
tail was used), ensuring the resolved path mirrors sccache cache-dir resolution
instead of the hardcoded "~/.cache/sccache".

@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: 4

🤖 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-runtime/src/scope/sandbox_spawn/macos.rs`:
- Around line 58-71: The current loop emitting "(allow file-read* (literal
...))" for each runtime.extra_ro_binds source only whitelists the leaf inode;
modify the loop over runtime.extra_ro_binds to first synthesize and emit "(allow
file-read-metadata (literal ...))" entries for each strict ancestor of the
source path (from the immediate parent up to the filesystem root or sandbox
baseline) using seal_sandbox::sbpl_string_literal(&ancestor.to_string_lossy()),
then emit the existing "(allow file-read* ...)" for the leaf; ensure ancestor
rules are pushed into profile before the leaf rule (you can mirror the
ancestor-synthesis logic from the compile layer if available) so sandbox-exec
can traverse to the whitelisted file.

In `@crates/seal-runtime/src/scope/sandbox_spawn/mod.rs`:
- Around line 454-459: The cleanup currently sets removed_socket = true whenever
self.sock_path was Some, even if std::fs::remove_file failed; change the logic
so you call std::fs::remove_file(p) and set removed_socket based on its Result
(true on Ok, false on Err), and log the actual error (including the error
message) when remove_file returns Err; reference the self.sock_path.take()
usage, the removed_socket binding, and the std::fs::remove_file call so you
locate and replace the current unconditional-true behavior with a Result match
that logs failures and only marks removal as true on success.

In `@crates/seal-sandbox/src/compile.rs`:
- Around line 526-554: The dbus filter selection in resolve_dbus_filter_rules
duplicates the direnv/gh/pattern checks already used elsewhere (notably in
apply_bundles); extract that predicate into a shared helper function (e.g.,
bundle_applies_with_wrappers or is_bundle_applicable) that accepts a
&GrantToolEntry, &str matched_pattern, &[String] section_wrappers,
direnv_wrap_active: bool, gh_credential_helper: bool and returns bool, then
replace the inline condition in resolve_dbus_filter_rules with a call to this
new helper and update apply_bundles() to call the same helper so all
bundle-applicability logic is centralized and stays synchronized.

In `@crates/seal-sandbox/src/macos.rs`:
- Around line 423-438: When expanding FIRMLINKS into sources, handle the case
where strip_prefix yields an empty remainder so the firmlink twin itself is
emitted: inside the loop over FIRMLINKS where you do if let Ok(rest) =
p.strip_prefix(short) { ... }, detect if rest is empty and push
Path::new(long).to_path_buf() (or Path::new(long).join(rest) only when rest is
non-empty) so the `/private/...` twin itself is included in sources; this
ensures the later ancestor-walk in ancestors includes metadata for exact roots
like `/tmp`, `/etc`, `/var`. Also add a unit test exercising an exact `/tmp`
grant (FsBaseline::None) to assert the `/private/tmp` metadata path is present.
🪄 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: 8628a4ac-5c7b-479f-b2ab-7096197603ba

📥 Commits

Reviewing files that changed from the base of the PR and between b5957be and 7aa0145.

📒 Files selected for processing (14)
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest.rs
  • crates/seal-policy/src/manifest/dbus_filter.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/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

@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: 4

🤖 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-runtime/src/scope/sandbox_spawn/macos.rs`:
- Around line 58-71: The current loop emitting "(allow file-read* (literal
...))" for each runtime.extra_ro_binds source only whitelists the leaf inode;
modify the loop over runtime.extra_ro_binds to first synthesize and emit "(allow
file-read-metadata (literal ...))" entries for each strict ancestor of the
source path (from the immediate parent up to the filesystem root or sandbox
baseline) using seal_sandbox::sbpl_string_literal(&ancestor.to_string_lossy()),
then emit the existing "(allow file-read* ...)" for the leaf; ensure ancestor
rules are pushed into profile before the leaf rule (you can mirror the
ancestor-synthesis logic from the compile layer if available) so sandbox-exec
can traverse to the whitelisted file.

In `@crates/seal-runtime/src/scope/sandbox_spawn/mod.rs`:
- Around line 454-459: The cleanup currently sets removed_socket = true whenever
self.sock_path was Some, even if std::fs::remove_file failed; change the logic
so you call std::fs::remove_file(p) and set removed_socket based on its Result
(true on Ok, false on Err), and log the actual error (including the error
message) when remove_file returns Err; reference the self.sock_path.take()
usage, the removed_socket binding, and the std::fs::remove_file call so you
locate and replace the current unconditional-true behavior with a Result match
that logs failures and only marks removal as true on success.

In `@crates/seal-sandbox/src/compile.rs`:
- Around line 526-554: The dbus filter selection in resolve_dbus_filter_rules
duplicates the direnv/gh/pattern checks already used elsewhere (notably in
apply_bundles); extract that predicate into a shared helper function (e.g.,
bundle_applies_with_wrappers or is_bundle_applicable) that accepts a
&GrantToolEntry, &str matched_pattern, &[String] section_wrappers,
direnv_wrap_active: bool, gh_credential_helper: bool and returns bool, then
replace the inline condition in resolve_dbus_filter_rules with a call to this
new helper and update apply_bundles() to call the same helper so all
bundle-applicability logic is centralized and stays synchronized.

In `@crates/seal-sandbox/src/macos.rs`:
- Around line 423-438: When expanding FIRMLINKS into sources, handle the case
where strip_prefix yields an empty remainder so the firmlink twin itself is
emitted: inside the loop over FIRMLINKS where you do if let Ok(rest) =
p.strip_prefix(short) { ... }, detect if rest is empty and push
Path::new(long).to_path_buf() (or Path::new(long).join(rest) only when rest is
non-empty) so the `/private/...` twin itself is included in sources; this
ensures the later ancestor-walk in ancestors includes metadata for exact roots
like `/tmp`, `/etc`, `/var`. Also add a unit test exercising an exact `/tmp`
grant (FsBaseline::None) to assert the `/private/tmp` metadata path is present.
🪄 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: 8628a4ac-5c7b-479f-b2ab-7096197603ba

📥 Commits

Reviewing files that changed from the base of the PR and between b5957be and 7aa0145.

📒 Files selected for processing (14)
  • crates/seal-policy/src/grant.rs
  • crates/seal-policy/src/manifest.rs
  • crates/seal-policy/src/manifest/dbus_filter.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/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
🛑 Comments failed to post (4)
crates/seal-runtime/src/scope/sandbox_spawn/macos.rs (1)

58-71: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Grant ancestor metadata for dispatcher artifact paths, not just the leaf file.

The new (allow file-read* (literal ...)) rule only opens the final artifact inode. For artifacts outside the baseline tree (the current CA cert case under the per-session security dir), sandbox-exec still needs file-read-metadata on each strict ancestor to traverse to that leaf. Without those ancestor rules, TLS clients can still fail to open the injected cert even though the exact file is whitelisted.

Please emit file-read-metadata (literal ...) for the strict ancestors of each runtime.extra_ro_binds source path before the leaf file-read* rule, or reuse the same ancestor-synthesis logic the compile layer already uses for granted paths.

🤖 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-runtime/src/scope/sandbox_spawn/macos.rs` around lines 58 - 71,
The current loop emitting "(allow file-read* (literal ...))" for each
runtime.extra_ro_binds source only whitelists the leaf inode; modify the loop
over runtime.extra_ro_binds to first synthesize and emit "(allow
file-read-metadata (literal ...))" entries for each strict ancestor of the
source path (from the immediate parent up to the filesystem root or sandbox
baseline) using seal_sandbox::sbpl_string_literal(&ancestor.to_string_lossy()),
then emit the existing "(allow file-read* ...)" for the leaf; ensure ancestor
rules are pushed into profile before the leaf rule (you can mirror the
ancestor-synthesis logic from the compile layer if available) so sandbox-exec
can traverse to the whitelisted file.
crates/seal-runtime/src/scope/sandbox_spawn/mod.rs (1)

454-459: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Log the actual unlink result.

removed_socket is set to true whenever sock_path is present, even if remove_file fails or the file is already gone. That makes the cleanup log claim success when the socket artifact may still be on disk.

Suggested fix
-        let removed_socket = if let Some(p) = self.sock_path.take() {
-            let _ = std::fs::remove_file(p);
-            true
+        let removed_socket = if let Some(p) = self.sock_path.take() {
+            std::fs::remove_file(&p).is_ok()
         } else {
             false
         };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        let removed_socket = if let Some(p) = self.sock_path.take() {
            std::fs::remove_file(&p).is_ok()
        } else {
            false
        };
🤖 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-runtime/src/scope/sandbox_spawn/mod.rs` around lines 454 - 459,
The cleanup currently sets removed_socket = true whenever self.sock_path was
Some, even if std::fs::remove_file failed; change the logic so you call
std::fs::remove_file(p) and set removed_socket based on its Result (true on Ok,
false on Err), and log the actual error (including the error message) when
remove_file returns Err; reference the self.sock_path.take() usage, the
removed_socket binding, and the std::fs::remove_file call so you locate and
replace the current unconditional-true behavior with a Result match that logs
failures and only marks removal as true on success.
crates/seal-sandbox/src/compile.rs (1)

526-554: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Extract the shared bundle-applicability predicate.

Lines 536-540 duplicate the same direnv/gh/pattern logic already used on Lines 1001-1005. That makes dbus_filter_rules easy to desynchronize from the fs/env/network bundle surfaces the next time the widening rules change.

♻️ Extraction sketch
+fn bundle_entry_applies(
+    entry: &GrantToolEntry,
+    matched_pattern: &str,
+    section_wrappers: &[String],
+    direnv_wrap_active: bool,
+    gh_credential_helper: bool,
+) -> bool {
+    let is_direnv_wrap_target = direnv_wrap_active && entry.name == "direnv";
+    let is_gh_credential_helper_target = gh_credential_helper && entry.name == "gh";
+    is_direnv_wrap_target
+        || is_gh_credential_helper_target
+        || bundle_applies(entry, section_wrappers, matched_pattern)
+}
+
 fn resolve_dbus_filter_rules(
     entries: &[GrantToolEntry],
     matched_pattern: &str,
     section_wrappers: &[String],
     direnv_wrap_active: bool,
     gh_credential_helper: bool,
 ) -> Vec<String> {
     let mut seen: std::collections::HashSet<&str> = std::collections::HashSet::new();
     let mut rules: Vec<String> = Vec::new();
     for entry in entries {
-        let is_direnv_wrap_target = direnv_wrap_active && entry.name == "direnv";
-        let is_gh_credential_helper_target = gh_credential_helper && entry.name == "gh";
-        if !is_direnv_wrap_target
-            && !is_gh_credential_helper_target
-            && !bundle_applies(entry, section_wrappers, matched_pattern)
-        {
+        if !bundle_entry_applies(
+            entry,
+            matched_pattern,
+            section_wrappers,
+            direnv_wrap_active,
+            gh_credential_helper,
+        ) {
             continue;
         }
         for rule in &entry.dbus_filter {
             if seen.insert(rule.as_str()) {
                 rules.push(rule.clone());

Apply the same helper in apply_bundles() so all bundle surfaces stay locked together.

🤖 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 526 - 554, The dbus filter
selection in resolve_dbus_filter_rules duplicates the direnv/gh/pattern checks
already used elsewhere (notably in apply_bundles); extract that predicate into a
shared helper function (e.g., bundle_applies_with_wrappers or
is_bundle_applicable) that accepts a &GrantToolEntry, &str matched_pattern,
&[String] section_wrappers, direnv_wrap_active: bool, gh_credential_helper: bool
and returns bool, then replace the inline condition in resolve_dbus_filter_rules
with a call to this new helper and update apply_bundles() to call the same
helper so all bundle-applicability logic is centralized and stays synchronized.
crates/seal-sandbox/src/macos.rs (1)

423-438: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Exact /tmp//etc//var grants still miss the /private/... metadata hop.

The firmlink expansion only adds the /private/... twin as a source, and the later walk emits ancestors of that twin, not the twin itself. That means a grant rooted exactly at /tmp, /etc, or /var still won't get a metadata rule for /private/tmp, /private/etc, or /private/var, so canonicalization of the exact firmlink root can still fail under FsBaseline::None.

Please special-case the empty rest case so the /private/... twin itself is emitted as a metadata path, and add a test for an exact /tmp grant.

🤖 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/macos.rs` around lines 423 - 438, When expanding
FIRMLINKS into sources, handle the case where strip_prefix yields an empty
remainder so the firmlink twin itself is emitted: inside the loop over FIRMLINKS
where you do if let Ok(rest) = p.strip_prefix(short) { ... }, detect if rest is
empty and push Path::new(long).to_path_buf() (or Path::new(long).join(rest) only
when rest is non-empty) so the `/private/...` twin itself is included in
sources; this ensures the later ancestor-walk in ancestors includes metadata for
exact roots like `/tmp`, `/etc`, `/var`. Also add a unit test exercising an
exact `/tmp` grant (FsBaseline::None) to assert the `/private/tmp` metadata path
is present.

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