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]>
This commit is contained in:
Omar Sobh
2026-06-10 09:41:40 -05:00
co-authored by Claude Fable 5
parent 7392ce1d08
commit 4f253bec93
20 changed files with 531 additions and 28 deletions
+3
View File
@@ -136,6 +136,8 @@ impl Default for SlackConfig {
pub struct SandboxConfig {
/// Agent sandbox image (must exist locally / be preloaded in cluster).
pub image: String,
/// Chromium image for browser.goto (the only egress-enabled sandbox).
pub browser_image: String,
/// Disable to run without environment tools (shell.exec errors).
pub enabled: bool,
}
@@ -144,6 +146,7 @@ impl Default for SandboxConfig {
fn default() -> Self {
SandboxConfig {
image: "teamclaw/agent-base:dev".into(),
browser_image: "teamclaw/agent-browser:dev".into(),
enabled: true,
}
}