Skip to content

Security policy

This project handles payment-message construction and signing; we take vulnerability reports seriously and appreciate coordinated disclosure.

Do not open a public issue for security problems.

Preferred channel: GitHub private vulnerability reporting — use the “Report a vulnerability” button under the repository’s Security tab.

If you cannot use GitHub, email joaobuenosi@gmail.com with subject [SECURITY] fednow-oss.

What to include: affected crate and version/commit, reproduction steps or PoC, and impact assessment if you have one.

  • Acknowledgement within 72 hours.
  • A triage verdict (accepted / not a vulnerability / needs info) within 7 days.
  • Fix timeline agreed case by case; critical issues in released code get priority.
  • Credit in the release notes and a CVE requested via GitHub Security Advisories when applicable, unless you prefer to stay anonymous.
  • This project never ships with credentials, certificates or live endpoints; reports about the absence of those are out of scope.
  • The simulator (fednow-sim) is a development tool and is not hardened for exposure to untrusted networks; running it on the public internet is out of scope.

Every release is built by a public GitHub Actions workflow (.github/workflows/release.yml) and ships with:

Asset What it is
fednow-oss-<tag>-x86_64-linux.tar.gz the binaries, LICENSE, README.md, CHANGELOG.md
fednow-oss-<tag>.cdx.json CycloneDX SBOM of the workspace, generated by cargo-cyclonedx from Cargo.lock
fednow-oss-<tag>.spdx.json SPDX SBOM of the same release
SHA256SUMS SHA-256 of the tarball and both SBOMs
*.cosign.bundle one Sigstore signature bundle per asset above

Signing is keyless. There is no private key and no signing secret in this repository: the workflow exchanges its short-lived GitHub Actions OIDC token for an ephemeral certificate from Sigstore’s CA (Fulcio), signs with it, and records the signature in the public Rekor transparency log. The certificate binds the signature to this workflow, in this repository, at that tag — which is what you check when you verify.

Signing starts with v0.3.1. Earlier releases ship no .cosign.bundle; there is nothing to verify on them, and this section does not claim otherwise. v0.3.0 is a special case: its release run failed in the SBOM step, before cosign ran, so that release carries no assets at all — not the tarball, not the SBOMs, not the checksums. Do not treat v0.3.0 as a release; use v0.3.1.

You need cosign v3 or newer — the version the release workflow signs with, and the one that reads the bundle format it writes. Nothing else: no key to fetch, no keyring to trust.

Download the asset you want and its .cosign.bundle, then run the following with TAG set to the release tag you downloaded:

Terminal window
$ TAG=v0.3.1 # the release you downloaded
$ cosign verify-blob \
--bundle "fednow-oss-$TAG-x86_64-linux.tar.gz.cosign.bundle" \
--certificate-identity "https://github.com/joaoabuenosi/fednow-oss/.github/workflows/release.yml@refs/tags/$TAG" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
"fednow-oss-$TAG-x86_64-linux.tar.gz"
Verified OK

Verified OK means: this exact byte sequence was signed by a run of this release workflow at that tag, and the signature is in the transparency log. Any other output is a failure — do not use the artifact.

The same command verifies the SBOMs and SHA256SUMS; swap both filenames:

Terminal window
$ cosign verify-blob \
--bundle "SHA256SUMS.cosign.bundle" \
--certificate-identity "https://github.com/joaoabuenosi/fednow-oss/.github/workflows/release.yml@refs/tags/$TAG" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
SHA256SUMS

Two things worth being explicit about:

  • Do not verify with --certificate-identity-regexp and a permissive pattern. The point of the check is the exact workflow-and-tag identity above.
  • Checksums alone prove nothing about origin: SHA256SUMS is published through the same channel as the artifacts. Verify its signature, then check the sums against it.
  • Third-party GitHub Actions are pinned to a commit SHA, not a tag — in every workflow, with a trailing # <version> comment recording which release the SHA stands for. A pin is reviewed by reading the pinned commit’s action.yml and confirming it accepts the inputs the workflow passes, not by re-resolving the version comment: some upstream tags (dtolnay/rust-toolchain’s v1, rustsec/audit-check’s v2) are moved as new versions ship, so the comment can drift from the tag while the pin itself stays exactly as intended.
  • Workflows declare permissions: {} at the top level; each job requests only the scopes it needs, and each scope carries a comment saying which call needs it.
  • Both of the above are enforced on every CI run, not just asserted here: .github/scripts/check-workflow-hardening.sh fails the build if any workflow gains a uses: that is not a 40-character commit SHA, or loses its top-level permissions: key.
  • Action pins are kept current by Dependabot, which bumps the SHA and rewrites the version comment with it (.github/dependabot.yml).
  • cargo audit runs on every lockfile change and daily (.github/workflows/audit.yml).
  • OpenSSF Scorecard runs weekly and on pushes to main, publishes its results to the public OpenSSF API, and uploads findings to this repository’s code scanning tab (.github/workflows/scorecard.yml).
  • Container images are not yet published; when they are (see issue #64) they will be signed by digest with the same keyless flow.