Skip to content

Give the sandbox isolation contract one module #62

Description

@daniel-kindl

Problem

The sandbox isolation contract is written three times. The three copies no longer agree.

config/sandcastle/orchestrate.mjs:307-384 defines 9 probes and runs them on every real
run. config/sandcastle/selftest.mjs:58-85 defines 11 probes and runs them only in
agentbox selftest. config/sandcastle/adversarial.mjs:106-111 holds a third list.

docs/sandcastle.md:756 states:

agentbox selftest proves each row from inside a running sandbox, and the same
probes run on every run and pipeline.

The probes are not the same.

Probes that no real run proves

Three probes exist only in selftest.mjs:

  • no credential file from the host home — it tests ~/.claude/.credentials.json and
    ~/.codex/auth.json. Both are rows of the What never enters a sandbox table in
    docs/sandcastle.md:746-747.
  • the sandbox is not privileged — it reads capsh --print for cap_sys_admin.
  • the worktree is mounted and is a git worktree.

Probes that drifted

Probe orchestrate.mjs selftest.mjs
private key -name 'id_*' ! -name '*.pub' id_rsa*, id_ed25519*, id_ecdsa*, id_dsa*
Podman socket test ! -S test ! -e

The two socket tests are not equivalent. test ! -S passes when a podman.sock exists as
a regular file.

The podman({imageName, mounts, selinuxLabel, userns, containerUid, containerGid})
provider block is also written three times: orchestrate.mjs:286-298,
selftest.mjs:114-121 and adversarial.mjs:95-102.

verify/80-sandcastle.sh cannot see the drift. Check D6 searches $ORCH for the string
is absent from the sandbox.

A second integrity contract with the same shape

bin/agentbox:774-836 (repo_snapshot, diff_snapshot) and
config/sandcastle/clone-integrity.mjs:67-116 (snapshotIntegrity, diffIntegrity) both
answer one question: did anything about this Git repository change across the run? They
disagree on how.

Property bin/agentbox clone-integrity.mjs
config sha256sum of the whole file config --local --list, per entry
hooks mode, size and SHA per file, .sample included mode and size, .sample filtered
working tree status --porcelain not covered

The bash side hashes the whole config file, so a violation reports two opaque hashes. The
JavaScript side names the exact key, for example git config added: alias.pwn. The weaker
report guards the real repository. diffIntegrity has a 317-line probe.
diff_snapshot has none.

Do not merge the two implementations. The host half must run when the machine has no Node,
which verify/80-sandcastle.sh:53-58 already depends on.

Goal

State the sandbox isolation contract once, and prove every row of the documented table on
every run.

Proposed direction

  • Add config/sandcastle/isolation.mjs, beside clone-integrity.mjs and budget.mjs.
    Export isolationProbes(cfg), which returns the probe records, and
    evaluateProbeOutput(raw), which decodes clean, LEAK, MISSING and STALE_IMAGE.
  • Stage it the way stage_script already stages its two siblings (bin/agentbox:973).
  • Let orchestrate.mjs, selftest.mjs and adversarial.mjs declare only the probes they
    add. orchestrate.mjs adds the forbiddenPaths probes; adversarial.mjs adds its
    negative cases.
  • Resolve each drifted probe deliberately. Record which test is correct, and use it in
    both places.
  • Extract the shared podman({...}) provider block the same way.
  • Give repo_snapshot/diff_snapshot and snapshotIntegrity/diffIntegrity one property
    set and one violation grammar, then drive both against one fixture corpus.

clone-integrity.mjs:10-12 already states the reason this shape is right:

It lives in its own file so that the comparison can be exercised directly, with no
container and no model credential.

Acceptance criteria

  • One module defines the isolation probes, and the three entry points read it.
  • Every row of the What never enters a sandbox table in docs/sandcastle.md has a probe that runs on a normal agentbox run.
  • The private-key probe and the Podman-socket probe each have one definition.
  • A probe asserts that every row of the documented table has a probe. It runs with no container and no Podman.
  • The podman({...}) provider block has one definition.
  • repo_snapshot/diff_snapshot reports the Git config per entry, not as one hash.
  • One fixture corpus exercises both integrity comparators, and a divergence fails the suite.
  • docs/sandcastle.md:756 is true, or it states the exact difference.
  • ./verify.sh remains green.
  • agentbox selftest --adversarial remains green on a live machine.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactorStructural change without intended behavior changesecuritySecurity-sensitive work

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions