Skip to content

chore(ci): Harden the supply chain — harden-runner, Dependabot, pinned dependencies - #80

Merged
lperdereau merged 7 commits into
mainfrom
chore/harden-ci
Sep 17, 2026
Merged

lperdereau merged 7 commits into
mainfrom
chore/harden-ci

Conversation

@lcaflc

@lcaflc lcaflc commented May 12, 2026

Copy link
Copy Markdown
Contributor

Started as harden-runner on the workflows (step-security docs), to audit outbound calls during the release process. Grew into the rest of the supply-chain hardening.

What is in here

Commit
chore(ci) harden-runner on every job of both workflows, egress policy audit
ci(deps) Dependabot: weekly version updates for pip and github-actions, grouped into one PR per ecosystem. Commit prefixes follow the Angular convention so python-semantic-release keeps parsing the history
build(deps) requirements.lock.txt: the resolved runtime closure, pinned
build(deps) click>=8,<9: the CLI relies on click.MultiCommand, removed in click 9 — see #89
build! Security floors, and Python 3.9 dropped

Why the lock file

Dependabot's security updates rely on the dependency graph, and the graph could not see anything useful: unpinned requirements are recorded without a version, and GitHub does not resolve transitive pip dependencies without a lock. The repository showed 17 packages, none with a version, and no transitive dependency at all — so no alert could ever fire, including on cryptography, which is where four high-severity advisories were sitting.

The lock is the detection layer. It protects nobody downstream: it is not in the wheel, and pip never reads it. That is what the floors are for.

⚠️ Breaking change

Python 3.9 is no longer supported, the minimum is 3.10. The fixed releases of urllib3 (2.7.0), requests (2.33.0) and black (26.3.1) all require 3.10, so the floors below and 3.9 were mutually exclusive. Python 3.9 has been end-of-life since October 2025. setup.cfg and the CI matrix follow.

Floors added to requirements.txt, which setup.cfg feeds into install_requires — these are the only constraints that reach users installing from PyPI:

  • urllib3>=2.7.0 — 6 advisories, 4 high
  • requests>=2.33.0 — 2 advisories
  • cryptography>=50.0.0 — 6 advisories, 4 high; not imported directly, but paramiko's own >=3.3 allows vulnerable versions
  • black>=26.3.1 — 1 high advisory

Verification

  • pytest: 116 passed
  • black --check: clean
  • pylint src/: 10.00/10
  • GitHub advisory database queried for all 23 pinned runtime versions and the 4 dev tools: no advisory remains
  • Package metadata checked after build: Requires-Python: >=3.10 and the floors are present in Requires-Dist

Follow-up

🤖 Generated with Claude Code

lcaflc and others added 5 commits September 17, 2026 15:12
From https://docs.stepsecurity.io/harden-runner use harden-runner to
test release process security
Check pip and github-actions dependencies every Monday, with all updates
of an ecosystem grouped in a single PR. Commit prefixes follow the Angular
convention so python-semantic-release keeps parsing the history.

Security updates (immediate PRs on a CVE) are enabled in the repository
settings, independently of this file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add requirements.lock.txt and requirements-dev.lock.txt: the full closure
(direct + transitive) of the existing requirements files, pinned to the
versions currently used for development.

The unpinned requirements*.txt stay the declaration files and keep driving
CI, which still covers Python 3.9 while the dev tooling now requires 3.10.
The locks give GitHub's dependency graph exact versions and the transitive
dependencies it cannot resolve on its own, which is what Dependabot needs
to raise actionable security alerts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Some of the click APIs the CLI relies on are deprecated in click 9, so
constrain the requirement to >=8,<9. setup.cfg reads install_requires from
requirements.txt, so the constraint also ships in the package metadata.

Tell Dependabot to skip major updates for click, otherwise it would keep
proposing to widen the constraint once 9.0 is out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every dependency with a known advisory now has a lower bound in
requirements.txt, which setup.cfg feeds into install_requires: these floors
are the only thing that reaches users installing from PyPI.

  urllib3>=2.7.0        6 advisories, 4 of them high
  requests>=2.33.0      2 advisories
  cryptography>=50.0.0  6 advisories, 4 of them high; not imported directly
                        but paramiko's own floor allows vulnerable versions
  black>=26.3.1         1 high advisory

The fixed releases of urllib3, requests and black all require Python 3.10,
which forces the drop of 3.9. It has been end-of-life since October 2025.

requirements-dev.lock.txt is removed: dev tooling is lower risk and the file
was the one creating friction with the test matrix. requirements.lock.txt
stays, refreshed from the upgraded environment, as the only way GitHub's
dependency graph can see transitive dependencies.

No advisory remains on any pinned runtime or dev version.

BREAKING CHANGE: Python 3.9 is no longer supported, the minimum is 3.10.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lcaflc lcaflc changed the title chore(ci): Add harden-runner chore(ci): Harden the supply chain — harden-runner, Dependabot, pinned dependencies Sep 17, 2026
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment thread .github/workflows/release.yml Fixed
plumber:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
with:
egress-policy: audit

- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
egress-policy: audit

- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
egress-policy: audit

- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
egress-policy: audit

- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
Comment thread .github/workflows/plumber.yml Fixed
Comment thread .github/workflows/release.yml Fixed
Comment thread .github/workflows/release.yml Fixed
Comment thread .github/workflows/tests.yml Fixed
Comment thread .github/workflows/tests.yml Fixed
Comment thread .github/workflows/tests.yml Fixed
ci: Add python-semantic-release to plumber whitelist

fix plumber

e
@lcaflc
lcaflc marked this pull request as ready for review September 17, 2026 14:45
@lperdereau
lperdereau merged commit 05f9e02 into main Sep 17, 2026
10 checks passed
@lperdereau
lperdereau deleted the chore/harden-ci branch September 17, 2026 15:12
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.

3 participants