Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/DevDonzo/warden/llms.txt

Use this file to discover all available pages before exploring further.

A security baseline is a committed snapshot of the vulnerabilities present in your project at the moment your team reviewed and accepted the current risk posture. Once a baseline exists, Warden’s CI check compares every subsequent scan against it and exits with an error only when genuinely new or worsened findings appear above your configured severity threshold. Findings that were already in the baseline are treated as accepted risk and do not block the pipeline. This eliminates the false-positive noise of failing on pre-existing issues you have no immediate plans to remediate, while still catching real regressions the moment they are introduced.

How baselines work

1

Run a scan to produce scan-results.json

Warden writes structured scan output to scan-results/scan-results.json after every warden scan invocation. The baseline command reads from this file, so a scan must complete successfully before you can create a baseline.
warden scan . --scanner npm-audit --severity high
warden baseline --create and warden baseline --check both require scan-results/scan-results.json to exist. If the file is absent, the command exits 1 with the message Run "warden scan" first.
2

Create the baseline file

Pass --create to snapshot the current scan results into .warden-baseline.json:
warden baseline --create
Warden reads scan-results/scan-results.json, converts each vulnerability into a stable fingerprint, and writes the full baseline snapshot to .warden-baseline.json in your project root.
Commit .warden-baseline.json in the same pull request as the code it describes. Keeping the baseline file co-located with the code makes it easy to see exactly which risk decisions were made at the time each change was merged.
3

Commit the baseline file

git add .warden-baseline.json
git commit -m "security: establish Warden baseline"
git push
Once the file is in the repository, CI can compare against it on every subsequent run.
4

Enable the baseline check in CI

Add a dedicated step after the scan step in your workflow:
- name: Baseline check
  run: warden baseline --check --severity high
Warden compares scan-results/scan-results.json (written moments earlier by the scan step) against the committed .warden-baseline.json. If the comparison finds new or worsened findings at high severity or above, the step exits 2 and the job fails.
5

Update the baseline when you accept new risk

After remediating a finding or consciously accepting new risk, regenerate the baseline and commit the updated file:
warden scan . --scanner npm-audit --severity high
warden baseline --create
git add .warden-baseline.json
git commit -m "security: update Warden baseline after triage"

Command flags

The warden baseline command accepts the following options:
FlagDefaultDescription
--createCreate or overwrite .warden-baseline.json from the current scan results.
--checkCompare the current scan results against the existing baseline and exit 2 if a regression is detected.
--baseline <path>.warden-baseline.jsonPath to the baseline file. Override when managing multiple baselines for a monorepo.
--scan-results <path>scan-results/scan-results.jsonPath to the scan results JSON produced by warden scan.
--severity <level>highMinimum severity that triggers a check failure. Accepted values: low, medium, high, critical.
--jsonEmit the full comparison result as JSON to stdout instead of formatted log output. Useful for downstream parsing or upload steps.
--create and --check are mutually exclusive. Passing both in the same invocation exits 1 with an error.

The WardenBaseline type

The .warden-baseline.json file is a serialized WardenBaseline object. Its structure is defined in src/types/index.ts:
interface WardenBaseline {
  schemaVersion: 1;
  generatedAt: string;        // ISO-8601 timestamp of when the baseline was written
  scanTimestamp: string;      // ISO-8601 timestamp from the originating scan
  scanner?: ScannerType;      // 'snyk' | 'npm-audit' | 'pip-audit' | 'nmap' | 'metasploit' | 'mock'
  scanMode?: ScanMode;        // 'sast' | 'dast'
  projectPath?: string;       // Absolute path that was scanned
  summary: ScanSummary;       // { total, critical, high, medium, low }
  riskScore: number;          // 0–100 computed risk score at baseline time
  findings: BaselineFinding[];
}
Each entry in findings is a BaselineFinding:
interface BaselineFinding {
  fingerprint: string;        // Stable identifier: "<ecosystem>|<package>|<vuln-id>"
  id: string;                 // Vulnerability ID (e.g. CVE-2024-1234 or SNYK-JS-…)
  title: string;
  severity: Severity;         // 'critical' | 'high' | 'medium' | 'low'
  packageName: string;
  version: string;
  fixedIn: string[];
  ecosystem?: Ecosystem;      // 'npm' | 'python'
  targetHost?: string;        // DAST-specific
  targetPort?: number;        // DAST-specific
  service?: string;           // DAST-specific
}
A minimal baseline file looks like this:
{
  "schemaVersion": 1,
  "generatedAt": "2025-01-15T09:00:00.000Z",
  "scanTimestamp": "2025-01-15T08:59:12.341Z",
  "scanner": "npm-audit",
  "scanMode": "sast",
  "projectPath": "/home/runner/work/my-app/my-app",
  "summary": {
    "total": 3,
    "critical": 0,
    "high": 1,
    "medium": 2,
    "low": 0
  },
  "riskScore": 42,
  "findings": [
    {
      "fingerprint": "npm|lodash|SNYK-JS-LODASH-567746",
      "id": "SNYK-JS-LODASH-567746",
      "title": "Prototype Pollution",
      "severity": "high",
      "packageName": "lodash",
      "version": "4.17.15",
      "fixedIn": ["4.17.21"],
      "ecosystem": "npm"
    }
  ]
}

BaselineComparison output

When you run warden baseline --check --json, Warden prints a BaselineComparison object to stdout:
interface BaselineComparison {
  generatedAt: string;
  baselineRiskScore: number;      // Risk score at baseline creation time
  currentRiskScore: number;       // Risk score from the current scan
  riskScoreDelta: number;         // currentRiskScore - baselineRiskScore (positive = worse)
  newFindings: BaselineFindingDelta[];
  resolvedFindings: BaselineFindingDelta[];
  worsenedFindings: BaselineFindingDelta[];
  unchangedCount: number;
  summary: {
    new: number;
    resolved: number;
    worsened: number;
    unchanged: number;
  };
}
Example --json output from a check that detects one new finding:
{
  "failed": true,
  "minimumSeverity": "high",
  "comparison": {
    "generatedAt": "2025-01-16T14:23:00.000Z",
    "baselineRiskScore": 42,
    "currentRiskScore": 58,
    "riskScoreDelta": 16,
    "newFindings": [
      {
        "fingerprint": "npm|axios|CVE-2025-0001",
        "current": {
          "fingerprint": "npm|axios|CVE-2025-0001",
          "id": "CVE-2025-0001",
          "title": "SSRF via redirect",
          "severity": "high",
          "packageName": "axios",
          "version": "1.6.0",
          "fixedIn": ["1.7.0"],
          "ecosystem": "npm"
        }
      }
    ],
    "resolvedFindings": [],
    "worsenedFindings": [],
    "unchangedCount": 1,
    "summary": {
      "new": 1,
      "resolved": 0,
      "worsened": 0,
      "unchanged": 1
    }
  }
}

BaselineFindingDelta

Each entry in newFindings, resolvedFindings, and worsenedFindings is a BaselineFindingDelta:
interface BaselineFindingDelta {
  fingerprint: string;
  baseline?: BaselineFinding;   // The finding as it existed in the baseline (absent for new findings)
  current?: BaselineFinding;    // The finding as seen in the current scan (absent for resolved findings)
  severityChanged?: {
    from: Severity;             // Severity recorded in the baseline
    to: Severity;               // Severity seen in the current scan
  };
}
  • New finding: current is present, baseline is absent. The vulnerability was not in the committed baseline.
  • Resolved finding: baseline is present, current is absent. The vulnerability no longer appears in the current scan.
  • Worsened finding: both baseline and current are present, and severityChanged is populated. The same vulnerability fingerprint exists in both scans but its severity increased (e.g. mediumhigh).

Complete CI workflow snippet

The following workflow runs a scan and then performs a baseline regression check. Both steps must pass for the job to succeed.
name: Warden Security Patrol

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  warden:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install project dependencies
        run: |
          if [ -f package-lock.json ]; then
            npm ci
          elif [ -f package.json ]; then
            npm install
          else
            echo "No package.json found; skipping dependency install"
          fi

      - name: Run Warden scan
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        run: >
          npx @devdonzo/warden scan .
          --ci
          --json
          --scanner npm-audit
          --severity high

      - name: Baseline regression check
        run: warden baseline --check --severity high

      - name: Upload Warden artifacts
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: warden-artifacts
          path: |
            scan-results
            .warden-baseline.json
            SECURITY-ADVISORY.md
The if: always() condition on the upload step ensures scan artifacts and the baseline file are preserved even when a previous step exits non-zero, giving you full context when diagnosing a gate failure.

Build docs developers (and LLMs) love