Skip to content

docs(security): fix release-verification instructions that cannot work - #755

Merged
CybotTM merged 1 commit into
mainfrom
fix/security-verification-docs
Jul 28, 2026
Merged

docs(security): fix release-verification instructions that cannot work#755
CybotTM merged 1 commit into
mainfrom
fix/security-verification-docs

Conversation

@CybotTM

@CybotTM CybotTM commented Jul 28, 2026

Copy link
Copy Markdown
Member

SECURITY.md and CONTRIBUTING.md describe a release pipeline this project no longer has. Every parameter is wrong, so an operator following the documented procedure verifies nothing — and signature verification is the only control they have before handing this daemon the Docker socket.

What is broken

Documented Reality
--certificate checksums.txt.pem --signature checksums.txt.sig Releases ship a bundle, checksums.txt.sigstore.json. Neither .pem nor .sig exists.
signer netresearch/ofelia/.github/workflows/release-slsa.yml That workflow does not exist. Releases are signed by the reusable netresearch/.github/.github/workflows/release-go-app.yml, so the identity never matches.
slsa-verifier ... --provenance-path ofelia-linux-amd64.intoto.jsonl v0.28.0 ships zero .intoto.jsonl assets.
cosign verify ghcr.io/netresearch/ofelia:<TAG> Keyless cosign requires --certificate-identity-regexp and --certificate-oidc-issuer; without them it fails.

Asset inventory of v0.28.0, for reference:

.sigstore.json ×7   .spdx.json ×7   .spdx.json.sigstore.json ×7
.txt ×1   .txt.sigstore.json ×1   (+ .exe variants)   .intoto.jsonl ×0

What this replaces it with

The procedure the pipeline actually produces, including the --signer-workflow flag that provenance verification needs because the signing identity is the reusable workflow rather than this repository — the single detail most likely to make a correct-looking command fail.

Verified by execution, not by reading

Every command in the new text was run against the published v0.28.0 assets before committing, with cosign v2.6.3:

cosign verify-blob --bundle ofelia-linux-amd64.sigstore.json  … → Verified OK
cosign verify-blob --bundle checksums.txt.sigstore.json       … → Verified OK
sha256sum -c checksums.txt --ignore-missing                      → ofelia-linux-amd64: OK
gh attestation verify … --signer-workflow …                      → exit 0

Follow-up worth considering

Nothing in CI verifies a release after it is published, which is why this drift went unnoticed across several releases. A job triggered on: release: [published] that runs exactly these commands against the uploaded assets would turn this class of drift into a build failure instead of a silent one. Raised separately rather than bundled here.

SECURITY.md and CONTRIBUTING.md describe a release pipeline this project no
longer has. Every parameter is wrong, so an operator following them verifies
nothing:

- they pass --certificate checksums.txt.pem --signature checksums.txt.sig;
  releases ship a bundle, checksums.txt.sigstore.json, and neither .pem nor
  .sig exists;
- they name the signer as
  netresearch/ofelia/.github/workflows/release-slsa.yml, a workflow that does
  not exist — releases are signed by the reusable
  netresearch/.github/.github/workflows/release-go-app.yml, so the identity
  never matches;
- they call slsa-verifier against ofelia-linux-amd64.intoto.jsonl; v0.28.0
  ships zero .intoto.jsonl assets;
- the container command omits --certificate-identity-regexp and
  --certificate-oidc-issuer, which keyless cosign requires.

Replace them with the procedure the pipeline actually produces, including the
--signer-workflow flag that provenance verification needs because the signing
identity is the reusable workflow rather than this repository.

Every command in the new text was executed against the published v0.28.0
assets before committing: both cosign verify-blob invocations report
"Verified OK", sha256sum -c passes, and gh attestation verify exits 0.

Signature verification is the only control an operator has before granting
this daemon access to the Docker socket. Instructions that fail teach people
to skip the step.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Copilot AI review requested due to automatic review settings July 28, 2026 06:49
@github-actions

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Jul 28, 2026
@sonarqubecloud

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Automated approval for maintainer PR

All automated quality gates passed. See SECURITY_CONTROLS.md for compensating controls.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.63%. Comparing base (71575aa) to head (568aaea).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #755      +/-   ##
==========================================
- Coverage   87.65%   87.63%   -0.02%     
==========================================
  Files          90       90              
  Lines       12058    12058              
==========================================
- Hits        10569    10567       -2     
- Misses       1198     1199       +1     
- Partials      291      292       +1     
Flag Coverage Δ
integration 87.63% <ø> (-0.02%) ⬇️
unittests 85.20% <ø> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@CybotTM
CybotTM added this pull request to the merge queue Jul 28, 2026
Merged via the queue into main with commit 0173f1d Jul 28, 2026
26 of 27 checks passed
@CybotTM
CybotTM deleted the fix/security-verification-docs branch July 28, 2026 17:39
CybotTM added a commit to netresearch/.github that referenced this pull request Jul 28, 2026
…uld (#315)

`SECURITY.md` tells users how to check a release: verify each signature
with cosign, check the checksums, confirm build provenance. Nothing
executed those instructions, so they were free to rot — and they had.
Every documented command in ofelia was wrong until
[netresearch/ofelia#755](netresearch/ofelia#755):
wrong file extensions, a signer workflow that does not exist, a verifier
pointed at assets no release ships. They looked fine to anyone who did
not run them.

This workflow runs them, against the published assets, with the same
commands and identity constraints the documentation hands out.

## The accounting is arithmetic, not pattern-based

```
total = payloads + bundles
every payload has <payload>.sigstore.json, and every bundle has its payload
the checksum manifest lists exactly the payloads minus itself
```

A name-pattern *exemption* — "skip files matching X" — is what lets an
asset slip through: a new artifact kind appears, matches no rule, and is
silently never verified. Classifying by suffix is fine as long as the
counts must close, because then an unclassified file breaks the sum
instead of being ignored. The signature and provenance steps
additionally require at least one artifact each, so an empty release
cannot pass by verifying nothing.

## Verified against a real release, not a fixture

Run against the published [ofelia
v0.28.0](https://github.com/netresearch/ofelia/releases/tag/v0.28.0):

| Check | Result |
|---|---|
| Accounting | 34 assets = 17 payloads + 17 bundles, no orphans |
| `cosign verify-blob` | `Verified OK` for `ofelia-linux-amd64` and
`checksums.txt` |
| `sha256sum -c` | clean, without `--ignore-missing` |
| `gh attestation verify` | exit 0 against the default signer workflow |
| Provenance classification | exactly the 8 binaries, no SBOMs or
bundles |

And five injected faults, each failing with its own specific finding:

| Fault | Finding |
|---|---|
| removed a binary's signature bundle | `no signature bundle for asset
'ofelia-linux-amd64'` |
| removed an asset, kept its bundle | `signature bundle '...' has no
matching asset` |
| added an unsigned asset | `no signature bundle for asset
'ofelia-linux-riscv64'` |
| deleted an entry from `checksums.txt` | manifest does not list exactly
the shipped payloads |
| added a phantom entry to `checksums.txt` | manifest does not list
exactly the shipped payloads |

## Notes

The defaults match how `release-go-app.yml` publishes today
(`.sigstore.json` bundles, `checksums.txt`, `.spdx.json` SBOMs); every
one of them is an input, so a repo that ships differently can adjust
without forking the workflow. `container-image` is optional and skipped
when empty.

Caller wiring for ofelia follows separately.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants