Getting Started

CSPctl Documentation

Complete guide to managing Content Security Policies with CSPctl

Installation

Install via Script (Recommended)

# macOS/Linux (automatic platform detection)
curl -fsSL https://install.cspctl.com/install.sh | sh
# or with wget:
wget -qO- https://install.cspctl.com/install.sh | sh
• Supports: Linux (x86_64, arm64, armv7), macOS (x86_64, arm64), Windows
• Installs to: $HOME/.local/bin (customizable with CSPCTL_INSTALL_DIR)
• Verifies installation and provides PATH setup instructions
• Custom version: CSPCTL_VERSION=v0.3.3-dev curl ... | sh

Download Binary (v0.3.3-dev)

Download pre-built binaries for your platform:

After downloading, extract the archive and add the binary to your PATH.

Manual Installation

After downloading the binary for your platform:

# Extract the archive (Linux/macOS)
tar -xzf cspctl-*.tar.gz
# Or for Windows (using PowerShell)
tar -xzf cspctl-windows-x86_64.tar.gz
# Move to a directory in your PATH
sudo mv cspctl /usr/local/bin/
# Or add to user directory
mkdir -p ~/.local/bin
mv cspctl ~/.local/bin/
# Verify installation
cspctl --version

Quick Start

1. Initialize a Policy

cspctl init
# Creates a csp.json file with secure defaults

2. Start the UI

cspctl ui
# Opens http://localhost:5173 with the management UI

3. Test Your Policy

cspctl policy validate -f csp.json
cspctl rollout dry-run --app myapp -f csp.json

4. Deploy

cspctl policy push --app myapp -f csp.json
cspctl rollout set --app myapp --mode report-only

Core Concepts

Content Security Policy (CSP)

CSP is a security standard that helps prevent XSS attacks by controlling which resources can be loaded on your website. It works by sending a special HTTP header that tells browsers what sources of content are trusted.

Policy Directives

  • default-src - Fallback for all resource types
  • script-src - Controls JavaScript sources
  • style-src - Controls CSS sources
  • img-src - Controls image sources
  • connect-src - Controls AJAX/WebSocket/EventSource
  • font-src - Controls font sources
  • media-src - Controls audio/video sources
  • frame-src - Controls iframe sources

Report-Only Mode

Test policies without breaking your site by using report-only mode. Violations are logged but not blocked, allowing you to fine-tune policies before enforcement.

Violation Reports

When CSP blocks a resource, it can send a JSON report to your specified endpoint. CSPctl provides real-time streaming of these reports with PII redaction options, helping you understand what resources are being blocked and adjust your policy accordingly.

Progressive Rollout

Deploy CSP changes gradually by controlling the percentage of traffic that receives the new policy. Start with 10%, monitor violations, then increase to 50% and eventually 100% as confidence grows.

Offline Caching

CSPctl automatically caches policies locally for resilience. If the API is unreachable, your application continues serving the last-known-good policy, ensuring security isn't compromised during outages.

AI-Powered Suggestions

Get intelligent recommendations for improving your CSP based on violation patterns and security best practices. CSPctl analyzes your reports and suggests additions or modifications to strengthen your policy.

CLI Reference

Command Line Interface

Complete reference for all CSPctl CLI commands

Authentication

cspctl login

Set up authentication (displays setup instructions)

Three authentication methods:
1. Environment variable: export CSPCTL_TOKEN=your-token
2. Config file: cspctl config set token your-token
3. Web UI setup wizard: cspctl ui
cspctl config set <key> <value>

Configure settings (valid keys: base, token)

