Found while auditing macos-ubuntu-bootstrap's consumption of these reusables. Filing here because the cause is in this repository, not in the consumer.
Both findings are the same class: an action pinned by commit SHA does not pin what that action executes. The repository's action-pinning discipline stops at the wrapper's metadata and does not follow it to the bytes.
1. scorecard-action executes a tag-addressed container image
.github/workflows/public-scorecard-json.yml:52 pins the action correctly:
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
That commit's action.yaml ends:
runs:
using: "docker"
image: "docker://ghcr.io/ossf/scorecard-action:v2.4.4"
The image is addressed by tag. Retagging or replacing ghcr.io/ossf/scorecard-action:v2.4.4 changes the container every consumer of this reusable executes, with no change to any pinned SHA and nothing in any consumer's diff to review.
Verified at the pinned commit:
$ gh api repos/ossf/scorecard-action/git/trees/2d1146689b8cda280b9bc96326124645441f03bc \
--jq '.tree[]|select(.path|test("^action"))'
action.yaml 048b824172dabec256108eeab49ea7e69cd08a39
# ... runs.image == "docker://ghcr.io/ossf/scorecard-action:v2.4.4"
No compromise observed. The finding is that the guarantee the SHA pin appears to give is not the guarantee it gives.
Suggested fix: pin the image by digest (docker://ghcr.io/ossf/scorecard-action@sha256:…) via a thin local wrapper, or invoke a checksum-verified binary the way osv-scan.yml and actionlint.yml already do in this repository — both of those are exemplary and are the reason this one stands out.
Proof it worked: a fixture that walks every pinned action's runs: metadata and fails on docker://…:tag or on a nested uses: resolving a floating version. Reverting the digest pin must fail it.
2. id-token: write is granted where publication is off
Same file:
permissions:
id-token: write # OIDC token for the Scorecard publish step
with publish_results an input that consumers can set to false. When it is false, the job holds a write-scoped OIDC capability it does not use. Least privilege here is cheap: make the permission conditional on the input, or document the runtime evidence that it is needed regardless.
I did not observe the runtime token request, so this half is unverified — if the action requests the token unconditionally, say so and close it.
For context on what the consumer did: macos-ubuntu-bootstrap had the same class of defect in its own ci.yml (raven-actions/actionlint pinned by SHA, version input defaulting to latest, no checksum) and fixed it by calling this repository's checksum-bound actionlint.yml instead — NDDev-it-com/macos-ubuntu-bootstrap#83. These two are what remain on this side of the boundary.
Found while auditing
macos-ubuntu-bootstrap's consumption of these reusables. Filing here because the cause is in this repository, not in the consumer.Both findings are the same class: an action pinned by commit SHA does not pin what that action executes. The repository's action-pinning discipline stops at the wrapper's metadata and does not follow it to the bytes.
1.
scorecard-actionexecutes a tag-addressed container image.github/workflows/public-scorecard-json.yml:52pins the action correctly:That commit's
action.yamlends:The image is addressed by tag. Retagging or replacing
ghcr.io/ossf/scorecard-action:v2.4.4changes the container every consumer of this reusable executes, with no change to any pinned SHA and nothing in any consumer's diff to review.Verified at the pinned commit:
No compromise observed. The finding is that the guarantee the SHA pin appears to give is not the guarantee it gives.
Suggested fix: pin the image by digest (
docker://ghcr.io/ossf/scorecard-action@sha256:…) via a thin local wrapper, or invoke a checksum-verified binary the wayosv-scan.ymlandactionlint.ymlalready do in this repository — both of those are exemplary and are the reason this one stands out.Proof it worked: a fixture that walks every pinned action's
runs:metadata and fails ondocker://…:tagor on a nesteduses:resolving a floating version. Reverting the digest pin must fail it.2.
id-token: writeis granted where publication is offSame file:
with
publish_resultsan input that consumers can set tofalse. When it is false, the job holds a write-scoped OIDC capability it does not use. Least privilege here is cheap: make the permission conditional on the input, or document the runtime evidence that it is needed regardless.I did not observe the runtime token request, so this half is unverified — if the action requests the token unconditionally, say so and close it.
For context on what the consumer did:
macos-ubuntu-bootstraphad the same class of defect in its ownci.yml(raven-actions/actionlintpinned by SHA,versioninput defaulting tolatest, no checksum) and fixed it by calling this repository's checksum-boundactionlint.ymlinstead — NDDev-it-com/macos-ubuntu-bootstrap#83. These two are what remain on this side of the boundary.