Skip to content

feat(harness): hidden-access log collected from the macOS sandbox's own denial reports - #591

Merged
heyong4725 merged 3 commits into
mainfrom
feat/sandbox-access-log
Sep 19, 2026
Merged

heyong4725 merged 3 commits into
mainfrom
feat/sandbox-access-log

Conversation

@heyong4725

@heyong4725 heyong4725 commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Scope

Adds a macOS sandbox denial collector for the hidden-access log required by postflight (TRT-3/TRT-6/TRT-9, CON-5/CON-8). This is unscored engineering evidence and does not authorize pilot or confirmatory collection.

The controller starts log stream for the launch window, parses sandbox denial reports, selects process names admitted by the arm, validator and prepared workers, and classifies denied targets against the arm's authority roots. It retains the four-key hidden-access-log.json and a companion hidden-access-collection.json containing the window, counts, process names, PIDs, marker outcomes and coverage, without target paths. Denials by other processes or operations against a canary remain in the event log.

At both ends of the window the collector runs an admitted arm executable under the arm's profile against a fresh controller-owned canary. A marker passes only when the stream reports that executable's file-read denial on the exact canary. Missing reports, an unhealthy reader or stream, or any selected process name without a positive control leaves complete: false. The log is retained even when the launch or teardown fails. Canaries use /private/tmp on macOS and the system temporary directory where that path is unavailable.

matched_campaign.py accepts hidden_access_log: {"collect": "macos-sandbox-reports"} for an engineering session. It refuses dynamic worker policies and arms without a marker-capable executable. adapter_active is tied to the verified real sandbox-exec command. The collector source joins the controller files bound by admission.

Limits

macOS can enforce a denial without reporting it to the unified log; this was observed for third-party binaries on the development host. A missing positive control excludes the session. Process selection is by name because the report has no ancestry; distinct binaries with the same process name cannot be separated by this sensor. The operator must run one session at a time and avoid simultaneous capability audits. Independent coverage evidence remains necessary before using the log for study collection.

Stack and registrations

This branch includes merged main through #590 and now targets main. It preserves CSE v25 and pilot v11 unchanged and adds append-only CSE v26 and pilot v12 registrations. Their seed commitments are inherited unverified, all pending gates remain pending, and neither authorizes collection.

Verification

Locally: 79 collector and confinement tests, 213 matched-session and postflight tests, and 27 freeze-registry tests pass. Both new manifests pass freeze check --allow-withheld-seeds. Ruff, trace coverage, docs inventory and claim-evidence checks pass. macOS and Linux full CI are running.

Refs #347, #567, ADR-66. Original implementation authored with Claude Code; review findings and fixes are recorded in PR comments.

…wn denial reports

The postflight excludes any session whose hidden-access log is not
`complete`, and nothing produced one. `hidden_access_log.SandboxReportCollector`
streams the kernel's sandbox denial reports (`log stream`, sender Sandbox)
for the launch window, keeps those whose process name is an executable
admitted by the arm, validator or any prepared worker policy, classifies
targets against the arm's readable and hidden roots (hidden roots win), and
retains the postflight's four-key log beside a collection record (window,
selection, counts, pids, marker outcomes, never targets).

The kernel does not report every denial it enforces: on the development host
it stopped reporting denials by third-party binaries (the admitted uv
interpreter, node) while still reporting Apple-signed ones and enforcing all.
Completeness therefore rests on positive controls: at the start and end of
the window the collector runs the arm's own admitted executable under the
arm's profile against a fresh controller-owned canary and requires the
sandbox to report that exact denial. A missing marker, a late, dead or
killed stream, a reader or teardown failure all leave `complete` false and
the log is always retained. A duplicate summary weighs its N further
occurrences (the first is its own line); rows are parsed, never searched
(the stream escapes slashes). `run_engineering_session` accepts
`hidden_access_log = {"collect": "macos-sandbox-reports"}`, refuses arms
without an interpreter, shell or cat for the markers and dynamically
provisioned workers, and marks the log confinement-active only when the
verified wrapped command runs under the real sandbox-exec. Successors
cse-causal-study-v25 / cse-causal-study-pilot-v11; registry count -> 62.

TRT-6, TRT-3, CON-8, CON-5. Refs #347, #567, ADR-66.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMTXwWm3ZppWcJvQQw2qbf
@heyong4725

Copy link
Copy Markdown
Contributor Author

Review findings for PR #591:

  1. The branch is stacked on feat(harness): probe-widened attestation for interpreter-only sandboxes and a batch audit #590's original head (cae291c), before feat(harness): probe-widened attestation for interpreter-only sandboxes and a batch audit #590's fixes and merge. Its v25/pilot-v11 registrations collide with the versions already on main, and it carries the earlier unsupported loopback policy. Reconcile with current main, preserve the historical registrations, and create append-only successors.
  2. SandboxReportCollector.__enter__ hard-codes /private/tmp for canaries. Linux does not have that directory, so three new unit tests fail before the collector can retain a log. Use a portable controller-private temporary root for the stand-in tests while keeping the short macOS socket/canary location where needed.
  3. The live macOS test requires the start marker to appear. The PR itself documents that the kernel may stop reporting denials while still enforcing them, and CI showed exactly that behavior. The test should verify fail-closed retention when reporting is absent and verify positive behavior when reports are present, without treating host telemetry state as a code failure.
  4. complete is asserted after markers for only one selected executable, while reports from every executable admitted by the arm, validator, and workers are selected. A Python marker can pass while codex or node reports are suppressed, yielding a false complete: true. The marker match also checks only the canary target, not the reporting process or denied operation. Completeness needs to reflect which identities were actually probed, and marker matching needs to bind to the intended executable and read denial.

I’m fixing these on the PR branch and will follow up with verification results.

@heyong4725
heyong4725 changed the base branch from feat/audit-probe-widening to main September 18, 2026 21:27
@heyong4725
heyong4725 force-pushed the feat/sandbox-access-log branch from 30a79e0 to bb69b2c Compare September 18, 2026 21:33
@heyong4725

Copy link
Copy Markdown
Contributor Author

Review resolution for the findings above:

  • Retargeted the PR to current main, merged the feat(harness): probe-widened attestation for interpreter-only sandboxes and a batch audit #590 fixes, and preserved the existing v25/pilot-v11 registrations. New append-only v26/pilot-v12 declarations and manifests carry the collector source hash.
  • Made canary creation portable when /private/tmp does not exist.
  • Made marker matching require the marker executable, a file-read denial, and the exact canary. Denials by other processes or operations on a canary remain in the event log.
  • Made complete fail closed for every selected process name without a positive control. The companion record lists unprobed names.
  • Made the live macOS test accept the documented case where the sandbox enforces a denial but does not emit a report; it still checks the actual denied read.

Local verification: 79 collector/confinement tests, 213 matched-session/postflight tests, and 27 freeze-registry tests pass; both new manifests pass freeze check --allow-withheld-seeds; Ruff, trace, docs inventory, and claim-evidence checks pass. The replacement macOS and Linux CI jobs are still running.

@heyong4725
heyong4725 merged commit 0b92521 into main Sep 19, 2026
2 checks passed
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