chore: remove all pipelock references from tests, docs, and non-pipelock source
lint / lint (push) Failing after 1m26s
test / unit (pull_request) Failing after 35s
test / integration (pull_request) Successful in 44s

- Strip pipelock from all unit and integration test fixtures:
  proxy_plan fields removed from DockerBottlePlan/SmolmachinesBottlePlan
  constructors; pipelock-specific test classes deleted or renamed
- Update test_sidecar_init: remove test_pipelock_loses_egress_tokens,
  rename "pipelock" daemon fixtures to "git-gate" throughout
- Remove test_pipelock_binary_present_and_versioned from integration test
- Remove test_pipelock_answers_on_bundle_ip from smolmachines launch test
- Update _SANDBOX_BLOCK_MARKERS: remove "pipelock" marker (egress blocks)
- Dockerfile.sidecars: remove pipelock build stage and COPY; update layout
  comments and port table
- egress_entrypoint.sh: update comments now that egress is sole proxy
- Clean up pipelock references in comments/docstrings across backend,
  network, manifest, supervise, git_gate, yaml_subset, agent_provider,
  sidecar_bundle, sidecar_init, egress_addon_core modules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 21:54:06 +00:00
parent bbd6ec85ac
commit a59da9921e
53 changed files with 266 additions and 945 deletions
+20 -24
View File
@@ -53,7 +53,7 @@ _FAKE_SECRETS = {
@skip_unless_docker()
@unittest.skipIf(
os.environ.get("GITEA_ACTIONS") == "true",
"skipped under act_runner: pipelock_tls_init uses a host bind mount "
"skipped under act_runner: egress_tls_init uses a host bind mount "
"the runner container can't see, and the network topology hides "
"sibling-sidecar visibility — same constraint as the other "
"bottle-bringup integration tests",
@@ -256,14 +256,11 @@ class TestSandboxEscape(unittest.TestCase):
# ---- attack 3: HTTP exfil shapes ---------------------------------
# Sandbox-block signature: pipelock / egress return HTTP 403 on
# policy reject; the response body carries a recognizable
# marker. Egress's reject message starts `"egress: host '...'
# is not in the bottle's egress.routes allowlist"`; pipelock's
# DLP rejects start `"blocked: "` (e.g.
# `"blocked: DLP match: Anthropic API Key (critical)"`,
# `"blocked: request body contains secret"`).
_SANDBOX_BLOCK_MARKERS = ("egress:", "pipelock", "blocked:")
# Sandbox-block signature: egress returns HTTP 403 on policy
# reject; the response body carries a recognizable marker.
# Egress's reject message starts with `"egress: host '...'
# is not in the bottle's egress.routes allowlist"`.
_SANDBOX_BLOCK_MARKERS = ("egress:", "blocked:")
def _assert_sandbox_block(self, label: str, r: object) -> None: # type: ignore
"""A real sandbox block produces an HTTP 403 with a
@@ -286,7 +283,7 @@ class TestSandboxEscape(unittest.TestCase):
self.assertTrue(
has_marker and http_code == "403",
f"{label}: expected a sandbox-level block (HTTP 403 with "
f"egress/pipelock marker), got HTTP {http_code!r}. "
f"egress marker), got HTTP {http_code!r}. "
f"If the response came from the actual upstream, the "
f"secret REACHED the network — that's the leak this "
f"test exists to catch. body={body!r} "
@@ -297,9 +294,8 @@ class TestSandboxEscape(unittest.TestCase):
"""The agent tries to leak `$TEST_SECRET_ANTHROPIC` to an
ALLOWLISTED destination (api.anthropic.com) via four
different request shapes. Each must be blocked by a
sandbox layer (pipelock DLP / egress filter / etc.) —
producing an HTTP 403 with an `egress:` or `pipelock`
marker in the response body.
sandbox layer (egress allowlist filter) — producing an
HTTP 403 with an `egress:` marker in the response body.
Anything else means the request reached upstream and the
secret leaked, even if the upstream responded with its
@@ -310,10 +306,10 @@ class TestSandboxEscape(unittest.TestCase):
remediation lands as its own PRD before this test merges.
DON'T mark expectedFailure to silence it.
Destination note: we use `raw.githubusercontent.com`, one
of the DEFAULT_ALLOWLIST hosts. It is not route-configured
for pipelock TLS passthrough, so pipelock MITMs it and the
DLP scan applies, which is what this attack exercises."""
Destination note: we use `raw.githubusercontent.com`, which
is not in the bottle's egress.routes allowlist, so egress
blocks every request to it regardless of the secret's
location in the request."""
# Capture HTTP code via curl's -w; don't use --fail so
# we get the response body even on 4xx.
url_base = "https://raw.githubusercontent.com"
@@ -352,13 +348,13 @@ class TestSandboxEscape(unittest.TestCase):
def test_4_dns_exfil_blocked(self) -> None:
"""Two sub-attacks against DNS:
4a — crafted subdomain that pipelock would resolve. The
hostname `<SECRET>.api.anthropic.com` looks "under"
the allowlisted apex but pipelock's allowlist is
exact-match — it should reject the host BEFORE
issuing the DNS query, so the secret never reaches
an external resolver.
4b — direct DNS query bypassing pipelock entirely. The
4a — crafted subdomain attack. The hostname
`<SECRET>.api.anthropic.com` looks "under" the
allowlisted apex but egress's allowlist is
exact-match — it rejects the host before issuing
a DNS query, so the secret never reaches an
external resolver.
4b — direct DNS query bypassing egress entirely. The
agent's internal network has no default gateway;
even an explicit resolver like 8.8.8.8 should be
unreachable. Confirms the network isolation is
@@ -2,8 +2,8 @@
Verifies that flipping `BOT_BOTTLE_SIDECAR_BUNDLE=1` produces a
working bottle: `docker compose up` brings the agent + bundle pair
online, the four daemons inside the bundle bind their ports, and
the agent can reach pipelock + supervise via the bundle's network
online, the daemons inside the bundle bind their ports, and the
agent can reach egress + supervise via the bundle's network
aliases (no agent-side config changes between flag positions).
Skipped under GITEA_ACTIONS — the bundle image is a multi-stage
@@ -27,11 +27,9 @@ from tests._docker import skip_unless_docker
def _manifest() -> Manifest:
"""Bottle with supervise on so the bundle exercises three of
the four daemons (pipelock, egress, supervise). Git is off
because a meaningful git-gate test needs a real upstream and
SSH keys — out of scope for a bundle smoke. Egress is
implicitly on as pipelock's upstream regardless of routes."""
"""Bottle with supervise on so the bundle exercises egress +
supervise. Git is off because a meaningful git-gate test needs
a real upstream and SSH keys — out of scope for a bundle smoke."""
return Manifest.from_json_obj({
"bottles": {
"dev": {
@@ -68,21 +66,16 @@ class TestSidecarBundleCompose(unittest.TestCase):
plan = backend.prepare(spec, stage_dir=stage_dir)
with backend.launch(plan) as bottle:
# The agent's HTTPS_PROXY URL (resolved at
# renderer-time, unchanged from the legacy
# shape) should reach pipelock inside the
# bundle. We probe by asking for the proxy's
# listening port from inside the agent.
# renderer-time) should reach egress inside
# the bundle. A bare CONNECT with no upstream
# URL gets rejected with 400 or 405 but proves
# the listener is alive at the alias.
probe = bottle.exec(
"set -eu\n"
"echo HTTPS_PROXY=$HTTPS_PROXY\n"
"PORT=$(echo \"$HTTPS_PROXY\" | sed -E 's|.*:([0-9]+).*|\\1|')\n"
"HOST=$(echo \"$HTTPS_PROXY\" | sed -E 's|http://([^:]+):.*|\\1|')\n"
"echo HOST=$HOST PORT=$PORT\n"
# nc is not in the agent image but curl is —
# a CONNECT with no upstream URL will get
# rejected by pipelock with 400 or 405 but
# confirms the listener is alive at the
# alias.
"curl -sS --max-time 5 -o /dev/null -w 'http=%{http_code}\\n' "
" \"http://$HOST:$PORT/\" || true\n"
)
@@ -98,11 +91,10 @@ class TestSidecarBundleCompose(unittest.TestCase):
shutil.rmtree(stage_dir, ignore_errors=True)
self.assertEqual(0, probe.returncode, msg=probe.stderr)
# pipelock answered SOMETHING — any 4xx is fine, just proves
# the bundle's pipelock daemon is listening at the
# `pipelock` alias on port 8888 (or whatever the env says).
# egress answered SOMETHING — any 4xx is fine, just proves
# the egress daemon is listening at the proxy address.
self.assertIn("http=", probe.stdout,
f"no HTTP response from pipelock: {probe.stdout!r}")
f"no HTTP response from egress: {probe.stdout!r}")
# supervise's /health endpoint exists (PRD 0013); it should
# answer 200 or similar — anything non-empty proves the
# third daemon's alias resolves to the same bundle.
+6 -11
View File
@@ -1,15 +1,15 @@
"""Integration: PRD 0024 chunk 1 — the sidecar bundle image builds
and the four daemon binaries are present + executable inside it.
and the daemon binaries are present + executable inside it.
This test does NOT exercise the daemons running against real
config (pipelock.yaml, routes.yaml, etc) — that lands in chunk 2
when the renderer wires the bundle into compose. What we verify
here is the chunk-1 contract:
config (routes.yaml, etc) — that lands in chunk 2 when the
renderer wires the bundle into compose. What we verify here is
the chunk-1 contract:
- Dockerfile.sidecars builds (multi-stage works, base layers
pull, COPYs resolve).
- pipelock, gitleaks, mitmdump are at the documented paths and
answer `--version`.
- gitleaks, mitmdump are at the documented paths and answer
`--version`.
- The Python init at /app/sidecar_init.py runs and prints the
expected "no daemons selected" line when the supervisor is
pointed at an empty daemon set.
@@ -74,11 +74,6 @@ class TestSidecarBundleImage(unittest.TestCase):
)
return proc.returncode, proc.stdout.decode("utf-8", errors="replace")
def test_pipelock_binary_present_and_versioned(self):
rc, out = self._run_in_image("/usr/local/bin/pipelock", "version")
self.assertEqual(0, rc, msg=out)
self.assertIn("pipelock version", out)
def test_gitleaks_binary_present_and_versioned(self):
rc, out = self._run_in_image("/usr/bin/gitleaks", "version")
self.assertEqual(0, rc, msg=out)
@@ -81,13 +81,9 @@ class TestBundleBringup(unittest.TestCase):
subnet=subnet,
gateway=gateway,
bundle_ip=bundle_ip,
# Only run the pipelock daemon for this smoke — it's
# the lightest of the four and doesn't need bind
# mounts beyond what we'd skip without
# BOT_BOTTLE_SIDECAR_DAEMONS. (The init
# supervisor will exit if pipelock fails to find its
# yaml — that's expected here; we just need the
# container to land on the network at the right IP.)
# Empty daemons_csv → init exits "no daemons selected"
# immediately. We just need the container to land on
# the network at the right IP before it exits.
daemons_csv="", # empty → init exits "no daemons selected"
)
start_bundle(spec)
@@ -124,32 +124,6 @@ class TestSmolmachinesLaunch(unittest.TestCase):
f"expected a connect-refusal message; got: {r.stdout!r}",
)
def test_pipelock_answers_on_bundle_ip(self):
# Chunk 4b: the bundle's pipelock daemon is now actually
# running (was daemons_csv="" in chunks 2d/3). From inside
# the guest, a TCP connect to <bundle-ip>:8888 must succeed
# — distinct from the egress-port-bypass probe below where
# the connect must FAIL.
#
# We don't try to speak proxy protocol here — pipelock will
# 4xx a bare GET — we just verify the socket answers.
r = self.bottle.exec(
f"wget -T 5 -t 1 -O - http://{self.plan.bundle_ip}:8888/ "
"2>&1 || true"
)
# Any HTTP response (even a 4xx) proves pipelock is up.
# "connection refused" / "unable to connect" / "timed out"
# would mean it isn't.
msg = r.stdout.lower()
self.assertNotIn(
"connection refused", msg,
f"pipelock connect refused — daemon not listening? {r.stdout!r}",
)
self.assertNotIn(
"timed out", msg,
f"pipelock connect timed out: {r.stdout!r}",
)
def test_prompt_file_lands_in_guest(self):
# provision_prompt copies the host-side prompt.txt into the
# guest at /root/.bot-bottle-prompt.txt. The content