PST-061: CI and release workflows trust mutable references and unreviewed packages, and publish unsigned artefacts #790
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#790
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found in the security audit of 2026-10-10 (
SECURITY-AUDIT-2026-10-10.md), run againstmainatc30851bfrom 8 to 10 October 2026, as PST-061. Severity: Low.The text below is the audit's entry, unchanged apart from links to the other issues it names. The finding was produced by automated review and checked by a second automated pass; re-check it before acting on it.
.forgejo/workflows/ci.yml(66,79-80,83,154);.forgejo/workflows/dependencies.yml(63,78,91,105,119);.forgejo/workflows/release.yml(64-75)Who can do it, and what they need. Whoever can move a tag or replace a download: the Forgejo instance's mirror of actions/checkout, actions/cache or actions/upload-artifact; astral.sh's install.sh; the ghcr.io uv image tag. Precondition: compromise of one of those upstreams.
How it works.
actions/checkout@v4,actions/cache@v4andactions/upload-artifact@v3by mutable tag; zizmor's unpinned-uses rule is relaxed to ref-pin for actions/* (zizmor.yml:14-17) because Forgejo resolves them locally.curl -LsSf "https://astral.sh/uv/${UV_VERSION}/install.sh" | shwith no checksum, and the image copies uv fromghcr.io/astral-sh/uv:0.12.17by tag rather than digest.Impact. A compromised upstream could alter the published sdist/wheel or container image. The reasons for each choice are documented (Forgejo action resolution, deliberate Debian drift); the uv binary and install script are the two places a digest or checksum would cost nothing.
Evidence.
Verification (verdict: confirmed; severity after verification: Low; first-pass claim: Informational). Attacker is an upstream party only: the astral.sh host, the astral-sh GHCR namespace, or whoever controls the Forgejo instance's actions/* mirror (the tag-moving party). Corrections: (a) the second uv COPY is Dockerfile:147, which is the runtime stage that ships, so a tampered uv binary would reach the published image directly; (b) the release job's token is exported to all steps (ci.yml:587-590), so the installer step sees it despite no step-level
env; (c) ci.yml:8-11 still says 'Only actions/checkout is used', which is stale (cache and upload-artifact are used too) and zizmor.yml:3-4 names only checkout and upload-artifact while the policy covers all actions/*. Remediation: pin the uv image by digest (uv:0.12.17@sha256:...) in both Dockerfile COPY lines and bump it with the version; download the uv release archive and check its SHA-256 (or a checked-in installer hash) instead ofcurl | sh; where the Forgejo mirror allows it, pin actions to a commit SHA.Suggested remediation. Verify install.sh (or the uv tarball) against a pinned sha256 before running it; pin the uv image by digest in the Dockerfile (the tag is already a patch version, so a digest is one line); mirror the three actions at a commit on the Forgejo instance and pin to that commit.
Also covered by this issue.
.forgejo/workflows/dependencies.yml(63,78,91,105,119); Informational). Code execution on the CI runner with no write credential in scope; a well-scoped design. Recorded so the synthesis can note the token scoping is what makes it safe, and that--forcewould overwrite a human's branch of the same name..forgejo/workflows/release.yml(64-75); Informational). No way for operators to verify provenance of what they pull; a registry compromise would be invisible.install.shwith no version and no checksum; the core fixed this in #790 #20curl | shand audits no dependency, and no existing issue names this repository #15