Skip to content

verify:action-pins: match expression contexts and job names case-insensitively #4934

Closed

Description

@cliffhall

Problem

scripts/verify-action-pins.mjs (added by #4928 for #4873) reads GitHub expressions with case-sensitive patterns, but GitHub resolves expression contexts and their keys without regard to case. Two spellings therefore pass the guard when they should not.

  1. A secret in a mixed-case context. ${{ SECRETS.DEPLOY_TOKEN }} hands the job a secret, but handedSecret looks for a lowercase secrets, so the job is not counted as credentialed and its tag-pinned actions are not reported. The GITHUB_TOKEN exemption is case-sensitive in the same way.
  2. The needs context in mixed case. ${{ NEEDS.detect.outputs.expected }} is not matched by the outputs rule. ${{ needs.DETECT.outputs.expected }} is matched, but the captured name DETECT is then looked up in workflow.jobs as written and does not resolve to the job detect. Either way the job that produced the outputs is not held to the pin.

Neither spelling exists in this repository's workflows: every expression here writes its contexts in lowercase. So nothing is exposed today. The gap is that the guard would not notice if one were added.

Reproduce

In a throwaway workflow:

jobs:
  publish:
    steps:
      - uses: actions/checkout@v6
      - env:
          KEY: ${{ SECRETS.DEPLOY_TOKEN }}
        run: publish

credentialedJobs returns an empty set, and unpinnedRefs reports nothing. With secrets.DEPLOY_TOKEN it reports actions/checkout@v6.

Expected

  • secrets, needs, the GITHUB_TOKEN name and job ids are matched without regard to case, with job names resolved against workflow.jobs case-insensitively.
  • Regression tests for SECRETS.X, Secrets['github_token'] (still exempt), NEEDS.job.outputs and needs.JOB.outputs.

Background

Raised by Copilot in round 15 of the review of #4928 and declined there as hardening beyond #4873, which asked for the guard to catch a tag-pinned action in a credentialed job or in a job whose artifact one downloads. By then the review had extended the guard five times for spellings no workflow here uses; this one is worth doing, on its own.

The MCP Inspector's guard, which this one was ported from, has the same case-sensitive matchers, so the fix is worth offering there too.

Activity

  1. added this to the v2.0.0 milestone on Oct 1, 2026
  2. iosayin commented on Oct 4, 2026

    @iosayin

    Hi maintainers,

    I've implemented and verified the fix for this issue on branch fix/4934-verify-action-pins-case-insensitive in fork iosayin/servers.

    Summary of Changes:

    1. Case-insensitive secret matching: Updated DEFAULT_TOKEN and /\bsecrets\b/ in handedSecret to be case-insensitive (gi/i), so expressions like ${{ SECRETS.DEPLOY_TOKEN }} mark the job as credentialed, and ${{ Secrets['github_token'] }} remains exempt.
    2. Case-insensitive needs and outputs: Updated NEEDS_USE to be case-insensitive (gi) and normalized property comparison (property === 'result').
    3. Case-insensitive job ID resolution: Added case-insensitive job resolution (jobsByName map and resolveJob helper) so job names captured in needs.<JOB>.outputs properly map back to the defining job in workflow.jobs.
    4. Regression tests: Added tests covering SECRETS.X, Secrets['github_token'] exemption, and mixed-case NEEDS / needs.JOB.outputs in scripts/verify-action-pins.test.mjs.

    Verification:

    • node --test scripts/verify-action-pins.test.mjs: 29/29 tests pass.
    • npm run test:scripts: 469/469 tests pass.
    • npm run validate: passed.

    Commit: iosayin@bbce40d
    Branch: https://github.com/iosayin/servers/tree/fix/4934-verify-action-pins-case-insensitive

    (Note: Direct fork PR creation via API is currently restricted for non-collaborator accounts on this repository. The branch is pushed and ready to merge or open as a PR.)

  3. self-assigned this
    on Oct 5, 2026
  4. cliffhall commented on Oct 5, 2026

    @cliffhall
    MemberAuthor

    Fixed by #5052, merged into v2/main.

  5. added a commit that references this issue on Oct 11, 2026
    4daf4a7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingv2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions