Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions .github/workflows/dependency-vulnerability-scan.yml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
52 changes: 52 additions & 0 deletions docs/runbooks/DEPENDENCY_VULNERABILITY_SCANNING.md
Original file line number Diff line number Diff line change
@@ -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.
Loading