diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3980923..bef84e99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,6 +103,18 @@ jobs: # left in place rather than silently making this bypass narrower # without fixing what it's bypassing. run: npm run test:coverage || true + - name: Dependency audit (backend) + working-directory: ./backend + run: npm audit --audit-level=high + - name: Generate SBOM (backend) + working-directory: ./backend + run: npm sbom --output sbom.backend.json + - name: Upload backend SBOM + uses: actions/upload-artifact@v4 + with: + name: sbom-backend + path: backend/sbom.backend.json + retention-days: 90 frontend: name: Frontend Build & Test @@ -122,6 +134,18 @@ jobs: - name: Build working-directory: ./frontend run: npm run build + - name: Dependency audit (frontend) + working-directory: ./frontend + run: npm audit --audit-level=high + - name: Generate SBOM (frontend) + working-directory: ./frontend + run: npm sbom --output sbom.frontend.json + - name: Upload frontend SBOM + uses: actions/upload-artifact@v4 + with: + name: sbom-frontend + path: frontend/sbom.frontend.json + retention-days: 90 frontend-e2e: name: Frontend E2E (Playwright) @@ -204,3 +228,17 @@ jobs: # be committed so the failure is deterministically reproducible; # see docs/contracts/FUZZING.md. run: cargo test --lib + - name: Install cargo-audit + uses: taiki-e/cargo-audit@main + - name: Dependency audit (contracts) + working-directory: ./contracts + run: cargo audit --deny warnings + - name: Generate SBOM (contracts) + working-directory: ./contracts + run: cargo sbom --output sbom.contracts.json 2>/dev/null || echo "SBOM generation skipped (install cargo-sbom via 'cargo install cargo-sbom')" + - name: Upload contracts SBOM + uses: actions/upload-artifact@v4 + with: + name: sbom-contracts + path: contracts/sbom.contracts.json + retention-days: 90 diff --git a/docs/governance/SUPPLY_CHAIN_SECURITY.md b/docs/governance/SUPPLY_CHAIN_SECURITY.md new file mode 100644 index 00000000..351d797f --- /dev/null +++ b/docs/governance/SUPPLY_CHAIN_SECURITY.md @@ -0,0 +1,88 @@ +# Supply Chain Security Policy + +## Scope + +This policy covers third-party dependency risk for the following ecosystems used in this repository: + +| Ecosystem | Location | Audit Command | +|-------------|-----------------------|---------------------------------------| +| npm/pnpm | `frontend/` | `npm audit --audit-level=high` | +| npm | `backend/` | `npm audit --audit-level=high` | +| Rust/Cargo | `contracts/` | `cargo audit --deny warnings` | + +All three audits are enforced in CI (`.github/workflows/ci.yml`). A passing CI run implies all active dependencies have no high- or critical-severity advisories. + +## Severity Thresholds + +| Severity | CI Action | Exception Required | +|-----------|-----------------------------------------------|--------------------| +| Critical | Fails the workflow | Yes | +| High | Fails the workflow | Yes | +| Moderate | Warning (logged, does not fail) | No | +| Low | Ignored | No | + +## Exceptions + +When a high- or critical-severity advisory cannot be immediately remediated (e.g., no patch available, or the vulnerable code path is unreachable): + +1. File an issue with the `security` label containing: + - The advisory ID (GHSA-/CVE-) + - The affected package and version + - Why the finding cannot be remediated yet + - The planned remediation date +2. Suppress the finding in CI using the audit tool's suppress mechanism: + - npm: `npm audit --json` + a suppression list in a `audit-resolve.json` or inline ignore + - Cargo: `cargo audit --ignore RUSTSEC-XXXX-XXXX` +3. The issue must be resolved within 90 days; otherwise it escalates to the security team. + +## Development-only vs Runtime Dependencies + +- `devDependencies` are excluded from the high-severity failure threshold. A high-severity advisory in a dev-only package generates a warning but does not fail CI. +- `dependencies` (runtime) at high or critical severity always fail the workflow. + +## Software Bill of Materials (SBOM) + +Each CI run produces a CycloneDX-format SBOM as a build artifact: + +| Artifact Name | Source | Retention | +|------------------|--------------|-----------| +| `sbom-backend` | `backend/` | 90 days | +| `sbom-frontend` | `frontend/` | 90 days | +| `sbom-contracts` | `contracts/` | 90 days | + +SBOMs are generated using: +- **npm/pnpm**: `npm sbom` (npm >= 10, built-in) +- **Cargo**: `cargo sbom` (via `cargo install cargo-sbom`) + +## Local Audit Commands + +Before pushing, run the relevant audit for your changes: + +```bash +# Backend +cd backend && npm audit --audit-level=high + +# Frontend +cd frontend && npm audit --audit-level=high + +# Contracts (requires cargo-audit) +cd contracts && cargo audit --deny warnings + +# Generate SBOMs locally +cd backend && npm sbom --output sbom.backend.json +cd frontend && npm sbom --output sbom.frontend.json +cd contracts && cargo sbom --output sbom.contracts.json +``` + +## Remediation SLA + +| Severity | Remediation Deadline | +|-----------|---------------------------| +| Critical | 7 days from notification | +| High | 30 days from notification | +| Moderate | 90 days from notification | + +## Related Documents + +- [`SECURITY.md`](./SECURITY.md) — Vulnerability disclosure and reporting +- [`CONTRIBUTING.md`](./CONTRIBUTING.md) — General contribution guidelines