verify-action-build: require a lock file for every dependency manifest#770
Open
potiuk wants to merge 2 commits intoapache:mainfrom
Open
verify-action-build: require a lock file for every dependency manifest#770potiuk wants to merge 2 commits intoapache:mainfrom
potiuk wants to merge 2 commits intoapache:mainfrom
Conversation
Every action type — node, python, deno, dart, ruby, go, rust — now fails
verification if a dependency manifest (package.json, pyproject.toml, go.mod,
...) is found without a matching lock file. Without a lock file, a rebuild
of the action pulls whatever transitive versions are latest at build time,
which makes the dist/ we verify non-reproducible.
The new analyze_lock_files check runs for every action type (not just
composite/docker, where analyze_dependency_pinning already runs), since JS
actions are the primary case where a missing lock file breaks our rebuild
comparison.
Recognised manifest-to-lock mappings:
package.json -> package-lock.json / yarn.lock / pnpm-lock.yaml / bun.lock
pyproject.toml -> uv.lock / poetry.lock / pdm.lock / requirements.txt
Pipfile -> Pipfile.lock
deno.json(c) -> deno.lock
pubspec.yaml -> pubspec.lock
Gemfile -> Gemfile.lock
go.mod -> go.sum
Cargo.toml -> Cargo.lock
Heuristics to avoid false positives:
- pyproject.toml with only tool config (ruff/black/mypy) is skipped.
- go.mod with no require directives is skipped.
- Rust library crates ([lib] without [[bin]]) skip the Cargo.lock
requirement per Cargo convention; binary crates and workspaces don't.
- Sub-path manifests fall back to repo-root manifests when absent.
Some upstream projects are libraries or CLI tools that also ship a GitHub Action wrapper — e.g. pypa/cibuildwheel (Python library on PyPI), dart-lang/setup-dart (Dart package on pub.dev). These repos legitimately don't commit a lock file because doing so would over-constrain their library consumers. Hard-failing on those would block otherwise-valid Dependabot bumps. Add lock_file_exemptions.yml at the repo root listing per-(org/repo) per-ecosystem exemptions. analyze_lock_files consults this file and reports an exempted manifest as a dim skipped entry (⊘) instead of a red failure. Exemptions are scoped per-ecosystem, so an action exempted for one ecosystem is still checked for others it declares (e.g. dart-lang/setup-dart's node side must still have package-lock.json). Preseeded entries: pypa/cibuildwheel → python (library on PyPI) dart-lang/setup-dart → dart (Dart library convention) Lookups lowercase org/repo so case mismatches don't silently miss.
Member
Author
Smoke-test results on previously-approved actionsRan the new check end-to-end against 5 representative actions drawn from
One more I tried didn't complete:
Follow-up in this PRThe two
Pushed as a second commit ( Generated-by: Claude Opus 4.7 (1M context) |
dave2wave
requested changes
Apr 24, 2026
| @@ -0,0 +1,36 @@ | |||
| # lock_file_exemptions.yml | |||
Member
There was a problem hiding this comment.
RAT Check complains about the missing license header here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every action type — node, python, deno, dart, ruby, go, rust — now fails verification if a dependency manifest (
package.json,pyproject.toml,go.mod, …) is found without a matching lock file. Without a lock file, a rebuild of the action pulls whatever transitive versions happen to be latest at build time, and any comparison we do against the committeddist/becomes noise.The new
analyze_lock_filescheck runs for every action type, including JS actions — which is the primary case where a missing lock file breaks our rebuild-and-compare workflow. Previouslyanalyze_dependency_pinningran only in the composite/docker branch and only warned on unpinned deps; nothing required a lock file at all.Why now
This came out of the #761 (
mozilla-actions/sccache-action0.0.9 → 0.0.10) investigation, which exposed that we have no affirmative check that actions we approve pin their transitive deps.sccache-actionhappens to ship a lock file (the drift there is a separate toolchain issue, addressed upstream at Mozilla-Actions/sccache-action#252), but the class of "action with no lock file" is wider and equally bad for reproducibility.Manifest → lock file mappings
package.jsonpackage-lock.json/yarn.lock/pnpm-lock.yaml/bun.lock/bun.lockbpyproject.tomluv.lock/poetry.lock/pdm.lock/requirements.txtPipfilePipfile.lockdeno.json(c)deno.lockpubspec.yamlpubspec.lockGemfileGemfile.lockgo.modgo.sumCargo.tomlCargo.lockHeuristics to avoid false positives
pyproject.tomlwith only tool config (ruff / black / mypy / ...) and no[project.dependencies]or[tool.<x>.dependencies]section is skipped.go.modwith norequiredirectives (no third-party deps) is skipped.[lib]without[[bin]]) skip theCargo.lockrequirement per Cargo convention; binary crates and workspaces do not.Output example
A new Lock File Presence section appears in the verification output, with one line per detected manifest:
And a new
Lock file presencerow shows in the verification summary table (pass/fail).Behaviour
overall_passedalongside the JS-build and binary-download checks, so theRESULTpanel turns red with a specific message.Test plan
test_security.py::TestAnalyzeLockFilescovering each ecosystem, the library / no-deps / no-require heuristics, sub-path handling, and multi-ecosystem aggregation. All 170 tests in the suite pass.actions/checkout) — should pass.Generated-by: Claude Opus 4.7 (1M context)