mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-20 16:47:59 +02:00
fix(hooks): match the index case-insensitively, and isolate the pytest probe
Red-teaming the guard from 3c317470 found two more ways to get a repository's own
code executed. Both are demonstrated by a planted binary that appends to a witness
file, counted before and after.
Case folding. git matches index pathspecs case-sensitively even where
core.ignorecase is set, but APFS does not -- so a repository that commits
`.venv/bin/Python` gets `$venv/bin/python` opening and running that file while the
guard's lowercase query finds nothing in the index and reports it untracked. The
witness logged two invocations. It applies to `venv` and `env` as well, and to any
folding of the name. The query now uses a `:(icase)` pathspec; all nine
directory-by-spelling combinations are refused, and an untracked venv still runs.
Module shadowing. `python -c "import pytest"` puts the working directory first on
sys.path, so a repository that commits a `pytest.py` in its root has that file
imported, and executed, by a check whose only job is to answer whether pytest is
installed. The probe is now `python -I -c "import pytest"` on the virtualenv, uv
and poetry paths alike. Isolation does not hide a real pytest -- it lives in the
interpreter's own site-packages, confirmed against a venv holding pytest 9.1.1.
Still true, and not something this hook can fix: running the repository's declared
suite runs the repository's code. `pytest` imports conftest.py, and the Node arm
runs package.json scripts. That is what a pre-push verification hook is for. The
line this guard draws is narrower and worth keeping -- a capability probe, and the
choice of which interpreter to trust, should not be things the pushed repository
gets to decide.
This commit is contained in:
@@ -169,9 +169,20 @@ repo_ships_interpreter() {
|
||||
top="$(git rev-parse --show-toplevel 2>/dev/null)" || return 1
|
||||
top="$(cd -P -- "$top" 2>/dev/null && pwd -P)" || return 1
|
||||
[[ -n "$top" && "$real" == "$top/"* ]] || return 1
|
||||
git ls-files --error-unmatch -- "${real#"$top"/}" >/dev/null 2>&1
|
||||
# `:(icase)` because git matches index pathspecs case-sensitively even where
|
||||
# core.ignorecase is set, while the filesystem underneath does not. On macOS's
|
||||
# APFS -- the platform this hook most often runs on -- a committed
|
||||
# `.venv/bin/Python` is what `$venv/bin/python` opens and executes, but a
|
||||
# case-sensitive query for the lowercase name finds nothing in the index and the
|
||||
# guard waves it through. Measured: that spelling ran the planted binary twice.
|
||||
git ls-files --error-unmatch -- ":(icase)${real#"$top"/}" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# `-I` isolates the probe: without it Python puts the working directory first on
|
||||
# sys.path, so a repository that commits a `pytest.py` in its root gets that file
|
||||
# imported -- and executed -- by a check whose only job is to answer whether pytest
|
||||
# exists. Measured: a committed pytest.py ran during the probe. Isolation does not
|
||||
# hide a real pytest, which lives in the interpreter's own site-packages.
|
||||
resolve_pytest() {
|
||||
# `${VAR+set}` rather than `-n "${VAR:-}"`, so that a variable set to nothing is
|
||||
# still an override: `ECC_PYTEST_CMD=` and `ECC_PYTEST_CMD=" "` now behave
|
||||
@@ -206,20 +217,20 @@ resolve_pytest() {
|
||||
log " this hook runs on every push in every repository."
|
||||
continue
|
||||
fi
|
||||
if "$venv/bin/python" -c "import pytest" >/dev/null 2>&1; then
|
||||
if "$venv/bin/python" -I -c "import pytest" >/dev/null 2>&1; then
|
||||
PYTEST_CMD=("$venv/bin/python" -m pytest)
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [[ -f "uv.lock" ]] && command -v uv >/dev/null 2>&1; then
|
||||
if uv run --no-sync python -c "import pytest" >/dev/null 2>&1; then
|
||||
if uv run --no-sync python -I -c "import pytest" >/dev/null 2>&1; then
|
||||
PYTEST_CMD=(uv run --no-sync pytest)
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
if [[ -f "poetry.lock" ]] && command -v poetry >/dev/null 2>&1; then
|
||||
if poetry run python -c "import pytest" >/dev/null 2>&1; then
|
||||
if poetry run python -I -c "import pytest" >/dev/null 2>&1; then
|
||||
PYTEST_CMD=(poetry run pytest)
|
||||
return 0
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user