Commit Graph
2 Commits
Author SHA1 Message Date
Omar SobhandClaude Fable 5 4f253bec93 P6: browser.goto — real Chromium browsing with live web taint
- SandboxSpec gains an egress flag (default false — the kernel suite
  still proves zero-network for agent sandboxes). Egress-enabled
  containers exist ONLY for the browser: no credentials, no broker
  route, bridge network with host-gateway alias for local test pages
- images/agent-browser: Alpine Chromium, uid 10001, setuid bits
  stripped — same non-root hardening as agent-base
- browser.goto tool: headless chromium --dump-dom in the agent's
  browser container; HTML stripped to readable text (4k cap) and
  returned with output_taint=web; viewport screenshot captured,
  base64'd out of the container, stored in the blob store
- Taint semantics tightened: the step that PRODUCED untrusted output
  now carries its own taint (recorded before the step row), not just
  later steps — chat.inbox test updated to the stricter §15 reading
- GET /api/claws/{id}/browser/viewport.png serves the latest capture;
  BrowserApp polls it and renders the live viewport (spec §7.1),
  keeping the empty state until the agent has browsed
- Proven end to end with REAL Chromium against a REAL local page:
  content 'Revenue up 14 percent' returned tainted web; the gated
  email.send that follows carries 'web' in its approval taint_sources
  (untrusted content can never quietly reach outward); screenshot
  verified by PNG magic bytes

152 Rust tests + 63 frontend + 27 Playwright journeys.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-10 09:41:40 -05:00
Omar SobhandClaude Fable 5 ea5162ac65 P2 complete: Docker sandbox with kernel assertions + secret broker
tc-sandbox:
- SandboxSpec/SandboxDriver + DockerDriver (bollard): uid 10001, cap-drop
  ALL, no-new-privileges, embedded seccomp deny profile (unshare/ptrace/
  bpf/keyctl/mount/...), read-only rootfs with tmpfs /tmp + /home/agent,
  network=none, mem/cpu/pids limits
- agent-base image: non-root, all setuid binaries stripped
- 6 kernel-level assertion tests probing from INSIDE real containers:
  uid + CapEff==0, rootfs read-only, seccomp EPERM on unshare, zero
  traffic-carrying interfaces + failed egress connect, no setuid +
  NoNewPrivs=1, lifecycle

tc-secrets:
- ChaCha20-Poly1305 envelope encryption under a FileKey (generated 0600,
  AEAD tamper detection tested); secrets table ciphertext-at-rest
- teamclaw-broker daemon: length-prefixed JSON over a unix socket; no
  protocol operation ever returns plaintext; InvokeHttp independently
  consumes the single-use execution grant against Postgres BEFORE touching
  any credential, then performs the call itself with the secret injected
- Tests over the real socket + real Postgres + a real local HTTP receiver:
  encrypted at rest, pending approval refused, approved call carries the
  bearer token exactly once, grant replay refused, non-http URLs rejected

116 Rust + 61 frontend tests + 14 E2E journeys green.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-10 05:07:46 -05:00