Skip to content

security-guidance: sg-python.sh discards probe stderr, misreporting any failing interpreter as 'no working Python 3' #86709

Description

@AndrewDongminYoo

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:hooksbugSomething isn't workinghas reproHas detailed reproduction stepsplatform:macosIssue specifically occurs on macOSreproducedBug reproduced by maintainers on a released build

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions