9cd583fbbb
Finishes PRD 0017. The `cred-proxy-block` MCP tool is renamed and
its remediation apply path is repointed at egress-proxy.
- `claude_bottle/supervise.py` — `TOOL_CRED_PROXY_BLOCK` →
`TOOL_EGRESS_PROXY_BLOCK`; `COMPONENT_FOR_TOOL` maps the new
tool ID to `egress-proxy` for audit-log routing.
- `claude_bottle/supervise_server.py` — tool definition renamed
+ description rewritten: "Call when egress-proxy refused your
HTTPS request ... Read the current routes.yaml from /etc/
claude-bottle/current-config/routes.yaml, compose a modified
version, pass the full new file plus a justification." The
syntactic validator dispatches on the new tool ID.
- `claude_bottle/backend/docker/egress_proxy_apply.py` — renamed
from `cred_proxy_apply.py`. Reads routes.yaml from
/etc/egress-proxy/routes.yaml via `docker exec cat`; validates
via `egress_proxy_addon_core.load_routes` (so both sides use
the same parser); writes via `docker cp`; SIGHUPs egress-proxy
with `docker kill --signal HUP`. `EgressProxyApplyError`
replaces `CredProxyApplyError`.
- `claude_bottle/cli/dashboard.py` — wires the new apply +
`discover_egress_proxy_slugs` helper; the operator-initiated
`routes edit <bottle>` verb now writes to egress-proxy with
`.yaml` suffix. Stale follow-up comment about path-aware
filtering removed — PRD 0017 settled that question.
- `tests/integration/test_supervise_sidecar.py` — restores the
approval round-trip test (chunk 2 had switched it to a reject
path because no cred-proxy existed). Approval stubs
`apply_routes_change` so the test focuses on the supervise
queue/response plumbing rather than docker-exec into a real
egress-proxy sidecar (that's covered separately).
- `tests/unit/test_egress_proxy_apply.py` — rewritten against
the new validator; covers JSON shape, missing routes key,
partial-auth-pair rejection (the addon-core parser catches
these before SIGHUP).
- PRDs 0010 + 0014 — status headers updated to
Superseded / Retargeted with a callout block pointing at PRD
0017's migration section. Historical text preserved.
384 unit + integration tests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
57 lines
1.9 KiB
Python
57 lines
1.9 KiB
Python
"""Unit: validate_routes_content (PRD 0014 retargeted by PRD 0017
|
|
chunk 3). docker exec / cp / kill paths are covered by the
|
|
integration test."""
|
|
|
|
import unittest
|
|
|
|
from claude_bottle.backend.docker.egress_proxy_apply import (
|
|
EgressProxyApplyError,
|
|
validate_routes_content,
|
|
)
|
|
|
|
|
|
class TestValidateRoutesContent(unittest.TestCase):
|
|
def test_accepts_minimal_route_table(self):
|
|
validate_routes_content('{"routes": []}')
|
|
validate_routes_content(
|
|
'{"routes": [{"host": "api.github.com"}]}'
|
|
)
|
|
|
|
def test_accepts_full_route(self):
|
|
validate_routes_content(
|
|
'{"routes": [{"host": "api.github.com",'
|
|
' "path_allowlist": ["/repos/x/"],'
|
|
' "auth_scheme": "Bearer",'
|
|
' "token_env": "EGRESS_PROXY_TOKEN_0"}]}'
|
|
)
|
|
|
|
def test_rejects_bad_json(self):
|
|
with self.assertRaises(EgressProxyApplyError) as cm:
|
|
validate_routes_content("{not json")
|
|
self.assertIn("not valid", str(cm.exception))
|
|
|
|
def test_rejects_non_object_top_level(self):
|
|
with self.assertRaises(EgressProxyApplyError):
|
|
validate_routes_content("[]")
|
|
|
|
def test_rejects_missing_routes_key(self):
|
|
with self.assertRaises(EgressProxyApplyError):
|
|
validate_routes_content('{"other": []}')
|
|
|
|
def test_rejects_non_list_routes(self):
|
|
with self.assertRaises(EgressProxyApplyError):
|
|
validate_routes_content('{"routes": "not a list"}')
|
|
|
|
def test_rejects_partial_auth_pair(self):
|
|
# The addon-core parser enforces both-or-neither — the apply
|
|
# path picks this up before SIGHUP'ing the sidecar.
|
|
with self.assertRaises(EgressProxyApplyError):
|
|
validate_routes_content(
|
|
'{"routes": [{"host": "x.example",'
|
|
' "auth_scheme": "Bearer"}]}'
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|