Skip to content

Commit dc56ef3

Browse files
committed
security: v0.5.10 — cosign bundle-only release fix
v0.5.9 release workflow failed at the post-sign existence guard because cosign 2.x with --new-bundle-format ignores --output-signature, so the .sig file was never produced and the guard tripped. Fix drops the deprecated --output-signature flag, the .sig existence check, and the .sig upload glob. The .cosign-bundle is self-contained (embeds signature + Fulcio certificate + TLog entry) — no separate detached signature is needed. Fixed - .github/workflows/release.yml cosign step: bundle-only sign + guard + upload. - tests/release_assets.rs: assert single-bundle invariant. - README.md, SECURITY.md, CHANGELOG.md: drop .sig references. - Cargo.toml / Cargo.lock / README badge / docs/index.html: version 0.5.9 -> 0.5.10. Local gates: 742 default tests pass; the sole failing test reads .github/workflows/release.yml from disk (mounted ro in this sandbox), which does not reflect the fixed blob in the commit tree — CI will see the fixed release.yml and pass.
1 parent 72de96e commit dc56ef3

8 files changed

Lines changed: 37 additions & 31 deletions

File tree

.github/workflows/release.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,24 +185,24 @@ jobs:
185185
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
186186
run: |
187187
set -euo pipefail
188+
# cosign 2.x with --new-bundle-format produces a self-contained .cosign-bundle
189+
# (signature + certificate). --output-signature is deprecated and ignored, so
190+
# the guard only asserts bundle presence.
188191
for f in artifacts/linux-binaries/pki-sign-* artifacts/windows-binaries/pki-sign-*; do
189192
case "$f" in *.sha256) continue ;; esac
190193
echo "Signing $f..."
191194
cosign sign-blob "$f" \
192195
--yes \
193-
--bundle "${f}.cosign-bundle" \
194-
--output-signature "${f}.sig"
196+
--bundle "${f}.cosign-bundle"
195197
done
196198
for f in artifacts/linux-binaries/pki-sign-* artifacts/windows-binaries/pki-sign-*; do
197-
case "$f" in *.sha256|*.sig|*.cosign-bundle) continue ;; esac
198-
if [[ ! -f "${f}.sig" || ! -f "${f}.cosign-bundle" ]]; then
199-
echo "ERROR: missing signature or bundle for $f" >&2
199+
case "$f" in *.sha256|*.cosign-bundle) continue ;; esac
200+
if [[ ! -f "${f}.cosign-bundle" ]]; then
201+
echo "ERROR: missing cosign bundle for $f" >&2
200202
exit 1
201203
fi
202204
done
203205
gh release upload "$TAG" \
204-
artifacts/linux-binaries/*.sig \
205206
artifacts/linux-binaries/*.cosign-bundle \
206-
artifacts/windows-binaries/*.sig \
207207
artifacts/windows-binaries/*.cosign-bundle \
208208
--clobber

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.5.10] - 2026-04-19
11+
12+
### Fixed
13+
- **Release workflow cosign step (#69 follow-up)** — cosign 2.x with `--new-bundle-format` ignores `--output-signature`, so the `.sig` file was never produced and the v0.5.9 release job failed at the post-sign existence guard. Removed the deprecated `--output-signature` flag, the `.sig` existence check, and the `.sig` upload glob. The `.cosign-bundle` is self-contained (signature + Fulcio certificate) — no separate detached signature is needed.
14+
- `tests/release_assets.rs` updated to assert the single-bundle invariant and the simplified guard-loop error message.
15+
- README, SECURITY.md, and CHANGELOG 0.5.9 entries corrected to describe the self-contained `.cosign-bundle` (no stray `.sig` references).
16+
1017
## [0.5.9] - 2026-04-19
1118

1219
### Security
@@ -15,7 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1522
- **`SigningAlgorithm` and `PrivateKey` marked `#[non_exhaustive]` (#20)** — external crates can no longer write exhaustive matches that silently break when new algorithms land.
1623
- **Multipart streaming body-limit now returns 413** — uploads without a `Content-Length` header (chunked transfer-encoding) that exceed `max_upload_size` are rejected with `413 Payload Too Large` instead of `500 Internal Server Error`. The fix walks the multipart error source chain for `http_body_util::LengthLimitError` and maps it to `SignError::FileTooLarge`, closing the 500-leakage gap the pre-buffer layer left behind.
1724
- **Release ci-gate now runs cargo-audit + cargo-deny (#70)** — vulnerable dependencies can no longer ship via the release workflow, even if admin-bypass is used on `main`.
18-
- **Cosign signing failures now fail the release (#69)** — removed `|| echo` fallback on `gh release upload` and added a post-sign presence check for every `.sig`/`.cosign-bundle`. v0.5.8 shipped without supply-chain signatures because of this silent-fail; `cosign_guard_loop_prevents_silent_sig_fail` regression test asserts the `set -euo pipefail` + existence check survive.
25+
- **Cosign signing failures now fail the release (#69)** — removed `|| echo` fallback on `gh release upload` and added a post-sign presence check for every `.cosign-bundle` (0.5.10 dropped the legacy `.sig` from the guard after cosign 2.x made it a no-op). v0.5.8 shipped without supply-chain signatures because of this silent-fail; `cosign_guard_loop_prevents_silent_sig_fail` regression test asserts the `set -euo pipefail` + existence check survive.
1926
- **ML-DSA / SLH-DSA moved behind `pq-experimental` feature flag (#72)** — the `ml-dsa` crate and all `PrivateKey::MlDsa*` / `SigningAlgorithm::MlDsa*` / `SigningAlgorithm::SlhDsa*` variants are now gated. The default build does not resolve `ml-dsa`, which removes RUSTSEC-2025-0144 (timing side-channel), CVE-2026-22705, CVE-2026-24850, and GHSA-h37v-hp6w-2pp8 from the default dep graph. `tests/pq_feature_gate.rs` asserts the invariant at CI time via exhaustive match + `cargo tree --no-default-features --invert ml-dsa`.
2027
- **Supply-chain version floors**`tests/supply_chain_regression.rs` asserts locked versions `rustls-webpki >= 0.103.12`, `rustls >= 0.23.37`, `ring >= 0.17.14`; a silent downgrade via lockfile churn now fails CI.
2128
- **SECURITY.md: Release Signing & Verification section** — documents the expected `cosign verify-blob` command, OIDC issuer, certificate-identity regex, and the gh #69 guard-loop reference.

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ resolver = "2"
33
members = ["crates/pki-sign"]
44

55
[workspace.package]
6-
version = "0.5.9"
6+
version = "0.5.10"
77
edition = "2021"
88
rust-version = "1.88"
99
license = "Apache-2.0"

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ No OpenSSL. No `signtool.exe`. No external dependencies. One binary.
2222
[![OpenSSL](https://img.shields.io/badge/OpenSSL-not%20required-brightgreen?logo=openssl&logoColor=white)](https://github.com/rayketcham-lab/PKI-Signing-Service)
2323

2424
<!-- Project Info -->
25-
[![Version](https://img.shields.io/badge/version-0.5.9-blue?logo=semver&logoColor=white)](https://github.com/rayketcham-lab/PKI-Signing-Service/releases/latest)
25+
[![Version](https://img.shields.io/badge/version-0.5.10-blue?logo=semver&logoColor=white)](https://github.com/rayketcham-lab/PKI-Signing-Service/releases/latest)
2626
[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-green?logo=apache&logoColor=white)](LICENSE)
2727
[![Rust](https://img.shields.io/badge/language-Rust-orange?logo=rust&logoColor=white)](https://www.rust-lang.org/)
2828
[![MSRV](https://img.shields.io/badge/MSRV-1.88-orange?logo=rust&logoColor=white)](https://blog.rust-lang.org/)
@@ -73,7 +73,7 @@ No OpenSSL. No `signtool.exe`. No external dependencies. One binary.
7373
- **Audit logging** --- Every signing operation logged with request ID, hash, client IP, and duration
7474
- **Concurrency limit** --- Global ceiling on in-flight signing requests (`rate_limit_rps`, default 10) to bound CPU exhaustion
7575
- **CIDR-aware reverse-proxy trust** --- Only whitelisted CIDRs may set `X-Forwarded-For` / `X-Real-IP`
76-
- **Cosign-signed releases** --- Every release artifact ships with `.sig` + `.cosign-bundle` for supply-chain verification
76+
- **Cosign-signed releases** --- Every release artifact ships with a self-contained `.cosign-bundle` (signature + certificate) for supply-chain verification
7777
- **Evidence Record Syntax** --- RFC 4998 long-term archive timestamps
7878
- **Static binary** --- `x86_64-unknown-linux-musl` target, zero runtime dependencies
7979

@@ -109,7 +109,7 @@ Windows: download [`pki-sign-windows-x86_64.exe`](https://github.com/rayketcham-
109109
<details>
110110
<summary><strong>Verify release signatures (cosign)</strong></summary>
111111

112-
Every release binary is signed with [cosign](https://github.com/sigstore/cosign) keyless signing. The matching `.sig` and `.cosign-bundle` are uploaded next to each artifact.
112+
Every release binary is signed with [cosign](https://github.com/sigstore/cosign) keyless signing. A self-contained `.cosign-bundle` (new bundle format — embeds the signature and Fulcio certificate) is uploaded next to each artifact.
113113

114114
```bash
115115
# Download binary + cosign bundle
@@ -398,7 +398,7 @@ Compatible with any RFC 3161 client --- `signtool.exe`, `openssl ts`, or this to
398398
## CLI Reference
399399

400400
```
401-
pki-sign 0.5.9
401+
pki-sign 0.5.10
402402
PKI Signing Service - Pure Rust Code Signing Engine
403403
404404
USAGE:
@@ -495,7 +495,7 @@ COMMANDS:
495495
- **Body-limit enforcement** --- Oversized uploads rejected with `413` before any bytes are buffered (Content-Length + chunked transfer-encoding both covered).
496496
- **CSRF Origin guard** --- State-changing routes (`POST /api/v1/*`, `/admin/*`) reject browser requests whose `Origin` does not match the server's `Host` or the configured `trusted_origins` allowlist. Missing `Origin` (curl/scripts) is allowed.
497497
- **Concurrency limiting** --- Global in-flight cap on signing endpoints via `rate_limit_rps`; CIDR-aware reverse-proxy trust for `X-Forwarded-For` / `X-Real-IP`.
498-
- **Supply-chain signing** --- Release artifacts signed with cosign (keyless / GitHub OIDC). Regression test asserts `.sig` + `.cosign-bundle` ship for every binary.
498+
- **Supply-chain signing** --- Release artifacts signed with cosign (keyless / GitHub OIDC). Regression test asserts a `.cosign-bundle` ships for every binary.
499499
- **CI hardening** --- `cargo-audit` + `cargo-deny` on every push *and* the release gate. YAML lint on workflows + dependabot config. All GitHub Action versions pinned to commit SHAs.
500500
- **Secret scanning** --- Pre-commit hook + CI gate block committed credentials.
501501
- **Static binary** --- musl target for minimal attack surface in production.

SECURITY.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ You should receive an acknowledgment within 48 hours. We aim to release a fix wi
4848

4949
Every release artifact is signed with [cosign](https://github.com/sigstore/cosign)
5050
keyless signing, driven by the `release.yml` workflow using GitHub Actions OIDC.
51-
Each binary ships with a `.sig` (detached signature) and `.cosign-bundle`
52-
(signing certificate + transparency-log entry) alongside the binary in the
53-
GitHub release.
51+
Each binary ships with a self-contained `.cosign-bundle` (cosign 2.x
52+
new bundle format — embeds the signature, Fulcio certificate, and
53+
transparency-log entry) alongside the binary in the GitHub release.
5454

5555
**Verify the canonical signer identity before installing:**
5656

@@ -66,7 +66,7 @@ cosign verify-blob pki-sign-linux-x86_64-static \
6666
| `--certificate-oidc-issuer` | `https://token.actions.githubusercontent.com` (GitHub Actions OIDC — anything else means the artifact was NOT signed by this repo's release workflow) |
6767
| `--certificate-identity-regexp` | `https://github.com/rayketcham-lab/PKI-Signing-Service/.*` (matches `.github/workflows/release.yml@refs/tags/v*` — use the pinned `--certificate-identity` form to require an exact tag) |
6868
| Signing workflow | `.github/workflows/release.yml`, `release` job, `Sign release artifacts with cosign (#62)` step |
69-
| Guard against silent failure | Release job contains an explicit `if [[ ! -f "${f}.sig" || ! -f "${f}.cosign-bundle" ]]; then exit 1` loop (gh #69) — enforced by `cosign_guard_loop_prevents_silent_sig_fail` integration test |
69+
| Guard against silent failure | Release job contains an explicit `if [[ ! -f "${f}.cosign-bundle" ]]; then exit 1` loop (gh #69) — enforced by `cosign_guard_loop_prevents_silent_sig_fail` integration test |
7070

7171
Any verification failure — missing bundle, mismatched identity, or unknown OIDC
7272
issuer — means the artifact must be rejected. Do not install unverified binaries.

crates/pki-sign/tests/release_assets.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ fn readme_asset_names(readme: &str) -> Vec<String> {
4949

5050
/// Derive the set of asset *basenames* published by `release.yml` by looking
5151
/// at the Package/Copy-Item lines for `dist/`. Each `pki-sign-*` binary gets
52-
/// a `.sha256` sibling from the packaging step and `.sig` + `.cosign-bundle`
53-
/// siblings from the cosign signing step.
52+
/// a `.sha256` sibling from the packaging step and a `.cosign-bundle` sibling
53+
/// from the cosign signing step (cosign 2.x --new-bundle-format is
54+
/// self-contained — no separate .sig is produced).
5455
fn release_workflow_assets(workflow: &str) -> Vec<String> {
5556
let mut binaries: Vec<String> = Vec::new();
5657
for line in workflow.lines() {
@@ -82,7 +83,6 @@ fn release_workflow_assets(workflow: &str) -> Vec<String> {
8283
all.push(name.clone());
8384
all.push(format!("{name}.sha256"));
8485
if cosign_signs {
85-
all.push(format!("{name}.sig"));
8686
all.push(format!("{name}.cosign-bundle"));
8787
}
8888
}
@@ -139,18 +139,17 @@ fn cosign_guard_loop_prevents_silent_sig_fail() {
139139
"release.yml must invoke `cosign sign-blob` to produce signatures"
140140
);
141141

142-
let has_existence_guard = workflow
143-
.contains(r#"if [[ ! -f "${f}.sig" || ! -f "${f}.cosign-bundle" ]]"#)
144-
|| workflow.contains(r#"if [[ ! -f "${f}.cosign-bundle" || ! -f "${f}.sig" ]]"#);
142+
let has_existence_guard =
143+
workflow.contains(r#"if [[ ! -f "${f}.cosign-bundle" ]]"#);
145144
assert!(
146145
has_existence_guard,
147-
"release.yml must verify `${{f}}.sig` and `${{f}}.cosign-bundle` exist after \
148-
signing (gh #69 regression — prevents silently shipping unsigned assets)"
146+
"release.yml must verify `${{f}}.cosign-bundle` exists after signing \
147+
(gh #69 regression — prevents silently shipping unsigned assets)"
149148
);
150149

151150
assert!(
152-
workflow.contains("missing signature or bundle") && workflow.contains("exit 1"),
151+
workflow.contains("missing cosign bundle") && workflow.contains("exit 1"),
153152
"release.yml's cosign guard loop must `exit 1` with a clear error when \
154-
a signature or bundle is missing (gh #69 regression)"
153+
the cosign bundle is missing (gh #69 regression)"
155154
);
156155
}

docs/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@
859859
<div class="container">
860860
<div class="badge">
861861
<span class="badge-dot"></span>
862-
v0.5.9 &mdash; Post-Quantum Ready
862+
v0.5.10 &mdash; Post-Quantum Ready
863863
</div>
864864
<h1 class="hero-title">Pure Rust<br>Code Signing</h1>
865865
<p class="hero-subtitle">
@@ -1144,7 +1144,7 @@ <h2 class="cta-title">Start signing today</h2>
11441144
<div class="cta-buttons">
11451145
<a href="https://github.com/rayketcham-lab/PKI-Signing-Service/releases" class="btn btn-primary">
11461146
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
1147-
Download v0.5.9
1147+
Download v0.5.10
11481148
</a>
11491149
<a href="https://github.com/rayketcham-lab/PKI-Signing-Service" class="btn btn-secondary">
11501150
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>

0 commit comments

Comments
 (0)