base - API base URL (default: http://127.0.0.1:5173)
token - Authentication token

Policy Management

cspctl init

Initialize a new CSP policy file with secure defaults

Creates csp.json with default-src: ['self'], script-src: ['self'], base-uri: ['none']

cspctl policy pull --app <app> -f <file>

Pull the current policy from the server (with automatic offline caching)

Default app: default-app
Environment: Always uses prod (hardcoded)
Cache location: ~/.cspctl/cache/{app}/prod/policy.json
Default file: csp.json (short option: -f)
--json - Output JSON to stdout
cspctl policy push --app <app> -f <file>

Push a policy to the server

Default app: default-app
Environment: Always uses prod (hardcoded)
Default file: csp.json (short option: -f)
--json - Output policy as JSON
Note: Currently returns mock confirmation
cspctl policy diff -f <file>

Show unified diff between local file and remote policy

Default file: csp.json (short option: -f)
Note: Currently returns mock diff for testing
cspctl policy suggest --app <app> [--env <env>]

Get AI-powered suggestions based on violation reports

Required: --app <app> - Application ID
Default env: prod
--apply - Automatically apply suggestions to policy
--export <path> - Export suggestions to file
--json - Output as JSON

Headers & Security

cspctl header render -f <file> [--strict-dynamic] [--nonce <value>]

Generate CSP header from policy file

--strict-dynamic - Add strict-dynamic to script-src
--nonce <value> - Use specific nonce (auto-generated if not specified)
Automatically includes report-uri and report-to if configured
cspctl sri --path <file1,file2,...> [--algo <algorithm>]

Generate Subresource Integrity hashes

Algorithms: sha256, sha384 (default), sha512
Add --json for JSON output
Output format: file.js\tsha384-hash...
cspctl nonce [--len <length>]

Generate a cryptographic nonce

Default length: 16 characters (alphanumeric)

Monitoring & Reports

cspctl reports --app <app> --env <env>

Stream CSP violation reports (real-time)

Default app: default-app, env: prod
--filter <pattern> - Filter by directive, URL, or app name
--group-by <field> - Group by: directive, host, path
--pii-mode <mode> - PII handling: none, redact (default: redact)
--full - Show full PII (deprecated, use --pii-mode none)
--json - Output as JSON
Streams up to 10 report groups then stops
cspctl suggestions --app <app> --env <env>

Get AI-powered policy improvement suggestions

Analyzes violation patterns and suggests sources to add
Includes confidence scores and reasoning
--apply - Apply suggestions to current policy
--json - Output as JSON

Deployment & Rollout

cspctl rollout dry-run --app <app> [--env <env>] -f <file>

Test policy impact before deployment (returns mock data)

--no-fail - Don't exit with code 10 if more than 20 requests would be blocked
--json - Output report as JSON
Reports: blocked count, top hosts, risky tokens like 'unsafe-inline'
cspctl rollout set --app <app> [--env <env>] --mode <mode> [--traffic <%>]

Configure rollout settings

Default env: prod
Default traffic: 100 (percent)
Mode values: any string (e.g., enforce, report-only, disabled)
--json - Output result as JSON

User Interface

cspctl ui [--port <port>]

Launch the web-based management UI

Default port: 5173
Note: Requires binary built with --features ui
cspctl tui

Launch the terminal UI (Interactive mode)

Commands: All CLI commands work interactively
Special commands: help, exit, quit, :q
Features: Command completion, history, colored output
REST API

REST API Reference

HTTP API for violation reporting and policy management

Authentication

API requests can include Bearer token authentication when available. The client API supports both authenticated and unauthenticated requests:

Authorization: Bearer YOUR_API_TOKEN

Token Configuration

The API client looks for tokens in this order:
1. CSPCTL_TOKEN environment variable
2. token field in config file (~/.config/cspctl/config.toml)
# Set via environment
export CSPCTL_TOKEN=your_token_here
# Set via CLI
cspctl config set token your_token_here

Base URL

Default: http://127.0.0.1:5173
Override with: cspctl config set base https://api.example.com

Offline Support

The client caches policies and rollout configurations locally. When offline, it falls back to cached data and displays a warning message.

Endpoints

GET/api/policy?appId={app_id}&env={env}

Retrieve current policy for an application and environment

Query Parameters:
  • appId - Application ID (required)
  • env - Environment name (required)
Response (PolicyBundle):
{
"policy": {
"default_src": ["'self'"],
"script_src": ["'self'", "cdn.example.com"],
"style_src": ["'self'"],
"img_src": [],
"connect_src": [],
"font_src": [],
"frame_src": [],
"object_src": [],
"base_uri": ["'none'"],
"report_to": "default"
},
"id": "policy_123abc"
}
GET/api/apps

List all applications

Response:
{
"apps": [
{
"id": "app_123",
"name": "My Application"
}
]
}
Also accepts raw array format
POST/api/apps

Create a new application

Request Body:
{
"name": "My New Application"
}
Response:
{
"id": "app_456",
"name": "My New Application"
}
STREAM/api/reports/stream

Stream CSP violation reports (real-time)

Query Parameters:
  • app - Application ID (required)
  • env - Environment name (required)
  • filter - Filter pattern (optional)
  • group_by - Group by: directive, host, path (default: directive)
  • pii_mode - PII handling: redact (default), full
Response Stream:
{
"key": "script-src", // Group key
"count": 5 // Number of violations
}
// Individual events include:
// ts, url, blocked, app_id, app_name, ip
GET/api/rollout?appId={app_id}&env={env}

Get rollout configuration

Response:
{
"mode": "enforce",
"traffic": 100
}
POST/api/rollout

Configure rollout settings

Request Body:
{
"appId": "my-app",
"env": "prod",
"mode": "enforce", // Any string value
"traffic": 25 // 0-100 percent
}
GET/api/suggestions?appId={app_id}&env={env}

Get AI-powered policy suggestions

Response (array of Suggestion):
[
{
"directive": "script-src",
"source": "'strict-dynamic'",
"count": 10,
"confidence": 0.9,
"reason": "Improve resilience"
},
{
"directive": "script-src",
"source": "'nonce-…'",
"count": 5,
"confidence": 0.8,
"reason": "Lock down inline"
}
]
POST/api/apply-suggestions

Apply suggestions to a policy

Request Body:
{
"policyId": "policy_123abc",
"suggestions": [
{
"directive": "script-src",
"source": "'strict-dynamic'",
"count": 10,
"confidence": 0.9,
"reason": "Improve resilience"
}
]
}
Response:
{
"id": "policy_456def"
}
POST/api/rollout/dry-run

Simulate policy deployment impact

Request Body:
{
"app": "my-app",
"env": "prod",
"candidate": {
"default_src": ["'self'"],
"script_src": ["'self'"]
}
}
Response:
{
"blocked": 42,
"top_hosts": [
["cdn.bad", 21],
["img.bad", 10]
],
"risky": ["'unsafe-inline'"]
}

Rate Limits

  • 1000 requests per hour for read operations
  • 100 requests per hour for write operations
  • Streaming endpoints count as 1 request per connection

Error Responses

401 Unauthorized - Invalid or expired token
403 Forbidden - Insufficient permissions
409 Conflict - Version conflict, pull latest
429 Too Many Requests - Rate limit exceeded

SDK Support

Node.js / TypeScript

npm install @cspctl/sdk

Python

pip install cspctl-sdk

Rust

cargo add cspctl-client
Policies

Policy Configuration

Learn how to configure and manage CSP policies

Policy File Format

CSPctl uses JSON files to define policies. Each directive accepts an array of source expressions. Only the fields shown below are supported by the current Policy struct.

{
"default_src": ["'self'"],
"script_src": [
"'self'",
"'nonce-{NONCE}'",
"https://cdn.jsdelivr.net",
"https://unpkg.com"
],
"style_src": [
"'self'",
"'unsafe-inline'",
"https://fonts.googleapis.com"
],
"img_src": [
"'self'",
"data:",
"https:",
"blob:"
],
"font_src": [
"'self'",
"https://fonts.gstatic.com"
],
"connect_src": [
"'self'",
"https://api.example.com",
"wss://realtime.example.com"
],
"frame_src": ["'none'"],
"object_src": ["'none'"],
"base_uri": ["'self'"],
"form_action": ["'self'"],
"frame_ancestors": ["'none'"],
"upgrade_insecure_requests": true,
"block_all_mixed_content": true
}
Actually Supported Fields:
The current Policy struct only supports these fields (all Vec<String> except report_to):
default_src
script_src
style_src
img_src
connect_src
font_src
frame_src
object_src
base_uri
report_to (Option<String>)
Other CSP directives like form_action, frame_ancestors are not yet supported.

Source Expressions

Keywords

  • 'self' - Same origin
  • 'none' - Block all
  • 'unsafe-inline' - Inline scripts/styles
  • 'unsafe-eval' - eval() and similar
  • 'strict-dynamic' - Trust script loads
  • 'unsafe-hashes' - Event handlers

Schemes & Hosts

  • https: - Any HTTPS source
  • data: - Data URIs
  • blob: - Blob URLs
  • *.example.com - Wildcard subdomain
  • example.com:8080 - Specific port

Security Best Practices

Use Nonces or Hashes

Instead of 'unsafe-inline', use nonces or SHA hashes for inline scripts

Avoid 'unsafe-eval'

Refactor code to avoid eval(), Function(), and setTimeout with strings

Specify Exact Sources

Use specific domains instead of broad wildcards or scheme-only sources

Use HTTPS

Always use HTTPS sources and enable upgrade-insecure-requests

Test with Report-Only

Deploy in report-only mode first to identify issues before enforcement

Common Patterns

SPA with CDN

{
"default_src": ["'self'"],
"script_src": ["'self'", "https://cdn.jsdelivr.net"],
"style_src": ["'self'", "'unsafe-inline'"],
"connect_src": ["'self'", "https://api.example.com"]
}

E-commerce Site

{
"default_src": ["'self'"],
"script_src": ["'self'", "https://checkout.stripe.com"],
"frame_src": ["https://checkout.stripe.com"],
"connect_src": ["'self'", "https://api.stripe.com"]
}

Marketing Site with Analytics

{
"default_src": ["'self'"],
"script_src": ["'self'", "https://www.google-analytics.com"],
"img_src": ["'self'", "https://www.google-analytics.com"],
"connect_src": ["'self'", "https://www.google-analytics.com"]
}
Advanced

Advanced Topics

Deep dive into advanced CSP features and techniques

Gradual Rollout

Deploy policies gradually to minimize risk and monitor impact on real traffic. CSPctl supports flexible rollout configurations with traffic percentage and mode controls.

Rollout Configuration

mode: Any string value (commonly: "enforce", "report-only", "disabled")
traffic: Percentage from 0-100
• Configuration is cached locally with offline fallback
# Check current rollout status
cspctl rollout get --app myapp --env prod
# Start with report-only mode
cspctl rollout set --app myapp --env prod --mode report-only --traffic 100
# After validation, enforce for 25% of traffic
cspctl rollout set --app myapp --env prod --mode enforce --traffic 25
# Gradually increase to full rollout
cspctl rollout set --app myapp --env prod --mode enforce --traffic 50
cspctl rollout set --app myapp --env prod --mode enforce --traffic 100

Dry Run Analysis

# Test policy impact before deployment
cspctl rollout dry-run --app myapp --env prod -f new-policy.json
# Returns mock data: blocked count, top hosts, risky tokens
# Exit code 10 if more than 20 requests would be blocked (use --no-fail to override)

Nonce Generation

Use cryptographic nonces for inline scripts without unsafe-inline. CSPctl includes built-in nonce generation using Rust's secure random number generator.

Generate Nonces with CSPctl

# Generate 16-character alphanumeric nonce (default)
cspctl nonce
# Generate custom length nonce
cspctl nonce --len 24
# Render header with automatic nonce
cspctl header render -f policy.json
Uses rand::thread_rng().sample_iter(&Alphanumeric) for secure generation.

Server-side Integration (Node.js)

const crypto = require('crypto');
app.use((req, res, next) => {
res.locals.nonce = crypto.randomBytes(16).toString('base64');
res.setHeader(
'Content-Security-Policy',
`script-src 'nonce-${res.locals.nonce}'`
);
next();
});
// In your HTML template
<script nonce="<%= nonce %>">
// Your inline script
</script>

Subresource Integrity (SRI)

Ensure third-party resources haven't been tampered with.

# Generate SRI hash for files
cspctl sri --path app.js,style.css --algo sha384
# Use in HTML
<script src="app.js" integrity="sha384-..."></script>

Report Collection

Set up an endpoint to collect CSP violation reports.

// Express.js endpoint
app.post('/csp-report', express.json({ type: 'application/csp-report' }),
(req, res) => {
const report = req.body['csp-report'];
// Send to CSPctl
fetch('https://api.cspctl.com/v1/reports', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(report)
});
res.status(204).send();
}
);

Integration Examples

Node.js / Express with CSPctl SDK

import express from 'express'
import { nonceMiddleware, cspMiddleware, setConfig } from '@cspctl/node-sdk'
// Configure CSPctl SDK
setConfig({
base: process.env.CSPCTL_BASE || 'http://127.0.0.1:5173',
appId: process.env.CSPCTL_APP_ID || 'myapp',
env: process.env.CSPCTL_ENV || 'prod',
token: process.env.CSPCTL_TOKEN,
refreshSecs: 10, // Refresh policy every 10 seconds
failOpenSecs: 300, // Use cache for 5 mins if API is down
useSse: true // Use Server-Sent Events for real-time updates
})
const app = express()
// Add nonce generation middleware
app.use(nonceMiddleware())
// Add CSP header middleware with automatic policy fetching
app.use(cspMiddleware({
mode: 'report-only', // Start with report-only
strictDynamic: true // Add 'strict-dynamic' to script-src
}))
app.get('/', (req, res) => {
const nonce = res.locals.cspNonce
res.send(`
<script nonce="${nonce}">
console.log('This script is CSP-protected!')
</script>
`)
})
app.listen(3000)

Rust / Axum with CSPctl SDK

use axum::{Router, middleware, routing::get};
use cspctl_rust_sdk::{csp_layer, set_csp_config, CspSdkConfig};
#[tokio::main]
async fn main() {
// Configure CSPctl SDK
set_csp_config(CspSdkConfig {
base: std::env::var("CSPCTL_BASE").ok(),
app_id: std::env::var("CSPCTL_APP_ID").ok(),
env: std::env::var("CSPCTL_ENV").ok(),
token: std::env::var("CSPCTL_TOKEN").ok(),
refresh_secs: 10,
fail_open_secs: 300,
use_sse: true,
});
// Build app with CSP middleware
let app = Router::new()
.route("/", get(handler))
.layer(middleware::from_fn(csp_layer));
// Start server
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn handler(req: axum::http::Request<Body>) -> Html<String> {
let nonce = nonce_from_request(&req).unwrap_or_default();
Html(format!(
r#"<script nonce="{}">console.log('Protected!');</script>"#,
nonce
))
}

Next.js

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export async function middleware(request: NextRequest) {
const response = NextResponse.next()
// Generate nonce
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
// Fetch policy from CSPctl API
const policy = await fetch(
`${process.env.CSPCTL_BASE}/api/policy?appId=${process.env.CSPCTL_APP_ID}&env=prod`
).then(r => r.json())
// Render CSP header with nonce
const cspHeader = renderPolicy(policy, { nonce, strictDynamic: true })
response.headers.set('Content-Security-Policy', cspHeader)
response.headers.set('X-CSP-Nonce', nonce)
return response
}

Nginx

# nginx.conf
location / {
add_header Content-Security-Policy
"default-src 'self'; script-src 'self' 'nonce-$request_id'";
# Pass nonce to application
proxy_set_header X-CSP-Nonce $request_id;
proxy_pass http://upstream;
}

Cloudflare Workers

addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const response = await fetch(request)
const newResponse = new Response(response.body, response)
// Fetch policy from CSPctl
const policy = await getPolicy()
newResponse.headers.set('Content-Security-Policy', policy)
return newResponse
}
Deployment

Deployment & CI/CD

Integrate CSPctl into your deployment pipeline

GitHub Actions

name: CSP Management
on:
pull_request:
paths:
- 'csp-policies/**'
push:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install CSPctl
run: |
curl -fsSL https://files.cspctl.com/uploads/releases/v0.3.3-dev/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Validate Policy
run: |
cspctl policy validate -f csp-policies/production.json
- name: Dry Run Analysis
run: |
cspctl rollout dry-run \
--app ${{ vars.APP_ID }} \
--env staging \
-f csp-policies/production.json
- name: Get AI Suggestions
if: github.event_name == 'pull_request'
run: |
cspctl suggestions \
--app ${{ vars.APP_ID }} \
--env staging \
--output suggestions.json
# Post suggestions as PR comment
cat suggestions.json
- name: Progressive Rollout
if: github.ref == 'refs/heads/main'
run: |
# Deploy to staging first
cspctl policy push \
--app ${{ vars.APP_ID }} \
--env staging \
-f csp-policies/production.json
# Start production rollout at 10%
cspctl rollout set \
--app ${{ vars.APP_ID }} \
--env production \
--mode enforce \
--traffic 10
# Monitor for 5 minutes
sleep 300
# Check violation rate
VIOLATIONS=$(cspctl reports \
--app ${{ vars.APP_ID }} \
--env production \
--json | jq '.count')
if [ "$VIOLATIONS" -lt 100 ]; then
# Increase to 50%
cspctl rollout set \
--app ${{ vars.APP_ID }} \
--env production \
--traffic 50
fi
Use Cases

Complete Workflows

End-to-end examples of common CSPctl workflows

New Project Setup

Setting up CSP for a new project requires careful planning and iterative refinement. This guide walks you through the complete process from initial policy creation to production deployment.

Phase 1: Initial Policy Creation

Start with a restrictive baseline policy. It's easier to loosen restrictions based on actual needs than to tighten them after deployment.

# Initialize with secure defaults
cspctl init
# This creates csp.json with:
# - default-src: ['self']
# - script-src: ['self']
# - base-uri: ['none']
# - object-src: ['none']
💡 Pro Tip: Always start with 'self' only and add external sources as needed. This approach reveals exactly what external resources your application depends on.

