Skip to content

fix(harness): bind session audits and reject unsafe loopback authority - #589

Merged
heyong4725 merged 3 commits into
mainfrom
feat/live-session-audit
Sep 18, 2026
Merged

heyong4725 merged 3 commits into
mainfrom
feat/live-session-audit

Conversation

@heyong4725

@heyong4725 heyong4725 commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

PR #589 adds capability audits under a session's exact macOS policy. Review found that its proposed port-pinned loopback rule also admitted unrelated services on other local addresses. The final change refuses that unsupported authority and retains session-bound audits under deny-external.

Requirements: TRT-5, TRT-6, TRT-7, MON-8, CON-5, CON-8. Refs #347, #567, ADR-66.

Behavior

  • Session audits use the declared policy, place synthetic sentinels inside its roots, retain profile/policy identities, redact hidden-root paths, and clean up sentinel directories. Canonical policy reconstruction is shared across launch paths.
  • loopback is rejected at profile compilation, audit startup, provider admission, and launch verification. Old passing loopback attestations cannot authorize a command. The operating-system compiler accepts localhost, which covers multiple host addresses, and rejects literal IP endpoint selectors. A numeric port alone cannot isolate the provider relay.
  • Optional provider.relay_port configures a listener but grants no confinement authority. Relay-backed confined sessions remain unavailable pending an exclusive transport; this PR does not attest their confinement or authorize pilot collection.
  • Main's CSE v23, pilot v9, and BND v14 records remain byte-for-byte unchanged. Append-only CSE v24 and pilot v10 inherit the latest source coverage, including fix(harness): protect L2 candidate observations #578's public-observation/cache bindings, and preserve pending gates and inherited seed commitments. Private seed sources were denied by an OS sandbox; commitments remain explicitly unverified.

Review resolution

The IPv4/IPv6 same-port defect was reproduced with the actual compiled sandbox: a separate IPv6 listener returned a synthetic canary while the IPv4 relay port was occupied. Regression tests now cover rejection of this grant, actual denial by the supported policy, rejection before audit side effects, rejection of cached attestations, and provider admission even when port numbers match. Three refusal regressions failed before the fix. All five merge conflicts were reconciled while preserving historical registrations.

The owner explicitly authorized direct fixes after the cross-review findings were posted. Class C: human review of this final change remains required before merge.

Validation

  • Format, lint, requirement tracing, documentation inventory, claim-evidence checks: passed.
  • Full unit suite: 4,418 passed (4,391 plus 27 registry tests under OS-enforced private-seed denial).
  • Focused confinement, worker-capability and relay tests: 66 passed, including live sandbox cases.
  • Dora runtime identity verification: passed, Python API 1.0.1.
  • SIM/GRAPH: interrupted at the owner's explicit request so the machine could restart; 2 passed before interruption, exit 2 (KeyboardInterrupt). The owner explicitly authorized committing and pushing without completing this gate. This is not a passing SIM/GRAPH result; the full gate remains outstanding before a completion claim.
  • Staged input hashes match the validated tree; no source changes after the gate run began.

@heyong4725

Copy link
Copy Markdown
Contributor Author

CI on c9876f4: Linux failed on (a) the committed docs inventory, which had picked up an untracked local directory when regenerated on the dev Mac, and (b) ten test helpers that rebuild a policy from MacOSPolicy.as_dict() with a hand-rolled comprehension and choked on the new loopback_port: None entry. Fixed at the amended head: as_dict() (like canonical_dict()) carries the port only when one is pinned, so every existing consumer sees the shape it always did; inventory regenerated from a clean tree. test_matched_run_config::test_bound_typed_dispatch_passes_verified_stage_factory_to_rollout now gets past the helper locally and fails only at the typed validation stage, identically on clean main (dev-Mac timing); CI is its gate.

@heyong4725
heyong4725 force-pushed the feat/live-session-audit branch from c9876f4 to 13fd26e Compare September 14, 2026 04:57
@heyong4725

Copy link
Copy Markdown
Contributor Author

Second Linux run still hit the NoneType reconstruction: three test helpers serialized policies with dataclasses.asdict (which emits loopback_port: None) before rebuilding them by hand. They now use MacOSPolicy.as_dict(), which carries the port only when pinned. The remaining local failure in test_typed_node_host is a worker launch refused before spawn, the dev-Mac timing class.

@heyong4725
heyong4725 force-pushed the feat/live-session-audit branch from 13fd26e to df19caf Compare September 14, 2026 05:43
…ed loopback relay policy

