Skip to content

fix(ci): check-action-pins matches quoted uses scalars, anchors SHA (#2669) - #2715

Merged
automagik-genie merged 2 commits into
devfrom
fix/action-pins-quoted-scalars
Jul 27, 2026
Merged

automagik-genie merged 2 commits into
devfrom
fix/action-pins-quoted-scalars

Conversation

@automagik-genie

Copy link
Copy Markdown
Contributor

Closes #2669 (follow-up deferred out of PR #2660, CodeRabbit Major on scripts/check-action-pins.sh:20).

Problem

The scanner matched exactly one YAML spelling and never anchored the SHA:

[[ "$rest" =~ uses:[[:space:]]*([A-Za-z0-9._-]+/[A-Za-z0-9._-]+)(/[A-Za-z0-9./_-]+)?@([0-9a-f]{40}) ]]
  1. uses: 'owner/repo@<sha>' and uses: "owner/repo@<sha>" were skipped — a quoted pin was invisible to the resolvability gate.
  2. Unanchored {40} accepted any 40-hex prefix, so uses: owner/repo@<sha>oops "resolved" against the first 40 hex chars while the workflow itself would fail at runtime with Unable to resolve action.

No current exposure (all 10 pins in .github/workflows are bare and resolve), so this is pure hardening of the Action Pin Resolvability gate before someone quotes a pin.

Change

scripts/check-action-pins.sh

  • extract_pin() picks the scalar out of all three spellings — bare, 'single', "double" — then requires the ref to match ^owner/repo(/subpath)?@[0-9a-f]{40}$, i.e. the SHA must terminate the scalar. Trailing # v5 comments and CRLF endings still parse; local (./…), docker://, tag and branch refs are still ignored.
  • Scan set now includes any tracked action.yml/action.yaml anywhere in the tree (issue point 3), not just .github/workflows + .github/actions. Overlap is harmless — pins are deduped as before.
  • New offline --extract mode reads YAML lines from stdin and prints the pins the matcher accepts. No gh, no network — this is what makes the regex layer testable.

tests/integration/check-action-pins-matcher.test.ts (new, 9 tests / 151 assertions)

Feeds fixture lines through --extract: accepts bare/single/double-quoted, subpath (owner/repo/sub@sha → owner/repo@sha), trailing comments, CRLF; rejects @<sha>deadbeef, @<sha>oops in both quote styles, 39-hex, @v5, @main, ./…, docker://…. A final case replays every SHA-pinned uses: line from the real .github/workflows and asserts each one still extracts, so the parity with today's behaviour is locked.

Evidence

$ bash scripts/check-action-pins.sh
  ok           actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
  ok           actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
  ok           sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac
  ok           slsa-framework/slsa-verifier@ea584f4502babc6f60d9bc799dbbb13c1caa9ee6
  ok           actions/attest@67422f5511b7ff725f4dbd6fb9bd2cd925c65a8d
  ok           actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
  ok           oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
  ok           actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444
  ok           actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
  ok           actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349
exit=0   # same 10 unique pins the old matcher found — no regression, no drop
$ bun test tests/integration/check-action-pins-matcher.test.ts
 9 pass / 0 fail / 151 expect() calls

$ bun test
 2833 pass / 1 fail (2834 across 123 files)
 # the single failure is the pre-existing macOS-only
 # "ui-bridge lifetime > holds zero listening TCP sockets" case, which needs Linux `ss`

$ bun run check:fast
 typecheck + lint + dead-code + skills/wishes lint + complexity budget +
 council-workflow + hook-bundle + hook-content + plugin-executables: all OK
 (2 pre-existing doctor.ts complexity warnings, budget intact)

$ bash -n scripts/check-action-pins.sh   # clean; matcher is bash 3.2-compatible (verified on 3.2.57)

Caveat

--extract deliberately reports owner/repo@sha for subpath actions, dropping the subpath: the commit being resolved lives in the parent repository, which is what gh api repos/<repo>/commits/<sha> needs and what the dedupe key has always used.

…2669)

The scanner only matched bare `uses:` scalars and accepted any 40-hex run
inside the value, so a quoted pin was skipped entirely and `@<sha>oops` passed
as a valid pin.

- extract_pin() recognises bare, 'single-quoted' and "double-quoted" scalars
- the ref must span the WHOLE scalar and end in exactly 40 hex chars
- scan set now also covers action manifests tracked outside .github/ (dedupe
  makes the overlap harmless)
- offline `--extract` mode exposes the matcher for tests (no gh, no network)

Closes #2669
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9d5a3e6c-b896-4982-b2e6-e13ab6f8d948

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/action-pins-quoted-scalars

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 159bce46c8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread scripts/check-action-pins.sh Outdated
Comment on lines +35 to +39
if [[ $line =~ $uses_dquoted_re ]]; then
scalar="${BASH_REMATCH[1]}"
elif [[ $line =~ $uses_squoted_re ]]; then
scalar="${BASH_REMATCH[1]}"
elif [[ $line =~ $uses_bare_re ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Anchor matching to the actual uses value

When a bare uses: value has a trailing comment that itself contains a quoted uses: example, the double-quoted regex wins because it is searched across the whole line before the bare form. For example, uses: actual/bad@<sha> # previous uses: "comment/good@<sha>" checks only comment/good, so a resolvable historical reference can hide an unresolvable action that the workflow actually executes. Match the YAML key's immediate scalar before considering its quoting style.

Useful? React with 👍 / 👎.

Comment thread scripts/check-action-pins.sh Outdated
# treats quoted regex fragments as literals inside [[ =~ ]].
uses_dquoted_re='uses:[[:space:]]*"([^"]*)"'
uses_squoted_re="uses:[[:space:]]*'([^']*)'"
uses_bare_re='uses:[[:space:]]*([^[:space:]#]+)'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize delimiters after bare flow scalars

When a workflow uses a valid YAML flow mapping such as - { uses: owner/repo@<sha>, name: Checkout }, this regex includes the comma in scalar, causing the anchored pin regex to reject the reference and silently skip its resolvability check. The previous unanchored matcher did extract such pins, so the bare-scalar boundary should also account for YAML flow delimiters such as ,, }, and ].

Useful? React with 👍 / 👎.

Two regressions in the #2669 matcher, both from matching the whole line:

- a quoted pin inside a trailing comment shadowed the real bare pin,
  because the unanchored uses: patterns re-matched at the comment;
- a flow-mapping step (`- { uses: owner/repo@<sha>, name: X }`) was
  silently skipped, because the comma stayed glued to the bare scalar
  and the anchored pin_re then rejected it.

Cut to the first `uses:` key, ltrim, and match the three scalar
spellings anchored on that remainder; bare scalars now also terminate at
`,` and `}`. Covered by two regression tests that fail on the pre-fix
script. bash 3.2 compatible; the live run still reports the same 10 ok
pins.
@automagik-genie
automagik-genie merged commit 7b1fc74 into dev Jul 27, 2026
13 checks passed
@automagik-genie
automagik-genie deleted the fix/action-pins-quoted-scalars branch September 25, 2026 04:49
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.

1 participant