The sidebar's gstackInjectToTerminal callers (toolbar Cleanup,
Inspector "Send to Code") were piping page-derived text directly into
the live claude PTY with ZERO classifier processing — the gap codex
flagged in #1370. The documented sidebar security stack had a hole
the size of every Cleanup-button click.
Adds POST /pty-inject-scan to browse/src/server.ts:
- Local-only binding (NOT in TUNNEL_PATHS — tunnel attempts get the
general 404 path; never reaches the scan logic)
- Root-token auth via existing validateAuth() — 401 on unauth
- 64KB request cap → 413 + payload-too-large body
- 5s scan timeout via sidecar client
- URL-blocklist forced to BLOCK in PTY context (page-derived REPL
input is higher-risk than ordinary tool output)
- L4 ML classifier via the sidecar when available; degrades to WARN
per D7 when sidecar is unavailable
- Response goes through JSON.stringify(..., sanitizeReplacer) per
v1.38.0.0 Unicode-egress hardening
- Imports only from security-sidecar-client.ts, never directly from
security-classifier.ts (which would brick the compiled Bun binary)
Seven static-invariant tests pin the POST verb, auth gate, 64KB cap,
tunnel-listener exclusion, sanitizeReplacer wrapping, l4 availability
shape, and the no-direct-classifier-import rule.
C19 of the security-stack wave. C20 routes the extension through it;
C21 adds the invariant AST check.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>