Six fixes, every one of them a case where nox reported something that was not
true, or failed for a reason that had nothing to do with security. Four of the
six blocked a repository outright.
-
A throttled version lookup no longer fails the security gate. The action
resolves version: latest through the GitHub API, and GitHub answers 403 when
rate-limiting. resolve_version was a bare curl -fsSL, which exits non-zero
on any 403 — so the action died before it ever reached fetch_asset, the
function directly below it that had already been hardened against exactly this
throttle. The symptom was distinctive and uninformative: Nox PR Gate failing
in nine seconds with one log line, curl: (22) The requested URL returned error: 403, followed by a second red check when the SARIF upload found no
file. Two failed checks, no scan performed, neither of them about security.
Underneath it, the reason the throttle was reachable at all: action.sh has
always sent Authorization: Bearer ${GITHUB_TOKEN} when that variable is set,
but action.yml’s env: block never mapped it. A composite action’s step
sees only what that block maps, so the header expanded to nothing on every
run, and both the version lookup and the asset download went out anonymous —
against the 60-requests-per-hour budget shared by every job on the runner’s IP
address. A burst of CI runs hits that routinely.
Both halves are fixed: the lookup retries on the same schedule as the
download, and the token now reaches the script. If you would rather not depend
on the lookup at all, pinning an explicit version: skips it entirely — the
failure message now says so. (#375)
-
VULN-001 no longer tells you to downgrade. The remediation advice
reported the first version listed as fixed for an advisory, which is not
necessarily one that is newer than what you have installed — for a vulnerable
package on a maintained older line, the suggestion could move you backwards.
Fix versions are now resolved against the affected-range intervals and the
installed version, so the recommendation is always forward. (#372)
-
One dataflow reported from both ends is now one finding. A taint path
discovered from its source and again from its sink produced two findings for a
single flow, inflating counts and, on a gated repository, presenting the same
problem twice. Flows are deduplicated on rule, path, source line, sink line
and source variable. (#373)
-
A location-less finding no longer breaks the whole SARIF upload. GitHub
rejects an entire submission — every finding in it — when any result carries
an empty artifactLocation.uri, with locationFromSarifResult: expected artifact location. One finding that could not be tied to a file therefore
discarded the report. Findings without a location now emit no locations
array at all, which SARIF permits, and the rest of the report uploads. (#370)
-
An explicit -format is no longer overridden by .nox.yaml. A flag given
on the command line lost to the config file, so the one way to override a
project setting for a single run did not work. The flags now default to empty
so that “absent” is distinguishable from “set to the default”, and an
explicitly-passed value wins. Same fix for -output. (#371)
-
Five rule-precision defects that put false high/critical findings on the
gate. Each fired on ordinary code containing no credential and no
vulnerability, and all five land in the high/critical band the shared CI gate
fails on, so each one blocked a repository outright.
SEC-147 matched the Resend prefix at the seam of an identifier —
TestSessionStore_LoadsLegacySnapshot… supplies re_ and 38
alphanumerics — and reported a high-severity API key on a Go test function
name. SEC-003 and SEC-213 had the same seam for ghs_. All three now
require a word boundary on the left; an issued key never continues a
preceding word.
SEC-435 required its gh[pousr]_ prefix and exactly ONE further character,
so any five-character run beginning ghs_ was a GitHub token — including
"ghs_fake_install_token", a shape GitHub does not issue. It now requires
the issued shape (prefix plus 36 alphanumerics), which costs no detection:
every well-formed token is already covered by SEC-003/SEC-213/SEC-215/
SEC-216/SEC-217.
SEC-004 reported critical on an HTML placeholder= attribute holding
-----BEGIN RSA PRIVATE KEY-----. That is the hint shown in an empty field,
telling a user what to paste; the key material is theirs and arrives at
runtime. Matches inside display-text attributes (placeholder, aria-label,
alt, title, label) are dropped. value= and every other attribute are
untouched — a key really can be pasted into one of those.
SEC-240, the Terraform password-field rule, fired on a Go doc comment:
its separator alternation includes ,, so
// "bot_token" pops a password input, "imap_password" pops … parses as a
field, a separator and a quoted value. A comment holds prose describing
configuration, not configuration. The assignment-shaped rules — derived from
the rule table, not a hand-kept list — no longer match inside comments.
Provider rules are unchanged: a full token in a comment is a real leak and is
still reported, as is a credential genuinely left in a commented-out
assignment, which the generic keyword rules still catch.
AI-049 (“AI output passed to eval/exec”, CWE-95) fired on textbook
parameterised SQL — db.Exec(`INSERT INTO schedules (id, prompt) VALUES (?, ?)`) — because the AI-vocabulary token it gates on was a column name
inside the query text. database/sql’s Exec evaluates SQL, not code, so
CWE-95 cannot apply to it. A call executing a SQL statement is dropped. The
discriminator is the SQL text, not the receiver: db.Exec(model_output), a
model emitting raw SQL that is then executed, still fires. (#374)