Skip to content

fix(ci): harden GitHub Actions workflows (#2119) - #2410

Closed
hf-security-analysis[bot] wants to merge 0 commit into
feat/train_encode_splitfrom
security/workflow-hardening/pr-2119
Closed

hf-security-analysis[bot] wants to merge 0 commit into
feat/train_encode_splitfrom
security/workflow-hardening/pr-2119

Conversation

@hf-security-analysis

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

Copy link
Copy Markdown
Contributor

Automated hardening of the workflow files flagged on #2119.

Warning

This narrows what the workflow can reach. Job permissions were declared in .github/workflows/bitsplit.yml, .github/workflows/docs-check.yml, .github/workflows/node.yml, .github/workflows/rust-release.yml, .github/workflows/rust.yml. Each job now gets only the scopes its steps were read to need — if one of them does something this could not see, it will fail on the next run. The table below says which step drove each scope.

Targets feat/train_encode_split. Files changed:

  • .github/workflows/CI.yml
  • .github/workflows/bitsplit.yml
  • .github/workflows/docs-check.yml
  • .github/workflows/node.yml
  • .github/workflows/python.yml
  • .github/workflows/rust-release.yml
  • .github/workflows/rust.yml

Fixed by this PR:

  • HIGH unpinned-action (pinact) — .github/workflows/CI.yml:200
  • HIGH unpinned-action (pinact) — .github/workflows/bitsplit.yml:18
  • HIGH unpinned-action (pinact) — .github/workflows/bitsplit.yml:32
  • HIGH unpinned-action (pinact) — .github/workflows/bitsplit.yml:53
  • HIGH unpinned-action (pinact) — .github/workflows/bitsplit.yml:56
  • HIGH unpinned-action (pinact) — .github/workflows/node.yml:33
  • HIGH unpinned-action (pinact) — .github/workflows/node.yml:46
  • HIGH unpinned-action (pinact) — .github/workflows/node.yml:55
  • HIGH unpinned-action (pinact) — .github/workflows/node.yml:90
  • HIGH unpinned-action (pinact) — .github/workflows/python.yml:76
  • HIGH unpinned-action (pinact) — .github/workflows/python.yml:86
  • HIGH unpinned-action (pinact) — .github/workflows/python.yml:128
  • HIGH unpinned-action (pinact) — .github/workflows/rust.yml:47
  • HIGH unpinned-action (pinact) — .github/workflows/rust.yml:56
  • HIGH unpinned-action (pinact) — .github/workflows/rust.yml:258
  • HIGH unpinned-action (pinact) — .github/workflows/rust.yml:267
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/bitsplit.yml:1
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/bitsplit.yml:15
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/bitsplit.yml:29
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/bitsplit.yml:50
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/docs-check.yml:1
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/docs-check.yml:16
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/node.yml:1
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/node.yml:13
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/node.yml:62
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/node.yml:100
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/rust-release.yml:1
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/rust-release.yml:12
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/rust.yml:1
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/rust.yml:10
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/rust.yml:101
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/rust.yml:128
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/rust.yml:145
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/rust.yml:176
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/rust.yml:200
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/rust.yml:233

Reported on the pull request but not fixed here — each needs a decision this bot should not make for you:

  • CRITICAL impostor-commit (zizmor) — .github/workflows/docs-check.yml:31
  • CRITICAL impostor-commit (zizmor) — .github/workflows/node.yml:24
  • CRITICAL impostor-commit (zizmor) — .github/workflows/node.yml:73
  • CRITICAL impostor-commit (zizmor) — .github/workflows/node.yml:111
  • CRITICAL impostor-commit (zizmor) — .github/workflows/python.yml:26
  • CRITICAL impostor-commit (zizmor) — .github/workflows/python.yml:60
  • CRITICAL impostor-commit (zizmor) — .github/workflows/python.yml:112
  • CRITICAL impostor-commit (zizmor) — .github/workflows/python.yml:161
  • CRITICAL impostor-commit (zizmor) — .github/workflows/rust-release.yml:19
  • CRITICAL impostor-commit (zizmor) — .github/workflows/rust.yml:26
  • CRITICAL impostor-commit (zizmor) — .github/workflows/rust.yml:115
  • CRITICAL impostor-commit (zizmor) — .github/workflows/rust.yml:138
  • CRITICAL impostor-commit (zizmor) — .github/workflows/rust.yml:155
  • CRITICAL impostor-commit (zizmor) — .github/workflows/rust.yml:186
  • CRITICAL impostor-commit (zizmor) — .github/workflows/rust.yml:210
  • CRITICAL impostor-commit (zizmor) — .github/workflows/rust.yml:243
  • HIGH unpinnable-reference (pinact) — .github/workflows/bitsplit.yml:19
  • HIGH unpinnable-reference (pinact) — .github/workflows/bitsplit.yml:33
  • HIGH unpinnable-reference (pinact) — .github/workflows/bitsplit.yml:54
  • HIGH unpinnable-reference (pinact) — .github/workflows/rust.yml:280
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/build_pr_documentation.yml:1
  • MEDIUM excessive-permissions (zizmor) — .github/workflows/build_pr_documentation.yml:17

Permissions

.github/workflows/bitsplit.yml

job granted why
native contents: read Only actions/checkout needs the token; the cargo fmt/clippy/test and cargo run -p bitmap_gen && git diff --exit-code drift check operate on the local checkout without any API access.
avx512-sde contents: read actions/checkout drives contents: read; the Intel SDE download is a plain curl to downloadmirror.intel.com (external host, no GitHub token) and the cargo test/SDE run needs nothing.
wasm contents: read actions/checkout needs contents: read; the bytecodealliance wasmtime setup action only installs a toolchain (it may fetch a public release, still read-only) and cargo test under wasmtime needs no token.

.github/workflows/build_pr_documentation.yml

build was left as it is — This job only delegates to the external reusable workflow huggingface/doc-builder/.github/workflows/build_pr_documentation.yml, which is not in this file, so the token scopes its jobs declare cannot be read here (it typically only checks out and builds docs, but that is unverifiable from this file).

.github/workflows/docs-check.yml

job granted why
build contents: read Only actions/checkout needs the token (contents: read); the pip installs, Sphinx build, and actions/upload-artifact within the same run require no token scopes.

.github/workflows/node.yml

job granted why
build_and_test contents: read Only actions/checkout touches the GitHub token; the make test step downloads Hub fixtures with secrets.HF_TOKEN (unrelated to GITHUB_TOKEN) and caching/setup actions need no scopes — the Makefile contents are not in this file, so double-check that step if it ever calls the GitHub API.
quality contents: read Job only checks out the repo and runs cargo fmt/clippy and yarn lint locally, so actions/checkout is the sole token consumer.
audit contents: read Job only checks out the repo and runs cargo install cargo-audit plus cargo audit, which query crates.io/RustSec rather than the GitHub API, so actions/checkout drives the only needed scope.

.github/workflows/rust-release.yml

job granted why
rust_publish contents: read Only actions/checkout needs the token (contents: read); the cargo build, bitmap_gen regeneration, local git diff check and cargo publish all use crates.io's CRATES_TOKEN secret rather than GITHUB_TOKEN, so no write scopes are required.

.github/workflows/rust.yml

job granted why
build_and_test contents: read Only actions/checkout needs the token (contents: read); the cargo build/test, cache, uv and make test/make oracle steps fetch fixtures from the HF Hub using HF_TOKEN, not GITHUB_TOKEN — the Makefile targets are not in this file, so a reviewer may want to confirm they make no GitHub API calls.
clippy contents: read actions/checkout is the only token consumer; rust-toolchain, rust-cache and cargo clippy need no repository scopes.
format contents: read Only actions/checkout requires the token; cargo fmt --check needs nothing.
readme contents: read actions/checkout drives contents: read; installing cargo-readme and diffing generated READMEs is local work with no API writes.
audit contents: read actions/checkout drives contents: read; cargo audit reads the RustSec advisory database over plain HTTPS and does not upload results to GitHub, so no security-events is needed.
semver contents: read Only actions/checkout needs the token; cargo semver-checks compares against crates.io, not the GitHub API.
feature_matrix contents: read actions/checkout drives contents: read; taiki-e/install-action fetches cargo-hack release assets (read-only, covered by contents: read) and the fixture download uses HF_TOKEN rather than GITHUB_TOKEN.

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.

@hf-security-analysis hf-security-analysis Bot mentioned this pull request Sep 15, 2026
@hf-security-analysis
hf-security-analysis Bot force-pushed the security/workflow-hardening/pr-2119 branch from 4802855 to 6f8f8eb Compare September 18, 2026 07:47
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.

0 participants