diff --git a/.github/workflows/dependency-vulnerability-scan.yml b/.github/workflows/dependency-vulnerability-scan.yml new file mode 100644 index 0000000..de6072d --- /dev/null +++ b/.github/workflows/dependency-vulnerability-scan.yml @@ -0,0 +1,116 @@ +name: Dependency Vulnerability Scan + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + schedule: + - cron: '17 3 * * *' + workflow_dispatch: + +permissions: + contents: read + security-events: write + pull-requests: read + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +concurrency: + group: dependency-vulnerability-scan-${{ github.ref }} + cancel-in-progress: true + +jobs: + dependency-review: + name: Pull request dependency review + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Block vulnerable dependency changes + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: moderate + deny-licenses: GPL-2.0, GPL-3.0, AGPL-1.0, AGPL-3.0 + comment-summary-in-pr: always + + rust-audit: + name: Rust cargo audit + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + - name: Install cargo-audit + uses: taiki-e/install-action@cargo-audit + - name: Audit root workspace dependencies + run: cargo audit --deny warnings --file Cargo.lock + - name: Audit contracts workspace dependencies + run: cargo audit --deny warnings --file contracts/Cargo.lock + + npm-audit: + name: Node npm audit + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + project: + - meter-simulator + - usage-dashboard + defaults: + run: + working-directory: ${{ matrix.project }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: npm + cache-dependency-path: ${{ matrix.project }}/package-lock.json + - name: Install locked dependencies without running package scripts + run: npm ci --ignore-scripts + - name: Audit production dependency tree + run: npm audit --audit-level=moderate --omit=dev + - name: Audit full dependency tree + run: npm audit --audit-level=high + + vulnerability-summary: + name: Vulnerability scan summary + runs-on: ubuntu-latest + needs: + - rust-audit + - npm-audit + if: always() + timeout-minutes: 5 + steps: + - name: Publish pipeline summary + run: | + cat <<'SUMMARY' >> "$GITHUB_STEP_SUMMARY" + ## Dependency vulnerability scan + + | Scanner | Scope | Blocking threshold | + | --- | --- | --- | + | Dependency Review | Pull request manifest and lockfile changes | Moderate | + | cargo-audit | Root and contracts Cargo.lock files | RustSec warnings | + | npm audit | meter-simulator and usage-dashboard | Moderate production / high full tree | + + Failed jobs block the pull request until the dependency is upgraded, + replaced, or an explicitly documented security exception is approved. + SUMMARY + - name: Enforce successful scanners + run: | + if [ "${{ needs.rust-audit.result }}" != "success" ] || [ "${{ needs.npm-audit.result }}" != "success" ]; then + echo "One or more dependency vulnerability scanners failed. Review job logs before merging." + exit 1 + fi diff --git a/README.md b/README.md index 74dc917..0bb7c74 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,10 @@ The GitHub Actions workflow (`.github/workflows/ci.yml`) automatically runs on: - **Push to main branch** - Ensures main branch is always tested - **Pull Requests to main** - Prevents breaking changes from being merged +### Dependency Vulnerability Scanning + +A dedicated GitHub Actions workflow (`.github/workflows/dependency-vulnerability-scan.yml`) runs on pull requests, pushes to `main`, a daily schedule, and manual dispatch. It blocks vulnerable dependency changes with GitHub Dependency Review, audits Rust lockfiles with `cargo audit`, audits Node.js projects with `npm audit`, and publishes a workflow summary for security review. See `docs/runbooks/DEPENDENCY_VULNERABILITY_SCANNING.md` for triage, monitoring, and rollout procedures. + ### Testing Stages 1. **Environment Setup**: Rust toolchain with WASM target, Stellar CLI v25.1.0, dependency caching diff --git a/docs/runbooks/DEPENDENCY_VULNERABILITY_SCANNING.md b/docs/runbooks/DEPENDENCY_VULNERABILITY_SCANNING.md new file mode 100644 index 0000000..57f952d --- /dev/null +++ b/docs/runbooks/DEPENDENCY_VULNERABILITY_SCANNING.md @@ -0,0 +1,52 @@ +# Dependency Vulnerability Scanning Runbook + +## Purpose + +The dependency vulnerability scanning pipeline provides a repeatable security gate for Rust and Node.js dependencies across the repository. It is designed to fail fast on exploitable dependency risk while keeping the critical contract, simulator, and dashboard paths unaffected at runtime. + +## Architecture + +The workflow lives in `.github/workflows/dependency-vulnerability-scan.yml` and runs on pull requests, pushes to `main`, a daily scheduled scan, and manual dispatch. It has four stages: + +1. **Dependency review** inspects pull request manifest and lockfile changes before merge. +2. **Rust audit** runs `cargo audit` against both the repository root `Cargo.lock` and `contracts/Cargo.lock`. +3. **Node audit** runs `npm audit` for `meter-simulator` and `usage-dashboard` from committed lockfiles without running package lifecycle scripts. +4. **Summary enforcement** publishes the scanner thresholds and blocks the workflow if a required scanner fails. + +This architecture is intentionally out-of-band from production request handling. The scanners execute in CI only, so they add no latency to contract calls, dashboard requests, or simulator critical paths and preserve the <100ms P99 runtime target. + +## Blocking policy + +| Scanner | Scope | Blocking threshold | +| --- | --- | --- | +| Dependency Review | Pull request dependency changes | Moderate or higher vulnerability, or denied copyleft license | +| cargo-audit | Rust workspaces | RustSec warnings | +| npm audit | Node.js projects | Moderate or higher production vulnerability; high or critical full-tree vulnerability | + +## Triage procedure + +1. Open the failed GitHub Actions job and identify the package, advisory, severity, and vulnerable version range. +2. Prefer upgrading to a patched version and committing the resulting lockfile change. +3. If no patched version exists, replace the package or remove the affected feature path. +4. If the dependency is not reachable or requires time to remediate, document a temporary exception in the security review notes with compensating controls, owner, and expiry date. +5. Re-run the workflow from the pull request after remediation. + +## Monitoring and alerting + +GitHub Actions branch protection should require the `Dependency Vulnerability Scan` workflow before merge. Scheduled failures create visible failed workflow runs; repository maintainers should route GitHub Actions failure notifications to the security operations channel. The workflow summary acts as the lightweight dashboard for scanner coverage and thresholds. + +## Deployment strategy + +Dependency scanner changes are deployed with the normal GitHub Actions rollout model: + +1. Merge the workflow change through a pull request after review. +2. Observe the first pull request run as the canary. +3. Observe the next scheduled scan on `main` as the blue-green cutover validation. +4. If scanner configuration causes noisy false positives, revert the workflow change or adjust thresholds through a reviewed pull request. + +## Security review checklist + +- Confirm all dependency manifests and lockfiles are covered by at least one scanner. +- Confirm scanner failures block pull requests through branch protection. +- Confirm any ignored or deferred advisory has an owner, compensating control, and expiry date. +- Confirm remediation updates are committed with the relevant lockfile changes.