Skip to content

fix(ci): harden GitHub Actions workflows (#14789) - #14825

Open
hf-security-analysis[bot] wants to merge 1 commit into
dependabot/github_actions/actions-ca6b5df6adfrom
security/workflow-hardening/pr-14789
Open

hf-security-analysis[bot] wants to merge 1 commit into
dependabot/github_actions/actions-ca6b5df6adfrom
security/workflow-hardening/pr-14789

Conversation

@hf-security-analysis

@hf-security-analysis hf-security-analysis Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Automated hardening of the workflow files flagged on #14789.

Warning

This changes when the workflow runs and what it can reach. Both triggers and permissions were rewritten in .github/workflows/claude_review.yml, .github/workflows/issue_labeler.yml, .github/workflows/pr_labeler.yml. Read the diff before merging — either one can change what the workflow is able to do.

Targets dependabot/github_actions/actions-ca6b5df6ad. Files changed, and what changed them:

  • .github/workflows/bot_pytest.yml — action pins; auth gate
  • .github/workflows/claude_review.yml — action pins; job permissions
  • .github/workflows/issue_labeler.yml — job permissions; llm injection
  • .github/workflows/mirror_community_pipeline.yml — action pins
  • .github/workflows/notify_slack_about_release.yml — action pins
  • .github/workflows/pr_dependency_test.yml — action pins
  • .github/workflows/pr_labeler.yml — job permissions; dangerous trigger
  • .github/workflows/pr_modular_tests.yml — action pins
  • .github/workflows/pr_test_fetcher.yml — action pins
  • .github/workflows/pr_tests.yml — action pins
  • .github/workflows/pr_tests_gpu.yml — action pins
  • .github/workflows/pr_torch_dependency_test.yml — action pins
  • .github/workflows/push_tests.yml — action pins
  • .github/workflows/push_tests_fast.yml — action pins
  • .github/workflows/push_tests_mps.yml — action pins
  • .github/workflows/pypi_publish.yaml — action pins
  • .github/workflows/release_tests_fast.yml — action pins
  • .github/workflows/ssh-runner.yml — action pins
  • .github/workflows/stale.yml — action pins
  • .github/workflows/trufflehog.yml — action pins
  • .github/workflows/update_metadata.yml — action pins

Fixed by this PR:

  • HIGH broken_auth_gate (claude) — .github/workflows/bot_pytest.yml
  • HIGH unpinned-action (pinact) — .github/workflows/bot_pytest.yml:78
  • HIGH unpinned-action (pinact) — .github/workflows/bot_pytest.yml:119
  • HIGH excessive-permissions (zizmor) — .github/workflows/claude_review.yml:10
  • HIGH excessive-permissions (zizmor) — .github/workflows/claude_review.yml:11
  • HIGH unpinned-action (pinact) — .github/workflows/claude_review.yml:37
  • HIGH llm_prompt_injection (claude) — .github/workflows/issue_labeler.yml
  • HIGH excessive-permissions (zizmor) — .github/workflows/issue_labeler.yml:9
  • HIGH unpinned-action (pinact) — .github/workflows/mirror_community_pipeline.yml:72
  • HIGH unpinned-action (pinact) — .github/workflows/mirror_community_pipeline.yml:78
  • HIGH unpinned-action (pinact) — .github/workflows/notify_slack_about_release.yml:16
  • HIGH unpinned-action (pinact) — .github/workflows/notify_slack_about_release.yml:19
  • HIGH unpinned-action (pinact) — .github/workflows/pr_dependency_test.yml:25
  • HIGH unpinned-action (pinact) — .github/workflows/pr_dependency_test.yml:27
  • HIGH dangerous-triggers (zizmor) — .github/workflows/pr_labeler.yml:3
  • HIGH excessive-permissions (zizmor) — .github/workflows/pr_labeler.yml:9
  • HIGH unpinned-action (pinact) — .github/workflows/pr_modular_tests.yml:47
  • HIGH unpinned-action (pinact) — .github/workflows/pr_modular_tests.yml:49
  • HIGH unpinned-action (pinact) — .github/workflows/pr_modular_tests.yml:67
  • HIGH unpinned-action (pinact) — .github/workflows/pr_modular_tests.yml:69
  • HIGH unpinned-action (pinact) — .github/workflows/pr_modular_tests.yml:95
  • HIGH unpinned-action (pinact) — .github/workflows/pr_modular_tests.yml:125
  • HIGH unpinned-action (pinact) — .github/workflows/pr_modular_tests.yml:154
  • HIGH unpinned-action (pinact) — .github/workflows/pr_test_fetcher.yml:34
  • HIGH unpinned-action (pinact) — .github/workflows/pr_test_fetcher.yml:48
  • HIGH unpinned-action (pinact) — .github/workflows/pr_test_fetcher.yml:89
  • HIGH unpinned-action (pinact) — .github/workflows/pr_test_fetcher.yml:115
  • HIGH unpinned-action (pinact) — .github/workflows/pr_test_fetcher.yml:144
  • HIGH unpinned-action (pinact) — .github/workflows/pr_test_fetcher.yml:170
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests.yml:42
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests.yml:44
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests.yml:62
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests.yml:64
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests.yml:122
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests.yml:166
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests.yml:198
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests.yml:226
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests.yml:252
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests.yml:293
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:43
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:45
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:63
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:65
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:95
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:113
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:133
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:182
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:205
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:251
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:267
  • HIGH unpinned-action (pinact) — .github/workflows/pr_tests_gpu.yml:299
  • HIGH unpinned-action (pinact) — .github/workflows/pr_torch_dependency_test.yml:25
  • HIGH unpinned-action (pinact) — .github/workflows/pr_torch_dependency_test.yml:27
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:40
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:58
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:78
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:109
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:131
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:166
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:183
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:209
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:226
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:251
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:267
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests.yml:298
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests_fast.yml:63
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests_fast.yml:96
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests_mps.yml:29
  • HIGH unpinned-action (pinact) — .github/workflows/push_tests_mps.yml:71
  • HIGH unpinned-action (pinact) — .github/workflows/pypi_publish.yaml:75
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:39
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:58
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:78
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:109
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:131
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:166
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:183
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:223
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:240
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:266
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:283
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:309
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:326
  • HIGH unpinned-action (pinact) — .github/workflows/release_tests_fast.yml:359
  • HIGH unpinned-action (pinact) — .github/workflows/ssh-runner.yml:41
  • HIGH unpinned-action (pinact) — .github/workflows/stale.yml:18
  • HIGH unpinned-action (pinact) — .github/workflows/stale.yml:21
  • HIGH unpinned-action (pinact) — .github/workflows/trufflehog.yml:18
  • HIGH unpinned-action (pinact) — .github/workflows/update_metadata.yml:21

This does not fix everything. 43 further finding(s) (39 high, 4 medium) need a decision this bot should not make for you. They are in the security channel with their locations — deliberately not repeated here, since this repository may be public and they are not fixed yet.

Permissions

.github/workflows/bot_pytest.yml

job granted why
gate pull-requests: write Both steps write via the issue-comments REST API on a pull request — POST .../issues/comments/{id}/reactions and POST .../issues/{pr}/comments — which the token satisfies with pull-requests: write; if this command were ever run on a plain issue rather than a PR, issues: write would be needed instead.
gpu contents: read actions/checkout of the PR head ref needs contents: read; the upload-artifact step is within the same run and needs no scope, and the pytest steps use no token.
report pull-requests: write The final step issues a literal gh api -X PATCH repos/$REPO/issues/comments/$CID to edit the bot's comment on the PR, which requires pull-requests: write (issues: write would be the equivalent if the target were a non-PR issue).

.github/workflows/claude_review.yml

job granted why
claude-review contents: write, issues: read, pull-requests: write The final "Open follow-up PR with Claude's changes" step pushes branches (git push origin) and runs gh pr create / gh pr comment, driving contents: write and pull-requests: write; checkout and the gh pr view calls only need read, and issues: read covers reading the triggering issue_comment payload — the anthropics/claude-code-action step is the one to double-check, since it posts/updates its progress comment on the PR (covered by pull-requests: write) and may want actions: read if you later let it inspect CI logs.

.github/workflows/codeql.yml

codeql was left as it is — This job only delegates to an external reusable workflow (huggingface/security-workflows/.github/workflows/codeql-reusable.yml) whose steps are not in this file, so its actual token use cannot be verified here; the declared set (security-events: write, packages: read, actions: read, contents: read) matches a typical CodeQL analysis and should be checked against that called workflow.

.github/workflows/issue_labeler.yml

job granted why
label contents: read, issues: write actions/checkout needs contents: read, and the final step runs gh issue edit --add-label with GITHUB_TOKEN, which requires issues: write; the 'Get labels from LLM' step runs utils/label_issues.py which is not in this file, but it is only given HF_TOKEN (no GitHub token), so it should not need repository scopes — worth a quick check by the reviewer.

.github/workflows/pr_labeler.yml

job granted why
label contents: read, pull-requests: write actions/labeler reads the .github/labeler.yml config from the repo (contents: read) and applies/removes labels on the PR with sync-labels enabled (pull-requests: write).
missing-tests contents: read, pull-requests: write actions/checkout needs contents: read and the final step calls gh pr edit --add-label/--remove-label, requiring pull-requests: write; the helper script utils/check_test_missing.py is not in this file but only consumes piped API output on stdin, so it is the step a reviewer should double-check.
fixes-issue pull-requests: write The GraphQL query only reads PR metadata, but gh pr edit --add-label/--remove-label writes labels on the pull request, so pull-requests: write is required; no checkout is performed so no contents scope is needed.
size-label pull-requests: write Reads PR additions/deletions via gh api repos/.../pulls/N and then adds/removes size labels with gh pr edit, which needs pull-requests: write.

.github/workflows/pr_link_issue_reminder.yml

remind was left as it is — The 'Run reminder script' step executes utils/remind_link_issue.py, a file not present in this workflow, so its actual token usage cannot be verified; based on the job's stated purpose it likely needs contents: read (actions/checkout) plus pull-requests: write and issues: write for commenting/labeling/closing PRs, but a reviewer should confirm against the script.

.github/workflows/stale.yml

job granted why
close_stale_issues contents: read, issues: write, pull-requests: write actions/checkout needs contents: read, and the python utils/stale.py step (a PyGithub stale bot that comments on and closes stale issues and PRs) drives issues: write plus pull-requests: write — the script body is not in this file, so a reviewer should confirm it does not also write repository contents.

Anything not listed above keeps the permissions it had. To measure a job this could not read, add GitHubSecurityLab/actions-permissions/monitor to it and run the workflow — it reports the minimum the run actually used.

Pinning changes come from pinact and are mechanical. Any other change was generated by Claude — read it before merging.

@github-actions github-actions Bot added CI size/L PR with diff > 200 LOC labels Sep 21, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI size/L PR with diff > 200 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants