Skip to content

fix: tighten workflow permissions, add security hardening, and fix uv tool invocations#506

Merged
jmeridth merged 2 commits intomainfrom
fix/tighten-workflow-permissions-and-security-hardening
Mar 14, 2026
Merged

fix: tighten workflow permissions, add security hardening, and fix uv tool invocations#506
jmeridth merged 2 commits intomainfrom
fix/tighten-workflow-permissions-and-security-hardening

Conversation

@jmeridth
Copy link
Collaborator

What

Move elevated permissions from workflow level to job level in mark-ready-when-ready and scorecard workflows so each job only holds the permissions it actually needs. Add step-security/harden-runner to all eight workflows that define steps. Add CodeQL SAST scanning and dependency-review workflows. Add pre-commit configuration with gitleaks, formatting hooks, and local linter hooks. Fix Makefile to invoke flake8, pytest, pylint, and mypy via uv run python -m since they lack console script entry points in the uv venv. Upgrade PyJWT from 2.11.0 to 2.12.1 to address CVE-2026-32597.

Why

Workflow-level write permissions apply to every job in the workflow, granting broader access than necessary. Moving them to job level follows the principle of least privilege. Harden-runner audits outbound network calls from GitHub-hosted runners, improving supply-chain visibility. CodeQL and dependency-review close gaps in static analysis and vulnerable-dependency detection. The Makefile commands failed under uv because those packages don't install console scripts; python -m ensures the tools are always found. PyJWT <= 2.11.0 doesn't validate the RFC 7515 crit header parameter (CVSS 7.5).

Notes

  • The uv run to uv run python -m change also affects CI since python-ci calls make lint and make test
  • release.yml, auto-labeler.yml, and pr-title.yml use reusable workflows at the job level so harden-runner cannot be added there; it must go in the reusable workflow definitions instead
  • pylint was also changed to python -m beyond what the upstream issue-metrics PR did, since it failed the same way as flake8/mypy/pytest
  • The scorecard workflow previously used permissions: read-all which granted read access to all scopes; now explicitly scoped to only what's needed
  • .pre-commit-config.yaml was removed from .gitignore so it can be tracked in version control
  • PyJWT is a transitive dependency; verify downstream consumers aren't relying on the old crit-header-ignored behavior

@jmeridth jmeridth requested a review from zkoppert as a code owner March 14, 2026 02:16
Copilot AI review requested due to automatic review settings March 14, 2026 02:16
@github-actions github-actions bot added the fix label Mar 14, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the repo’s security and developer tooling by adding runner hardening and security scanning workflows, introducing a pre-commit configuration, and updating a dependency and local tooling invocation patterns.

Changes:

  • Add step-security/harden-runner to multiple GitHub Actions workflows and introduce new CodeQL + Dependency Review workflows.
  • Add .pre-commit-config.yaml and stop ignoring it in .gitignore.
  • Bump pyjwt in uv.lock and update Makefile to invoke tooling via python -m ....

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
uv.lock Updates pyjwt to a newer locked version.
Makefile Switches to uv run python -m ... invocations for test/lint tooling.
.pre-commit-config.yaml Adds pre-commit hooks (gitleaks + whitespace fixes + local Python linters/formatters).
.gitignore Stops ignoring .pre-commit-config.yaml so it can be committed.
.github/workflows/super-linter.yaml Adds runner hardening step.
.github/workflows/stale.yaml Adds runner hardening step.
.github/workflows/scorecard.yml Tightens permissions and adds runner hardening step.
.github/workflows/python-ci.yml Adds runner hardening step.
.github/workflows/mark-ready-when-ready.yml Adjusts permissions scoping and adds runner hardening step.
.github/workflows/docker-ci.yml Adds runner hardening step.
.github/workflows/dependency-review.yml Adds Dependency Review workflow for PR dependency changes.
.github/workflows/copilot-setup-steps.yml Adds runner hardening step.
.github/workflows/contributors_report.yaml Adds runner hardening step.
.github/workflows/codeql.yml Adds CodeQL scanning workflow for Python.

… tool invocations

## What

Move elevated permissions from workflow level to job level in mark-ready-when-ready
and scorecard workflows so each job only holds the permissions it actually needs.
Add step-security/harden-runner to all eight workflows that define steps. Add CodeQL
SAST scanning and dependency-review workflows. Add pre-commit configuration with
gitleaks, formatting hooks, and local linter hooks. Fix Makefile to invoke flake8,
pytest, pylint, and mypy via `uv run python -m` since they lack console script entry
points in the uv venv. Upgrade PyJWT from 2.11.0 to 2.12.1 to address CVE-2026-32597.

## Why

Workflow-level write permissions apply to every job in the workflow, granting broader
access than necessary. Moving them to job level follows the principle of least privilege.
Harden-runner audits outbound network calls from GitHub-hosted runners, improving
supply-chain visibility. CodeQL and dependency-review close gaps in static analysis and
vulnerable-dependency detection. The Makefile commands failed under uv because those
packages don't install console scripts; `python -m` ensures the tools are always found.
PyJWT <= 2.11.0 doesn't validate the RFC 7515 `crit` header parameter (CVSS 7.5).

## Notes

- The `uv run` to `uv run python -m` change also affects CI since python-ci calls `make lint` and `make test`
- release.yml, auto-labeler.yml, and pr-title.yml use reusable workflows at the job level so harden-runner cannot be added there; it must go in the reusable workflow definitions instead
- pylint was also changed to `python -m` beyond what the upstream issue-metrics PR did, since it failed the same way as flake8/mypy/pytest
- The scorecard workflow previously used `permissions: read-all` which granted read access to all scopes; now explicitly scoped to only what's needed
- `.pre-commit-config.yaml` was removed from `.gitignore` so it can be tracked in version control
- PyJWT is a transitive dependency; verify downstream consumers aren't relying on the old crit-header-ignored behavior

Signed-off-by: jmeridth <jmeridth@gmail.com>
@jmeridth jmeridth force-pushed the fix/tighten-workflow-permissions-and-security-hardening branch from 75f1f97 to 9c723c2 Compare March 14, 2026 02:28
@zkoppert
Copy link
Collaborator

The Autobuild step in codeql.yml is a no-op for Python since CodeQL analyzes source directly without a build step. Please remove the Autobuild step and its surrounding comments to keep the workflow clean.

Signed-off-by: jmeridth <jmeridth@gmail.com>
Copy link
Collaborator

@zkoppert zkoppert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - minor note: consider removing the Autobuild step from codeql.yml since it's a no-op for Python.

@jmeridth jmeridth enabled auto-merge (squash) March 14, 2026 04:02
@jmeridth jmeridth merged commit d2af62b into main Mar 14, 2026
36 checks passed
@jmeridth jmeridth deleted the fix/tighten-workflow-permissions-and-security-hardening branch March 14, 2026 04:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants