actions-pinning
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseactions-pinning
actions-pinning
Pins every reference in GitHub Actions workflows, composite
actions, and reusable workflows to a commit SHA, verifies each SHA
belongs to the claimed action repository, and keeps the pins current
with Dependabot.
uses:This skill covers supply-chain pinning only. covers
and hardening for the same
workflow files; covers wall-clock speed. Run those
separately.
/oss-hygieneToken-PermissionsDangerous-Workflow/ci-optimize将 GitHub Actions 工作流、复合 Action 和可复用工作流中的每个 引用锁定到提交 SHA,验证每个 SHA 确实属于所声明的 Action 仓库,并使用 Dependabot 让这些锁定版本保持最新。
uses:此 skill 仅涵盖供应链锁定。 负责对同一工作流文件进行 和 加固; 负责实际运行时间优化。这两项应单独运行。
/oss-hygieneToken-PermissionsDangerous-Workflow/ci-optimizeProtocol
协议
1. Inventory
1. 盘点
Run the bundled scanner against every workflow and composite/reusable
workflow file:
bash
python3 scripts/pin_check.py .It walks , , and every
or manifest in the tree. It skips VCS and
dependency directories: , , , , ,
, , , , , and . It scans
, because a reference can run a vendored action.
It prints a table of for every
line and exits 1 when any reference is unpinned. Use
for machine-readable output when scripting the remaining steps.
.github/workflows/**.github/actions/**action.ymlaction.yaml.git.hg.svnnode_modules.venvvenv__pycache__distbuild.toxtargetvendor/./vendor/...(file, line, action, ref, status)uses:--jsonThe scanner uses regular expressions, not a YAML parser. It removes one
matching pair of surrounding quotes from each value. It has two
known limits; check the workflow by hand when either applies:
uses:- A line inside a
uses:block scalar gives a false row.run: | - A flow-style mapping such as gives no row.
- {uses: owner/repo@v1}
对每个工作流文件以及复合/可复用工作流文件运行内置扫描器:
bash
python3 scripts/pin_check.py .它会遍历目录树中的 、,以及每个 或 manifest。它会跳过 VCS 和依赖目录:、、、、、、、、、 和 。它会扫描 ,因为 引用可以运行 vendored Action。
.github/workflows/**.github/actions/**action.ymlaction.yaml.git.hg.svnnode_modules.venvvenv__pycache__distbuild.toxtargetvendor/./vendor/...它会为每个 行打印 表格,并在任何引用未锁定时以状态码 1 退出。编写后续步骤的脚本时,使用 获取机器可读输出。
uses:(file, line, action, ref, status)--json扫描器使用正则表达式,而不是 YAML parser。它会从每个 值中移除一对匹配的外围引号。它有两个已知限制;出现以下任一情况时,应手动检查工作流:
uses:- 块标量内的
run: |行会产生误报行。uses: - 等流式映射不会产生对应行。
- {uses: owner/repo@v1}
2. Classify
2. 分类
The scanner assigns one status per reference:
| Status | Meaning | Action needed |
|---|---|---|
| already a full 40-character commit SHA | none |
| a tag-shaped ref such as | resolve to a SHA |
| a common branch name such as | resolve to a SHA |
| any other ref, such as | resolve to a SHA |
| a commit-like hex ref, such as a short SHA | resolve to the full lowercase SHA |
| no | resolve to a SHA |
| a | remove the |
| a local | skip — not a supply-chain risk |
| a | flag separately — SHA pinning applies to the image digest, not this scanner |
扫描器会为每个引用分配一种状态:
| 状态 | 含义 | 需要采取的操作 |
|---|---|---|
| 已经是完整的 40 字符提交 SHA | 无 |
| 形如标签的引用,例如 | 解析为 SHA |
| 常见分支名称,例如 | 解析为 SHA |
| 任何其他引用,例如 | 解析为 SHA |
| 类似提交的十六进制引用,例如短 SHA | 解析为完整的小写 SHA |
| 完全没有 | 解析为 SHA |
| 带有 | 移除 |
| 本地 | 跳过——不属于供应链风险 |
| | 单独标记——SHA 锁定应作用于镜像 digest,而不由该扫描器处理 |
3. Resolve each ref to a commit SHA
3. 将每个引用解析为提交 SHA
For each , , ,
, or finding:
unpinned-tagunpinned-branchunpinned-refunpinned-commitmissing-refbash
undefined对于每条 、、、 或 检查结果:
unpinned-tagunpinned-branchunpinned-refunpinned-commitmissing-refbash
undefinedTag: the ref object is the commit (lightweight) or a tag object (annotated).
Tag: the ref object is the commit (lightweight) or a tag object (annotated).
gh api repos/{owner}/{repo}/git/ref/tags/{tag} --jq '.object'
gh api repos/{owner}/{repo}/git/ref/tags/{tag} --jq '.object'
Branch: the ref object is the commit directly.
Branch: the ref object is the commit directly.
gh api repos/{owner}/{repo}/git/ref/heads/{branch} --jq '.object.sha'
gh api repos/{owner}/{repo}/git/ref/heads/{branch} --jq '.object.sha'
Annotated tag: dereference the tag object to get the commit SHA.
Annotated tag: dereference the tag object to get the commit SHA.
gh api repos/{owner}/{repo}/git/tags/{tag_sha} --jq '.object.sha'
For an `unpinned-ref`, try the `tags/` path first, then the `heads/`
path.
For an `unpinned-commit`, the tag and branch paths cannot resolve the
ref. Resolve it through the commits endpoint, which returns the full
lowercase SHA:
```bash
gh api repos/{owner}/{repo}/commits/{ref} --jq '.sha'Impostor-commit check — always run before rewriting a pin. Confirm
the resolved SHA actually belongs to the action's own repository, not
a fork, a rewritten ref, or a spoofed value:
bash
gh api repos/{owner}/{repo}/commits/{sha}A 404 here means the SHA does not belong to that repository. Stop and
re-resolve; do not write an unverified SHA.
Once verified, rewrite the line:
yaml
uses: owner/repo@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2Keep the original tag as a trailing comment — it is the human-readable
version marker; the SHA is the enforced pin.
gh api repos/{owner}/{repo}/git/tags/{tag_sha} --jq '.object.sha'
对于 `unpinned-ref`,先尝试 `tags/` 路径,然后尝试 `heads/` 路径。
对于 `unpinned-commit`,tag 和 branch 路径均无法解析该引用。通过 commits 端点进行解析,该端点会返回完整的小写 SHA:
```bash
gh api repos/{owner}/{repo}/commits/{ref} --jq '.sha'冒名提交检查——始终在重写锁定版本前执行。 确认解析出的 SHA 确实属于该 Action 自身的仓库,而不是来自 fork、被重写的 ref 或伪造值:
bash
gh api repos/{owner}/{repo}/commits/{sha}这里返回 404 意味着该 SHA 不属于对应仓库。停止并重新解析;不要写入未经验证的 SHA。
验证完成后,重写该行:
yaml
uses: owner/repo@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2保留原始标签作为尾随注释——它是便于人类阅读的版本标记;SHA 才是实际强制执行的锁定版本。
4. Keep pins fresh with Dependabot
4. 使用 Dependabot 保持锁定版本为最新
Add (or merge into an existing ) the
ecosystem entry in . Dependabot
resolves tag bumps to new commit SHAs automatically and opens a PR
with the version comment updated — review and merge those PRs rather
than hand-editing pins later.
dependabot.ymlgithub-actionsassets/dependabot-actions-snippet.ymlVersion updates (not security updates) default to a 3-day
registry-availability cooldown as of 2026-07-14; a newly published tag
won't appear in a Dependabot PR until it clears that window. See
for the source.
references/policy.mdFor the full scaffold across every ecosystem in the
repo, use — this skill only owns the
entry.
dependabot.yml/oss-hygienegithub-actions将 中的 ecosystem 条目添加到现有 中,或与之合并。Dependabot 会自动将标签升级解析为新的提交 SHA,并创建一个已更新版本注释的 PR——应审查并合并这些 PR,而不是之后手动编辑锁定版本。
assets/dependabot-actions-snippet.ymlgithub-actionsdependabot.yml截至 2026-07-14,版本更新(而非安全更新)默认采用 3 天的 registry 可用性冷却期;新发布的标签在通过该窗口前不会出现在 Dependabot PR 中。来源见 。
references/policy.md如需覆盖仓库中所有 ecosystem 的完整 脚手架,请使用 ——本 skill 仅负责 条目。
dependabot.yml/oss-hygienegithub-actions5. Repository and organization pinning policy
5. 仓库和组织级锁定策略
GitHub can enforce SHA pinning at the allowed-actions policy level
(repo, org, or enterprise), including blocking specific actions
outright. Read the current policy at the scope of the request before
proposing any change:
bash
undefinedGitHub 可以在 allowed-actions 策略层级(仓库、组织或企业)强制执行 SHA 锁定,包括直接阻止特定 Action。提出更改前,先读取请求范围内的当前策略:
bash
undefinedRepository scope.
Repository scope.
gh api repos/{owner}/{repo}/actions/permissions
gh api repos/{owner}/{repo}/actions/permissions
Organization scope. Use this for an organization-wide request.
Organization scope. Use this for an organization-wide request.
gh api orgs/{org}/actions/permissions
These are reads. Never call the corresponding write endpoints
(`PUT repos/{owner}/{repo}/actions/permissions`,
`PUT orgs/{org}/actions/permissions`, and the `selected-actions` /
`allowed_actions` variants) without the user's explicit authorization
for that specific change — policy changes can block other workflows
from running.
The SHA-pinning policy applies to actions only. Reusable workflows
can still use a tag under that policy, so this skill pins reusable
workflow references itself.gh api orgs/{org}/actions/permissions
以上调用仅用于读取。未经用户明确授权进行该项具体更改,绝不能调用对应的写入端点(`PUT repos/{owner}/{repo}/actions/permissions`、`PUT orgs/{org}/actions/permissions`,以及 `selected-actions`/`allowed_actions` 变体),因为策略更改可能会阻止其他工作流运行。
SHA 锁定策略仅适用于 Action。在该策略下,可复用工作流仍可使用标签,因此本 skill 会自行锁定可复用工作流引用。6. Flag mutable downloads inside workflow scripts
6. 标记工作流脚本中的可变下载
Regex pinning covers only. While reading each workflow, also
flag (findings only, no rewrite) any step that fetches code
outside the pinned-action mechanism:
uses:run:- or
curl ... | shpiping a remote script straight into a shell.curl ... | bash - from a URL with no version or checksum.
wget - (or
pip install package,npm install, ...) with no version pin.gem install
These are supply-chain risk in the same family as an unpinned action,
but fixing them is per-ecosystem and often changes build behavior —
report them, do not rewrite them.
正则表达式锁定仅涵盖 。阅读每个工作流时,还要标记(仅报告发现,不重写)任何通过锁定 Action 机制之外获取代码的 步骤:
uses:run:- 或
curl ... | sh,将远程脚本直接通过管道交给 shell 执行。curl ... | bash - 使用 从没有版本或校验和的 URL 下载。
wget - (或
pip install package、npm install等)且未锁定版本。gem install
这些问题与未锁定的 Action 属于同一类供应链风险,但修复方式取决于具体 ecosystem,且通常会改变构建行为——请报告,不要重写。
7. Optional: zizmor
7. 可选:zizmor
If is installed, run it for a second opinion (it also checks
and composite-action internals this scanner does
not):
zizmorbot-conditionsbash
zizmor .github/workflows/Skip silently if it is not installed — it is optional tooling, not a
required gate.
如果已安装 ,可运行它以获得第二意见(它还会检查 以及此扫描器不会检查的复合 Action 内部机制):
zizmorbot-conditionsbash
zizmor .github/workflows/如果未安装则静默跳过——这是可选工具,不是必需的门禁。
8. Verify
8. 验证
Re-run the scanner after every rewrite batch:
bash
python3 scripts/pin_check.py . && echo "all uses: references pinned"Exit 0 with no , , ,
, , or rows means every
actionable reference is pinned. Report and rows
separately — they are informational, not failures.
unpinned-tagunpinned-branchunpinned-refunpinned-commitmissing-refinvalid-self-reflocaldocker每批重写完成后,重新运行扫描器:
bash
python3 scripts/pin_check.py . && echo "all uses: references pinned"以状态码 0 退出,且没有 、、、、 或 行,意味着每个可处理的引用均已锁定。 和 行应单独报告——它们只是信息,不是失败。
unpinned-tagunpinned-branchunpinned-refunpinned-commitmissing-refinvalid-self-reflocaldockerWhat this skill is NOT for
本 skill 不适用于
- and
Token-Permissionshardening (least-privilegeDangerous-Workflowblocks,permissions:risk,pull_request_targetinterpolation into shell) — use${{ github.event.* }}./oss-hygiene - CI run-time speed — use .
/ci-optimize - Choosing or writing the full for non-Actions ecosystems — use
dependabot.yml./oss-hygiene - Applying an org-wide allowed-actions policy without the repo owner's explicit authorization for that specific write.
- 和
Token-Permissions加固(最小权限Dangerous-Workflow块、permissions:风险、将pull_request_target插值到 shell 中)——使用${{ github.event.* }}。/oss-hygiene - CI 运行速度优化——使用 。
/ci-optimize - 为非 Actions ecosystem 选择或编写完整的 ——使用
dependabot.yml。/oss-hygiene - 在没有仓库所有者针对该项具体写入操作明确授权的情况下,应用组织范围的 allowed-actions 策略。
References
参考资料
- — cited primary sources for
references/policy.mdsyntax, SHA pinning, the allowed-actions policy, SHA resolution endpoints, the Dependabot cooldown, and zizmor.uses: - — the inventory/classification scanner used in steps 1, 2, and 8 (
scripts/pin_check.pyruns its embedded fixtures;--self-testfor machine-readable output).--json - — the
assets/dependabot-actions-snippet.ymlecosystem entry forgithub-actions.dependabot.yml
- ——
references/policy.md语法、SHA 锁定、allowed-actions 策略、SHA 解析端点、Dependabot 冷却期和 zizmor 的引用来源。uses: - ——步骤 1、2 和 8 中使用的盘点/分类扫描器(
scripts/pin_check.py运行其内置测试夹具;--self-test用于机器可读输出)。--json - ——用于
assets/dependabot-actions-snippet.yml的dependabot.ymlecosystem 条目。github-actions