{"skill":{"slug":"csp-policy-generator","displayName":"CSP Policy Generator","summary":"Generate, validate, and tighten Content Security Policy (CSP) headers for web applications. Analyze existing pages to discover resource origins, build least-...","description":"---\nname: csp-policy-generator\ndescription: Generate, validate, and tighten Content Security Policy (CSP) headers for web applications. Analyze existing pages to discover resource origins, build least-privilege policies, test for violations, and migrate from report-only to enforcing.\n---\n\n# CSP Policy Generator\n\nBuild Content Security Policy headers that actually work. Analyze your web application to discover all resource origins (scripts, styles, images, fonts, frames, APIs), generate a least-privilege CSP, test for violations, and provide a safe migration path from report-only to enforcement.\n\nUse when: \"create CSP header\", \"content security policy\", \"fix CSP violations\", \"tighten CSP\", \"XSS prevention headers\", \"security headers\", or when deploying CSP for the first time.\n\n## Commands\n\n### 1. `generate` — Build CSP from Page Analysis\n\n#### Step 1: Discover Resource Origins\n\n```bash\n# Fetch the page and extract all resource URLs\ncurl -sL \"https://$HOST\" | python3 -c \"\nimport sys, re\nfrom urllib.parse import urlparse\nhtml = sys.stdin.read()\n\nsources = {\n    'script-src': set(),\n    'style-src': set(),\n    'img-src': set(),\n    'font-src': set(),\n    'connect-src': set(),\n    'frame-src': set(),\n    'media-src': set(),\n    'object-src': set(),\n}\n\n# Script sources\nfor m in re.finditer(r'<script[^>]*src=[\\\"\\\\x27]([^\\\"\\\\x27]+)', html):\n    sources['script-src'].add(urlparse(m.group(1)).netloc or \\\"'self'\\\")\n\n# Inline scripts\nif re.search(r'<script(?!.*src)[^>]*>', html):\n    sources['script-src'].add(\\\"'unsafe-inline'\\\")\n\n# Style sources\nfor m in re.finditer(r'<link[^>]*href=[\\\"\\\\x27]([^\\\"\\\\x27]+)[\\\"\\\\x27][^>]*rel=[\\\"\\\\x27]stylesheet', html):\n    sources['style-src'].add(urlparse(m.group(1)).netloc or \\\"'self'\\\")\nfor m in re.finditer(r'style=[\\\"\\\\x27]', html):\n    sources['style-src'].add(\\\"'unsafe-inline'\\\")\n\n# Image sources\nfor m in re.finditer(r'<img[^>]*src=[\\\"\\\\x27]([^\\\"\\\\x27]+)', html):\n    sources['img-src'].add(urlparse(m.group(1)).netloc or \\\"'self'\\\")\n\n# Font sources\nfor m in re.finditer(r'url\\([\\\"\\\\x27]?([^)\\\"\\\\x27]+\\\\.(?:woff2?|ttf|eot|otf))', html):\n    sources['font-src'].add(urlparse(m.group(1)).netloc or \\\"'self'\\\")\n\nfor directive, origins in sources.items():\n    if origins:\n        print(f'{directive}: {\\\" \\\".join(sorted(origins))}')\n\"\n```\n\nAlso check JavaScript files for dynamic resource loading:\n```bash\n# Find fetch/XMLHttpRequest/import targets in JS files\ncurl -sL \"https://$HOST/main.js\" 2>/dev/null | \\\n  rg -o 'fetch\\([\"\\x27]https?://[^\"]*' 2>/dev/null\n```\n\n#### Step 2: Build Least-Privilege Policy\n\nStarting from a deny-all baseline, add only discovered origins:\n\n```\ndefault-src 'none';\nscript-src 'self' [discovered script origins];\nstyle-src 'self' [discovered style origins];\nimg-src 'self' data: [discovered image origins];\nfont-src 'self' [discovered font origins];\nconnect-src 'self' [discovered API origins];\nframe-src [discovered frame origins];\nframe-ancestors 'none';\nbase-uri 'self';\nform-action 'self';\nupgrade-insecure-requests;\n```\n\n#### Step 3: Security Recommendations\n\nFor each directive, flag concerns:\n- `'unsafe-inline'` in script-src → recommend nonce-based approach or hash\n- `'unsafe-eval'` → flag as high risk, identify which library needs it\n- `*` wildcards → replace with specific domains\n- `data:` in script-src → XSS risk\n- Missing `frame-ancestors` → clickjacking risk\n- Missing `upgrade-insecure-requests` → mixed content risk\n\n#### Step 4: Output\n\n```markdown\n# CSP Policy for $HOST\n\n## Recommended Policy (Report-Only — start here)\n```\nContent-Security-Policy-Report-Only: default-src 'none'; script-src 'self' cdn.example.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src 'self' data: images.example.com; font-src 'self' fonts.gstatic.com; connect-src 'self' api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests; report-uri /csp-report\n```\n\n## Enforcement Policy (after monitoring report-only)\n```\nContent-Security-Policy: [same as above without -Report-Only]\n```\n\n## Migration Path\n1. Deploy report-only policy (above)\n2. Monitor /csp-report for 1-2 weeks\n3. Fix any violations found\n4. Switch to enforcing mode\n5. Remove 'unsafe-inline' from style-src (use nonces instead)\n\n## Warnings\n- 🟡 `'unsafe-inline'` in style-src — fix by adding nonces\n- 🟢 No `'unsafe-eval'` — good\n- 🟢 `frame-ancestors 'none'` — clickjacking protected\n```\n\n### 2. `validate` — Test Existing CSP\n\nCheck a live site's CSP for weaknesses:\n```bash\ncurl -sI \"https://$HOST\" | grep -i \"content-security-policy\" 2>&1\n```\n\nParse the policy and flag:\n- Directives with `'unsafe-inline'` or `'unsafe-eval'`\n- Overly broad wildcards (`*.example.com` or `*`)\n- Missing directives (default-src without coverage)\n- report-uri vs report-to configuration\n\n### 3. `nonce` — Generate Nonce-Based CSP Setup\n\nFor frameworks that support it, generate nonce middleware:\n\n```javascript\n// Express middleware example\nconst crypto = require('crypto');\napp.use((req, res, next) => {\n  res.locals.nonce = crypto.randomBytes(16).toString('base64');\n  res.setHeader('Content-Security-Policy',\n    `script-src 'nonce-${res.locals.nonce}' 'strict-dynamic'; style-src 'self' 'nonce-${res.locals.nonce}'`\n  );\n  next();\n});\n```\n\n### 4. `hash` — Generate Hash-Based CSP for Static Sites\n\nFor static sites where nonces aren't practical, hash all inline scripts/styles:\n```bash\n# Hash each inline script\ngrep -oP '(?<=<script>).*?(?=</script>)' index.html | while read script; do\n  echo -n \"$script\" | openssl dgst -sha256 -binary | openssl enc -base64\ndone\n```\n","tags":{"latest":"1.0.1"},"stats":{"comments":0,"downloads":367,"installsAllTime":1,"installsCurrent":1,"stars":0,"versions":2},"createdAt":1777428121833,"updatedAt":1778492789909},"latestVersion":{"version":"1.0.1","createdAt":1777593650892,"changelog":"- No changes detected in files for version 1.0.1.\n- Documentation, commands, and recommendations remain the same as previous version.\n- No new features, fixes, or updates in this release.","license":"MIT-0"},"metadata":null,"owner":{"handle":"charlie-morrison","userId":"s17cttbdxry5kkyafjw983mq8s83p4y3","displayName":"charlie-morrison","image":"https://avatars.githubusercontent.com/u/271589886?v=4"},"moderation":null}