feat(egress-proxy): retarget remediation flow (PRD 0017 chunk 3) #30

Merged
didericis merged 18 commits from egress-proxy-block-remediation into main 2026-05-25 20:34:24 -04:00
4 changed files with 15 additions and 9 deletions
Showing only changes of commit fad76d3364 - Show all commits
+1 -1
View File
@@ -278,7 +278,7 @@ def _run_agent_container(plan: DockerBottlePlan, internal_network: str) -> str:
docker_args.extend(["-e", name])
# PRD 0013: read-only current-config mount so the agent can read
# routes.json / allowlist / Dockerfile before composing a
# routes.yaml / allowlist / Dockerfile before composing a
# supervise tool-call proposal. Mounted from the per-bottle
# stage_dir/current-config/ populated at prepare time.
if plan.supervise_plan is not None:
+8 -2
View File
@@ -425,7 +425,13 @@ def sha256_hex(content: str) -> str:
# Filenames inside the per-bottle current-config dir. The agent reads
# these (read-only) from CURRENT_CONFIG_DIR_IN_AGENT and proposes
# modified versions back via the three MCP tools.
CURRENT_CONFIG_ROUTES = "routes.json"
# Filename of the staged egress-proxy routes file inside the agent's
# read-only current-config mount. JSON content under a `.yaml`
# extension to match the live file the egress-proxy sidecar reads
# (`/etc/egress-proxy/routes.yaml`) — the egress-proxy-block tool
# description points at this exact path, and the apply step writes
# the new content to the matching live path.
CURRENT_CONFIG_ROUTES = "routes.yaml"
CURRENT_CONFIG_ALLOWLIST = "allowlist"
CURRENT_CONFIG_DOCKERFILE = "Dockerfile"
@@ -437,7 +443,7 @@ class SupervisePlan:
`queue_dir` is the host directory bind-mounted into the sidecar
at /run/supervise/queue. `current_config_dir` is the host
directory bind-mounted (read-only) into the *agent* container at
/etc/claude-bottle/current-config, holding routes.json + allowlist
/etc/claude-bottle/current-config, holding routes.yaml + allowlist
+ Dockerfile so the agent can read them before composing a
proposal. `internal_network` is empty at prepare time; the
backend's launch step fills it via dataclasses.replace before
+1 -1
View File
@@ -38,7 +38,7 @@ FIXED = datetime(2026, 5, 25, 12, 0, 0, tzinfo=timezone.utc)
def _proposal(slug: str = "dev", tool: str = TOOL_EGRESS_PROXY_BLOCK) -> Proposal:
# Per-tool payload shape: cred-proxy gets routes.json, pipelock
# Per-tool payload shape: cred-proxy gets routes.yaml, pipelock
# gets a failed URL (PR #25 follow-up), capability gets a
# Dockerfile-ish blob. Match the production dispatch in
# PROPOSED_FILE_FIELD.
+5 -5
View File
@@ -297,9 +297,9 @@ class TestDiffAndHash(unittest.TestCase):
self.assertEqual("", render_diff("a\nb\n", "a\nb\n"))
def test_render_diff_shows_changes(self):
diff = render_diff("a\nb\nc\n", "a\nB\nc\n", label="routes.json")
self.assertIn("routes.json (current)", diff)
self.assertIn("routes.json (proposed)", diff)
diff = render_diff("a\nb\nc\n", "a\nB\nc\n", label="routes.yaml")
self.assertIn("routes.yaml (current)", diff)
self.assertIn("routes.yaml (proposed)", diff)
self.assertIn("-b", diff)
self.assertIn("+B", diff)
@@ -365,7 +365,7 @@ class TestSupervisePrepare(unittest.TestCase):
self.assertTrue(plan.current_config_dir.is_dir())
self.assertEqual(
'{"routes": [{"path": "/x/"}]}\n',
(plan.current_config_dir / "routes.json").read_text(),
(plan.current_config_dir / "routes.yaml").read_text(),
)
self.assertEqual(
"example.com\n",
@@ -382,7 +382,7 @@ class TestSupervisePrepare(unittest.TestCase):
plan = _StubSupervise().prepare("dev", self.stage_dir)
self.assertEqual(
'{"routes": []}\n',
(plan.current_config_dir / "routes.json").read_text(),
(plan.current_config_dir / "routes.yaml").read_text(),
)