Phase 2: Visual Policy Customization

Use the interactive UI to understand and modify your policy. The visual editor helps prevent syntax errors and provides immediate feedback on policy implications.

# Launch the web UI on port 5173
cspctl ui
# Navigate to http://localhost:5173
# The UI provides:
# - Visual policy editor with syntax highlighting
# - Real-time validation
# - Directive documentation
# - Common pattern templates
Common Additions for Modern Apps:
  • • CDN for assets: script-src 'self' cdn.jsdelivr.net
  • • Analytics: connect-src 'self' *.google-analytics.com
  • • Fonts: font-src 'self' fonts.googleapis.com
  • • Images: img-src 'self' data: https:
Security Considerations:
  • • Avoid 'unsafe-inline' - use nonces instead
  • • Avoid 'unsafe-eval' - refactor code if needed
  • • Never use * as a source
  • • Always specify https: for external sources

Phase 3: Impact Analysis

Before deploying, analyze how your policy would affect your application. The dry-run feature simulates deployment and predicts potential issues.

# Run impact analysis
cspctl rollout dry-run --app myapp -f csp.json
# Example output:
# {
# "blocked": 42,
# "top_hosts": [
# ["cdn.unauthorized.com", 21],
# ["tracking.adnetwork.com", 10]
# ],
# "risky": ["'unsafe-inline' in script-src"]
# }
⚠️ Warning Thresholds:
  • • More than 20 blocked requests: Policy may be too restrictive
  • • Risky tokens detected: Security vulnerability potential
  • • Unknown hosts in top_hosts: Possible third-party dependencies

Phase 4: Local Testing

Generate the actual CSP header and test it locally with your development server before deployment.

# Generate CSP header with strict-dynamic and nonce
cspctl header render -f csp.json --strict-dynamic
# Output example:
# default-src 'self'; script-src 'self' 'nonce-Nc3n83cnSAd' 'strict-dynamic';
# style-src 'self' 'unsafe-inline'; base-uri 'none'; object-src 'none'
Testing in Your Application:
// Express.js example
app.use((req, res, next) => {
// Generate nonce for each request
const nonce = crypto.randomBytes(16).toString('base64');
// Apply CSP header
res.setHeader(
'Content-Security-Policy-Report-Only',
`default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`
);
// Make nonce available to templates
res.locals.nonce = nonce;
next();
});

Phase 5: Staged Deployment

Deploy your policy gradually, starting with report-only mode to collect violation data without breaking functionality.

# Step 1: Deploy to staging in report-only mode
cspctl policy push --app myapp-staging -f csp.json
cspctl rollout set --app myapp-staging --mode report-only --traffic 100
# Step 2: Monitor for 24-48 hours
cspctl reports --app myapp-staging --env prod
# Watch for unexpected violations
# Step 3: Analyze and adjust
cspctl policy suggest --app myapp-staging --env prod --export suggestions.json
# Review suggestions and update policy
# Step 4: Progressive production rollout
cspctl policy push --app myapp -f csp.json
cspctl rollout set --app myapp --mode enforce --traffic 10 # 10% of users
# Monitor for issues...
cspctl rollout set --app myapp --mode enforce --traffic 50 # 50% of users
# Continue monitoring...
cspctl rollout set --app myapp --mode enforce --traffic 100 # Full deployment
Success Metrics

Your CSP deployment is successful when:

  • ✓ Violation rate drops below 0.1% of page loads
  • ✓ No legitimate functionality is blocked
  • ✓ All third-party dependencies are explicitly allowed
  • ✓ No unsafe-inline or unsafe-eval directives remain
  • ✓ Monitoring is in place for ongoing violations

Debugging CSP Violations

CSP violations are your security policy working as intended, but they can also indicate legitimate resources being blocked. This guide helps you investigate, understand, and resolve violations systematically.

Understanding Violation Reports

Each violation report contains crucial information about what was blocked and why. Understanding these reports is key to refining your policy.

Anatomy of a Violation Report:
{
"blocked-uri": "https://evil-script.com/malware.js",
"violated-directive": "script-src",
"effective-directive": "script-src",
"original-policy": "default-src 'self'; script-src 'self'",
"document-uri": "https://myapp.com/dashboard",
"referrer": "https://myapp.com/login",
"status-code": 200,
"source-file": "https://myapp.com/js/app.js",
"line-number": 142,
"column-number": 15
}
🚨 Critical Fields
  • blocked-uri: What resource was blocked
  • violated-directive: Which CSP rule was violated
  • document-uri: Where the violation occurred
🔍 Debug Fields
  • source-file: Which script triggered the violation
  • line-number: Exact location in code
  • referrer: Previous page (useful for flows)

Step 1: Real-Time Monitoring

Start by streaming violations in real-time to understand the current state of your application.

# Stream all violations as they happen
cspctl reports --app myapp --env prod
# Example output (grouped by directive):
# script-src: 42 violations
# img-src: 15 violations
# connect-src: 8 violations
# font-src: 2 violations
⚡ Quick Triage:
  • • High volume from single source = Likely legitimate dependency
  • • Random/varied sources = Possible injection attempts
  • • Sudden spike = New feature or attack

Step 2: Pattern Analysis

Group and filter violations to identify patterns and prioritize fixes.

Filter by Directive Type:
# Focus on script violations (most critical)
cspctl reports --app myapp --filter script-src
# Common script-src violations:
# - Third-party analytics (Google Analytics, Mixpanel)
# - Chat widgets (Intercom, Zendesk)
# - Payment processors (Stripe, PayPal)
# - CDN libraries not explicitly allowed
Group by Host to Find Patterns:
# See which hosts are most frequently blocked
cspctl reports --app myapp --group-by host
# Example output:
# www.googletagmanager.com: 156 violations
# fonts.googleapis.com: 89 violations
# cdn.segment.com: 45 violations
# unknown-tracker.com: 2 violations <-- Suspicious!
Group by Path for Specific Pages:
# Identify which pages have the most violations
cspctl reports --app myapp --group-by path
# Helps identify:
# - Pages with unique dependencies
# - Legacy code that needs updating
# - Third-party integrations on specific routes

Step 3: Deep Investigation

For complex violations, export detailed reports for analysis.

# Export full violation details (includes PII)
cspctl reports --app myapp --full --json > violations-full.json
# Analyze with jq
cat violations-full.json | jq '.[] | select(.blocked | contains("googleapis"))'
# Count unique blocked URIs
cat violations-full.json | jq -r '.blocked' | sort | uniq -c | sort -rn
🔐 Privacy Note: The --full flag includes IP addresses and full URLs with query parameters. Handle this data according to your privacy policies and regulations.

Step 4: AI-Powered Resolution

Leverage AI suggestions to automatically identify safe additions to your policy based on violation patterns.

# Get AI suggestions based on violations
cspctl policy suggest --app myapp --env prod
# Example suggestions:
# [
# {
# "directive": "script-src",
# "source": "https://www.googletagmanager.com",
# "confidence": 0.95,
# "reason": "Consistent violations from analytics setup"
# },
# {
# "directive": "font-src",
# "source": "https://fonts.gstatic.com",
# "confidence": 0.98,
# "reason": "Google Fonts dependency detected"
# }
# ]
High Confidence (>0.9)

