Summary
In action.yml, the "Install dependencies" step installs jq via sudo apt-get install -y jq with no check that it actually succeeded:
- name: Install dependencies
shell: bash
run: |
...
sudo apt-get update && sudo apt-get install -y jq
Later, in the "Run ClaudeCode scan" step, the results-parsing logic uses jq to check for an error and to count findings, with a fallback that silently defaults to 0 on any jq failure (including "command not found"):
if jq -e '.error' claudecode/claudecode-results.json > /dev/null 2>&1; then
...
else
CLAUDECODE_FINDINGS_COUNT=$(jq -r '.findings | if . == null then 0 else length end' claudecode/claudecode-results.json 2>/dev/null || echo "0")
...
fi
If jq is missing or broken (e.g. the apt-get install step failed or was silently no-op'd by the runner's environment — see below), jq -e '.error' ... fails to even execute, so the if branch is skipped and control falls into the else branch. There, jq -r '.findings | ...' 2>/dev/null || echo \"0\" also fails (jq not found), and the || echo \"0\" fallback kicks in — reporting findings_count=0 even when claudecode-results.json actually contains real findings.
This means: if jq is unavailable for any reason, a scan that found real vulnerabilities gets reported as a clean pass (0 findings), with no warning or error surfaced anywhere in the job output. That's a meaningful blind spot for a security-scanning action — a broken dependency degrades to "looks clean" instead of "scan result unknown/untrusted."
How we hit this
On a self-hosted runner in our org, the sudo apt-get install -y jq step doesn't actually install jq (environment-specific sudo restriction), but it also doesn't fail the step or the job — so this went unnoticed until we traced through the parsing logic while debugging an unrelated wrapper-workflow bug. We haven't yet observed a run where jq was missing and real findings existed, but the code path is there and would silently swallow them.
Suggested fix
- Verify
jq is actually available after the install step (command -v jq or check the install step's exit code) and fail the job loudly if it isn't, rather than deferring to a silent per-call fallback.
- Alternatively/additionally, change the results-parsing fallback so a
jq failure (as opposed to a genuinely empty/null .findings array) is distinguishable from "0 real findings" — e.g. exit non-zero / set an explicit results-error output rather than defaulting findings_count to \"0\".
Happy to help test a fix if useful.
Summary
In
action.yml, the "Install dependencies" step installsjqviasudo apt-get install -y jqwith no check that it actually succeeded:Later, in the "Run ClaudeCode scan" step, the results-parsing logic uses
jqto check for an error and to count findings, with a fallback that silently defaults to0on anyjqfailure (including "command not found"):If
jqis missing or broken (e.g. theapt-get installstep failed or was silently no-op'd by the runner's environment — see below),jq -e '.error' ...fails to even execute, so theifbranch is skipped and control falls into theelsebranch. There,jq -r '.findings | ...' 2>/dev/null || echo \"0\"also fails (jq not found), and the|| echo \"0\"fallback kicks in — reportingfindings_count=0even whenclaudecode-results.jsonactually contains real findings.This means: if
jqis unavailable for any reason, a scan that found real vulnerabilities gets reported as a clean pass (0 findings), with no warning or error surfaced anywhere in the job output. That's a meaningful blind spot for a security-scanning action — a broken dependency degrades to "looks clean" instead of "scan result unknown/untrusted."How we hit this
On a self-hosted runner in our org, the
sudo apt-get install -y jqstep doesn't actually installjq(environment-specific sudo restriction), but it also doesn't fail the step or the job — so this went unnoticed until we traced through the parsing logic while debugging an unrelated wrapper-workflow bug. We haven't yet observed a run wherejqwas missing and real findings existed, but the code path is there and would silently swallow them.Suggested fix
jqis actually available after the install step (command -v jqor check the install step's exit code) and fail the job loudly if it isn't, rather than deferring to a silent per-call fallback.jqfailure (as opposed to a genuinely empty/null.findingsarray) is distinguishable from "0 real findings" — e.g. exit non-zero / set an explicitresults-erroroutput rather than defaultingfindings_countto\"0\".Happy to help test a fix if useful.