Skip to content

Hash-locked Python dependencies for CI (SonarCloud githubactions:S8544) #305

Description

@karoltheguy

Follow-up to #304, which hardened the CI installs against lifecycle-script execution but deliberately left the 4 githubactions:S8544 findings open. SonarCloud still reports them on .github/workflows/tests.yml at L156, L157, L604, L605 (the two pip install -r ... lines in each of test-github and test-forgejo).

Why it was deferred

requirements.txt and requirements-test.txt use >= ranges throughout. Satisfying S8544 means installing from a fully resolved, hash-pinned file, which is a dependency-management decision rather than a workflow edit. Folding it into a workflow-only PR would have made neither part reviewable.

Proposed approach

Adopt the pip-compile convention, which is also the layout Dependabot recognizes for lock updates:

  • requirements.in / requirements-test.in hold the human-edited >= ranges (the current file contents, moved)
  • requirements.txt / requirements-test.txt become pip-compile --generate-hashes output: every direct and transitive dependency pinned with hashes
  • CI installs with pip install --require-hashes -r requirements.txt, alongside the --only-binary :all: flag added in Harden CI dependency installs against lifecycle-script execution #304

This is a real security gain beyond satisfying the rule: hashes mean a compromised or substituted PyPI artifact fails the install rather than executing.

Constraint that shapes the design

tests/test_requirements.py parses requirements.txt for >= lines and asserts CVE-driven minimum floors:

  • asyncssh >= 2.14.2
  • cryptography >= 48.0.1
  • jinja2 >= 3.1.6
  • python-multipart >= 0.0.31

If requirements.txt becomes the compiled ==-pinned output, parse_requirements() returns an empty dict and the test fails on its first assert. The test must be retargeted to read requirements.in, where the ranges will live. Those floors are security bounds and must keep being enforced, not dropped.

Blast radius of the rename

Every reference to the requirements files needs review:

  • .github/workflows/tests.yml (4 install refs plus 2 hashFiles() cache keys)
  • Dockerfile (3 refs)
  • start.sh
  • .github/dependabot.yml
  • tests/test_requirements.py
  • docs/TESTING.md, docs/ARCHITECTURE.md, CONTRIBUTING.md

Tradeoff to accept explicitly

The current floating ranges mean CI surfaces upstream breakage the day it ships. Hash-locking removes that early warning until Dependabot bumps the lock. The weekly grouped pip Dependabot schedule already in .github/dependabot.yml is what keeps the lag bounded, and it should be confirmed to actually update the compiled files.

Done when

  • pip-compile --generate-hashes lock files are committed and CI installs with --require-hashes
  • tests/test_requirements.py enforces the same CVE floors against requirements.in and passes
  • All four githubactions:S8544 findings are cleared on the analysis
  • Dependabot is confirmed to update the compiled files, or the config is adjusted so it does

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions