Add GHA supply chain security rules (mutable-action-tag, pwn-request)#3783
Merged
Add GHA supply chain security rules (mutable-action-tag, pwn-request)#3783
Conversation
github-actions-mutable-action-tag (WARNING / CWE-1104): - Detects non-SHA-pinned uses: references via pattern-regex with negative lookahead for 40-char hex SHA. Catches all mutable refs including tagged versions, branches, and 'latest'. Validated against 853 peer-vendor repos (6,147 findings, 0 SHA-pinned false positives). - Motivated by TeamPCP campaign where trivy-action@0.29.0 and kics-github-action@master were repointed to malicious commits. gha-pwn-request-fork-checkout (ERROR / CWE-829): - Detects pull_request_target + fork-controlled checkout ref combination (the "Pwn Request" attack pattern). Uses pattern-inside for trigger detection + metavariable-regex for fork ref forms. - Confirmed TPs: sigstore/community (CRITICAL, PULUMI_ACCESS_TOKEN), jfrog/jfrog-cli (HIGH), SonarSource/official-images (HIGH). Both ported from semgrep/semgrep-rules-jsonnet PRs #10484 and #10485 per reviewer feedback that community rules belong in this repository. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
36aed10 to
cd59bf3
Compare
Contributor
Author
|
Real-world TP confirmation: The root cause of the TeamPCP Phase 07 trivy supply chain attack has been confirmed as This is a direct real-world TP for |
…ckout Absorbs the new rule's improvements into the existing rule rather than shipping a duplicate rule for the same vulnerability class: - Severity: WARNING → ERROR; subcategory: audit → vuln - Confidence/likelihood/impact: LOW/LOW/MEDIUM → HIGH/MEDIUM/HIGH - CWE-913 → CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) - OWASP A01 (Broken Access Control) → A08 (Software and Data Integrity Failures) - Drop actions/checkout requirement — any step with ref: is in scope - Replace broad github.event.pull_request metavariable-pattern with precise metavariable-regex targeting head.sha, head.ref, github.head_ref, refs/pull/ - Extend regex to also cover refs/pull/ merge refs (existing test coverage) - Absorb new test cases: github.head_ref, head.ref, sha||github.ref Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…iable-regex Keeps the original actions/checkout + jobs/steps scaffolding. Replaces the broad generic-language metavariable-pattern on $EXPR with a metavariable-regex that precisely targets the dangerous fork-head refs: github.event.pull_request.head.sha, github.event.pull_request.head.ref, github.head_ref, refs/pull/ merge refs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Semgrep reports matches at the `uses:` line, not the `ref:` line inside `with:`. Move ruleid comments to precede `- uses:` to match TP-1/2/3 style. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the previous approaches with the correct technique: keep the original
pattern structure (actions/checkout + jobs/steps scaffold), but improve the
metavariable-pattern on $EXPR to use pattern-either with a nested
metavariable-regex rather than a broad literal prefix match.
The generic-mode patterns match as substrings against the captured $EXPR value
(e.g. "${{ github.event.pull_request.head.sha }}"), so no ${{ }} wrapper is
needed in the sub-patterns:
- github.event.pull_request.head.$PR_REF + regex ^(sha|ref)$ catches the
two specific dangerous head fields while excluding .number, .body, etc.
- github.head_ref ... catches the shorthand form
Removes the refs/pull/ test case — that pattern uses .number (not a head ref)
and was never covered by the new rule being merged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
$...PR_REF captures the full multi-token expression between ${{ and }},
avoiding the single-token limitation of $PR_REF. metavariable-regex does
not support $...VAR, so a nested metavariable-pattern with pattern-either
is used instead to match the three dangerous fork-head ref forms:
- github.event.pull_request.head.sha
- github.event.pull_request.head.ref
- github.head_ref
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Uses pattern-inside to scope matches to within ${{ }} expressions,
then pattern-either to match the specific dangerous fork-head refs.
No metavar capture needed — cleaner than the nested metavariable-pattern approach.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds github.event.pull_request ... back to the pattern-either so the original broad coverage (including refs/pull/.../merge via .number) is preserved alongside the explicit head.sha, head.ref, and head_ref patterns. Restores the refs/pull/ spelling test case accordingly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
head.sha and head.ref are already matched by the broader prefix pattern. Test cases for both remain (TP-1/2/5 cover head.sha, TP-6 covers head.ref). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Quality issues fixed:
- pattern-regex (raw text scan) -> pattern-inside + pattern + metavariable-pattern,
matching the approach used by third-party-action-not-pinned-to-commit-sha and
pull-request-target-code-checkout; comment exclusion handled by YAML parser
- Add pattern-inside: "{steps: ...}" scope (consistent with comparable rule)
- CWE-1104 (Unmaintained Component) -> CWE-1357 + CWE-353 (Integrity Check);
CWE-353 is exact for "not verifying integrity of what you execute"
- Remove wrong reference to pwn-requests paper (unrelated topic)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
inkz
approved these changes
Mar 31, 2026
0xDC0DE
added a commit
that referenced
this pull request
Apr 7, 2026
* Fix languages mixed * Fix more * fix more rules * fix rules again * Add GHA supply chain security rules (mutable-action-tag, pwn-request) (#3783) * Add GHA supply chain security rules (mutable-action-tag, pwn-request) github-actions-mutable-action-tag (WARNING / CWE-1104): - Detects non-SHA-pinned uses: references via pattern-regex with negative lookahead for 40-char hex SHA. Catches all mutable refs including tagged versions, branches, and 'latest'. Validated against 853 peer-vendor repos (6,147 findings, 0 SHA-pinned false positives). - Motivated by TeamPCP campaign where trivy-action@0.29.0 and kics-github-action@master were repointed to malicious commits. gha-pwn-request-fork-checkout (ERROR / CWE-829): - Detects pull_request_target + fork-controlled checkout ref combination (the "Pwn Request" attack pattern). Uses pattern-inside for trigger detection + metavariable-regex for fork ref forms. - Confirmed TPs: sigstore/community (CRITICAL, PULUMI_ACCESS_TOKEN), jfrog/jfrog-cli (HIGH), SonarSource/official-images (HIGH). Both ported from semgrep/semgrep-rules-jsonnet PRs #10484 and #10485 per reviewer feedback that community rules belong in this repository. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Merge gha-pwn-request-fork-checkout into pull-request-target-code-checkout Absorbs the new rule's improvements into the existing rule rather than shipping a duplicate rule for the same vulnerability class: - Severity: WARNING → ERROR; subcategory: audit → vuln - Confidence/likelihood/impact: LOW/LOW/MEDIUM → HIGH/MEDIUM/HIGH - CWE-913 → CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) - OWASP A01 (Broken Access Control) → A08 (Software and Data Integrity Failures) - Drop actions/checkout requirement — any step with ref: is in scope - Replace broad github.event.pull_request metavariable-pattern with precise metavariable-regex targeting head.sha, head.ref, github.head_ref, refs/pull/ - Extend regex to also cover refs/pull/ merge refs (existing test coverage) - Absorb new test cases: github.head_ref, head.ref, sha||github.ref Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Restore original message text in pull-request-target-code-checkout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Restore original pattern structure; improve ref matching with metavariable-regex Keeps the original actions/checkout + jobs/steps scaffolding. Replaces the broad generic-language metavariable-pattern on $EXPR with a metavariable-regex that precisely targets the dangerous fork-head refs: github.event.pull_request.head.sha, github.event.pull_request.head.ref, github.head_ref, refs/pull/ merge refs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix ruleid comment placement in TP-4/5/6 test cases Semgrep reports matches at the `uses:` line, not the `ref:` line inside `with:`. Move ruleid comments to precede `- uses:` to match TP-1/2/3 style. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Use metavariable-pattern + nested metavar-regex for precise ref matching Replaces the previous approaches with the correct technique: keep the original pattern structure (actions/checkout + jobs/steps scaffold), but improve the metavariable-pattern on $EXPR to use pattern-either with a nested metavariable-regex rather than a broad literal prefix match. The generic-mode patterns match as substrings against the captured $EXPR value (e.g. "${{ github.event.pull_request.head.sha }}"), so no ${{ }} wrapper is needed in the sub-patterns: - github.event.pull_request.head.$PR_REF + regex ^(sha|ref)$ catches the two specific dangerous head fields while excluding .number, .body, etc. - github.head_ref ... catches the shorthand form Removes the refs/pull/ test case — that pattern uses .number (not a head ref) and was never covered by the new rule being merged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Use $...PR_REF ellipsis capture with nested metavariable-pattern $...PR_REF captures the full multi-token expression between ${{ and }}, avoiding the single-token limitation of $PR_REF. metavariable-regex does not support $...VAR, so a nested metavariable-pattern with pattern-either is used instead to match the three dangerous fork-head ref forms: - github.event.pull_request.head.sha - github.event.pull_request.head.ref - github.head_ref Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Simplify metavariable-pattern using pattern-inside ${{ ... }} Uses pattern-inside to scope matches to within ${{ }} expressions, then pattern-either to match the specific dangerous fork-head refs. No metavar capture needed — cleaner than the nested metavariable-pattern approach. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Retain original github.event.pull_request ... pattern alongside new refs Adds github.event.pull_request ... back to the pattern-either so the original broad coverage (including refs/pull/.../merge via .number) is preserved alongside the explicit head.sha, head.ref, and head_ref patterns. Restores the refs/pull/ spelling test case accordingly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Remove patterns subsumed by github.event.pull_request ... head.sha and head.ref are already matched by the broader prefix pattern. Test cases for both remain (TP-1/2/5 cover head.sha, TP-6 covers head.ref). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Rewrite github-actions-mutable-action-tag to use proper YAML patterns Quality issues fixed: - pattern-regex (raw text scan) -> pattern-inside + pattern + metavariable-pattern, matching the approach used by third-party-action-not-pinned-to-commit-sha and pull-request-target-code-checkout; comment exclusion handled by YAML parser - Add pattern-inside: "{steps: ...}" scope (consistent with comparable rule) - CWE-1104 (Unmaintained Component) -> CWE-1357 + CWE-353 (Integrity Check); CWE-353 is exact for "not verifying integrity of what you execute" - Remove wrong reference to pwn-requests paper (unrelated topic) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(kotlin): exclude ephemeral port detection from unencrypted-socket rule (#3797) ServerSocket(0) used to detect available ports (→ localPort → close()) is a common benign pattern that does not transmit cleartext data. Add pattern-not-inside clauses scoped to functions returning Int. Closes SRC-12442 Made-with: Cursor * Add secrets-inherit rule for GitHub Actions workflows (#3803) Detects use of `secrets: inherit` in reusable workflow calls, which passes all repository secrets to the called workflow. This violates least privilege — callers should explicitly pass only needed secrets. --------- Co-authored-by: Katrina Liu <katrina@semgrep.com> Co-authored-by: Kurt Boberg <98792107+kurt-r2c@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Pieter De Cremer (Semgrep) <pieter@r2c.dev> Co-authored-by: Jonathan Roemer <jon@roemersoftworks.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two GitHub Actions supply chain security rules:
github-actions-mutable-action-tag(WARNING / CWE-1104)Detects
uses:references not pinned to a full 40-character commit SHA. Usespattern-regexwith negative lookahead to reject exact 40-char hex SHAs while catching tags (@v3), branches (@master), and other mutable refs.TP examples:
actions/checkout@v3,bridgecrewio/checkov-action@master,aquasecurity/trivy-action@0.29.0TN examples: any
@<40-hex-chars>, local./.github/actions/,docker://Corpus: 853 peer-vendor repos, 6,147 findings, 0 SHA-pinned false positives.
Motivation: trivy-action@0.69.3 and kics-github-action@master were silently repointed to malicious commits in the TeamPCP campaign (2026-03).
gha-pwn-request-fork-checkout(ERROR / CWE-829)Detects the "Pwn Request" pattern:
pull_request_targettrigger + fork-controlled checkout ref (github.event.pull_request.head.sha,github.head_ref, orgithub.event.pull_request.head.ref). Higher severity and confidence than the existingpull-request-target-code-checkoutrule; does not requireactions/checkoutspecifically.Confirmed TPs from investigation:
sigstore/community— CRITICAL (PULUMI_ACCESS_TOKEN controls entire sigstore GitHub org)jfrog/jfrog-cli— HIGH (GITHUB_TOKEN write + FASTCI_TOKEN)SonarSource/official-images— HIGH (GITHUB_TOKEN write)Notes
semgrep/semgrep-rules-jsonnetPRs #10484 and #10485 per reviewer feedback that community rules belong in this repositorygha-pwn-request-fork-checkoutusespattern-insidefor trigger detection (replacessemgrep-internal-pattern-anywherefrom the Jsonnet version) matching the approach used by the existingpull-request-target-code-checkoutrule🤖 Generated with Claude Code