Skip to content

[python] Fix Sigstore verification for Python 3.14+ - #1771

Open
Venkumahanti Subhankar (V-Subhankar-infy) wants to merge 13 commits into
devcontainers:mainfrom
V-Subhankar-infy:patch-python
Open

Venkumahanti Subhankar (V-Subhankar-infy) wants to merge 13 commits into
devcontainers:mainfrom
V-Subhankar-infy:patch-python

Conversation

@V-Subhankar-infy

@V-Subhankar-infy Venkumahanti Subhankar (V-Subhankar-infy) commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

Fix Python 3.14+ Sigstore verification

Fixes #1764

Problem and root cause

  • Signing-method transition: Python 3.14 replaced PGP signatures with Sigstore, but the feature still used its legacy detached-signature flow.
  • Wrong artifacts: The verifier requested .sig and a nonexistent .pem instead of Python.org's .sigstore bundle.
  • Invalid curl flags: The Sigstore download combined incompatible -f and --fail-with-body flags, while 2>/dev/null hid curl's diagnostic.
  • Unrestricted signer: Wildcard identity and issuer patterns would accept any signer.
  • Misleading failure notice: Continuing after verification failure is an intentional compatibility policy, but the warning described failures as expected and obscured that the archive was unverified.

The bottom 4 are straightforward implementation fixes. The first involves a much bigger architectural decision around security versus long-term sustainability while deciding how the cosign verifies the identity. ( cosign verifies the authenticity and integrity of downloaded Python source archives )

Approaches considered

The signer policy and Cosign verifier both need a trust decision. Dynamic lookup reduces manual maintenance but relies more on external sources; static values offer more repository control but need regular upkeep.

The practical options are:

  • Static Python signer + static Cosign: Strongest independent trust, but both signer identities and verifier hashes need manual updates.
  • Dynamic Python signer + static Cosign: Python.org supplies the expected signer identity and issuer, while Cosign remains pinned. This reduces signer-map maintenance but still requires periodic Cosign updates.
  • Dynamic Python signer + dynamically resolved Cosign: Lowest routine maintenance, but adds external trust dependencies. The newer Cosign release must be authenticated before use; otherwise, the binary and its checksum would both come from the same external release source.

Chosen approach

Resolve the Python release-manager identity and OIDC issuer from Python.org's published metadata, then verify the Python .sigstore bundle against those exact values.

Use Cosign v3.1.4 as a temporary, architecture-specific, checksum-verified bootstrap. Resolve the latest stable Cosign v3 release at build time and verify its binary and Sigstore bundle with the pinned bootstrap, using the Cosign release identity and issuer. Use the newer binary only after verification succeeds; otherwise retain the pinned binary. If the newer binary cannot verify the Python bundle, retry with the pinned verifier.

The pinned v3.1.4 hashes come from official GitHub release metadata. This version is newer than the one affected by GHSA-fx35-mq7g-6g98 and remains the trust anchor for updating the verifier.

Cache the binaries and Python.org signer policy across Python installations in the same build, then remove the cache on exit.

Preserve the existing non-blocking policy: if signer lookup, Cosign bootstrap/update, or Python bundle verification cannot complete, emit a warning and continue without verified source authenticity or integrity. A Python checksum fallback does not provide source authentication.

Cosign maintenance

The installer resolves the latest stable Cosign v3 release at build time, so the verifier does not need a manual version bump for every release. Review the pinned bootstrap version and hashes about quarterly (roughly 5–10 minutes per review), and update them promptly if a security advisory affects the pin or compatibility requires it. Check the Cosign security advisories and Cosign releases.

Cosign v3.1.4 is the pinned bootstrap and addresses the legacy-bundle verification issue covered by GHSA-fx35-mq7g-6g98. When updating the bootstrap, change the version and hashes for all supported architectures together.

Changes

  • src/python/install.sh: Add Python.org signer-policy lookup and exact-identity bundle verification; use pinned Cosign v3.1.4 to authenticate the latest stable Cosign v3 before use, with pinned-verifier fallback. Cache temporary binaries and signer metadata, retry transient downloads, and clean up on exit. Install gawk with the required packages.
  • src/python/devcontainer-feature.json: Bump the feature version from 1.8.1 to 1.9.0.
  • src/python/NOTES.md: Document source verification and its failure policy.
  • src/python/README.md: Update the generated feature documentation to match the notes.
  • test/python/sigstore_verification_test.sh: Add mock-based checks for signer selection, Cosign arguments, supported architectures, checksum/download/signature failures, unsupported inputs, and cache cleanup.
  • PR description.md: Record the problem, design decisions, impact, and verification status.

Impact

Python 3.14+ source builds verify the release manager's identity and signature when verification succeeds. The GPG verification flow for Python versions before 3.14 remains unchanged.

The pinned bootstrap and dynamically verified Cosign binary are temporary; neither installs nor replaces a system-wide Cosign installation. The build needs GitHub access to obtain the Cosign release and Python.org access for signer metadata and bundles.

For compatibility, unsupported architectures, unknown signers, network failures, checksum mismatches, and signature failures still allow installation to continue with an explicit warning. Such builds do not have verified source authenticity or integrity.

Tests and verification

  • AWK parser: Focused checks pass for a valid four-cell policy row, a missing Python series, and duplicate policy entries. The parser removes the trailing cell separator before counting fields, fixing an early return that previously skips Cosign. The obsolete Python bootstrap dependency is removed from both installer copies.
  • Ubuntu build: The requested no-cache local build on ubuntu:latest (26.04, amd64) with Python 3.14.10 completes successfully. The first .sigstore download times out, but the retry succeeds. Cosign reports Verified OK with the dynamically resolved signer hugo@python.org, and the CLI reports outcome: success.
  • Source checks: Python.org's published signer table and the Cosign 3.1.4 release asset names and SHA-256 digests are inspected. The GitHub issue and maintainer's reproduction are also reviewed.
  • Regression checks: The mock-based Sigstore regression checks pass for signer selection, exact identity and issuer arguments, amd64 and armhf assets and hashes, checksum/download failures, signature rejection, unsupported versions and architectures, cache reuse, and cleanup.
  • Ubuntu build: The no-cache local build on ubuntu:latest (26.04, amd64) with Python 3.14.10 completes successfully. The first .sigstore download times out, the retry succeeds, and Cosign reports Verified OK for hugo@python.org; the CLI reports outcome: success.
  • Source checks: Python.org's published signer table and Cosign 3.1.4 release asset names and SHA-256 digests are inspected. The GitHub issue and maintainer's reproduction are also reviewed.

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

A human assessment is warranted because the new source-verification policy is documented in NOTES.md but not reflected in the corresponding README, leaving the user-facing documentation incomplete.

No open issues 🎉

5 resolved since last review

Review effort: Lite

This branch has not been deployed

No deployments
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.

python: signature verification can never succeed for Python 3.14+

2 participants