From 3299674c308fe67b1eae418222e276ac9f5e2273 Mon Sep 17 00:00:00 2001 From: codex Date: Thu, 28 May 2026 19:08:34 -0400 Subject: [PATCH] fix(pipelock): disable bip39 detector by default --- bot_bottle/pipelock.py | 26 ++++++++++---------------- tests/unit/test_pipelock_yaml.py | 32 ++++++++++++-------------------- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/bot_bottle/pipelock.py b/bot_bottle/pipelock.py index ecff1aa..59fdcd3 100644 --- a/bot_bottle/pipelock.py +++ b/bot_bottle/pipelock.py @@ -84,14 +84,13 @@ def pipelock_effective_allowlist(bottle: Bottle) -> list[str]: def pipelock_seed_phrase_detection_enabled(bottle: Bottle) -> bool: - """Whether pipelock's BIP-39 seed-phrase detector stays on for - this bottle. + """Whether pipelock's BIP-39 seed-phrase detector stays on. LLM conversation bodies legitimately trip the detector — any 12+ - English words that pass the BIP-39 checksum match — so any - bottle that routes claude through pipelock's body scanner gets - blocked on the first real chat. We tried two narrower knobs - first: + English words that pass the BIP-39 checksum match — so agents can + get blocked on ordinary prompts/responses regardless of provider + (Claude, Codex/OpenAI, or future harnesses). We tried two narrower + knobs first: - `suppress: [{rule, path}]` — pipelock accepts the schema but the entry only silences the alert; the body_dlp block @@ -102,16 +101,11 @@ def pipelock_seed_phrase_detection_enabled(bottle: Bottle) -> bool: Empirically only `seed_phrase_detection.enabled: false` actually stops the block (verified by sending a 12-word BIP-39 body through three pipelock instances). It is a global toggle — - no per-path / per-host knob in pipelock 2.3.0 — so we turn the - detector off for the entire bottle when the bottle declares an - egress route to `api.anthropic.com`. The trade-off is - accepted: BIP-39 detection has little value in bot-bottle's - threat model (the agent has no access to a user's crypto wallet - seeds; the patterns that matter — gh*_, sk-ant-, AKIA, etc. — - keep firing).""" - return not any( - r.Host == "api.anthropic.com" for r in bottle.egress.routes - ) + no per-path / per-host knob in pipelock 2.3.0 — so we turn off + only this detector for every bottle. The rest of pipelock's DLP + defaults and request-body/header scanning remain enabled.""" + del bottle # kept for call-site stability and future policy knobs. + return False def pipelock_effective_tls_passthrough(bottle: Bottle) -> list[str]: diff --git a/tests/unit/test_pipelock_yaml.py b/tests/unit/test_pipelock_yaml.py index 99bbd54..7461234 100644 --- a/tests/unit/test_pipelock_yaml.py +++ b/tests/unit/test_pipelock_yaml.py @@ -98,25 +98,23 @@ class TestBuildConfig(unittest.TestCase): self.assertIn("ssrf", cfg) self.assertEqual({"ip_allowlist": ["172.20.0.0/16"]}, cfg["ssrf"]) - def test_seed_phrase_detection_left_at_default_when_no_anthropic_route(self): - # No override emitted -> pipelock keeps its built-in default - # (BIP-39 detection enabled). Bottles that don't carry an - # Anthropic route don't need the false-positive workaround. + def test_seed_phrase_detection_disabled_by_default(self): + # Only the broad BIP-39 detector is disabled. The rest of + # DLP remains enabled via the `dlp` and request-body sections. cfg = pipelock_build_config(fixture_minimal().bottles["dev"]) - self.assertNotIn("seed_phrase_detection", cfg) + self.assertEqual({"enabled": False}, cfg["seed_phrase_detection"]) - def test_seed_phrase_detection_disabled_for_anthropic_route(self): - # claude-code's chat bodies trip pipelock's BIP-39 detector + def test_seed_phrase_detection_disabled_for_openai_route(self): + # OpenAI/Codex chat bodies trip pipelock's BIP-39 detector # (12+ English words that pass the checksum). pipelock 2.3.0 # has no per-path knob for this detector, and both `suppress` # and `rules.disabled` only silence alerts — the block still # fires. The only knob that actually skips the block is the - # global on/off, so we flip it off whenever the bottle is set - # up to route claude through pipelock. + # global on/off. from bot_bottle.manifest import Manifest bottle = Manifest.from_json_obj({ "bottles": {"dev": {"egress": {"routes": [ - {"host": "api.anthropic.com", + {"host": "api.openai.com", "auth": {"scheme": "Bearer", "token_ref": "T"}}, ]}}}, "agents": {"demo": {"skills": [], "prompt": "", "bottle": "dev"}}, @@ -207,16 +205,10 @@ class TestRenderAndWrite(unittest.TestCase): self.assertIn("ip_allowlist:", text) self.assertIn('- "172.20.0.0/16"', text) - def test_render_emits_seed_phrase_off_for_anthropic_route(self): - from bot_bottle.manifest import Manifest - bottle = Manifest.from_json_obj({ - "bottles": {"dev": {"egress": {"routes": [ - {"host": "api.anthropic.com", - "auth": {"scheme": "Bearer", "token_ref": "T"}}, - ]}}}, - "agents": {"demo": {"skills": [], "prompt": "", "bottle": "dev"}}, - }).bottles["dev"] - text = pipelock_render_yaml(pipelock_build_config(bottle)) + def test_render_emits_seed_phrase_off_by_default(self): + text = pipelock_render_yaml( + pipelock_build_config(fixture_minimal().bottles["dev"]) + ) self.assertIn("seed_phrase_detection:", text) self.assertIn("enabled: false", text)