Agent skill
secure-headers-csp-builder
Implements security headers and Content Security Policy with safe rollout strategy (report-only → enforce), testing, and compatibility checks. Use for "security headers", "CSP", "HTTP headers", or "XSS protection".
Install this agent skill to your Project
npx add-skill https://github.com/patricio0312rev/skills/tree/main/security/secure-headers-csp-builder
SKILL.md
Secure Headers & CSP Builder
Add security headers safely without breaking functionality.
Essential Security Headers
// middleware/security-headers.ts
import { Request, Response, NextFunction } from "express";
export function securityHeaders(
req: Request,
res: Response,
next: NextFunction
) {
// Prevent clickjacking
res.setHeader("X-Frame-Options", "DENY");
// Prevent MIME sniffing
res.setHeader("X-Content-Type-Options", "nosniff");
// XSS Protection (legacy browsers)
res.setHeader("X-XSS-Protection", "1; mode=block");
// Referrer Policy
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
// Permissions Policy (replaces Feature-Policy)
res.setHeader(
"Permissions-Policy",
"camera=(), microphone=(), geolocation=(self), payment=()"
);
// HSTS - Force HTTPS (only in production)
if (process.env.NODE_ENV === "production") {
res.setHeader(
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains; preload"
);
}
next();
}
Content Security Policy (CSP)
Phase 1: Report-Only Mode
// config/csp-report-only.ts
export const cspReportOnly = {
"default-src": ["'self'"],
"script-src": [
"'self'",
"'report-sample'",
"https://cdn.jsdelivr.net",
"https://www.googletagmanager.com",
],
"style-src": ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
"img-src": ["'self'", "data:", "https:"],
"font-src": ["'self'", "https://fonts.gstatic.com"],
"connect-src": ["'self'", "https://api.example.com"],
"frame-ancestors": ["'none'"],
"base-uri": ["'self'"],
"form-action": ["'self'"],
"report-uri": ["/api/csp-report"],
};
function formatCSP(policy: Record<string, string[]>): string {
return Object.entries(policy)
.map(([key, values]) => `${key} ${values.join(" ")}`)
.join("; ");
}
// Apply report-only header
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy-Report-Only",
formatCSP(cspReportOnly)
);
next();
});
CSP Violation Reporter
// routes/csp-report.ts
app.post(
"/api/csp-report",
express.json({ type: "application/csp-report" }),
(req, res) => {
const violation = req.body["csp-report"];
console.error("CSP Violation:", {
documentUri: violation["document-uri"],
violatedDirective: violation["violated-directive"],
blockedUri: violation["blocked-uri"],
sourceFile: violation["source-file"],
lineNumber: violation["line-number"],
});
// Store in monitoring system
trackCSPViolation({
directive: violation["violated-directive"],
blockedUri: violation["blocked-uri"],
userAgent: req.headers["user-agent"],
timestamp: new Date(),
});
res.status(204).send();
}
);
Phase 2: Enforce Mode
// config/csp-enforce.ts
export const cspEnforce = {
"default-src": ["'self'"],
"script-src": [
"'self'",
// Add nonces for inline scripts
"'nonce-{NONCE}'",
"https://cdn.jsdelivr.net",
"https://www.googletagmanager.com",
],
"style-src": [
"'self'",
// Replace unsafe-inline with nonces
"'nonce-{NONCE}'",
"https://fonts.googleapis.com",
],
"img-src": ["'self'", "data:", "https:"],
"font-src": ["'self'", "https://fonts.gstatic.com"],
"connect-src": ["'self'", "https://api.example.com"],
"frame-ancestors": ["'none'"],
"base-uri": ["'self'"],
"form-action": ["'self'"],
"upgrade-insecure-requests": [],
};
// Generate nonce for each request
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString("base64");
res.locals.cspNonce = nonce;
const policy = formatCSP(cspEnforce).replace(/{NONCE}/g, nonce);
res.setHeader("Content-Security-Policy", policy);
next();
});
Nonce Implementation
// views/index.ejs
<!DOCTYPE html>
<html>
<head>
<!-- Inline script with nonce -->
<script nonce="<%= cspNonce %>">
console.log('This script is allowed by CSP');
</script>
<!-- Inline style with nonce -->
<style nonce="<%= cspNonce %>">
body { background: white; }
</style>
</head>
<body>
<h1>Secure Page</h1>
</body>
</html>
Helmet.js Integration
// Using Helmet for comprehensive security headers
import helmet from "helmet";
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'nonce-{NONCE}'"],
styleSrc: ["'self'", "'nonce-{NONCE}'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.example.com"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
frameguard: {
action: "deny",
},
xssFilter: true,
noSniff: true,
referrerPolicy: {
policy: "strict-origin-when-cross-origin",
},
})
);
Rollout Plan
# CSP Rollout Plan
## Week 1: Report-Only Mode
- [ ] Deploy CSP in report-only mode
- [ ] Monitor violation reports
- [ ] Identify problematic resources
- [ ] Whitelist legitimate sources
## Week 2: Analysis
- [ ] Analyze 1 week of violations
- [ ] Update CSP policy based on reports
- [ ] Fix inline scripts/styles
- [ ] Test on staging
## Week 3: Staged Rollout
- [ ] Enable enforcement for 10% of traffic
- [ ] Monitor error rates
- [ ] Check user reports
- [ ] Adjust policy if needed
## Week 4: Full Enforcement
- [ ] Enable for 50% of traffic
- [ ] Verify no issues
- [ ] Enable for 100% of traffic
- [ ] Keep report-only header for monitoring
Testing CSP
// tests/csp.test.ts
import { describe, it, expect } from "vitest";
import request from "supertest";
import { app } from "../src/app";
describe("Content Security Policy", () => {
it("should set CSP header", async () => {
const response = await request(app).get("/");
expect(response.headers["content-security-policy"]).toBeDefined();
expect(response.headers["content-security-policy"]).toContain(
"default-src 'self'"
);
});
it("should block inline scripts without nonce", async () => {
const html = `
<!DOCTYPE html>
<html>
<head>
<script>alert('blocked')</script>
</head>
</html>
`;
// This would be blocked by CSP
// Verify in browser console or automated tests
});
it("should allow scripts with valid nonce", async () => {
const response = await request(app).get("/");
// Extract nonce from response
const nonceMatch = response.text.match(/nonce="([^"]+)"/);
expect(nonceMatch).toBeDefined();
});
});
Common CSP Issues & Fixes
// Issue 1: Inline event handlers
// ❌ Bad
<button onclick="handleClick()">Click</button>
// ✅ Good
<button id="myButton">Click</button>
<script nonce="<%= cspNonce %>">
document.getElementById('myButton').addEventListener('click', handleClick);
</script>
// Issue 2: Inline styles
// ❌ Bad
<div style="color: red;">Text</div>
// ✅ Good
<style nonce="<%= cspNonce %>">
.red-text { color: red; }
</style>
<div class="red-text">Text</div>
// Issue 3: eval() usage
// ❌ Bad
eval('console.log("test")');
// ✅ Good
// Don't use eval - refactor code
// Issue 4: Third-party scripts
// ❌ Bad - no CSP entry
<script src="https://cdn.example.com/script.js"></script>
// ✅ Good - whitelisted in CSP
script-src: ['self', 'https://cdn.example.com']
Monitoring & Alerts
// monitoring/csp-violations.ts
import { CloudWatch } from "@aws-sdk/client-cloudwatch";
const cloudwatch = new CloudWatch();
export async function trackCSPViolation(violation: {
directive: string;
blockedUri: string;
userAgent: string;
timestamp: Date;
}) {
await cloudwatch.putMetricData({
Namespace: "Security/CSP",
MetricData: [
{
MetricName: "Violations",
Value: 1,
Unit: "Count",
Timestamp: violation.timestamp,
Dimensions: [
{
Name: "Directive",
Value: violation.directive,
},
{
Name: "BlockedUri",
Value: violation.blockedUri,
},
],
},
],
});
// Alert if violations spike
if (await isViolationSpike()) {
await sendAlert({
title: "CSP Violation Spike Detected",
message: `High number of violations for ${violation.directive}`,
});
}
}
Best Practices
- Start report-only: Don't break production
- Gradual rollout: 10% → 50% → 100%
- Use nonces: Better than unsafe-inline
- Monitor violations: Track and analyze
- Test thoroughly: All pages and features
- Document exceptions: Why resources whitelisted
- Regular audits: Quarterly CSP review
Output Checklist
- Security headers implemented
- CSP policy defined (report-only)
- CSP violation reporter endpoint
- Nonce generation for inline scripts
- Helmet.js configured
- Rollout plan documented
- Testing strategy implemented
- Monitoring and alerts configured
- Team trained on CSP
- Staged rollout completed
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
rate-limiting-abuse-protection
Implements rate limiting and abuse prevention with per-route policies, IP/user-based limits, sliding windows, safe error responses, and observability. Use when adding "rate limiting", "API protection", "abuse prevention", or "DDoS protection".
rbac-permissions-builder
Implements role-based access control with permission matrix, route guards, policy functions, and UI permission hints. Provides middleware/guards, helper utilities, test suggestions, and permission checking patterns. Use when building "RBAC", "permissions", "access control", or "authorization".
websocket-realtime-builder
Implements real-time features using WebSockets with Socket.io, rooms, authentication, and reconnection handling. Use when users request "real-time updates", "WebSocket", "Socket.io", "live chat", or "push notifications".
webhook-receiver-hardener
Secures webhook receivers with signature verification, retry handling, deduplication, idempotency keys, and error responses. Provides verification code, dedupe storage strategy, runbook for incidents. Use when implementing "webhooks", "webhook security", "event receivers", or "third-party integrations".
auth-module-builder
Implements secure authentication patterns including login/registration, session management, JWT tokens, password hashing, cookie settings, and CSRF protection. Provides auth routes, middleware, security configurations, and threat model documentation. Use when building "authentication", "login system", "JWT auth", or "session management".
rest-to-graphql-migrator
Migrates REST APIs to GraphQL incrementally with schema stitching, REST datasources, and gradual endpoint migration. Use when users request "migrate to GraphQL", "REST to GraphQL", "GraphQL wrapper", or "API modernization".
Didn't find tool you were looking for?