security-guidance: sg-python.sh discards probe stderr, misreporting any failing interpreter as 'no working Python 3'
Summary
plugins/security-guidance/hooks/sg-python.sh probes each interpreter candidate with stderr discarded (2>/dev/null), so when every candidate fails, the script reports a single generic message — no working Python 3 interpreter found … install Python from https://python.org — regardless of why the probes failed. A present-but-broken interpreter (pyenv shim with no version installed, Microsoft Store stub, transient fork: Resource temporarily unavailable under load) is indistinguishable from "Python is not installed", and the evidence needed to tell them apart is exactly what was thrown away.
This is ironic in the best way: the shim exists precisely to route around a present-but-failing interpreter (the Store stub, per its own header comment). It handles that case fine when a later candidate works — but when nothing works, it hides the one thing a user needs to diagnose it.
Reproduction (deterministic, script unmodified)
mkdir -p /tmp/sgrepro/bin
printf '#!/bin/sh\necho "pyenv: version 3.12.1 is not installed (set by /Users/x/.python-version)" >&2\nexit 1\n' > /tmp/sgrepro/bin/python3
printf '#!/bin/sh\necho "simulated Store-stub failure" >&2\nexit 49\n' > /tmp/sgrepro/bin/python
chmod +x /tmp/sgrepro/bin/python3 /tmp/sgrepro/bin/python
PATH="/tmp/sgrepro/bin:/usr/bin:/bin" \
bash plugins/security-guidance/hooks/sg-python.sh -c 'print("unreachable")'
Observed output (macOS 15, sg-python.sh at current main):
security-guidance: no working Python 3 interpreter found.
tried: python3, python, py -3
on Windows, install Python from https://python.org (NOT the Microsoft Store)
Both stubs' stderr lines are gone. Note also that /usr/bin/python3 — a fully working interpreter — is on PATH in this repro, shadowed by the broken shim; the user is still told to install Python. That shadowing shape is exactly what a stale pyenv shim produces in real life.
How this surfaced
On macOS, a UserPromptSubmit firing while the machine was under heavy transient load (a cache-cleanup script running mass deletions concurrently) produced this message even though two working Pythons were on PATH. The likeliest mechanism is the probes failing fast with fork: Resource temporarily unavailable — but that is a guess, because the script discarded the stderr that would have said so. Diagnosing it meant re-running the probes manually and reasoning backwards; with the stderr preserved it would have been one glance.
Suggested fix (small)
Capture probe stderr and emit it only in the all-candidates-failed branch:
errlog=$(mktemp)
trap 'rm -f "$errlog"' EXIT
probe() {
"$@" -c 'import sys; print(sys.version_info[0])' 2>>"$errlog"
}
and in the failure block:
if [ -s "$errlog" ]; then
echo " probe errors:" >&2
sed 's/^/ /' "$errlog" >&2
fi
Happy paths are unchanged (a working candidate still gets exec'd; a skipped-over broken candidate's stderr is only shown if everything fails). The message could also distinguish "command not found" from "found but failed", but preserving stderr alone covers both.
Happy to send a PR if useful.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