Usually safe to add. Common services, CDNs, and well-known dependencies.

Low Confidence (<0.7)

Review manually. Could be tracking scripts, compromised resources, or attacks.

Step 5: Test and Deploy Fixes

Before applying changes, test the impact and deploy gradually.

# 1. Export current and suggested policies
cspctl policy pull --app myapp -f current.json
cspctl policy suggest --app myapp --export suggested.json
# 2. Review the differences
diff -u current.json suggested.json
# 3. Test the new policy impact
cspctl rollout dry-run --app myapp -f suggested.json
# 4. If safe (blocked count doesn't increase), apply changes
cspctl policy suggest --app myapp --apply
cspctl policy push --app myapp -f csp.json
# 5. Deploy with gradual rollout
cspctl rollout set --app myapp --mode enforce --traffic 10
# Monitor for new violations...
cspctl rollout set --app myapp --mode enforce --traffic 100
🎯 Common Violation Resolutions
Inline Scripts/Styles:

Move to external files or use nonces. Never add 'unsafe-inline'.

Third-party Services:

Add specific domains, not wildcards. Consider self-hosting critical resources.

Dynamic Script Loading:

Use 'strict-dynamic' with nonces for legitimate dynamic scripts.

Data URIs:

Add 'data:' to img-src for base64 images, but be cautious with other directives.

Implementing Subresource Integrity

Subresource Integrity (SRI) ensures that resources your application fetches (like scripts or stylesheets) haven't been tampered with. It's your last line of defense against compromised CDNs, supply chain attacks, and man-in-the-middle modifications.

Understanding SRI Protection

SRI works by comparing a cryptographic hash of the fetched resource against a known good hash. If they don't match, the browser blocks the resource, preventing potentially malicious code from executing.

✅ Protected Against
  • • CDN compromises (e.g., CDN gets hacked)
  • • Supply chain attacks (dependency tampering)
  • • MITM attacks (network-level modification)
  • • Accidental CDN updates breaking your app
⚠️ Limitations
  • • Only works for CORS-enabled resources
  • • Requires updating hashes when files change
  • • Can't protect dynamically generated content
  • • No protection for first-party resources

Step 1: Generate SRI Hashes

CSPctl can generate SRI hashes for your static resources. Choose your hash algorithm based on security needs (sha384 recommended, sha512 for critical resources).

# Generate hashes for specific files
cspctl sri --path dist/js/app.js,dist/js/vendor.js --algo sha384
# Output:
# dist/js/app.js sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC
# dist/js/vendor.js sha384-MBO9IDwOL7XdrMp3rJhiP3NOMJdUJ6pFmPwLgW2T7kE4qJphH0RTEgN8w7dGsqrQ
# Generate for all JS files with JSON output (for build automation)
cspctl sri --path "dist/**/*.js" --algo sha384 --json > sri-hashes.json
# Generate multiple algorithm hashes (for fallback support)
cspctl sri --path dist/critical.js --algo sha256,sha384,sha512
# Output:
# dist/critical.js sha256-RmX... sha384-oqV... sha512-9/u...
💡 Algorithm Choice:
  • sha256: Fastest, adequate for most resources
  • sha384: Best balance of security and performance (recommended)
  • sha512: Maximum security for critical resources

Step 2: Implement in HTML

Add the integrity attribute to your script and link tags. Always include crossorigin="anonymous" for resources loaded from different origins.

Basic Implementation:
<!-- JavaScript with SRI -->
<script
src="https://cdn.example.com/app.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
></script>
<!-- CSS with SRI -->
<link
rel="stylesheet"
href="https://cdn.example.com/styles.css"
integrity="sha384-MBO9IDwOL7XdrMp3rJhiP3NOMJdUJ6pFmPwLgW2T7kE4qJphH0RTEgN8w7dGsqrQ"
crossorigin="anonymous"
>
<!-- Multiple hashes for browser compatibility -->
<script
src="https://cdn.example.com/critical.js"
integrity="sha256-RmX... sha384-oqV... sha512-9/u..."
crossorigin="anonymous"
></script>
Fallback Strategy:
<!-- Primary CDN with SRI -->
<script
src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"
integrity="sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK"
crossorigin="anonymous"
></script>
<!-- Fallback to local copy if CDN fails integrity check -->
<script>
// Check if jQuery loaded successfully
if (typeof jQuery === 'undefined') {
document.write('<script src="/js/vendor/jquery.min.js"><\/script>');
}
</script>

Step 3: Automate in Build Pipeline

Integrate SRI generation into your build process to automatically update hashes when files change. This ensures your integrity checks always match your current resources.

// webpack.config.js - Webpack SRI Plugin
const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity');
module.exports = {
output: {
crossOriginLoading: 'anonymous',
},
plugins: [
new SubresourceIntegrityPlugin({
hashFuncNames: ['sha384'],
enabled: process.env.NODE_ENV === 'production',
}),
],
};
// build.js - Custom build script
const { execSync } = require('child_process');
const fs = require('fs');
// Generate SRI hashes after build
const sriOutput = execSync(
'cspctl sri --path dist/**/*.js --algo sha384 --json'
).toString();
const sriHashes = JSON.parse(sriOutput);
// Update HTML templates with SRI hashes
let html = fs.readFileSync('dist/index.html', 'utf8');
for (const [file, hash] of Object.entries(sriHashes)) {
const filename = file.split('/').pop();
html = html.replace(
new RegExp(`src="[^"]*${filename}"`, 'g'),
`src="/${file}" integrity="${hash}" crossorigin="anonymous"`
);
}
fs.writeFileSync('dist/index.html', html);

Step 4: Monitor and Maintain

SRI requires ongoing maintenance as your dependencies update. Set up monitoring to detect when resources fail integrity checks.

// sri-monitor.js - Monitor for SRI failures
window.addEventListener('error', (e) => {
if (e.target && e.target.integrity) {
// SRI failure detected
console.error('SRI failure:', e.target.src || e.target.href);
// Report to monitoring service
fetch('/api/sri-failure', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
resource: e.target.src || e.target.href,
integrity: e.target.integrity,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
}),
});
// Attempt fallback if configured
const fallbackUrl = e.target.dataset.fallback;
if (fallbackUrl) {
const newElement = e.target.cloneNode();
newElement.src = fallbackUrl;
newElement.integrity = e.target.dataset.fallbackIntegrity;
e.target.parentNode.replaceChild(newElement, e.target);
}
}
}, true);
🚀 Advanced SRI Strategies
Dynamic Resource Loading:

For dynamically loaded resources, store SRI hashes in a manifest and verify before loading:

// Dynamic loading with SRI
async function loadScript(url, hash) {
const script = document.createElement('script');
script.src = url;
script.integrity = hash;
script.crossOrigin = 'anonymous';
document.head.appendChild(script);
return new Promise((resolve, reject) => {
script.onload = resolve;
script.onerror = reject;
});
}
Version Pinning Strategy:

Always use exact versions in CDN URLs. Never use "latest" or version ranges with SRI:

  • ✅ Good: https://cdn.../jquery@3.6.0/...
  • ❌ Bad: https://cdn.../jquery@latest/...
CSP Integration:

Combine SRI with CSP's require-sri-for directive to enforce integrity checking:

require-sri-for script style;

Offline Development

CSPctl provides robust offline capabilities through intelligent caching, allowing you to continue working without internet connectivity. This is crucial for airplane coding, restricted environments, or when your API endpoints are temporarily unavailable.

How Offline Mode Works

Every time you interact with remote policies, CSPctl automatically caches them locally. When network connectivity is lost, the CLI seamlessly switches to cached data, ensuring uninterrupted workflow.

📁 Cache Structure
~/.cspctl/cache/
├── {app}/
├── {env}/
├── policy.json
├── reports.json
├── metadata.json
└── .timestamp
✨ Cached Operations
  • • Policy retrieval and viewing
  • • Report analysis (historical data)
  • • Header generation
  • • Dry-run simulations
  • • SRI hash generation
  • • Local policy validation

Step 1: Building Your Cache

Before going offline, ensure you have the latest data cached. CSPctl automatically caches on every network operation, but you can also explicitly prepare for offline work.

# Prepare for offline work - cache all environments
for env in dev staging prod; do
echo "Caching $env environment..."
cspctl policy pull --app myapp --env $env -f /tmp/cache-$env.json
cspctl reports --app myapp --env $env --limit 1000 --json > /dev/null
done
# Verify cache is populated
ls -la ~/.cspctl/cache/myapp/
# dev/
# staging/
# prod/
# Check cache freshness
find ~/.cspctl/cache -name ".timestamp" -exec sh -c 'echo "{}:"; cat {}' \;
# Shows when each cache was last updated
💡 Pro Tip: Run a cache refresh script before long flights or entering restricted environments. Cache remains valid for 7 days by default.

Step 2: Working Offline

When offline, CSPctl automatically detects the lack of connectivity and switches to cached mode. You'll see notifications about offline status but can continue working normally.

Policy Operations:
# Pull policy (uses cache when offline)
cspctl policy pull --app myapp -f policy.json
# ℹ️ Offline mode: using cached policy from 2024-01-15 14:30:22
# View policy details
cspctl policy show -f policy.json
# Generate headers from cached policy
cspctl header render -f policy.json
# Content-Security-Policy: default-src 'self'; script-src 'self' 'strict-dynamic';
# Validate policy syntax (fully offline)
cspctl policy validate -f policy.json
# ✅ Policy validation passed
Report Analysis:
# Analyze cached reports
cspctl reports --app myapp --env prod --offline
# ℹ️ Using cached reports (1,247 violations from last sync)
# Filter and analyze offline
cspctl reports --app myapp --filter script-src --group-by host --offline
# Grouped analysis from cached data...
# Export for deeper analysis
cspctl reports --app myapp --json --offline > analysis.json
jq '.[] | select(.directive == "img-src")' analysis.json
Testing and Simulation:
# Run dry-run simulations offline
cspctl rollout dry-run --app myapp -f new-policy.json --offline
# Simulating based on cached violation patterns...
# Estimated impact: 23 additional blocks
# Generate SRI hashes (fully offline)
cspctl sri --path dist/**/*.js --algo sha384
# Works entirely offline - no network needed
# Test policy changes locally
cspctl policy diff -f old.json -f new.json
# Shows changes between two local policy files

Step 3: Advanced Offline Workflows

Leverage offline mode for complex development scenarios, including policy development, testing, and preparation for deployment.

#!/bin/bash
# offline-dev.sh - Complete offline development workflow
# 1. Create development policy from cache
cspctl policy pull --app myapp --env prod -f base.json --offline
cp base.json dev-policy.json
# 2. Modify policy locally
cat dev-policy.json | jq '.directives."script-src" += ["https://new-cdn.com"]' > dev-policy.json
# 3. Validate changes
cspctl policy validate -f dev-policy.json
# 4. Simulate impact using cached violation data
echo "Analyzing policy impact..."
CACHED_REPORTS=$(cat ~/.cspctl/cache/myapp/prod/reports.json)
NEW_BLOCKS=$(echo "$CACHED_REPORTS" | jq '[.[] | select(.blocked_uri | contains("new-cdn.com"))] | length')
echo "Policy would block $NEW_BLOCKS additional requests"
# 5. Generate deployment artifacts
cspctl header render -f dev-policy.json > headers.txt
cspctl policy format -f dev-policy.json > formatted-policy.json
# 6. Create rollout plan
cat > rollout-plan.md << EOF
## Rollout Plan
- Changes: Added new-cdn.com to script-src
- Estimated impact: $NEW_BLOCKS blocks
- Deployment stages:
1. Deploy to staging (report-only)
2. Monitor for 24h
3. Deploy to prod at 10% traffic
4. Gradual increase to 100%
EOF
echo "✅ Offline development complete. Ready to sync when online."

Step 4: Syncing After Offline Work

When connectivity returns, sync your offline changes and refresh your cache with the latest data.

# Check connectivity status
cspctl status
# API: ✅ Connected
# Cache: 3 days old
# Pending changes: 1 policy update
# Push offline changes
cspctl policy push --app myapp -f dev-policy.json
# ✅ Policy updated successfully
# Refresh cache with latest data
cspctl cache refresh --app myapp --all-envs
# Refreshing cache for all environments...
# ✅ Cache updated (dev, staging, prod)
# Verify sync status
cspctl cache status
# myapp/dev: Fresh (&lt; 1 minute)
# myapp/staging: Fresh (&lt; 1 minute)
# myapp/prod: Fresh (&lt; 1 minute)
🎯 Offline Best Practices
Cache Management:
  • • Set up a cron job to refresh cache daily: 0 0 * * * cspctl cache refresh --all
  • • Keep cache size under control: du -sh ~/.cspctl/cache/
  • • Archive old cache before major changes: tar -czf cache-backup.tar.gz ~/.cspctl/cache/
Workflow Integration:
  • • Use --offline flag explicitly in scripts to avoid network attempts
  • • Combine with Git for version control of local policies
  • • Create offline test suites using cached violation data
Emergency Scenarios:

Keep a "break-glass" policy file locally for emergencies:

cp ~/.cspctl/cache/myapp/prod/policy.json ~/emergency-policy-backup.json

CI/CD Automation

Integrate CSPctl into your continuous integration and deployment pipelines to automatically validate, test, and deploy Content Security Policies. This ensures security policies are tested alongside your code changes and prevents unsafe policies from reaching production.

Pipeline Integration Strategy

A comprehensive CI/CD integration validates policies at multiple stages: during development, in pull requests, during staging deployment, and before production release.

🔄 Pipeline Stages
1
Pre-commit: Validate policy syntax and format
2
Pull Request: Run dry-run analysis and generate impact report
3
Staging: Deploy in report-only mode and monitor
4
Production: Progressive rollout with automatic rollback

GitHub Actions Workflow

Complete GitHub Actions workflow for CSP validation, testing, and deployment with automatic rollback capabilities and comprehensive reporting.

name: CSP Pipeline
on:
pull_request:
paths:
- 'csp.json'
- 'src/**'
- 'public/**'
push:
branches: [main, staging]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install CSPctl
run: |
curl -fsSL https://install.cspctl.com/install.sh | sh
echo "$HOME/.cspctl/bin" >> $GITHUB_PATH
- name: Validate Policy Syntax
run: |
cspctl policy validate -f csp.json
echo "✅ Policy syntax valid"
- name: Check for Risky Directives
run: |
RISKY=$(cspctl policy analyze -f csp.json --json | jq '.risky | length')
if [ "$RISKY" -gt 0 ]; then
echo "⚠️ Policy contains risky directives:"
cspctl policy analyze -f csp.json --json | jq '.risky'
exit 1
fi
- name: Generate SRI Hashes
run: |
cspctl sri --path dist/**/*.js --algo sha384 --json > sri-hashes.json
echo "Generated SRI hashes for $(jq 'length' sri-hashes.json) files"
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: csp-artifacts
path: |
csp.json
sri-hashes.json
impact-analysis:
runs-on: ubuntu-latest
needs: validate
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v3
- name: Install CSPctl
run: |
curl -fsSL https://install.cspctl.com/install.sh | sh
echo "$HOME/.cspctl/bin" >> $GITHUB_PATH
- name: Configure CSPctl
env:
CSPCTL_API_KEY: ${{ secrets.CSPCTL_API_KEY }}
run: |
cspctl auth login --api-key $CSPCTL_API_KEY
- name: Run Dry-Run Analysis
id: dryrun
run: |
OUTPUT=$(cspctl rollout dry-run --app ${{ vars.APP_ID }} -f csp.json --json)
echo "blocked=$(echo $OUTPUT | jq '.blocked')" >> $GITHUB_OUTPUT
echo "allowed=$(echo $OUTPUT | jq '.allowed')" >> $GITHUB_OUTPUT
echo "impact=$(echo $OUTPUT | jq '.impact_percentage')" >> $GITHUB_OUTPUT
- name: Generate AI Suggestions
run: |
cspctl policy suggest --app ${{ vars.APP_ID }} --env staging --json > suggestions.json
- name: Comment PR with Analysis
uses: actions/github-script@v6
with:
script: |
const blocked = ${{ steps.dryrun.outputs.blocked }};
const allowed = ${{ steps.dryrun.outputs.allowed }};
const impact = ${{ steps.dryrun.outputs.impact }};
const comment = `## 🔒 CSP Impact Analysis
### Dry-Run Results
- **Blocked Requests**: ${blocked}
- **Allowed Requests**: ${allowed}
- **Impact**: ${impact}% of traffic affected
### Recommendations
${blocked > 100 ? '⚠️ High impact detected. Review blocked resources carefully.' : '✅ Low impact. Safe to proceed.'}
<details>
<summary>View AI Suggestions</summary>
\`\`\`json
${require('fs').readFileSync('suggestions.json', 'utf8')}
\`\`\`
</details>`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
deploy-staging:
runs-on: ubuntu-latest
needs: [validate, impact-analysis]
if: github.ref == 'refs/heads/staging'
environment: staging
steps:
- uses: actions/checkout@v3
- name: Deploy to Staging
env:
CSPCTL_API_KEY: ${{ secrets.CSPCTL_API_KEY }}
run: |
cspctl auth login --api-key $CSPCTL_API_KEY
# Deploy in report-only mode
cspctl policy push --app ${{ vars.APP_ID }} --env staging -f csp.json
cspctl rollout set --app ${{ vars.APP_ID }} --env staging \
--mode report-only --traffic 100
echo "✅ Deployed to staging in report-only mode"
- name: Monitor Initial Violations
run: |
sleep 60 # Wait for initial data
cspctl reports --app ${{ vars.APP_ID }} --env staging \
--limit 100 --json > staging-violations.json
VIOLATION_COUNT=$(jq 'length' staging-violations.json)
echo "📊 Initial violations: $VIOLATION_COUNT"
deploy-production:
runs-on: ubuntu-latest
needs: validate
if: github.ref == 'refs/heads/main'
environment: production
strategy:
max-parallel: 1
steps:
- uses: actions/checkout@v3
- name: Progressive Rollout
env:
CSPCTL_API_KEY: ${{ secrets.CSPCTL_API_KEY }}
run: |
cspctl auth login --api-key $CSPCTL_API_KEY
# Stage 1: 10% traffic
echo "🚀 Stage 1: Deploying to 10% of traffic..."
cspctl policy push --app ${{ vars.APP_ID }} --env prod -f csp.json
cspctl rollout set --app ${{ vars.APP_ID }} --env prod \
--mode enforce --traffic 10
# Monitor for 5 minutes
sleep 300
VIOLATIONS_10=$(cspctl reports --app ${{ vars.APP_ID }} --env prod \
--limit 100 --json | jq 'length')
if [ "$VIOLATIONS_10" -gt 50 ]; then
echo "❌ Too many violations at 10%. Rolling back..."
cspctl rollout set --app ${{ vars.APP_ID }} --env prod \
--mode report-only --traffic 100
exit 1
fi
# Stage 2: 50% traffic
echo "🚀 Stage 2: Expanding to 50% of traffic..."
cspctl rollout set --app ${{ vars.APP_ID }} --env prod \
--mode enforce --traffic 50
sleep 300
VIOLATIONS_50=$(cspctl reports --app ${{ vars.APP_ID }} --env prod \
--limit 100 --json | jq 'length')
if [ "$VIOLATIONS_50" -gt 100 ]; then
echo "❌ Too many violations at 50%. Rolling back..."
cspctl rollout set --app ${{ vars.APP_ID }} --env prod \
--mode enforce --traffic 10
exit 1
fi
# Stage 3: 100% traffic
echo "🚀 Stage 3: Full deployment..."
cspctl rollout set --app ${{ vars.APP_ID }} --env prod \
--mode enforce --traffic 100
echo "✅ Successfully deployed to production"

Jenkins Pipeline

Declarative Jenkins pipeline with stages for validation, testing, and deployment with proper error handling and notifications.

pipeline {
agent any
environment {
CSPCTL_API_KEY = credentials('cspctl-api-key')
APP_ID = 'myapp'
}
stages {
stage('Setup') {
steps {
sh '''
curl -fsSL https://install.cspctl.com/install.sh | sh
export PATH="$HOME/.cspctl/bin:$PATH"
cspctl auth login --api-key $CSPCTL_API_KEY
'''
}
}
stage('Validate') {
steps {
script {
def validation = sh(
script: 'cspctl policy validate -f csp.json',
returnStatus: true
)
if (validation != 0) {
error('Policy validation failed')
}
// Check for unsafe directives
def unsafe = sh(
script: "grep -E 'unsafe-inline|unsafe-eval' csp.json",
returnStatus: true
)
if (unsafe == 0) {
unstable('Policy contains unsafe directives')
}
}
}
}
stage('Impact Analysis') {
when {
changeRequest()
}
steps {
script {
def dryRun = sh(
script: "cspctl rollout dry-run --app ${APP_ID} -f csp.json --json",
returnStdout: true
).trim()
def parsed = readJSON text: dryRun
if (parsed.blocked > 100) {
currentBuild.description = "⚠️ High impact: ${parsed.blocked} blocks"
unstable('High CSP impact detected')
} else {
currentBuild.description = "✅ Low impact: ${parsed.blocked} blocks"
}
// Archive analysis
writeFile file: 'dry-run-analysis.json', text: dryRun
archiveArtifacts artifacts: 'dry-run-analysis.json'
}
}
}
stage('Generate SRI') {
steps {
sh '''
cspctl sri --path dist/**/*.js --algo sha384 --json > sri-hashes.json
echo "Generated SRI hashes for:"
jq -r 'keys[]' sri-hashes.json
'''
archiveArtifacts artifacts: 'sri-hashes.json'
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
script {
// Deploy with monitoring
sh """
cspctl policy push --app ${APP_ID} --env prod -f csp.json
cspctl rollout set --app ${APP_ID} --env prod \
--mode report-only --traffic 100
"""
// Wait and check violations
sleep(time: 5, unit: 'MINUTES')
def violations = sh(
script: "cspctl reports --app ${APP_ID} --env prod --json | jq 'length'",
returnStdout: true
).trim().toInteger()
if (violations &lt; 50) {
sh """
cspctl rollout set --app ${APP_ID} --env prod \
--mode enforce --traffic 10
"""
echo "Deployed to 10% of production traffic"
} else {
error("Too many violations: ${violations}")
}
}
}
}
}
post {
failure {
emailext(
subject: "CSP Pipeline Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Policy deployment failed. Check violations at ${env.BUILD_URL}",
to: 'security-team@example.com'
)
}
success {
slackSend(
color: 'good',
message: "CSP deployed successfully to ${env.BRANCH_NAME}"
)
}
}
}

GitLab CI Configuration

GitLab CI/CD pipeline with parallel jobs, caching, and environment-specific deployments.

.gitlab-ci.yml
variables:
APP_ID: "myapp"
CSPCTL_VERSION: "latest"
stages:
- validate
- test
- deploy
before_script:
- curl -fsSL https://install.cspctl.com/install.sh | sh
- export PATH="$HOME/.cspctl/bin:$PATH"
- cspctl auth login --api-key $CSPCTL_API_KEY
# Cache CSPctl installation
cache:
key: cspctl-${CSPCTL_VERSION}
paths:
- $HOME/.cspctl/
validate:policy:
stage: validate
script:
- cspctl policy validate -f csp.json
- |
if grep -E 'unsafe-inline|unsafe-eval' csp.json; then
echo "⚠️ Policy contains unsafe directives"
exit 1
fi
artifacts:
reports:
dotenv: csp-validation.env
test:dry-run:
stage: test
script:
- |
OUTPUT=$(cspctl rollout dry-run --app $APP_ID -f csp.json --json)
BLOCKED=$(echo $OUTPUT | jq '.blocked')
echo "CSP_BLOCKED=$BLOCKED" >> impact.env
if [ "$BLOCKED" -gt 100 ]; then
echo "❌ High impact: $BLOCKED blocks expected"
exit 1
fi
artifacts:
reports:
dotenv: impact.env
paths:
- dry-run-report.json
test:sri:
stage: test
parallel:
matrix:
- ALGO: [sha256, sha384, sha512]
script:
- cspctl sri --path dist/**/*.js --algo $ALGO --json > sri-$ALGO.json
artifacts:
paths:
- sri-*.json
deploy:staging:
stage: deploy
environment:
name: staging
url: https://staging.example.com
only:
- staging
script:
- cspctl policy push --app $APP_ID --env staging -f csp.json
- cspctl rollout set --app $APP_ID --env staging --mode report-only --traffic 100
- echo "✅ Deployed to staging"
deploy:production:
stage: deploy
environment:
name: production
url: https://example.com
only:
- main
when: manual
script:
- |
# Progressive rollout with health checks
for TRAFFIC in 10 50 100; do
echo "🚀 Rolling out to $TRAFFIC% of traffic..."
cspctl rollout set --app $APP_ID --env prod --mode enforce --traffic $TRAFFIC
sleep 300 # 5 minutes
VIOLATIONS=$(cspctl reports --app $APP_ID --env prod --json | jq 'length')
if [ "$VIOLATIONS" -gt $((TRAFFIC * 2)) ]; then
echo "❌ Rollback triggered: $VIOLATIONS violations"
cspctl rollout set --app $APP_ID --env prod --mode report-only --traffic 100
exit 1
fi
done
echo "✅ Full production deployment complete"

Monitoring and Alerting

Set up continuous monitoring and alerting for CSP violations in your CI/CD pipeline to catch issues early and automate responses.

#!/bin/bash
# monitor-csp.sh - Continuous monitoring script
set -e
# Configuration
THRESHOLD_CRITICAL=100
THRESHOLD_WARNING=50
CHECK_INTERVAL=300 # 5 minutes
SLACK_WEBHOOK=$SLACK_CSP_WEBHOOK
monitor_violations() {
local env=$1
local violations=$(cspctl reports --app $APP_ID --env $env --json | jq 'length')
local unique_hosts=$(cspctl reports --app $APP_ID --env $env --group-by host --json | jq 'length')
echo "[$(date)] $env: $violations violations from $unique_hosts hosts"
if [ "$violations" -gt "$THRESHOLD_CRITICAL" ]; then
alert_critical "$env" "$violations" "$unique_hosts"
elif [ "$violations" -gt "$THRESHOLD_WARNING" ]; then
alert_warning "$env" "$violations" "$unique_hosts"
fi
# Log metrics for dashboards
echo "csp.violations.$env:$violations|g" | nc -u -w0 127.0.0.1 8125
echo "csp.unique_hosts.$env:$unique_hosts|g" | nc -u -w0 127.0.0.1 8125
}
alert_critical() {
local env=$1
local violations=$2
local hosts=$3
# Send Slack alert
curl -X POST $SLACK_WEBHOOK -H 'Content-Type: application/json' -d "{
"text": "🚨 Critical CSP Alert",
"attachments": [{
"color": "danger",
"fields": [
{"title": "Environment", "value": "$env", "short": true},
{"title": "Violations", "value": "$violations", "short": true},
{"title": "Unique Hosts", "value": "$hosts", "short": true},
{"title": "Action", "value": "Immediate review required", "short": true}
]
}]
}"
# Trigger automatic rollback if in production
if [ "$env" = "prod" ]; then
echo "Triggering automatic rollback..."
cspctl rollout set --app $APP_ID --env prod --mode report-only --traffic 100
fi
}
# Main monitoring loop
while true; do
for env in staging prod; do
monitor_violations $env
done
sleep $CHECK_INTERVAL
done
🚀 CI/CD Best Practices
Branch Protection:

Require CSP validation checks to pass before merging. Set up branch protection rules that block merges if dry-run analysis shows high impact.

Automated Rollback:

Implement automatic rollback triggers based on violation thresholds. If violations spike above normal levels, automatically switch to report-only mode.

Performance Metrics:

Track CSP impact on page load times. Monitor if strict policies cause performance degradation due to blocked resources.

Compliance Tracking:

Use audit logs to track all policy changes for compliance. Generate reports showing policy evolution and security improvements over time.

Team Collaboration

Policy Review Process

  1. Developer creates policy changes in a feature branch
  2. CI validates the policy and runs dry-run analysis
  3. AI suggestions are generated and reviewed
  4. Security team reviews suggested changes
  5. Deploy to staging with report-only mode
  6. Monitor for 24-48 hours with PII-redacted reports
  7. Gradually roll out to production (10% → 50% → 100%)
  8. Audit logs track all changes for compliance

Best Practices for Teams

  • Use CSPctl UI for visual policy editing and collaboration
  • Enable audit logging for all production changes
  • Set up alerts for high violation rates
  • Use offline caching to ensure resilience
  • Implement SRI hashes for all third-party resources
  • Regular reviews of AI-suggested improvements
  • Document rollback procedures for emergencies