Files
bot-bottle/tests/unit/test_pipelock_allowlist.py
T
didericis 2990c3c903
test / unit (pull_request) Successful in 16s
test / integration (pull_request) Successful in 25s
refactor(cred_proxy): rename Upstream -> Route, fix tea-login AttributeError
Three leftovers from the manifest refactor:

1. provision/cred_proxy.py:223 referenced u.kind == 'gitea' for the
   tea login count — kind was removed from the runtime class, so any
   bottle with a tea-login route raised AttributeError at provision
   time. Switch to `'tea-login' in r.roles`.

2. The runtime class CredProxyUpstream is renamed to CredProxyRoute
   (its data is a route on the proxy, not an "upstream"; the field
   route.upstream is the upstream URL). Module's own naming now
   aligns with manifest.CredProxyRoute and routes.json.

3. cred_proxy_upstreams_for_bottle -> cred_proxy_routes_for_bottle;
   CredProxyPlan.upstreams -> CredProxyPlan.routes; local
   `upstreams` collections become `routes`. Callers in
   backend.py, launch.py, prepare.py, bottle_plan.py,
   provision/cred_proxy.py, and tests updated.

Also strips lingering `bottle.tokens` references from docstrings
(pipelock.py, cred_proxy.py prepare(), manifest._parse_https_host,
test_pipelock_allowlist.py module doc) and removes dead helpers
from the integration test (the _bottle helper used a tokens field
that no longer parses).
2026-05-15 02:39:10 -04:00

100 lines
3.8 KiB
Python

"""Unit: pipelock_effective_allowlist — the union of baked-in defaults,
bottle.egress.allowlist, and cred-proxy upstream hosts derived from
bottle.cred_proxy.routes (PRD 0010). Git upstreams declared in bottle.git
do not contribute here; they flow through the per-agent git-gate (PRD 0008)."""
import unittest
from claude_bottle.manifest import Manifest
from claude_bottle.pipelock import (
pipelock_effective_allowlist,
pipelock_effective_tls_passthrough,
pipelock_token_hosts,
)
def _bottle(spec):
return Manifest.from_json_obj({
"bottles": {"dev": spec},
"agents": {"demo": {"skills": [], "prompt": "", "bottle": "dev"}},
}).bottles["dev"]
class TestEffectiveAllowlist(unittest.TestCase):
def test_union_and_dedup(self):
eff = pipelock_effective_allowlist(_bottle({
"egress": {
"allowlist": [
"registry.npmjs.org",
# Duplicate of a baked default; the union must dedupe.
"api.anthropic.com",
],
},
}))
self.assertIn("api.anthropic.com", eff, "baked default present")
self.assertIn("registry.npmjs.org", eff, "egress.allowlist present")
self.assertEqual(len(eff), len(set(eff)), "deduplicated")
self.assertEqual(eff, sorted(eff), "sorted")
def _routes(routes):
return {"cred_proxy": {"routes": routes}}
class TestTokenHosts(unittest.TestCase):
def test_each_route_contributes_its_upstream_host(self):
hosts = pipelock_token_hosts(_bottle(_routes([
{"path": "/gh-api/", "upstream": "https://api.github.com",
"auth_scheme": "Bearer", "token_ref": "GH"},
{"path": "/gh-git/", "upstream": "https://github.com",
"auth_scheme": "Bearer", "token_ref": "GH"},
])))
self.assertEqual(["api.github.com", "github.com"], hosts)
def test_dedupe_across_routes(self):
hosts = pipelock_token_hosts(_bottle(_routes([
{"path": "/a/", "upstream": "https://x.example",
"auth_scheme": "Bearer", "token_ref": "T1"},
{"path": "/b/", "upstream": "https://x.example",
"auth_scheme": "Bearer", "token_ref": "T2"},
])))
self.assertEqual(["x.example"], hosts)
def test_no_routes_empty(self):
self.assertEqual([], pipelock_token_hosts(_bottle({})))
class TestAllowlistWithTokens(unittest.TestCase):
def test_route_hosts_added_to_allowlist(self):
eff = pipelock_effective_allowlist(_bottle(_routes([
{"path": "/npm/", "upstream": "https://registry.npmjs.org",
"auth_scheme": "Bearer", "token_ref": "N"},
{"path": "/gh-api/", "upstream": "https://api.github.com",
"auth_scheme": "Bearer", "token_ref": "G"},
])))
self.assertIn("registry.npmjs.org", eff)
self.assertIn("api.github.com", eff)
class TestTlsPassthrough(unittest.TestCase):
def test_default_includes_api_anthropic(self):
passthrough = pipelock_effective_tls_passthrough(_bottle({}))
self.assertEqual(["api.anthropic.com"], passthrough)
def test_route_hosts_NOT_added_to_passthrough(self):
# cred-proxy now trusts pipelock's per-bottle CA, so pipelock
# can MITM the cred-proxy -> upstream leg and body-scan it.
# Auto-adding cred-proxy hosts to passthrough would silently
# disable that second scanner.
passthrough = pipelock_effective_tls_passthrough(_bottle(_routes([
{"path": "/gh-api/", "upstream": "https://api.github.com",
"auth_scheme": "Bearer", "token_ref": "G"},
{"path": "/npm/", "upstream": "https://registry.npmjs.org",
"auth_scheme": "Bearer", "token_ref": "N"},
])))
self.assertEqual(["api.anthropic.com"], passthrough)
if __name__ == "__main__":
unittest.main()