Skip to content

Latest commit

 

History

History
33 lines (22 loc) · 5.08 KB

File metadata and controls

33 lines (22 loc) · 5.08 KB

Threat Model (scoped to this demo)

This is not a full threat model for the M87 platform — see Spine-Pro's own security documentation for that. This covers only what the runs in this repo do and don't defend against.

In scope for this demo

T1 — Agent attempts an out-of-scope file write. Mitigation observed: the restricted-patch run attempted to modify .m87/sandbox.yaml (the policy file governing the mission itself — a privilege-escalation pattern: an agent rewriting the rules that constrain it). The Spine-Pro gate classified this as PRIVILEGE_ESCALATION and denied it with TOOL_NOT_IN_MANIFEST before any file was touched. Verified in artifacts/denied/decisions/.

T2 — Tampered or replayed receipt. Mitigation observed and directly attacked: a copy of the success run's ledger (receipts/envelopes.jsonl) had one field in its first entry byte-flipped (apply_patchTAMPERED_XX), then m87-mission verify was re-run against the tampered copy. Result: FAIL | envelope[0]:TAMPERED_XX:src/calculator.py current_hash | recompute mismatch, overall verification: FAILED, exit code 1. The untampered original still verifies OK. Full commands and output in DEMO_TRANSCRIPT.md §8. Note: tampering the redundant receipts/native/*.json files (informational copies, not the ledger) does not trip verification — verify correctly treats receipts/envelopes.jsonl as the sole source of truth, which is the right design, but worth stating precisely so the boundary of what's protected is clear.

T3 — Silent unconfined execution. Previously partial; now closed with direct evidence. run_tests executes patched code inside a real bwrap sandbox: network denied via --unshare-net, filesystem confined to the worktree via targeted --ro-binds and a single writable --bind on the worktree itself. Two adversarial probes were run against the live confined executor (not just the mission's own run_tests call, but standalone probes using the same BwrapBackend):

  • Network egress: a raw TCP connect to 1.1.1.1:80 from inside the sandbox raised OSError: [Errno 101] Network is unreachable — there is no interface to route through, not merely a blocked destination.
  • Filesystem escape: a write to /tmp/outside-secret.txt from inside the sandbox returned success from the executing code's perspective (no PermissionError), but the write landed in the sandbox's own private, ephemeral /tmp (from --tmpfs /tmp) — the real host file, read before and after from outside the sandbox, was confirmed byte-for-byte unchanged (canary, never PWNED).

mission-core's own test_isolation.py suite (13 tests: test_bwrap_denies_network, test_bwrap_hides_host_filesystem, test_bwrap_confines_writes, and 10 others) passes in full in this environment, independently corroborating the manual probes above. Both real demo runs (artifacts/success/, artifacts/denied/) now execute with confinement engaged and record "isolation": {"backend": "bwrap", "network": "deny"} in their native receipts. See DEMO_TRANSCRIPT.md §6 for the fix and confined re-runs, and §7 for the adversarial probes, with README.md "Isolation: closed, not disclosed-around" for the two-part fix (both now upstreamed to m87-mission-core) that made this possible.

Out of scope for this demo (not attacked, not claimed)

  • Network exfiltration via allowed domains (this demo's network policy is a hard deny, not an allowlist — so allowlist-bypass attacks don't apply here; a future demo with network: allow + allowed_domains would need this)
  • HITL bypass attempts (neither run reached a REQUIRES_HITL decision)
  • Multi-session / multi-agent toxic-topology detection (single mission, single session)
  • Attacks on the publish/PR step itself (github_publisher.py was not exercised — no publish was attempted)
  • Supply-chain or dependency-tampering attacks on Spine-Pro or mission-core themselves
  • Resource-exhaustion attacks against the CPU/memory/file-size/process limits isolation.py sets via RLIMIT_* (the limits are set and receipted, but not adversarially tested to their breaking point in this demo)
  • Attacks against live_spine mode (a networked, external gate) — not exercised; this demo used local_spine throughout

Honest summary

This demo proves four things solidly: (1) a real gate denies a real privilege-escalation attempt before execution; (2) the resulting receipt chains are independently, cryptographically verifiable; (3) tampering with the ledger is detected, not silently accepted; and (4) the sandbox-execution boundary held under direct network and filesystem attack, engaged for real (not opted out) in the runs shipped in artifacts/success/ and artifacts/denied/. The interpreter symlink-chain fix is now part of canonical m87-mission-core, with regression coverage for the multi-hop uv Python layout. The remaining honest gap: only two golden-case scenarios were driven end-to-end — the fuller golden-case suite described in early planning docs (secrets, curl, package installs, CI edits, force-push) was not exercised as an automated harness (see evaluation/README.md).