Files
bot-bottle/tests/unit/test_pipelock_allowlist.sh
T
didericis b0ee7da5be test: add bash test suite covering pipelock helpers and smoke flows
Adds tests/ with a tiny bash assert harness, manifest fixtures, and a
runner. No framework dependency — each test file is self-contained
and exits 0 on pass / 1 on fail; tests/run_tests.sh aggregates.

Unit tests (no docker):
  - pipelock_naming: container_name, proxy_url, proxy_host_port shape
  - pipelock_classify: _pipelock_is_ipv4_literal classifier coverage
  - pipelock_allowlist: bottle_allowlist + ssh hostnames/ip_cidrs/
    trusted_domains + effective_allowlist union/dedup/sort, plus
    rejection of non-string entries
  - pipelock_yaml: emitter shape (mode/enforce/api_allowlist/forward_proxy/
    dlp), conditional ssrf+trusted_domains blocks, secret hygiene
    (manifest env values must not appear in YAML), file mode 600

Integration tests (require docker, skip cleanly otherwise):
  - pipelock_image: pinned digest's ENTRYPOINT is /pipelock and CMD
    contains 'run' and the binary --version succeeds — would catch a
    future image bump that changes the launcher's argv contract
  - pipelock_sidecar_smoke: docker create + cp YAML to /etc/pipelock.yaml
    + start, then probe /health — the regression test for the bug
    where the YAML was written to /etc/pipelock/ (parent dir absent in
    the distroless image)
  - dry_run_plan: cli.sh start --dry-run shows the egress line,
    counts the bottle's entry into the effective allowlist, prints
    the dry-run banner, and creates zero docker resources
  - orphan_cleanup: the cleanup primitives the start-flow trap depends
    on (network_remove, pipelock_stop) are idempotent against
    missing/never-existed resources, so the trap is safe even if
    pipelock_start dies before everything is wired up

Assisted-by: Claude Code
2026-05-08 01:54:25 -04:00

90 lines
3.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Unit: allowlist resolution — pipelock_bottle_allowlist,
# pipelock_bottle_ssh_hostnames, pipelock_bottle_ssh_ip_cidrs,
# pipelock_bottle_ssh_trusted_domains, pipelock_effective_allowlist.
TEST_NAME="pipelock_allowlist"
. "$(dirname "$0")/../lib/common.sh"
# shellcheck source=../../lib/log.sh
. "${REPO_ROOT}/lib/log.sh"
# shellcheck source=../../lib/pipelock.sh
. "${REPO_ROOT}/lib/pipelock.sh"
# --- bottle_allowlist (egress.allowlist parsing) ---
m="$(write_fixture fixture_with_egress)"
out="$(pipelock_bottle_allowlist "$m" dev)"
assert_contains "$out" "github.com" "bottle_allowlist: github.com present"
assert_contains "$out" "gitlab.com" "bottle_allowlist: gitlab.com present"
assert_contains "$out" "registry.npmjs.org" "bottle_allowlist: npmjs present"
rm -f "$m"
m="$(write_fixture fixture_minimal)"
out="$(pipelock_bottle_allowlist "$m" dev)"
assert_eq "" "$out" "bottle_allowlist: empty when no egress block"
rm -f "$m"
# --- ssh hostnames + classification ---
m="$(write_fixture fixture_with_ssh)"
hosts="$(pipelock_bottle_ssh_hostnames "$m" dev)"
assert_contains "$hosts" "100.78.141.42" "ssh_hostnames: ipv4 included"
assert_contains "$hosts" "github.com" "ssh_hostnames: hostname included"
cidrs="$(pipelock_bottle_ssh_ip_cidrs "$m" dev)"
assert_contains "$cidrs" "100.78.141.42/32" "ssh_ip_cidrs: ipv4 emitted as /32"
assert_not_contains "$cidrs" "github.com" "ssh_ip_cidrs: hostname not in cidr list"
trusted="$(pipelock_bottle_ssh_trusted_domains "$m" dev)"
assert_contains "$trusted" "github.com" "ssh_trusted_domains: hostname present"
assert_not_contains "$trusted" "100.78.141.42" "ssh_trusted_domains: ipv4 not present"
rm -f "$m"
# --- effective_allowlist union (defaults + bottle.allowlist + ssh.Hostname) ---
# Combine egress + ssh fixtures into one manifest.
combined="$(mktemp)"
cat > "$combined" <<'JSON'
{
"bottles": {
"dev": {
"egress": { "allowlist": ["registry.npmjs.org"] },
"ssh": [
{ "Host": "ts", "IdentityFile": "/dev/null", "Hostname": "100.78.141.42", "User": "git", "Port": 30009 },
{ "Host": "gh", "IdentityFile": "/dev/null", "Hostname": "github.com", "User": "git", "Port": 22 }
]
}
},
"agents": { "demo": { "skills": [], "prompt": "", "bottle": "dev" } }
}
JSON
eff="$(pipelock_effective_allowlist "$combined" dev)"
assert_contains "$eff" "api.anthropic.com" "effective: baked-in default present"
assert_contains "$eff" "registry.npmjs.org" "effective: bottle egress entry present"
assert_contains "$eff" "100.78.141.42" "effective: ssh ipv4 hostname present"
assert_contains "$eff" "github.com" "effective: ssh hostname present"
# Ensure dedup + sort: count lines, then count unique lines, expect equal.
total="$(printf '%s\n' "$eff" | wc -l | tr -d ' ')"
uniq="$(printf '%s\n' "$eff" | sort -u | wc -l | tr -d ' ')"
assert_eq "$total" "$uniq" "effective: deduplicated"
rm -f "$combined"
# --- non-string entry rejection ---
bad="$(mktemp)"
cat > "$bad" <<'JSON'
{
"bottles": { "dev": { "egress": { "allowlist": ["github.com", 42] } } },
"agents": { "demo": { "skills": [], "prompt": "", "bottle": "dev" } }
}
JSON
assert_exit_nonzero "bottle_allowlist: rejects non-string entry" \
bash -c '. "'"${REPO_ROOT}"'/lib/log.sh"; . "'"${REPO_ROOT}"'/lib/pipelock.sh"; pipelock_bottle_allowlist "'"$bad"'" dev'
rm -f "$bad"
test_summary