🔒(ci) harden every workflow install and pin every action to a hash - #1607
Draft
davd-gzl wants to merge 5 commits into
Draft
🔒(ci) harden every workflow install and pin every action to a hash#1607davd-gzl wants to merge 5 commits into
davd-gzl wants to merge 5 commits into
Conversation
A package could run arbitrary code on the runner while installing, and the versions were resolved rather than taken from the pins. The two pip calls now take wheels only, and uv run no longer resolves an environment of its own.
gitlint-core keeps the linter at 0.19.1, which the wheels-only pin was resolving down to 0.18.0 through an sdist-only sh. --ignore-scripts stops three npm ci, one yarn install and one npm install -g running the scripts of what they fetch, and yarn is pinned to 1.22.22. curl holds the dockerize download and its redirects to https.
A tag and a branch both move, so actions/checkout@v6 and numerique-gouv/action-trivy-cache@main ran whatever the owner had last pushed. Each of the 65 uses now names a 40-character commit, with the version it resolved to in a trailing comment. dependabot.yml keeps the hash and that comment moving together.
The repository is already called meet, so the file name said nothing about what the workflow holds. The print-statement check now excludes the whole workflows directory rather than one file by name: its own grep carries the literal print(, so the rename would otherwise match it on the deleted lines and fail the job.
--no-sync already stops uv run resolving an environment of its own, and that is what the version findings were about. Building is a separate guarantee that nothing on the line carried, so --no-build now says it outright. It is inert beside --no-sync, and the three lint jobs are unchanged.
|
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.




Every install step in the four workflow files runs code shipped by the packages it fetches, and every
uses:names a tag or a branch, either of which its owner can repoint. SonarCloud counts 60 security findings across those files, 30 issues and 30 hotspots.This clears 55 of the 60, and it does not turn the gate green.
The five left are MAJOR, and the gate reads the worst finding in a pull request's own new code rather than a count, so
new_security_ratingis C against the A it asks for. Three are theuv sync --locked --all-extrascalls, where--no-buildrefuses the project's own editable install andbrevo-python==1.2.0with it. Two are thepip install --user .[dev]calls, where no lock file names the version installed. Clearing them needs a lock file forsrc/summaryor a maintainer marking the five, and neither is in this diff.meet.ymlbecomesci.ymlon the way, which git records as a rename of two changed lines.Five changes follow.
pip install,uv run: a step runs whatever the package it fetches asks it toBoth
pip installcalls take--only-binary=:all:, so a wheel is unpacked and nosetup.pyruns, and everyuv runtakes--no-sync --no-build.Two flags because they answer two rules.
--no-syncfixes the version, by using the environment the sync above provisioned; building is a separate guarantee that nothing on the line carried.gitlint: a wheels-only install drops it a minor versiongitlint0.19.1 depends ongitlint-core[trusted-deps], and that extra pinssh==1.14.3, which ships no wheel. So--only-binary=:all:on the package name does not fail, it backtracks:gitlint-coreis the same console script without that extra, and resolvesshto 2.4.0, which ships one.npm ci,yarn install,curl: five installs and a download run unconstrainedNothing in
src/frontend,src/sdk/libraryorsrc/mailpassed--ignore-scripts, so any package in those trees could execute code on the runner while it installed. The threepackage.jsonfiles declare nopostinstalland noprepare, so every script now skipped belongs to a dependency.npm install -g yarntook whatever the tag pointed at, and now readsyarn@1.22.22, the version it already resolved to.The dockerize download followed redirects to any protocol.
--protoand--proto-redirhold it to https.uses:: a tag and a branch both move under the pinactions/checkout@v6is a tag its owner can repoint, andnumerique-gouv/action-trivy-cache@mainis a branch that changes on every push. All 65 uses now name a 40-character commit, with the version it resolved to in a trailing comment.Every pin is the commit its old ref pointed at, so nothing here changes what runs. One pass proves it, dereferencing each comment and comparing:
Seven uses carry a branch name rather than a version, five of
action-trivy-cache@mainplusaction-argocd-webhook-notification@mainandhelm-gh-pages@add-overwrite-option. Those repositories publish no tag, so those workflows were following a branch and now stay where they are, and Dependabot cannot move them either.SonarCloud exempts GitHub's own actions, 28 of the 65. Pinning those too leaves one style in the file.
.github/dependabot.ymlis new, and it moves the 58 that carry a version.meet.yml: the file is named after the repository, not its contentsThe repository is already
meet, so the file name says nothing a reader can use. It becomesci.yml, and the workflow'sname:becomesCI.The other changed line is the print-statement check, whose exclusion names
meet.ymlby hand, because the step's owngrep "print("carries the pattern. A rename puts that deleted line back inside the diff and the job fails on its own removed text, so the exclusion now covers.github/workflows/**.