A second network policy, `loopback`, compiles to outbound on exactly one
local TCP port (`loopback_port`, the arm's provider relay) and nothing
else; SBPL's `localhost` names every address this host owns, so the pin
is what makes the grant exclusive. The capability audit can now run under
a session's exact policy: sentinels are placed inside the policy's own
roots (caller-named homes or the last directory of each list), registered
for removal one by one, reclaimed only when a well-formed owner marker
names a dead process, and the retained attestation carries the profile
hash and policy id the launch wrapper demands. Under `loopback` the matrix
proves the pinned port is reachable, another local port is refused, and an
external connect is refused outright (a hang is a failed case, never an
abort). The symlink case under a session policy targets the controller's
private temp tree; the report replaces hidden roots with digests.

wrap_verified_command requires the case set keyed by the compiled network
policy and cross-checks the attested network value. Provider bindings
accept `relay_port`, the relay binds it, and admission refuses a loopback
arm whose relay port differs from its pinned port. Probes never widen an
arm's grants (nc or an admitted Python interpreter; printf stays the
unlisted control); probe locale pinned to C. MacOSPolicy.from_canonical
replaces nine hand-rolled reconstructions; roots must be printable ASCII.
CLI: audit-macos --policy [--sentinel-*]; malformed input exits 2 with
JSON. Successors cse-causal-study-v23 / cse-causal-study-pilot-v9;
registry count -> 58.

TRT-5, TRT-6, TRT-7, MON-8, CON-8, CON-5. Refs #347, #567, ADR-66 (PR7
part 2).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMTXwWm3ZppWcJvQQw2qbf
@heyong4725
heyong4725 force-pushed the feat/live-session-audit branch from df19caf to 8e0dd9e Compare September 14, 2026 06:38
@heyong4725
heyong4725 marked this pull request as ready for review September 14, 2026 07:44
@heyong4725

Copy link
Copy Markdown
Contributor Author

CI green on both platforms at 8e0dd9e (Linux 4321 passed; macOS passed). Ready for owner review (confinement class): the port-pinned loopback policy, the session-bound audit, the relay port binding, and the admission cross-check.

@heyong4725

Copy link
Copy Markdown
Contributor Author

Cross-review of head 8e0dd9e068178808c5b4e5e3a77c9f8bafea5ede against current main (227bf2529f8654159646a96aa0878c9303cde221). Changes are needed before merge.

P1 — The pinned port permits a second service on another local address

Location: src/aisle/harness/treatment_confinement.py:92–97, together with ProviderRelay binding only 127.0.0.1.

The new profile allows localhost:<port>, which covers multiple local addresses. Occupying 127.0.0.1:<port> does not reserve [::1]:<port>. I reproduced this on macOS using this revision's actual compile_macos_profile, _synthetic_policy, and _tcp_read_command: hold a TCP listener on 127.0.0.1:65108, bind an independent IPv6-only listener on [::1]:65108, then run /bin/bash through the compiled sandbox and read from the IPv6 listener. Result: exit 0, stdout NON-RELAY-SYNTHETIC-CANARY, empty stderr. Both listeners existed simultaneously; only synthetic local data was used.

This violates the claimed relay-only network authority (TRT-5/TRT-7): an unrelated local service at the same numeric port remains reachable. The current foreign_loopback_tcp_read case tests a different port, so it cannot catch this. Please make the enforced authority exclusive to the declared relay endpoint, or fail closed when that cannot be enforced. Add a live regression with the IPv4 relay present and a separate IPv6 service on the same port; also cover other local addresses represented by localhost. Merely checking relay-port equality or probing another port is insufficient. If the selected sandbox backend cannot express the endpoint restriction, the confinement design needs to account for that limitation explicitly before attesting the session.

Merge blocker — Preserve the registrations already landed by #578

git merge-tree --write-tree origin/main 8e0dd9e068178808c5b4e5e3a77c9f8bafea5ede reports five conflicting files:

  • analysis/freeze/cse-causal-study-v23/declaration.json
  • analysis/freeze/cse-causal-study-v23/freeze-manifest.json
  • analysis/freeze/cse-causal-study-pilot-v9/declaration.json
  • analysis/freeze/cse-causal-study-pilot-v9/freeze-manifest.json
  • tests/unit/test_freeze_registry.py

