Skip to content

CI Integration

CSP Analyser can run in CI environments to enforce CSP quality gates, detect policy regressions, and keep your security posture tracked over time.

GitHub Actions

Basic workflow

yaml
name: CSP Analysis
on:
  push:
    branches: [main]
  pull_request:

jobs:
  csp:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browser
        run: npx @makerx/csp-analyser setup

      - name: Start dev server
        run: npm run dev &
        env:
          PORT: 3000

      - name: Wait for server
        run: npx wait-on http://localhost:3000

      - name: Analyse CSP
        run: npx @makerx/csp-analyser crawl http://localhost:3000 --format json > csp-report.json

      - name: Upload report
        uses: actions/upload-artifact@v4
        with:
          name: csp-report
          path: csp-report.json

Quality gate with score threshold

Fail the build if the CSP score drops below a threshold:

yaml
- name: Analyse CSP
  id: csp
  run: |
    npx @makerx/csp-analyser crawl http://localhost:3000 --format json > csp-policy.json

    # Score automatically uses the most recent session
    npx @makerx/csp-analyser score > csp-score.txt
    cat csp-score.txt

- name: Check score threshold
  run: |
    SCORE=$(head -1 csp-score.txt | grep -oP '\d+(?=/100)')
    echo "CSP Score: $SCORE"
    if [ "$SCORE" -lt 75 ]; then
      echo "::error::CSP score $SCORE is below threshold of 75"
      exit 1
    fi

JSON format for programmatic parsing

The json export format provides structured output for CI tools:

json
{
  "directives": {
    "default-src": ["'self'"],
    "script-src": ["'self'", "https://cdn.example.com"],
    "style-src": ["'self'", "https://fonts.googleapis.com"],
    "img-src": ["'self'", "data:"],
    "font-src": ["'self'", "https://fonts.gstatic.com"]
  },
  "policyString": "default-src 'self'; script-src 'self' https://cdn.example.com; ...",
  "isReportOnly": false
}

Parse it with jq or any JSON tool:

bash
# Count the number of directives
cat csp-report.json | jq '.directives | keys | length'

# List all allowed script sources
cat csp-report.json | jq '.directives["script-src"]'

# Get the raw policy string for deployment
cat csp-report.json | jq -r '.policyString'

Regression detection with diff

Compare the current analysis against a previous session to detect policy changes:

yaml
- name: Detect regressions
  run: |
    # Assume BASELINE_SESSION is stored from a previous run
    CURRENT=$(npx @makerx/csp-analyser crawl http://localhost:3000 --format json | jq -r '.sessionId')

    if [ -n "$BASELINE_SESSION" ]; then
      npx @makerx/csp-analyser diff "$BASELINE_SESSION" "$CURRENT"
    fi

The diff command shows:

  • Added directives: new source expressions that were not in the baseline
  • Removed directives: source expressions that are no longer needed
  • Changed directives: directives where the source list has changed

This is useful for pull request reviews: if a PR introduces a new third-party script, the diff will show the new script-src entry.

Storing baseline sessions

The CSP Analyser database is stored at .csp-analyser/data.db. To persist baselines across CI runs:

yaml
- name: Cache CSP database
  uses: actions/cache@v4
  with:
    path: .csp-analyser
    key: csp-baseline-${{ github.ref }}
    restore-keys: |
      csp-baseline-refs/heads/main

Score as a quality gate

ThresholdUse case
90+ (A)Security-critical applications, compliance requirements
75+ (B)Standard web applications
55+ (C)Legacy applications being gradually hardened

TIP

Start with a lower threshold and raise it over time. A failing CI gate that nobody fixes is worse than no gate at all.

Environment considerations

Browser installation

Playwright's Chromium browser must be installed in CI. The csp-analyser setup command handles this:

yaml
- name: Install Playwright browser
  run: npx @makerx/csp-analyser setup

On Ubuntu runners, system dependencies are installed automatically. For other environments, see the troubleshooting guide.

Headless mode

CI environments are headless by default. The crawl command runs in headless mode. Do not use the interactive command in CI. It requires a visible browser.

Network access

The crawled site must be reachable from the CI runner. For local development servers, start the server in the background and wait for it to be ready before crawling. For remote sites, ensure the CI runner has network access and consider using --storage-state for authentication.

Project name in CI

Sessions are scoped to a project name, auto-detected from package.json. In CI environments where the working directory may not have a package.json (e.g., Docker containers, monorepo root), set the CSP_ANALYSER_PROJECT environment variable to ensure consistent project scoping:

yaml
- name: Analyse CSP
  run: npx @makerx/csp-analyser crawl http://localhost:3000
  env:
    CSP_ANALYSER_PROJECT: my-app

Alternatively, use the --project flag:

bash
npx @makerx/csp-analyser crawl http://localhost:3000 --project my-app


Released under the MIT License.