Step-by-step playbook for supply-chain compromise, leaked credentials, unauthorized releases, and reported user-safety vulnerabilities. Per NIST CSF 2.0 Respond function and CISA CVD Program.
Last review: 2026-05-13.
| Sev | Examples | First action SLA |
|---|---|---|
| Critical | Vault key extraction; panic wipe defeat; identity leak; telemetry phone-home; remote unauthenticated RCE | ≤4h |
| High | Connector sandbox escape; capability bypass; auth bypass on management API | ≤24h |
| Medium | Path traversal in a single connector; logged secret; CSP bypass | ≤72h |
| Low | Verbose error leaking software version; ICU/font load over plaintext on dashboard | ≤7d |
User-safety bugs (identity leak across contexts, duress detectability, panic wipe defeat, any phone-home) are always Critical regardless of CVSS.
Trigger: RustSec / npm-advisory / KEV match on a dep we ship; or any Dependabot security PR landing in our queue.
- Triage — Is this dep on our release path?
- Yes: stop, follow steps 2–7.
- No: open a
cargo denyissue to ban + advance to step 8.
- Pin / rollback — Open a feature branch reverting to the last known-good version. If patched upstream exists, bump there instead.
- Confirm green — Run full CI (
gh workflow run ci.yml,sca.yml,sbom.yml) on the branch. - Audit usage —
cargo tree -i <crate>to enumerate everything that pulls it. Read each call site. Document in the PR body. - Issue a VEX — Add
vex/<crate>-<cve>.jsondeclaring affected/ not_affected with justification. OpenVEX format. - Merge + tag patch release —
v0.X.Y-secfix.1. Runprovenance.ymlto sign + attest. - Publish advisory — GitHub Security Advisory with CWE-ID, CVSS, and
patched version. Update
CHANGELOG.mdsecurity section. - Long-term — Add the crate to
dependabot.ymlignorelist if it needs manual review going forward. Updatedocs/security/SUPPLY-CHAIN.mdif policy changed.
Trigger: maintainer detects unauthorized publish, or GitHub secret scanning fires, or upstream registry sends a compromised-account email.
- Revoke immediately:
- crates.io API token → Settings → Account → revoke
- npm classic token → npmjs.com/settings//tokens → revoke
- PyPI token → PyPI account → revoke
- GitHub PAT → Settings → Developer settings → revoke
- Sigstore: OIDC tokens are short-lived; no revoke needed, but rotate the linked GitHub identity if compromised
- Yank malicious releases —
cargo yank <ver>,npm unpublish(within window),pip-yank, or contact registry support. - Audit publish history — Identify any unauthorized version. Compare SHA-256 against signed-bundle expectations.
- Rotate signing identity — Re-do Sigstore keyless flow with fresh
GitHub OIDC; cosign verify recipe in
docs/operations/verifying-releases.mdcarries new identity claim. - Notify — GitHub Security Advisory; CHANGELOG security section; mailing list / channel where users learned of releases.
- Replay attestation — Build the legitimate version on a clean runner; sign + push provenance; instruct users to verify against new identity.
- Move to OIDC trusted publishing if not already on it.
Trigger: a release tag appears on main that wasn't created by the maintainer,
or a GitHub Action we use is reported compromised (e.g. tj-actions Mar 2025).
- Disable the workflow that consumed the compromised action — revert
on
mainimmediately. Branch-protect to require all workflows reviewed. - Audit Actions logs — search for the compromise window's runs; download logs; look for known exfil indicators (double-base64 strings, exfil POSTs to non-allowed endpoints).
- Rotate every token used in the window. See Playbook 2.
- Yank or unpublish any artifact built by a compromised workflow.
- Update
docs/security/CI-TRUST.md— remove the action from the allowlist or pin to known-safe SHA. - Update org Action policy to block the compromised action.
- Notify — Security Advisory.
- Re-publish clean artifacts from the new SHA-pinned workflow with full attestation.
Trigger: GitHub Security Advisory opened or email to maintainer with anything in the user-safety class (identity leak, duress detectability, panic wipe defeat, telemetry).
- Acknowledge ≤24h to the reporter.
- Verify — Try to reproduce on the current main against a fresh vault.
- Triage — Critical user-safety always. CVSS often understates impact; we score by reach into the threat model, not raw CVSS.
- Develop fix on private branch — ≤14d target. Coordinate with reporter if they want to be credited.
- Test — Full CI + manual reproduction. Add regression test.
- Coordinated disclosure — Negotiate window with reporter (default 90d, shorter for active exploitation).
- Publish — Security Advisory, signed patch release, CHANGELOG entry, credit reporter (real name, handle, or anonymous per their preference).
- Post-mortem — Document root cause; update STRIDE register
(
RISK-REGISTER.md); add lint or property test that would have caught it.
Trigger: .github/workflows/sca.yml kev-check job fails.
- Read the job output — which dep matches which KEV entry.
- Confirm not a false positive — sometimes KEV matches by CPE but the advisory doesn't apply to our usage path.
- If real: follow Playbook 1 from step 2.
- If false positive: add a VEX
not_affectedjustification invex/, re-run.
Trigger: llm-redteam.yml corpus run finds a bypass, or a user reports
exfiltration via an AI adapter.
- Identify the adapter and the prompt path.
- Quarantine — disable the adapter in
springtale.toml.exampledefaults; document inCHANGELOG.md. - Patch
AiGuardrail— strengthen redaction / output validation / capability check. - Add the attack to the corpus —
tests/llm-redteam/corpus/. - Verify with
llm-redteam.yml— must refuse. - Release with advisory if user data exposure occurred.
**Summary**: <one-line>
**Affected**: <versions>
**Patched**: <version>
**CVE**: <if-assigned>
**CWE**: <id>
**CVSS v4**: <vector + score>
**User-safety impact**: <how this affects vulnerable users specifically>
**Reporter**: <credit>
**Acknowledgement**: <date>
**Triage**: <date>
**Fix**: <date>
**Disclosure**: <date>
- security: <scope> — fix <CWE/CVE>; user-safety impact: <one line>. Credit: <reporter>.
For now (single maintainer):
- Triage / Triage owner:
@radicalkjax - Crypto reviewer:
@radicalkjax - Connector reviewer:
@radicalkjax - Release engineer:
@radicalkjax
When the project grows, this section gets per-role assignees. The runbook itself does not change.
GitHub Advisories acts as our CNA proxy today. The flow:
- A vulnerability is reported via this repo's private vulnerability
reporting surface (
SECURITY.md→ "Reporting a Vulnerability"). - The maintainer files a GitHub Security Advisory in the
Security→Security advisoriestab. - From the advisory, click
Request CVE— GitHub assigns a CVE ID under their CNA scope (CVE-2026-XXXXX namespace). - The advisory is published once the fix lands in
main, with the CVE ID embedded. - Disclosure timeline follows the policy in
SECURITY.md(typically 90 days from initial report, sooner if exploitation is observed).
This path requires zero CNA registration on Springtale's part. It is the recommended posture per GitHub's CNA documentation and CVE Numbering Authority program rules for projects under the GitHub-as-CNA umbrella.
Why we might want this: a direct CNA registration gives Springtale control over its own CVE namespace, sets disclosure timelines without GitHub's gatekeeping, and makes the project visible in the CNA list. The signal value for vulnerable users — "this project is mature enough to own its security identifier issuance" — is real.
Trigger to apply:
- ≥1 CVE assigned per quarter sustained for 4 consecutive quarters (this is roughly MITRE's published threshold for considering a project's CNA application credible).
- AND demonstrated 90-day-or-better disclosure track record on at least 4 prior assigned CVEs.
- AND a documented security contact reachable via at least two
independent channels (the existing
security.txt+ the GitHub advisories surface satisfies this once both are stable).
Application process (per cve.org/About/CNARules):
- Read the CNA Operational Rules — currently CNA Rules v4.0.
- Identify the CNA scope statement: which products fall under Springtale's CNA. The natural scope is "vulnerabilities in software developed by the Springtale project, including connector crates published under the project's namespace." Third-party connectors are out of scope.
- Identify a Root CNA. For an open-source project of this scope the Root CNA is MITRE (the top-level Root for projects without an existing Root assignment).
- Submit the application via the CNA registration form on
cve.org/PartnerInformation. Required fields:
- Organization name and abbreviation (e.g. "Springtale")
- Scope statement (see step 2)
- Disclosure policy URL (point at
SECURITY.md) - Security contact (point at the address in
security.txt) - Web disclosure venue (this repo's Security tab — GitHub Advisories surface remains the publication channel)
- Expect a ~3-month review per MITRE published timelines. The review consists of (a) scope sanity-check, (b) disclosure-policy review, (c) Root CNA approval, (d) onboarding training.
- On acceptance, Springtale receives a CVE ID block and onboarding credentials for the CVE Services API (Rest-based CVE ID reservation + publication endpoint).
Post-acceptance commitments (the things we sign up for):
- Timely CVE assignment — within 72 hours of a confirmed vulnerability report, per CNA rules.
- Annual CNA-rules compliance attestation.
- Public CNA scope statement maintained on
SECURITY.md. - Participation in CVE Services API + Advisory format compliance.
- Subscribe to the cve-cna-list announce channel for rule changes.