From c4de42ea3c4914e67f525e7378f30c28762e7043 Mon Sep 17 00:00:00 2001 From: didericis Date: Tue, 12 May 2026 13:40:31 -0400 Subject: [PATCH] feat(mitmproxy): render mitmproxy in the dry-run preflight MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third step of PRD 0005. The preflight now surfaces the TLS- intercept layer so the operator sees it before agreeing to launch. - Text output: one new line under the egress summary — "tls intercept : mitmproxy (per-bottle ephemeral CA, generated at launch)". - JSON output (--format=json contract): new egress.mitm: { enabled: true, ca_fingerprint: null } block. Fingerprint is always null at dry-run because the CA only exists after the sidecar starts; real launches print it as a stderr log line from provision_ca. - Pin the new shape in the dry-run integration test. Co-Authored-By: Claude Opus 4.7 --- claude_bottle/backend/docker/bottle_plan.py | 9 +++++++++ tests/integration/test_dry_run_plan.py | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/claude_bottle/backend/docker/bottle_plan.py b/claude_bottle/backend/docker/bottle_plan.py index c3d76a7..293e965 100644 --- a/claude_bottle/backend/docker/bottle_plan.py +++ b/claude_bottle/backend/docker/bottle_plan.py @@ -95,6 +95,7 @@ class DockerBottlePlan(BottlePlan): else: info(" ssh hosts : (none)") info(f" egress : {self.allowlist_summary}") + info(" tls intercept : mitmproxy (per-bottle ephemeral CA, generated at launch)") info( f"prompt : {len(v.agent.prompt)} chars; " f"first line: {v.prompt_first_line or '(empty)'}" @@ -119,6 +120,14 @@ class DockerBottlePlan(BottlePlan): "egress": { "host_count": len(hosts), "hosts": hosts, + # Reserved for PRD 0005: TLS interception via mitmproxy. + # ca_fingerprint is always null at dry-run because the + # CA is generated by the sidecar at launch time. Real + # launches print the fingerprint to stderr. + "mitm": { + "enabled": True, + "ca_fingerprint": None, + }, }, "prompt": { "length": len(v.agent.prompt), diff --git a/tests/integration/test_dry_run_plan.py b/tests/integration/test_dry_run_plan.py index c0ae3eb..45c4564 100644 --- a/tests/integration/test_dry_run_plan.py +++ b/tests/integration/test_dry_run_plan.py @@ -92,6 +92,12 @@ class TestDryRunPlan(unittest.TestCase): self.assertEqual(sorted(set(hosts)), hosts, "hosts must be sorted and deduplicated") + # PRD 0005: TLS interception block is part of the JSON + # contract. Fingerprint is null at dry-run (CA doesn't + # exist yet); real launches print it to stderr. + self.assertEqual({"enabled": True, "ca_fingerprint": None}, + plan["egress"]["mitm"]) + # No Docker side effects (see the GITEA_ACTIONS skip note # above — this guard runs locally only). if check_side_effects: