fix(bash-guard): close scheme-less curl bypass

Previous regex used a greedy [^|]* and required at least one / before
the host, so 'curl roboco-orchestrator:8000/api' (no scheme, no slash)
slipped through. Split into two simpler checks: (a) line starts with
curl/wget/http/https/httpie, AND (b) line contains a forbidden host.
Probe-verified: scheme-ful, scheme-less, and protocol-relative forms
all denied; external URLs still allowed; GitHub-specific deny still
fires first.
This commit is contained in:
Renn F
2026-05-03 06:17:37 +02:00
parent 8381ade3ce
commit c0c5838baa
2 changed files with 38 additions and 5 deletions
+29
View File
@@ -48,5 +48,34 @@ def test_blocks_internal_curl_to_127() -> None:
assert _run("curl http://127.0.0.1:8000/api/health") == _DENIED
def test_blocks_scheme_less_curl_to_orchestrator() -> None:
assert _run("curl roboco-orchestrator:8000/api") != _ALLOWED
def test_blocks_scheme_less_curl_to_localhost() -> None:
assert _run("curl localhost:8000/api") != _ALLOWED
def test_blocks_scheme_less_curl_to_127() -> None:
assert _run("curl 127.0.0.1:8000/api") != _ALLOWED
def test_allows_external_curl_to_documentation() -> None:
assert _run("curl https://docs.python.org/3/") == _ALLOWED
def test_github_url_still_uses_github_specific_deny() -> None:
"""Existing GitHub deny rule must fire BEFORE the new gateway deny."""
payload = json.dumps(
{"tool_name": "Bash", "tool_input": {"command": "curl https://api.github.com/user"}}
)
result = subprocess.run(
[str(GUARD)],
input=payload,
capture_output=True,
text=True,
)
assert result.returncode != _ALLOWED
# The GitHub-specific message should appear, not the gateway message.
combined = (result.stdout + result.stderr).lower()
assert "github" in combined or "pat" in combined