ci: add SECURITY.md, restrict workflow permissions and pin actions to SHAs - #304
Merged
Merged
Conversation
SECURITY.md names the supported release line (latest 0.3.x), where to report (GitHub private vulnerability reporting, not public issues), what to include and what to expect. Linked from CONTRIBUTING (English and zh-TW), the README and the zh-TW home page. Refs #300
- lint.yml, docs.yml and renovate-validate.yml get a top-level permissions: contents: read; none of them writes anything. - Every actions/checkout that does not push sets persist-credentials: false, including the coverage badge job: the deploy action unsets the checkout's auth header and pushes with its own token input. The release job's checkout keeps its credentials, since it pushes the release commit and tag with git. - Every action is pinned to a full commit SHA with a version comment; pypa/gh-action-pypi-publish@release/v1 is pinned to v1.14.2, the current head of that branch. - Renovate extends helpers:pinGitHubActionDigests so the pins keep updating. Refs #300
Actions are pinned to SHAs and non-major updates automerge, so a compromised upstream release would be merged as soon as CI passed. minimumReleaseAge 3 days (timestamp-required, internalChecksFilter strict) delays them; automerge still applies afterwards. Refs #300
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.
Closes #300
Summary
SECURITY.md(docs commit)SECURITY.mdat the repo root. It says to report privately through GitHub private vulnerability reporting and not in public issues. It also covers supported versions (the latest 0.3.x release only, currently 0.3.8), what to put in a report, and what to expect. It makes no SLA promises: reports get an acknowledgement and a follow-up, and a confirmed issue gets a 0.3.x patch plus a published advisory.docs/CONTRIBUTING.md(new "Reporting a Security Vulnerability" section), from the README resource list (which is also the docs home page), and from the zh-TW mirror (i18n/zh-TW/docs/CONTRIBUTING.mdandindex.md). The links are absolute GitHub URLs becauseSECURITY.mdis outsidedocs/.Workflow hardening (ci commit)
Permissions: added top-level
permissions: contents: readtolint.yml,docs.ymlandrenovate-validate.yml. None of them writes to the repo, uploads artifacts or calls the API.persist-credentials: false: added to every checkout that does not push: lint, docs, renovate-validate, test, lowest, coverage (coveragejob), and coverage (badgejob).badgejob:JamesIves/github-pages-deploy-actiondoes not use the checkout's credentials. In CI it runsgit config --local --unset-all http.https://github.com/.extraheader, then pushes tohttps://x-access-token:${token}@github.com/<repo>.git, built from its owntokeninput (default: the job'sGITHUB_TOKEN). Seesrc/git.tsandsrc/util.tsat v4.9.0. Its README also asks forpersist-credentials: falsewhen a different token is used, to avoid auth conflicts.release.ymlbuildjob already hadpersist-credentials: false.release.ymlreleasejob's checkout keeps its credentials because it runsgit pushfor the release commit and tag. I added a comment saying so. zizmor flags it asartipacked(low confidence), and that finding is expected.SHA pinning: every
uses:now points to a full commit SHA with a# vX.Y.Zcomment. Annotated tags were dereferenced to their commits.pypa/gh-action-pypi-publish@release/v1is pinned to v1.14.2, which is also the current head ofrelease/v1.Renovate: added
helpers:pinGitHubActionDigeststoextendsso the pins keep updating.Renovate release-age delay (third commit): non-major and
digestupdates automerge. Without a delay, a compromised upstream action release could be merged as soon as CI passed. That includespypa/gh-action-pypi-publish, whichrelease.ymlruns. The new rule:Automerge still applies once the 3 days have passed, and the
github-actionsgroup rule is unchanged.Timestamps. Per Renovate's minimum release age docs:
github-actionsupdates come from thegithub-tagsdatasource, which provides a release timestamp: thecommittedDateof the commit the tag points to.timestamp-requiredhas been the default since Renovate 42. I set it explicitly so that an update with no timestamp stays pending instead of going through at once, whatever Renovate version runs.internalChecksFilter: "strict"(also the default) means no branch is created until the age check passes. Held updates appear under "Pending Status Checks" on the Dependency Dashboard, where they can be forced.Limitation. The committer sets
committedDate, and a force-pushed tag is aged against its original date. So this delays ordinary releases but cannot guarantee protection against a determined attacker. The config comment says the same.release.yml: onlyuses:refs and one comment changed. Its logic, inputs and job structure are the same.Validation
npx --yes --package renovate -- renovate-config-validator --strict(same command as renovate-validate.yml):Config validated successfullyfor.github/renovate.json5(Renovate 44.115.12). I re-ran it after the release-age rule was added.actionlint1.7.12 (with shellcheck) on all 7 files in.github/workflows/: 0 errors.zizmor1.30.1 (online audits) on all 7 files in.github/workflows/: 2 unsuppressed findings, both expected:artipacked(low): therelease.ymlreleasejob checkout, which has to push with git.superfluous-actions(info):softprops/action-gh-releasecould begh release. I left it alone because changing it would change release logic.uv run pre-commit run --all-files: all passed.uv run pytest: 857 passed, 191 skipped (the live Redis/Memcached suites are opt-in).zensical build --strictfor the English and zh-TW sites: no issues.CHANGELOG
The entry is in
changelog.d/300.added.mdand is merged intoCHANGELOG.mdat release time.The fragments have no Documentation section, so the SECURITY.md entry is filed under Added. The workflow changes are CI-only, so there is no entry for them.