Skip to content

Pin every third-party action to a commit SHA - #16

Merged
danielhanchen merged 1 commit into
masterfrom
security/pin-actions-to-shas
Sep 22, 2026
Merged

danielhanchen merged 1 commit into
masterfrom
security/pin-actions-to-shas

Conversation

@danielhanchen

Copy link
Copy Markdown
Member

Found while auditing all 26 unslothai repositories against the 2026-09-21 Rust disclosure, where cargo miri wrote the whole process environment under target/, CI cached it, and pull_request runs could restore caches written on the default branch.

This repository is not an instance of that class. The two jobs that write caches (windows-latest-rocm, ubuntu-latest-rocm) contain no secrets.* reference at all, so the chain never starts. What the same audit does turn up here is the adjacent supply-chain problem.

The change

52 uses across 20 distinct actions referenced a mutable tag. A tag is a pointer the upstream owner can move whenever they like, so each one was an agreement to run whatever that repository contains on the day CI happens to run. Two stand out: ilammy/msvc-dev-cmd@v1 and anzz1/action-create-release@v1 are major-version tags on small single-maintainer repositories, and the second runs in the release job (build.yml:735), which holds secrets.GITHUB_TOKEN at :777. That token expires with the job, which bounds the damage without removing it.

Every ref is now a 40-character commit SHA with the tag it resolved to kept in a trailing comment, so the version stays readable and tooling can still see it. No version changes: each SHA is what the tag pointed at when this was written, so behaviour is identical to the last run.

One incidental finding, which looks worse than it is

build.yml used two spellings: prompt/actions-commit-hash in seven places and pr-mpt/actions-commit-hash in one. That reads like a typosquat. It is not — pr-mpt was renamed to prompt, GitHub redirects the old path, and both resolve to the same commit 01d19a83c242e1851c9aa6cf9625092ecd095d09. Pinning collapses them onto one identity, so the question cannot arise again.

These files are inherited from upstream leejet/stable-diffusion.cpp, so the diff is deliberately nothing but the refs.

Not addressed here

The ROCm cache at build.yml:476 stores C:\TheRock\build, which is a Python virtual environment the build later activates and executes (:497-498), under the key rocm-wheels-${{ env.ROCM_VERSION }}-${{ runner.os }}. That key records no content identity, and there is no lockfile in the repository to hash, because the wheels come from an index at install time.

The exposure is cache poisoning rather than secret disclosure, and it is bounded: cache writes are scoped to the branch that made them, so a pull request cannot reach the entry master uses, and poisoning would need push access to a branch matching the target scope. Fixing it properly means splitting the combined actions/cache into a restore plus a gated save in inherited upstream code, which is a larger and more opinionated change than this one should carry.

Verification

Every workflow still parses as YAML, and a re-scan for third-party uses: refs not matching @<40 hex> now returns nothing.

Found while auditing all 26 unslothai repositories against the 2026-09-21
cargo-miri cache disclosure. This is not an instance of that class: the two jobs
here that write caches hold no secrets at all, so the chain never starts. It is
the adjacent supply-chain problem the same audit looks for.

52 uses across 20 distinct actions referenced a mutable tag. A tag is a pointer
the upstream owner can move at any time, so every one of them was an agreement to
run whatever that repository contains on the day CI happens to run, decided by
someone outside this project. Two matter more than the rest:
ilammy/msvc-dev-cmd@v1 and anzz1/action-create-release@v1 are major-version tags
on small single-maintainer repositories, and the second runs in the release job,
which holds secrets.GITHUB_TOKEN. That token is ephemeral and expires with the
job, which bounds the damage but does not remove it.

Each ref is now a 40-character commit SHA with the tag it resolved to kept in a
trailing comment, so the version stays readable and renovate-style tooling can
still see it. No version changes: every SHA is what the tag pointed at when this
was written, so the behaviour is identical to the last run.

One incidental finding, recorded because it looks alarming and is not.
build.yml used two spellings, prompt/actions-commit-hash in seven places and
pr-mpt/actions-commit-hash in one, which reads like a typosquat. They are the same
repository: pr-mpt was renamed to prompt, GitHub redirects the old path, and both
resolve to 01d19a83c242e1851c9aa6cf9625092ecd095d09. Pinning collapses them onto
one identity, so the question cannot come up again.

These files are inherited from upstream leejet/stable-diffusion.cpp, so the diff
is deliberately nothing but the refs.

Not addressed here, so it is not lost: the ROCm cache at build.yml:476 stores
C:\TheRock\build, which is a Python virtual environment the later build activates
and executes, under the key rocm-wheels-<ROCM_VERSION>-<runner.os>. That key
records no content identity, and there is no lockfile in the repository to hash
because the wheels come from an index at install time. The exposure is cache
poisoning rather than secret disclosure, and it is bounded by cache writes being
scoped to the branch that made them, so a pull request cannot reach the entry
master uses. Fixing it properly means splitting the combined action into a restore
and a gated save in inherited upstream code, which is a larger change than this
one should carry.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 22, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-22T09:07:37.158648Z daf77e0 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@danielhanchen
danielhanchen merged commit 7bbffa3 into master Sep 22, 2026
7 checks passed
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