feat(pipelock): allow route tls passthrough policy
test / unit (pull_request) Successful in 37s
test / integration (pull_request) Successful in 58s

This commit is contained in:
2026-05-28 19:19:40 -04:00
parent 3299674c30
commit bcadc07d09
11 changed files with 164 additions and 78 deletions
@@ -12,12 +12,11 @@ pipelock's per-bottle CA so curl trusts pipelock's bumped leaf, and
pipelock sees the decrypted body and returns its known
`blocked: request body contains secret: <pattern>` 403.
The host has to be allowlisted (so the CONNECT is accepted) but NOT
in `tls_interception.passthrough_domains` (so the body actually gets
scanned). `api.anthropic.com` is passthrough'd to skip MITM on the
LLM endpoint, so this probe targets `raw.githubusercontent.com` —
also on the baked allowlist (Claude Code fetches release assets from
it) and intercepted+scanned like any non-passthrough host."""
The host has to be allowlisted (so the CONNECT is accepted) but must
not opt into `pipelock.tls_passthrough` (so the body actually gets
scanned). This probe targets `raw.githubusercontent.com`, which is on
the baked allowlist and intercepted+scanned like any non-passthrough
host."""
from __future__ import annotations
@@ -1,17 +1,14 @@
"""Integration: pipelock's `tls_interception.passthrough_domains`
exempts api.anthropic.com from MITM, so request bodies that would
otherwise trip the body-scan layer (notably the BIP-39 seed-phrase
detector firing on user-authored Claude conversation text) are not
inspected and the request reaches Anthropic's TLS endpoint.
"""Integration: route-owned `pipelock.tls_passthrough` renders into
pipelock's `tls_interception.passthrough_domains`, so request bodies
that would otherwise trip the body-scan layer are not inspected and the
request reaches the provider TLS endpoint.
Probe: POST the canonical zero-entropy 12-word BIP-39 mnemonic
(`abandon` × 11 + `about`) — checksum-valid by construction — to
`https://api.anthropic.com/v1/messages`. Without the passthrough,
pipelock returns a 403 `blocked: request body contains secret:
BIP-39 Seed Phrase`. With it, pipelock relays the CONNECT opaquely
and the upstream replies with whatever it likes (401/4xx from
Anthropic for an unauthenticated junk POST). We assert that the
verdict is NOT pipelock's block.
`https://api.anthropic.com/v1/messages`. With the route policy,
pipelock relays the CONNECT opaquely and the upstream replies with
whatever it likes (401/4xx from Anthropic for an unauthenticated junk
POST). We assert that the verdict is NOT pipelock's block.
"""
from __future__ import annotations
@@ -46,7 +43,13 @@ class TestPipelockLlmPassthrough(unittest.TestCase):
def test_bip39_body_to_anthropic_is_not_blocked(self):
manifest = Manifest.from_json_obj({
"bottles": {
"dev": {"env": {"SEED": _BIP39_PHRASE}},
"dev": {
"env": {"SEED": _BIP39_PHRASE},
"egress": {"routes": [{
"host": "api.anthropic.com",
"pipelock": {"tls_passthrough": True},
}]},
},
},
"agents": {
"demo": {"skills": [], "prompt": "", "bottle": "dev"},
+4 -9
View File
@@ -310,15 +310,10 @@ class TestSandboxEscape(unittest.TestCase):
remediation lands as its own PRD before this test merges.
DON'T mark expectedFailure to silence it.
Destination note: we use `raw.githubusercontent.com` (one
of the DEFAULT_ALLOWLIST hosts) rather than
api.anthropic.com because pipelock passthrough's the
Anthropic API endpoint specifically — its DLP scanners
false-positive on real LLM conversation bodies (BIP-39
seed phrases, etc.). That trade-off is documented in
`pipelock.DEFAULT_TLS_PASSTHROUGH`. For non-passthrough
hosts pipelock MITMs and the DLP scan applies, which is
what this attack exercises."""
Destination note: we use `raw.githubusercontent.com`, one
of the DEFAULT_ALLOWLIST hosts. It is not route-configured
for pipelock TLS passthrough, so pipelock MITMs it and the
DLP scan applies, which is what this attack exercises."""
# Capture HTTP code via curl's -w; don't use --fail so
# we get the response body even on 4xx.
url_base = "https://raw.githubusercontent.com"