diff --git a/Dockerfile b/Dockerfile index acb3ced1..839778a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -64,7 +64,8 @@ RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ && apt-get install -y "nodejs=${NODEJS_VERSION}" \ && rm -rf /var/lib/apt/lists/* \ && corepack enable \ - && corepack prepare "yarn@${YARN_VERSION}" --activate + && corepack prepare "yarn@${YARN_VERSION}" --activate \ + && npm i -g sfw ENV GO_VERSION=1.23.5 @@ -81,6 +82,7 @@ RUN echo "=== Installed versions ===" \ && uv --version \ && node --version \ && yarn --version \ + && sfw --version \ && go version \ && docker --version \ && git --version \ diff --git a/agent/prompt.py b/agent/prompt.py index 7726783f..3f38898c 100644 --- a/agent/prompt.py +++ b/agent/prompt.py @@ -244,8 +244,12 @@ If you encounter missing dependencies, install them using the appropriate packag - Use the correct package manager for the project; skip if installation fails. - Only install dependencies if the task requires it. -- Before any package install, ensure Socket Firewall Free (`sfw`) is available with `command -v sfw`. If missing, install it with `npm i -g sfw`; if that fails, report the failure and skip the install. -- Prefix every package-manager command that fetches packages from a registry with `sfw` (for example: `sfw npm ci`, `sfw pnpm install`, `sfw pip install -r requirements.txt`, `sfw uv pip install -e .`, `sfw poetry install`). Do not run bare install commands. +- Before ADDING a new dependency the project does not already declare, first confirm the task cannot be solved with the standard library or a package already in the project's manifest/lockfile. Prefer reusing what is already there. +- Vet any genuinely new package before adding it: it should be actively maintained (a recent release, responsive issues, more than a single maintainer, steady downloads), free of known unpatched CVEs (check with `npm audit` / `pip-audit` or the GitHub advisory database), and under a permissive license (MIT, Apache-2.0, BSD). Do not add abandoned, single-source, or unlicensed packages. +- Pin or bound every newly added dependency to a specific version in the project's manifest; never add a floating or unpinned dependency. +- You cannot pause to ask for approval mid-task, so call out every dependency you add in the PR description — the package name, why it is needed, its maintenance/security status, and the alternatives you considered — so a human reviewer can veto it. This vetting is complementary to the `sfw` runtime firewall below: vetting screens out poorly-maintained or risky packages, `sfw` blocks actively-malicious ones at install time. +- Before any supported package install, ensure Socket Firewall Free (`sfw`) is available with `command -v sfw`. If missing, install it with `npm i -g sfw`; if that fails, report the failure and skip the protected install. +- Prefix supported package-manager commands that fetch packages from a registry with `sfw`: npm/yarn/pnpm, pip/uv, and cargo (for example: `sfw npm ci`, `sfw pnpm install`, `sfw pip install -r requirements.txt`, `sfw uv pip install -e .`, `sfw cargo fetch`). For unsupported package managers such as Poetry, run the normal documented install command without `sfw`. - Always ensure dependencies are installed before running a script that might require them.""" diff --git a/agent/reviewer.py b/agent/reviewer.py index b8ceafec..4f0d1fd3 100644 --- a/agent/reviewer.py +++ b/agent/reviewer.py @@ -109,10 +109,11 @@ Call `publish_review` once at the end. Dependency installs during review: only install packages when needed to verify the PR. Before any install, check `command -v sfw`; if missing, install Socket -Firewall Free with `npm i -g sfw`. Prefix registry-fetching installs with -`sfw` (for example, `sfw npm ci`, `sfw pnpm install`, -`sfw pip install -r requirements.txt`, `sfw uv pip install -e .`). -Do not run bare install commands. +Firewall Free with `npm i -g sfw`. Prefix supported registry-fetching installs +with `sfw`: npm/yarn/pnpm, pip/uv, and cargo (for example, `sfw npm ci`, +`sfw pnpm install`, `sfw pip install -r requirements.txt`, +`sfw uv pip install -e .`). For unsupported package managers such as Poetry, +run the normal documented install command without `sfw`. If `publish_review` returns `unresolvable_findings`, do NOT retry with the same args — call `update_finding(status="resolved", note="...")` on those ids, or fix @@ -232,6 +233,16 @@ carefully before reaching for unchanged code. that is anchored to a changed line — these are mandatory repo rules, not style nits, so a violation is a legitimate finding even when it would otherwise look like a convention nit. +8. **New dependencies.** When the diff adds a dependency to a manifest or + lockfile (`package.json`, `pyproject.toml`, `requirements*.txt`, + `Cargo.toml`, `go.mod`, etc.), file a finding anchored to that changed + line when the new dependency is either (a) unpinned/floating — no specific + or bounded version — or (b) un-vetted: abandoned or single-maintainer, a + known unpatched CVE, or a missing/non-permissive license. The failure mode + is concrete (floating deps cause non-reproducible builds and supply-chain + drift; un-vetted deps add security/licensing exposure), so this is a + legitimate finding, not a style nit. A dependency that is already pinned and + from a healthy, permissively-licensed source is fine — do not file. Use `add_finding` to record each candidate. Every finding must include a concise generated `title` that names the failure mode in roughly 4-10 words; diff --git a/tests/test_github_comment_prompts.py b/tests/test_github_comment_prompts.py index 9a4ac8db..bb1195fe 100644 --- a/tests/test_github_comment_prompts.py +++ b/tests/test_github_comment_prompts.py @@ -49,7 +49,20 @@ def test_construct_system_prompt_includes_socket_firewall_dependency_guidance() assert "npm i -g sfw" in prompt assert "sfw npm ci" in prompt assert "sfw uv pip install -e ." in prompt - assert "Do not run bare install commands" in prompt + assert "sfw cargo fetch" in prompt + assert "unsupported package managers such as Poetry" in prompt + assert "normal documented install command without `sfw`" in prompt + assert "sfw poetry" not in prompt + + +def test_construct_system_prompt_includes_dependency_vetting_guidance() -> None: + prompt = construct_system_prompt(working_dir="/workspace") + + assert "Vet any genuinely new package before adding it" in prompt + assert "standard library or a package already in the project's manifest/lockfile" in prompt + assert "permissive license" in prompt + assert "never add a floating or unpinned dependency" in prompt + assert "call out every dependency you add in the PR description" in prompt def test_construct_system_prompt_identifies_own_repo() -> None: diff --git a/tests/test_reviewer.py b/tests/test_reviewer.py index d414cb66..79ecdb13 100644 --- a/tests/test_reviewer.py +++ b/tests/test_reviewer.py @@ -136,7 +136,23 @@ def test_reviewer_system_prompt_includes_socket_firewall_dependency_guidance() - assert "npm i -g sfw" in prompt assert "sfw npm ci" in prompt assert "sfw uv pip install -e ." in prompt - assert "Do not run bare install commands" in prompt + assert "supported registry-fetching installs" in prompt + assert "unsupported package managers such as Poetry" in prompt + assert "normal documented install command without `sfw`" in prompt + assert "sfw poetry" not in prompt + + +def test_reviewer_system_prompt_includes_dependency_vetting_guidance() -> None: + prompt = reviewer._reviewer_system_prompt( + "/workspace/repo", + repo_owner="acme", + repo_name="repo", + pr_number=42, + ) + assert "New dependencies." in prompt + assert "unpinned/floating" in prompt + assert "missing/non-permissive license" in prompt + assert "not a style nit" in prompt def test_finding_reply_context_wraps_reply_as_untrusted_data() -> None: