Problem
The "Check for known vulnerabilities in PHP extensions" step in the security audit workflow can never fail, regardless of what it finds:
# .github/workflows/security-audit.yml:32-34
- name: Check for known vulnerabilities in PHP extensions
run: |
php -m | grep -E "(openssl|curl|libxml)" || echo "Core security extensions not found"
grep's exit code is discarded by the ||, and echo always exits 0. So even if none of openssl, curl, or libxml are loaded, the shell step (and therefore the job) still reports success — it just prints a message to the log that nobody is required to read. This is different from #152 (which is about the pinned PHP version being stale) — this is a control-flow bug where the check itself is structurally incapable of failing the workflow.
Where
.github/workflows/security-audit.yml:32-34
Why it matters
composer audit in the same job is a real gate (non-zero exit fails CI), but this step gives the illusion of an equivalent extension check while actually enforcing nothing. If openssl/curl/libxml were ever missing from a runner image (e.g. a future setup-php regression or image change), CI would stay green.
Suggested fix
Make the check actually fail when the extensions are missing, e.g.:
run: |
php -m | grep -qE "(openssl|curl|libxml)" || { echo "Core security extensions not found"; exit 1; }
or better, assert each extension individually so a partial failure is visible in the log.
Problem
The "Check for known vulnerabilities in PHP extensions" step in the security audit workflow can never fail, regardless of what it finds:
grep's exit code is discarded by the||, andechoalways exits0. So even if none ofopenssl,curl, orlibxmlare loaded, the shell step (and therefore the job) still reports success — it just prints a message to the log that nobody is required to read. This is different from #152 (which is about the pinned PHP version being stale) — this is a control-flow bug where the check itself is structurally incapable of failing the workflow.Where
.github/workflows/security-audit.yml:32-34Why it matters
composer auditin the same job is a real gate (non-zero exit fails CI), but this step gives the illusion of an equivalent extension check while actually enforcing nothing. Ifopenssl/curl/libxmlwere ever missing from a runner image (e.g. a futuresetup-phpregression or image change), CI would stay green.Suggested fix
Make the check actually fail when the extensions are missing, e.g.:
or better, assert each extension individually so a partial failure is visible in the log.