PRD: Egress token-block policy (supervise / redact / block) #262
Reference in New Issue
Block a user
Delete Branch "prd-0062-egress-supervisor-token-allow"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #261.
PRD: https://gitea.dideric.is/didericis/bot-bottle/src/branch/prd-0062-egress-supervisor-token-allow/docs/prds/0062-egress-supervisor-token-override.md
Summary
Gives each egress route a policy for what happens when an outbound DLP detector matches a token —
dlp.outbound_on_match: block | redact | supervise. The goal (issue #261) is cutting false-positive friction without weakening default-deny.supervise(default for manifest routes) — hold the request and surface anegress-token-allowproposal in./cli.py supervise. On approval the matched value joins an in-memory safelist for the life of the proxy, so the request and later ones carrying it flow through. Fails closed on rejection / timeout / missing supervise wiring.redact— scrub the matched value(s) from the body, non-hostheaders, and path/query, then forward. Fails closed (403) if a match lands on a surface redaction can't rewrite (the hostname, or a unicode-evasion token).block— the original hard403, never overridable.Structural blocks (CRLF, no safelist-able value) stay hard
403s underblock/supervise. The supervise flow mirrors git-gate'sgitleaks-allow(PRD 0061): the addon writes the proposal directly to the shared queue and polls for the response on an async hook so the proxy event loop isn't stalled.Provider routes default to
redactAgent-provider routes (the agent talking to its own LLM API —
api.anthropic.com, the Codex backend, etc.) carry the whole conversation payload, which is the worst source of token-shaped false positives. They default toredact(filled centrally inegress_routes_for_bottle), so a match there is scrubbed and forwarded rather than blocked or queued. A provider that setsoutbound_on_matchexplicitly keeps its choice; user-declared manifest routes still default tosupervise. (Tradeoff: token-shaped values in a prompt are scrubbed before reaching the LLM — a leak-prevention plus, but the model won't see a real token you ask it to debug.)CRLF false-positive fix
The
URL-encoded CRLF (%0d%0a)detector was firing on legitimate requests (the Claude Code login flow) and bypassing the policy as a hard 403. Root cause: CRLF injection is only an attack in the request line + headers — an HTTP body is delimited by Content-Length, so CRLF bytes there can't split anything — but the scan flattened the body into the same blob. CRLF is now scanned only over the body-excluded request line + headers (websocket data frames excluded too), andredactalso scrubs CRLF sequences.Schema
New route field, threaded manifest → rendered
routes.yaml→ addon and round-tripped vialist-egress-routes:Scope / non-goals
allow/egress-blockMCP path; inbound prompt-injection blocks are unchanged.EGRESS_TOKEN_ALLOW_TIMEOUT_SECONDS(default 300) bounds the supervise wait.Tests
1117 unit tests pass. Coverage:
matched/safe_tokensplumbing andbuild_token_allow_payload(asserts the raw token never appears), theegress-token-allowtool round-trip and TUI approve/reason/suffix paths,outbound_on_matchparse+validation at the manifest and core layers, a full manifest→render→addon round-trip forredact, the provider-default (+ explicit-override preservation), CRLF body-exclusion /strip_crlf. The pre-existing integration failures (test_smolmachines_launch,test_sandbox_escape) need real backends and fail identically on a clean tree.PRD 0062: Supervisor override for egress token blocksto PRD 0062: Egress token-block policy (supervise / redact / block)PRD 0062: Egress token-block policy (supervise / redact / block)to PRD: Egress token-block policy (supervise / redact / block)