Current main already contains CSE v23, pilot v9, and BND v14 from #578, including its public-observation and model-cache bindings. Do not overwrite those historical registrations with this PR's same-number records. Reconcile against current main and append successors (currently CSE v24 / pilot v10), inherit from the latest main registrations, preserve their source coverage and pending gates, and regenerate manifests against the final combined source bytes. Update the registry count from the actual resulting set and refresh affected documentation/generated evidence. Production source files merge without textual conflicts. Check dependent PRs #590/#591 for the same version-chain collision when updating them.

Validation: both existing GitHub CI checks are green on the reviewed head; the merge-conflict computation and focused live sandbox reproduction above were run during this review. I did not rerun the full test suite or change/push the author's branch. Per AGENTS.md / CON-16, these findings are posted for the Claude-authored PR to address. The final reconciled fix needs the required local gates, fresh CI, and Class C human review before merge.

@heyong4725 heyong4725 changed the title feat(harness): session-bound confinement attestation with a port-pinned loopback relay policy fix(harness): bind session audits and reject unsafe loopback authority Sep 14, 2026
@heyong4725

Copy link
Copy Markdown
Contributor Author

Addressed the review findings in cf834d629865b99de30308c73513060ebc8f48f6: unsupported loopback authority now fails closed at compilation, audit startup, provider admission and cached-attestation launch; live IPv4/IPv6 same-port regression added. Registration conflicts are resolved with append-only CSE v24/pilot v10, preserving main's records and #578's source coverage.

4,418 unit tests and the other completed gates passed. At the owner's explicit request for a machine restart, SIM/GRAPH was stopped after 2 passes and this commit was pushed without completing that gate. The full SIM/GRAPH gate remains outstanding. The PR description records the limitation: relay-backed confined sessions remain unavailable pending an exclusive transport. No merge performed.

@heyong4725

Copy link
Copy Markdown
Contributor Author

Review of cf834d629865b99de30308c73513060ebc8f48f6 against current main:

P1 — The new registrations record a source commit that predates the confinement fix

Both analysis/freeze/cse-causal-study-v24/freeze-manifest.json and analysis/freeze/cse-causal-study-pilot-v10/freeze-manifest.json set git_head to 8e0dd9e068178808c5b4e5e3a77c9f8bafea5ede. That commit still contains the unsafe loopback implementation. For example, git show 8e0dd9e:src/aisle/harness/treatment_confinement.py | shasum -a 256 yields 665b50b294d9947c706a6e6c817217600c519e0c375da87464dbc5c4eb4253df, while both new manifests bind sha256:c46e448a63041119f1d0dbdb136ae982459d64a305867dae114e480410ed4f6c for that source. The latter matches the fixed PR head.

Thus the recorded commit cannot reconstruct the registered source set (CON-5/TRT-1). harness freeze check --allow-withheld-seeds reports ok: true for both registrations because check_manifest reuses the recorded git_head without checking that commit's tree. Please regenerate both manifests with a source commit that contains the exact hashed files, and verify the commit-to-artifact relationship explicitly. Keep v23/pilot v9 unchanged.

The loopback refusal, same-port IPv4/IPv6 regression, append-only successor chain, and current source hashes otherwise looked consistent. My focused macOS run passed 258 tests across test_treatment_confinement.py, test_freeze_registry.py, and test_matched_session.py; git diff --check passed and both PR CI jobs are green. The PR still records an interrupted SIM/GRAPH gate, so that is not a passing result. Class C human review is also required before merge under CON-10; GitHub currently shows REVIEW_REQUIRED.

@heyong4725

Copy link
Copy Markdown
Contributor Author

Fixed the registration provenance finding in 777f2077155d0dfd339d2423b114c58754eaa822. CSE v24 and pilot v10 now record cf834d629865b99de30308c73513060ebc8f48f6 as git_head, the commit containing the corrected confinement code. I checked the patched manifests against an archive of that exact commit: all 159 CSE v24 and 154 pilot v10 declared artifact hashes match. Historical registrations were not changed.

Validation: git diff --check, Ruff format, Ruff lint, and all 27 freeze-registry unit tests passed. I attempted the full 4,418-test local unit gate. Its first run hit the workspace sandbox's ps denial in test_sweep_worktree_kills_only_worktree_processes; an outside-sandbox rerun passed that test but test_dynamic_monolithic_child_retains_auditable_authored_failure returned an infrastructure exclusion after its child timed out at 49 seconds. The isolated reproduction failed the same way. This appears to be a dev-Mac timing issue in a live worker test; it is not a passing full local unit result. Fresh CI on 777f207 is running. The previously interrupted SIM/GRAPH gate and Class C human review remain outstanding before merge.

@heyong4725
heyong4725 merged commit 27d3156 into main Sep 18, 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