Published per NIST SP 800-161r1 (C-SCRM, SR control family in SP 800-53), the CISA Open Source Software Security Roadmap, and OWASP Top 10:2025 A03 Software Supply Chain Failures.
Reviewed annually; next review 2027-05-13.
A new direct dependency (anything appearing as a top-level entry in any of
our Cargo.toml, package.json, or pyproject.toml) requires:
- License compatible — listed in
deny.toml[licenses] allow. Default set: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, Unicode-3.0, Unicode-DFS-2016, Zlib, BSL-1.0, MPL-2.0. Copyleft (GPL/AGPL/LGPL-3) is denied. - No known advisories —
cargo audit,pnpm audit,pip-auditall green. - No KEV match — daily KEV check (
sca.ymljobkev-check) green. - Active maintainer signal — ≥1 commit in last 12 months OR explicit
note in
supply-chain/audits.tomlaccepting maintenance risk. - OpenSSF Scorecard ≥ 5/10 for any new direct dep (advisory; we may accept lower with documented reason).
cargo vetaudit entry — added tosupply-chain/audits.tomleither by us auditing or by importing frommozilla,google, orbytecodeallianceaudits.- Build-script review — any crate with a
build.rsgets read before merge..npmrc ignore-scripts=trueblocks npm post-install scripts by default (see CISA Shai-Hulud advisory). - No new transitive
unsafeoutside the existing C-binding boundary documented inMEMORY-SAFETY.md. Validated bycargo-geigerdelta.
Justification lives in the ADR PR that introduces the dep.
| Channel | Cadence | Auto-merge? |
|---|---|---|
| Dependabot security updates | as published | No — maintainer review |
| Dependabot semver-compatible (patch/minor) | weekly | No — maintainer review |
| Dependabot major | as opened | No — usually requires code change |
| GitHub Actions | monthly (Dependabot config) | No — verify new SHA pin |
| Renovate cooldown | n/a (we use Dependabot) | n/a |
Manual rotation list — dependabot.yml ignore keeps these from auto-PRs
because they touch the wire format / sandbox / store:
wasmtimerusqliterustlschitchatfocapyo3
These get reviewed each release.
Per NIST SP 800-218 PS.2.1 / PS.3.2 and the CISA Secure-by-Demand Guide:
- Every release artifact (Tauri bundle,
springtaled,springtale-cli, Python wheel) is signed by Sigstore cosign in keyless OIDC mode via theprovenance.ymlworkflow. Bundle published alongside artifact on GitHub Releases. - SLSA Build Level 3 provenance attestation emitted via
slsa-framework/slsa-github-generator. Published to Sigstore Rekor transparency log. - CycloneDX 1.7 SBOM (Rust + npm + Python merged) + SPDX 2.3 SBOM emitted per release. Both signed with cosign.
- Release tags are signed (Sigstore
gitsignor GPG); seeCONTRIBUTING.mdfor maintainer setup.
Users verify with:
cosign verify-blob \
--bundle springtaled.cosign.bundle \
--certificate-identity-regexp 'https://github.com/ScopeCreep-zip/Springtale/.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
springtaled
Full recipe in docs/operations/verifying-releases.md.
| Ecosystem | Audit graph | Continuous monitor |
|---|---|---|
| Rust / crates.io | cargo vet with mozilla, google, bytecodealliance imports |
cargo-audit (RustSec), cargo-deny advisories, osv-scanner |
| npm / pnpm | none today (no equivalent of vet); use pnpm audit + npm-lockfile-scan known-bad list |
pnpm audit, daily KEV cross-ref |
| Python / PyPI (pyo3) | none today; pinned lockfile via uv when pyproject.toml lands |
pip-audit --strict |
| GitHub Actions | explicit allowlist in docs/security/CI-TRUST.md; all actions SHA-pinned |
zizmor, actionlint, GitHub Actions org policy (SHA pinning enforced) |
| Container base images | digest-pinned (no tag refs in production layers) | trivy, grype, syft SBOM |
| Dep | Reason | VEX statement |
|---|---|---|
matrix-sdk (and connector-matrix) |
matrix-sdk 0.16 requires rusqlite 0.37 which has CVE-2025-70873 (heap info disclosure). Store uses rusqlite 0.39 (patched). Downgrading would expose vulnerable users to heap leaks. |
vex/connector-matrix-rusqlite-cve-2025-70873.json |
rsa |
RUSTSEC-2023-0071 Marvin Attack timing sidechannel; no upstream fix. | vex/rsa-rustsec-2023-0071.json |
Accepted-with-mitigation entries live in .cargo/audit.toml with full
justification.
Every advisory we accept ships a machine-readable VEX statement under
vex/, mirrored by the ignore lists in .cargo/audit.toml and
deny.toml:
| VEX file | Advisory |
|---|---|
atomic-polyfill-rustsec-2023-0089.json |
atomic-polyfill unmaintained |
bincode-rustsec-2025-0141.json |
bincode advisory |
connector-matrix-rusqlite-cve-2025-70873.json |
rusqlite heap info disclosure (matrix deferral) |
instant-rustsec-2024-0384.json |
instant unmaintained |
lru-rustsec-2026-0002.json |
lru advisory |
number-prefix-rustsec-2025-0119.json |
number-prefix unmaintained |
rand-rustsec-2026-0097.json |
rand 0.8.5 log-feature unsoundness |
rsa-rustsec-2023-0071.json |
Marvin Attack timing sidechannel |
rustls-pemfile-rustsec-2025-0134.json |
rustls-pemfile advisory |
Adding an ignore without a matching VEX file (or vice versa) should fail review — the two lists are maintained together.
| Secret | Used by | Notes |
|---|---|---|
GITLEAKS_LICENSE |
secrets.yml (gitleaks job) |
Required for org-owned repos per gitleaks-action licensing; the job fails without it |
What we attest: every release artifact is produced by cargo auditable build --release --locked from a fixed Git commit under a
fixed Rust toolchain. The same (commit, toolchain) pair, rebuilt
on a clean Ubuntu runner with the release commit's SOURCE_DATE_EPOCH
and CARGO_INCREMENTAL=0, produces byte-identical artifacts. The
provenance.yml repro-check job enforces this on every release: it
rebuilds springtaled and springtale-cli on a fresh runner, hashes
both copies, and fails the workflow if the SHA-256 differs. A
GitHub-native build-provenance attestation is then emitted for the
cross-runner-verified hashes (actions/attest-build-provenance@v2),
which a verifier can fetch via gh attestation verify to prove the
attestation came from a same-toolchain, same-commit rebuild rather
than from a single ad-hoc compile.
What we do not attest: binaries produced on a different rustc release, with different host LLVM / glibc, or under a different linker (lld vs gold) are NOT expected to match bit-for-bit. The attestation predicate records the toolchain via the SLSA generator metadata, so a verifier knows exactly which input set the reproducibility claim covers.
Knobs that enforce this:
Cargo.toml [profile.release]—codegen-units = 1,lto = "fat",panic = "abort",strip = "symbols", and cruciallyincremental = false. Incremental compilation injects per-machine state that breaks bit-for-bit reproducibility..github/workflows/provenance.yml—SOURCE_DATE_EPOCHfrom the release tag commit timestamp,CARGO_INCREMENTAL=0in the build env (belt-and-braces against future config drift), separateSwatinem/rust-cache@v2cache keys forrelease-buildvsrepro-checkso a stale incremental cache can't fake a match.
Verifying a release locally: clone at the release tag, run
cargo auditable build --release --locked with
SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD) and
CARGO_INCREMENTAL=0, then sha256sum target/release/springtaled
and compare against the value in the release's
actions/attest-build-provenance attestation.
See docs/security/INCIDENT-RUNBOOK.md for the maintainer playbook covering:
- Compromised upstream package landing in a Dependabot PR
- Leaked npm/PyPI/crates.io publish token
- Unauthorized release tag
- Malicious GitHub Action
- Re-evaluate license allow-list and ban list
- Re-evaluate Scorecard threshold
- Refresh
cargo vetimports list against Mozilla/Google/BA churn - Refresh KEV-cross-ref job (rotate API endpoints if CISA URL pattern changes)
- Refresh manifest signing algorithm against NIST PQC progress