When a project has pyproject.toml but no uv.lock, audit isn't
possible without mutating the project (running 'uv lock' or
'uv sync'). The bare 'pip-audit' fallback was incorrect — it
audited pip-audit's own bundled venv, producing false positives
for pip/urllib3. Skip with a clear message pointing to 'uv sync'
instead, so the gauntlet stays read-only.
Exit code 2 mirrors the existing convention used when a check
tool isn't installed (e.g., pip-audit not installed at line 27);
run-all.sh recognizes it as "skipped" rather than passed or failed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the PIPAPI_PYTHON_LOCATION approach (reverted in previous
commit) which assumed pip in the project venv; uv-created venvs don't
ship pip by default, so pip-audit failed when it tried to enumerate
packages via `<python> -m pip --version`.
New approach: export the lockfile as a flat requirements list via
`uv export --no-hashes --format requirements-txt` and pipe it to
`pip-audit -r /dev/stdin --disable-pip --no-deps`. This audits exactly
what uv.lock resolves to, without touching either the project venv or
pip-audit's bundled venv.
Also switch the audit_cmd invocation from `$audit_cmd` to
`eval "$audit_cmd"` so the pipeline operator survives variable
expansion. The other audit_cmd branches (requirements.txt mode,
environment mode) are simple commands and eval handles them
transparently.
Verified clean on trellis-mcp and the herbylab worktree (both uv
projects); no spurious pip/urllib3 CVEs. Trellis's editable install
produces a benign "could not deduce package version" note that
pip-audit treats as a skip, not a failure.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`uv run --directory <project> pip-audit` runs the pip-audit BINARY
through uv, but pip-audit's binary lives at
~/.local/share/uv/tools/pip-audit/ with its own embedded Python. When
invoked without `PIPAPI_PYTHON_LOCATION`, pip-audit defaults to using
the python it shipped with for its dependency resolution — so it audits
ITS OWN venv (which has its own bundled pip + urllib3 versions, both
currently flagged with CVEs) instead of the project's venv.
pip-audit emits a stderr warning about this:
pip-audit will run pip against
~/.local/share/uv/tools/pip-audit/bin/python, but you have a virtual
environment loaded at <project>/.venv. This may result in unintuitive
audits, since your local environment will not be audited. You can
forcefully override this behavior by setting PIPAPI_PYTHON_LOCATION
to the location of your virtual environment's Python interpreter.
Reproduced as a false positive on every uv project on this host —
trellis-mcp and herbylab both got the identical 4 CVEs despite neither
having pip or urllib3 in its dep tree.
Fix: set PIPAPI_PYTHON_LOCATION to the project's .venv/bin/python when
it exists. When it doesn't exist, fall back to the old behavior with a
yellow warning that the audit may be inaccurate (avoids silently
running `uv sync` as a side effect of validation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`set -o pipefail` at the top of tests.sh combined with `find ... | head -1`
is a silent-killer pattern. When `find` produces lots of matches (e.g.
when uv has installed dozens of third-party packages with test_*.py
files under .venv), `head -1` reads its single line and closes the pipe.
`find` writes again, gets SIGPIPE, exits 141. pipefail propagates 141.
`set -e` fires. tests.sh exits 141 before printing `-- Python (pytest ...) --`
or any other output. run-all.sh records `tests: failed` with no
indication of WHY.
Reproduced directly:
bash -c 'set -euo pipefail; x=$(find <large-tree> -name "test_*.py" -o -name "*_test.py" | head -1); echo "got: $x"'
exit=141 # "got:" never prints
Fix: replace `| head -1` with `-print -quit` so find stops itself
after the first match instead of getting cut off mid-write. As a free
bonus, also prune the well-known noise dirs (.venv, .git, etc.) so the
probe doesn't even consider third-party test files in the first place —
this keeps the probe fast on dense trees and avoids matching
@pytest.fixture-decorated files in installed packages.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Preserve pre-existing WIP from master's working tree. When a project
has a uv.lock alongside pyproject.toml, run pip-audit and pytest under
`uv run --directory $project` so they execute in the project's own
managed venv instead of whatever pytest/pip-audit happens to be on PATH.
Lifted verbatim from master's uncommitted working tree; committed on
this branch as the base for subsequent Bug B (tests.sh SIGPIPE) and
Bug C (pip-audit venv) fixes which both modify these regions.
The `-not -path "*/.*"` filter on the `find` probes in lint.sh and sast.sh
was intended to exclude .venv / .git / __pycache__ but also excludes any
target path that lives UNDER a hidden ancestor directory.
Concrete trigger: running the gauntlet on a Claude Code worktree at
.../.claude/worktrees/<name>/. Every file in the tree matches `*/.*` via
the .claude/ component, so the probes return empty, the scripts skip
with "No Python or Go files found", and run-all.sh records the check as
passed by virtue of the 0 exit. The check never actually ran.
Switch to explicit prunes for the well-known noise dirs. As a free
bonus, replace `| head -1` with `-print -quit` so the probe also stops
being susceptible to SIGPIPE under `set -o pipefail` (relevant on dense
trees with many matches).
Verified: probe now returns first .py file in both .claude/-rooted and
normal project paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>