diff --git a/capabilities/web-security/capability.yaml b/capabilities/web-security/capability.yaml index 4d8e418..6367238 100644 --- a/capabilities/web-security/capability.yaml +++ b/capabilities/web-security/capability.yaml @@ -1,6 +1,6 @@ schema: 1 name: web-security -version: "2.0.3" +version: "2.0.4" description: > Web application penetration testing with 83 attack technique playbooks covering HTTP desync/request smuggling, cache poisoning, SSRF, SSTI, DOM diff --git a/capabilities/web-security/docker/Dockerfile.runtime b/capabilities/web-security/docker/Dockerfile.runtime index c6c7604..80dc1d7 100644 --- a/capabilities/web-security/docker/Dockerfile.runtime +++ b/capabilities/web-security/docker/Dockerfile.runtime @@ -143,9 +143,15 @@ RUN pip install --no-cache-dir \ "fastmcp>=2.0" \ "httpx>=0.28" \ "caido-sdk-client>=0.3.0" \ - "pacu" \ "ast-grep-cli" +# ── Pacu (AWS exploitation framework) ─────────────────────────────── +# Own tool environment: pacu pins urllib3<2 and botocore<1.32, which would +# downgrade the packages above. The venv lives outside /root so `runner` can +# run it; the launcher is linked into /usr/local/bin. +RUN UV_TOOL_DIR=/opt/uv-tools UV_TOOL_BIN_DIR=/usr/local/bin \ + uv tool install --python /usr/bin/python3 pacu==1.7.0 + # ── fireprox (AWS API Gateway IP rotation) ────────────────────────── # Installed to a predictable path; requires AWS credentials at runtime. RUN git clone --depth 1 https://github.com/ustayready/fireprox /root/git/fireprox \ diff --git a/capabilities/web-security/scripts/install_tools.sh b/capabilities/web-security/scripts/install_tools.sh index 15442af..5812c2f 100755 --- a/capabilities/web-security/scripts/install_tools.sh +++ b/capabilities/web-security/scripts/install_tools.sh @@ -435,8 +435,10 @@ install_waymore() { run_stage waymore install_waymore # -- Pacu (AWS exploitation framework) ---------------------------------------- +# Installed in its own uv tool environment: pacu pins urllib3<2 and +# botocore<1.32, which would downgrade the shared sandbox venv. install_pacu() { - have pacu || retry py_install pacu || { echo "WARN: pacu install failed, skipping" >&2; return 1; } + have pacu || retry uv tool install pacu==1.7.0 || { echo "WARN: pacu install failed, skipping" >&2; return 1; } } run_stage pacu install_pacu diff --git a/capabilities/web-security/tests/test_install_tools_offline.py b/capabilities/web-security/tests/test_install_tools_offline.py index 4ecfaff..391424d 100644 --- a/capabilities/web-security/tests/test_install_tools_offline.py +++ b/capabilities/web-security/tests/test_install_tools_offline.py @@ -811,3 +811,20 @@ def test_sealed_install_checks_local_tools_without_fetches_or_retries( assert "WARN: optional artifact unavailable:" in result.stderr if missing_required: assert "Missing required tool: nuclei" in result.stderr + + +class TestPacuOwnToolEnvironment: + """pacu pins urllib3<2 and botocore<1.32, so it must not share the venv.""" + + def test_install_script_uses_isolated_uv_tool(self) -> None: + stage = _shell_function("install_pacu") + assert "have pacu || retry uv tool install pacu==1.7.0" in stage + assert "py_install pacu" not in INSTALL_SCRIPT + + def test_runtime_image_installs_pacu_in_own_environment(self) -> None: + dockerfile = (ROOT / "docker" / "Dockerfile.runtime").read_text( + encoding="utf-8" + ) + assert "uv tool install" in dockerfile + assert "pacu==1.7.0" in dockerfile + assert not re.search(r'^\s*"pacu"', dockerfile, re.M)