You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
verify:action-pins: match expression contexts and job names case-insensitively #4934
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.
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.
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.
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.
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:
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.
Case-insensitive needs and outputs: Updated NEEDS_USE to be case-insensitive (gi) and normalized property comparison (property === 'result').
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.
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.
(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.)
Activity
iosayin commentedon Oct 4, 2026
Hi maintainers,
I've implemented and verified the fix for this issue on branch
fix/4934-verify-action-pins-case-insensitivein forkiosayin/servers.Summary of Changes:
DEFAULT_TOKENand/\bsecrets\b/inhandedSecretto be case-insensitive (gi/i), so expressions like${{ SECRETS.DEPLOY_TOKEN }}mark the job as credentialed, and${{ Secrets['github_token'] }}remains exempt.NEEDS_USEto be case-insensitive (gi) and normalized property comparison (property === 'result').jobsByNamemap andresolveJobhelper) so job names captured inneeds.<JOB>.outputsproperly map back to the defining job inworkflow.jobs.SECRETS.X,Secrets['github_token']exemption, and mixed-caseNEEDS/needs.JOB.outputsinscripts/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.)
fix(ci): match expression contexts and job names case-insensitively i…
cliffhall commentedon Oct 5, 2026
Fixed by #5052, merged into v2/main.
fix(scripts): verify:action-pins matches contexts and job ids case-in…