#!/usr/bin/env python3 """Content Security Policy (CSP) analysis and bypass testing agent. Fetches and analyzes CSP headers from web applications to identify misconfigurations, overly permissive directives, and potential bypass vectors. Tests for unsafe-inline, unsafe-eval, wildcard sources, missing directives, and known CSP bypass patterns. AUTHORIZED TESTING ONLY: Only use against targets you have explicit written permission to test. """ import argparse import json import re import sys from datetime import datetime, timezone try: import requests except ImportError: print("[!] 'requests' library required: pip install requests", file=sys.stderr) sys.exit(1) CSP_DIRECTIVES = [ "default-src", "script-src", "style-src", "img-src", "font-src", "connect-src", "media-src", "object-src", "frame-src", "child-src", "worker-src", "frame-ancestors", "form-action", "base-uri", "manifest-src", "prefetch-src", "navigate-to", ] def fetch_csp(url, headers=None, cookies=None): """Fetch CSP header(s) from a URL.""" print(f"[*] Fetching CSP from {url}") h = headers or {} c = cookies or {} resp = requests.get(url, headers=h, cookies=c, timeout=15, allow_redirects=True) csp_header = resp.headers.get("Content-Security-Policy", "") csp_ro = resp.headers.get("Content-Security-Policy-Report-Only", "") print(f"[+] Status: {resp.status_code}") if csp_header: print(f"[+] CSP header found ({len(csp_header)} chars)") else: print("[!] No CSP header found") if csp_ro: print(f"[+] CSP-Report-Only header found ({len(csp_ro)} chars)") return csp_header, csp_ro, resp.status_code def parse_csp(csp_string): """Parse a CSP string into a structured dict.""" directives = {} if not csp_string: return directives for part in csp_string.split(";"): part = part.strip() if not part: continue tokens = part.split() if tokens: directive_name = tokens[0].lower() values = tokens[1:] if len(tokens) > 1 else [] directives[directive_name] = values return directives def analyze_csp(directives, csp_string): """Analyze CSP directives for security weaknesses.""" findings = [] # Missing CSP entirely if not directives: findings.append({ "check": "CSP Header Present", "severity": "HIGH", "status": "MISSING", "description": "No Content-Security-Policy header", "recommendation": "Implement a CSP header", }) return findings # Check for missing critical directives if "default-src" not in directives: findings.append({ "check": "default-src directive", "severity": "HIGH", "status": "MISSING", "description": "No default-src fallback directive", "recommendation": "Add default-src 'none' or default-src 'self'", }) if "script-src" not in directives and "default-src" not in directives: findings.append({ "check": "script-src directive", "severity": "CRITICAL", "status": "MISSING", "description": "No script-src or default-src; scripts unrestricted", }) if "object-src" not in directives: findings.append({ "check": "object-src directive", "severity": "MEDIUM", "status": "MISSING", "description": "Missing object-src; plugin-based XSS possible", "recommendation": "Add object-src 'none'", }) if "base-uri" not in directives: findings.append({ "check": "base-uri directive", "severity": "MEDIUM", "status": "MISSING", "description": "Missing base-uri; base tag injection possible", "recommendation": "Add base-uri 'self' or base-uri 'none'", }) if "frame-ancestors" not in directives: findings.append({ "check": "frame-ancestors directive", "severity": "MEDIUM", "status": "MISSING", "description": "Missing frame-ancestors; clickjacking possible", "recommendation": "Add frame-ancestors 'self'", }) # Analyze each directive for directive, values in directives.items(): values_str = " ".join(values) # unsafe-inline if "'unsafe-inline'" in values: sev = "CRITICAL" if directive in ("script-src", "default-src") else "MEDIUM" findings.append({ "check": f"unsafe-inline in {directive}", "severity": sev, "status": "FAIL", "description": f"'unsafe-inline' allows inline scripts/styles in {directive}", "bypass": "Inject inline