Skip to content

fix(GHA004,GHA006): establish need before reporting, and say what was accepted - #74

Merged
bgard68 merged 2 commits into
mainfrom
claude/gha004-gha006-establish-need
Aug 25, 2026
Merged

fix(GHA004,GHA006): establish need before reporting, and say what was accepted#74
bgard68 merged 2 commits into
mainfrom
claude/gha004-gha006-establish-need

Conversation

@bgard68

@bgard68 bgard68 commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Follow-up to #73, in two parts: finish applying "establish need before reporting" to the rules that needed it, then make the result of that reasoning visible instead of silent.

Part 1 — two more rules named their own exception and ignored it

GHA004 — pull_request_target

Reported the trigger as Critical on its presence alone. But the trigger is not a defect: it exists so a workflow can label a fork's pull request or post a comment — work pull_request cannot do because it has no token worth using. What makes it dangerous is executing the contributor's code in that context, which GHA007 already reports precisely.

So the documented-correct pattern scored the same Critical as a live remote-execution path. A Critical nobody can act on is the one that teaches a reader to stop reading Criticals.

Workflow Before After
A job checks out the PR's head Critical Critical, unchanged
No job does Critical Low — "confirm this boundary"

The untrusted-checkout definition GHA007 carried privately moves into UntrustedPullRequestCheckout, so both rules answer from one list and cannot drift. GHA007's behaviour is unchanged.

GHA006 — persisted credentials

Told a job to remove the credential it pushes with. Its remediation already said "unless a later step needs to push with the job token" while nothing established whether one did, so a release job running git push had a finding it could not close. It now stays quiet when a script after the checkout, in the same job, pushes.

The signal is deliberately narrow, because wrongly suppressing this leaves a real credential exposure unreported. Only git push in script text counts — step names are not searched, since - name: Set up git push credentials would otherwise silence a real finding. A push in another job, or before the checkout, does not excuse it.

Part 2 — silence was the wrong answer

Establishing need removed findings and left nothing behind. A workflow reported clean gave no way to tell "the rule examined this grant and accepted it" from "the rule never looked".

Acknowledgements carry that reasoning without becoming findings, which matters concretely: the client reads any finding as Action required and counts it toward the risk level, so shipping these as findings would push a correct workflow into the state the check just cleared it of.

codeql.yml — Clean · 0 findings

  REVIEWED AND ACCEPTED
  ✓ security-events: write is required, not excessive   GHA002  Line 8
    'github/codeql-action/analyze' in job 'analyze' cannot work without it,
    and the grant is held to this job.

IWorkflowSecurityRule.Acknowledge is a defaulted interface member, so the seven rules that never suppress anything needed no change — and a test pins that their default stays empty.

Also audited, no change needed

GHA003 already worked this way: it skips reusable-workflow-call jobs that cannot carry timeout-minutes, and its comment notes the auto-fix would otherwise emit a broken patch. It was the model for both changes here.

GHA001, GHA005, GHA007, GHA008, GHA009, GHA011 have no equivalent "required" case — each reports something for which a safer alternative always exists.

Verification

314 backend tests and 28 frontend tests pass; both stacks build. Every case that goes quiet is paired with the neighbouring case that must still report, and the acknowledgement tests assert both directions — what is acknowledged is never also reported, and a file carrying one still reads as Clean with zero findings.

Neither this repository's workflows nor the repository that prompted the work change their findings.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N7HjzX5e8Wv7PKENDo2Uj7

claude added 2 commits August 25, 2026 04:31
Both rules named their own exception in the finding they emitted, and neither
checked whether it applied.

GHA004 reported the pull_request_target trigger as Critical on its presence
alone. The trigger is not a defect: it exists so a workflow can label a fork's
pull request or post a comment, work pull_request cannot do because it has no
token worth using. What makes it dangerous is executing the contributor's code
in that context, which GHA007 already reports precisely. So the documented
correct pattern scored the same Critical as a live remote-execution path, and a
Critical nobody can act on is the one that teaches a reader to stop reading
Criticals.

  - a job checks out the pull request's head -> Critical, as before
  - no job does -> Low, "confirm this boundary", since a later edit could add one

The untrusted-checkout definition GHA007 carried privately moves to
UntrustedPullRequestCheckout so both rules answer from one list and cannot drift.
GHA007's behaviour is unchanged.

GHA006 told a job to remove the credential it pushes with. Its remediation
already said "unless a later step needs to push with the job token" while
nothing established whether one did, so a release job running git push had a
finding it could not close. It now stays quiet when a script after the checkout,
in the same job, pushes.

That signal is deliberately narrow, because suppressing this finding wrongly
leaves a real credential exposure unreported: only git push in script text
counts. Step names are not searched - "Set up git push credentials" would
otherwise silence the finding - and a push in another job, or before the
checkout, does not excuse it.

11 new tests, each quiet case paired with the neighbouring case that must still
report. 309 pass across the solution. Neither this repository's workflows nor
the repository that prompted the work change their results.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7HjzX5e8Wv7PKENDo2Uj7
Establishing need removed findings and left nothing in their place. A workflow
reported clean gave no way to tell "the rule examined this grant and accepted
it" from "the rule never looked", so the reasoning that made a finding
disappear was invisible to the person deciding whether to trust the result.

Acknowledgements carry that reasoning without becoming findings. They are a
separate list on the analysis result, because the client reads any finding as
Action required and counts it in the risk level - a correct workflow must not
be pushed into that state by the very check that approved it.

  codeql.yml - Clean, 0 findings
    Reviewed and accepted
      security-events: write is required, not excessive        GHA002  Line 8
      'github/codeql-action/analyze' in job 'analyze' cannot work without it.

IWorkflowSecurityRule.Acknowledge is a defaulted interface member, so the seven
rules that never suppress anything needed no change and a test pins that their
default stays empty. GHA002 names the action that requires the grant; GHA006
names the job that pushes with the credential.

Five backend tests and one client test, each asserting both directions: what is
acknowledged is not also reported, and a file carrying an acknowledgement still
reads as Clean with zero findings.

314 backend tests and 28 frontend tests pass; both stacks build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7HjzX5e8Wv7PKENDo2Uj7
@bgard68 bgard68 changed the title fix(GHA004,GHA006): establish need before reporting, as GHA002 now does fix(GHA004,GHA006): establish need before reporting, and say what was accepted Aug 25, 2026
@bgard68
bgard68 merged commit a2f7e9c into main Aug 25, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants