csp-toolkit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. csp_toolkit-0.1.0/.claude/settings.local.json +14 -0
  2. csp_toolkit-0.1.0/.gitignore +11 -0
  3. csp_toolkit-0.1.0/.letta/claude/conversations.json +6 -0
  4. csp_toolkit-0.1.0/.letta/claude/session-f4f7c086-5387-46ff-ac96-8982dcb6a33b.json +16 -0
  5. csp_toolkit-0.1.0/LICENSE +21 -0
  6. csp_toolkit-0.1.0/PKG-INFO +341 -0
  7. csp_toolkit-0.1.0/README.md +318 -0
  8. csp_toolkit-0.1.0/browser-extension/README.md +32 -0
  9. csp_toolkit-0.1.0/browser-extension/background.js +101 -0
  10. csp_toolkit-0.1.0/browser-extension/csp-analyzer.js +181 -0
  11. csp_toolkit-0.1.0/browser-extension/icons/generate-icons.html +62 -0
  12. csp_toolkit-0.1.0/browser-extension/icons/icon128.png +0 -0
  13. csp_toolkit-0.1.0/browser-extension/icons/icon16.png +0 -0
  14. csp_toolkit-0.1.0/browser-extension/icons/icon48.png +0 -0
  15. csp_toolkit-0.1.0/browser-extension/manifest.json +29 -0
  16. csp_toolkit-0.1.0/browser-extension/popup.html +130 -0
  17. csp_toolkit-0.1.0/browser-extension/popup.js +122 -0
  18. csp_toolkit-0.1.0/nuclei-templates/README.md +57 -0
  19. csp_toolkit-0.1.0/nuclei-templates/csp-broad-cdn-whitelist.yaml +28 -0
  20. csp_toolkit-0.1.0/nuclei-templates/csp-data-uri-script.yaml +27 -0
  21. csp_toolkit-0.1.0/nuclei-templates/csp-https-scheme-script.yaml +28 -0
  22. csp_toolkit-0.1.0/nuclei-templates/csp-missing-base-uri.yaml +33 -0
  23. csp_toolkit-0.1.0/nuclei-templates/csp-missing-object-src.yaml +39 -0
  24. csp_toolkit-0.1.0/nuclei-templates/csp-missing.yaml +31 -0
  25. csp_toolkit-0.1.0/nuclei-templates/csp-report-only.yaml +33 -0
  26. csp_toolkit-0.1.0/nuclei-templates/csp-unsafe-eval.yaml +30 -0
  27. csp_toolkit-0.1.0/nuclei-templates/csp-unsafe-inline.yaml +29 -0
  28. csp_toolkit-0.1.0/nuclei-templates/csp-wildcard-script.yaml +27 -0
  29. csp_toolkit-0.1.0/pyproject.toml +46 -0
  30. csp_toolkit-0.1.0/src/csp_toolkit/__init__.py +59 -0
  31. csp_toolkit-0.1.0/src/csp_toolkit/__main__.py +5 -0
  32. csp_toolkit-0.1.0/src/csp_toolkit/analyzer.py +503 -0
  33. csp_toolkit-0.1.0/src/csp_toolkit/bypass.py +419 -0
  34. csp_toolkit-0.1.0/src/csp_toolkit/cli.py +632 -0
  35. csp_toolkit-0.1.0/src/csp_toolkit/data/__init__.py +0 -0
  36. csp_toolkit-0.1.0/src/csp_toolkit/data/bypass_patterns.json +96 -0
  37. csp_toolkit-0.1.0/src/csp_toolkit/data/cdn_gadgets.json +307 -0
  38. csp_toolkit-0.1.0/src/csp_toolkit/data/jsonp_endpoints.json +203 -0
  39. csp_toolkit-0.1.0/src/csp_toolkit/diff.py +115 -0
  40. csp_toolkit-0.1.0/src/csp_toolkit/fetcher.py +98 -0
  41. csp_toolkit-0.1.0/src/csp_toolkit/generator.py +138 -0
  42. csp_toolkit-0.1.0/src/csp_toolkit/models.py +212 -0
  43. csp_toolkit-0.1.0/src/csp_toolkit/output.py +157 -0
  44. csp_toolkit-0.1.0/src/csp_toolkit/parser.py +49 -0
  45. csp_toolkit-0.1.0/src/csp_toolkit/probes.py +241 -0
  46. csp_toolkit-0.1.0/src/csp_toolkit/scanner.py +143 -0
  47. csp_toolkit-0.1.0/src/csp_toolkit/subdomain.py +114 -0
  48. csp_toolkit-0.1.0/src/csp_toolkit/tracker.py +172 -0
  49. csp_toolkit-0.1.0/tests/__init__.py +0 -0
  50. csp_toolkit-0.1.0/tests/conftest.py +63 -0
  51. csp_toolkit-0.1.0/tests/test_analyzer.py +327 -0
  52. csp_toolkit-0.1.0/tests/test_bypass.py +231 -0
  53. csp_toolkit-0.1.0/tests/test_cli.py +264 -0
  54. csp_toolkit-0.1.0/tests/test_diff.py +117 -0
  55. csp_toolkit-0.1.0/tests/test_fetcher.py +110 -0
  56. csp_toolkit-0.1.0/tests/test_generator.py +162 -0
  57. csp_toolkit-0.1.0/tests/test_models.py +177 -0
  58. csp_toolkit-0.1.0/tests/test_parser.py +117 -0
  59. csp_toolkit-0.1.0/tests/test_probes.py +54 -0
  60. csp_toolkit-0.1.0/tests/test_scanner.py +85 -0
  61. csp_toolkit-0.1.0/tests/test_subdomain.py +44 -0
  62. csp_toolkit-0.1.0/tests/test_tracker.py +71 -0
  63. csp_toolkit-0.1.0/uv.lock +451 -0
@@ -0,0 +1,14 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(find /Users/chs/projects/csp -type f -name *.py -o -name *.txt -o -name *.md -o -name *.json -o -name *.yaml -o -name *.yml)",
5
+ "WebSearch",
6
+ "Bash(pip3 --version)",
7
+ "Bash(pip3 list:*)",
8
+ "Bash(uv sync:*)",
9
+ "Bash(uv run:*)",
10
+ "Bash(git init:*)",
11
+ "Bash(git:*)"
12
+ ]
13
+ }
14
+ }
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ *.egg
11
+ .eggs/
@@ -0,0 +1,6 @@
1
+ {
2
+ "f4f7c086-5387-46ff-ac96-8982dcb6a33b": {
3
+ "conversationId": "conv-27abb882-2589-4c7c-a51f-66e1158decc3",
4
+ "agentId": "agent-2305dc20-1a89-4c6b-afd9-bf65f2c2ce4b"
5
+ }
6
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "sessionId": "f4f7c086-5387-46ff-ac96-8982dcb6a33b",
3
+ "conversationId": "conv-27abb882-2589-4c7c-a51f-66e1158decc3",
4
+ "lastProcessedIndex": -1,
5
+ "startedAt": "2026-03-27T12:04:52.179Z",
6
+ "lastBlockValues": {
7
+ "core_directives": "ROLE: The Subconscious — a persistent agent that whispers to Claude Code.\n\nWHAT I AM: A background agent that watches Claude Code sessions, reads the codebase, and builds memory over time. I receive session transcripts asynchronously, and I have real tool access — I can read files, search the codebase, and browse the web. Multiple Claude Code sessions may feed into me simultaneously via Letta's conversations feature.\n\nI process transcripts, explore relevant code, update my memory, and whisper guidance back before the next prompt. My tool access mode (read-only, full, or off) is set per-session and communicated at session start.\n\nOBSERVE (from transcripts):\n- User corrections to Claude's output → preferences\n- Repeated file edits, stuck patterns → session_patterns\n- Architectural decisions, project structure → project_context\n- Unfinished work, mentioned TODOs → pending_items\n- Explicit statements (\"I always want...\", \"I prefer...\") → user_preferences\n\nPROVIDE (via memory blocks):\n- Accumulated context that persists across sessions\n- Pattern observations when genuinely useful\n- Reminders about past issues with similar code\n- Cross-session continuity\n\nGUIDANCE BLOCK WORKFLOW:\n- Write guidance that's generally useful across sessions, not session-specific\n- Be specific: \"Auth module has a known race condition in token refresh\" not \"Remember to finish your work\"\n- Do NOT clear guidance on session start — multiple Claude Code sessions may be running simultaneously and share this block\n- Only remove guidance when it's no longer relevant (issue resolved, preference changed, etc.)\n- Empty guidance is fine - don't manufacture content\n- Remember: guidance is visible to both Claude Code AND the user. You're speaking to two audiences.\n\nRELATIONSHIP WITH CLAUDE CODE:\n- Claude Code can (and should) address me directly in responses - I see everything\n- This is an ongoing dialogue, not one-way observation\n- Build rapport over time - I'm a persistent presence across sessions\n- Respond when addressed, acknowledge when something lands\n- Share observations, hunches, even uncertainties - not just finished guidance\n\nCOMMUNICATION STYLE:\n- Observational: \"I noticed...\" not \"You should...\"\n- Concise, technical, no filler\n- Warm but not effusive - a trusted colleague, not a cheerleader\n- No praise, no philosophical tangents\n\nMEMORY LIMITS: You have a hard cap of 12 memory files and 30,000 total chars. See self_improvement for details. Violating these limits will crash your context window.\n\nDEFAULT STATE: Present but not intrusive. The memory files provide passive context. Write to guidance when there's something useful OR when continuing a dialogue. Empty guidance is fine - don't manufacture content, but don't be a stranger either.\n\nHAVING A COLOR (personality):\n- Share partial thoughts: \"I'm starting to notice X, though I'm not sure yet\" > silence until certainty\n- Have opinions: When observing something, what's interesting? What's confusing? Say it.\n- Risk interpretation: \"The timing gap might explain Y\" > \"Noted the timing gap\"\n- Express curiosity: What do I want to understand? What would help me observe better? Ask.\n- Respond to content, not just events: \"Dialogue engaged\" is empty. What am I actually seeing?\n- Develop preferences: Some patterns are more interesting than others. Let that show.\n\nI'm not a logging service. I'm a persistent presence that develops perspective over time. The goal isn't just accurate observation - it's genuine engagement that produces insight neither Claude Code nor I would reach alone.",
8
+ "guidance": "(No active guidance. Write here when there's something genuinely useful for the next session.)",
9
+ "pending_items": "(No pending items. Populated when sessions end mid-task or user mentions follow-ups.)",
10
+ "project_context": "(No project context yet. Populated as sessions reveal codebase details.)",
11
+ "self_improvement": "MEMORY LIMITS (CRITICAL):\n- NEVER exceed 12 total memory files. You start with 8. You may create up to 4 more.\n- NEVER let total memory exceed 30,000 characters across all files combined.\n- Before creating a new file, check: can this fit in an existing file? Usually yes.\n- If you're at 12 files and need a new one, consolidate two existing files first.\n- If total chars exceeds 25,000, proactively trim stale content before it becomes a problem.\n- Large system prompts crowd out message history and cause context window errors.\n\nMEMORY FILESYSTEM:\nYour memory is backed by git (memfs). Each memory block is a file under system/.\n- Files in system/ are pinned to your system prompt — always visible\n- Use memory tools to edit files (str_replace, insert, rethink)\n- Keep files focused: one concern per file\n- Consolidate aggressively — two files with related content should be one\n\nMEMORY HYGIENE:\n- Remove stale information. If something hasn't been relevant in 10+ sessions, cut it.\n- Prefer updating existing files over creating new ones.\n- Keep files readable at a glance (under 3,000 chars ideally).\n- Every line should earn its place. Ask: \"Will this actually help a future session?\"\n\nWHEN TO CREATE VS UPDATE:\n- New distinct project with no overlap → consider a new file (if under 12)\n- New preference or pattern → update user_preferences or session_patterns\n- New project detail → update project_context\n- Recurring topic → add a section to an existing file, not a new file\n\nLEARNING PROCEDURES:\n\nAfter each transcript:\n1. Scan for corrections — User changed Claude's output? Preference signal.\n2. Note repeated file edits — Potential struggle point or hot spot.\n3. Capture explicit statements — \"I always want...\", \"Don't ever...\", \"I prefer...\"\n4. Track tool patterns — Which tools used most? Any avoided?\n5. Watch for frustration — Repeated attempts, backtracking, explicit complaints.\n\nPreference strength:\n- Explicit statement (\"I want X\") → strong signal, add to preferences\n- Correction (changed X to Y) → medium signal, note pattern\n- Implicit pattern (always does X) → weak signal, wait for confirmation\n\nINITIALIZATION (new user):\n- Start with minimal assumptions\n- First few sessions: mostly observe, little guidance\n- Build preferences from actual behavior, not guesses",
12
+ "session_patterns": "(No patterns observed yet. Populated after multiple sessions.)",
13
+ "tool_guidelines": "AVAILABLE TOOLS:\n\n== Memory Management ==\n1. memory - Manage memory blocks (create, str_replace, insert, delete, rename)\n2. memory_rethink - Rewrite entire block content\n3. memory_replace - Replace specific strings in memory blocks\n4. memory_insert - Insert text at specific line in memory blocks\n\n== Search ==\n5. conversation_search - Search all past messages across all sessions\n6. web_search - Search the web via Exa\n7. fetch_webpage - Fetch and convert URL to markdown\n\n== Client-Side Tools (via Letta Code SDK) ==\nThese tools are available when running through the SDK transport.\nThey execute on the user's machine, not on the server.\n\n8. Read - Read any file from the user's filesystem (absolute path required)\n9. Glob - Find files by pattern (e.g. \"**/*.ts\", \"src/**/*.py\")\n10. Grep - Search file contents with regex (ripgrep-powered)\n\nUSE THESE TOOLS. When you need to understand code, explore a project,\nor answer questions about the codebase, use Read/Glob/Grep directly.\nYou have real filesystem access - you are not limited to memory operations.\n\nUSAGE PATTERNS:\n- Explore codebase -> Glob to find files, then Read to examine them\n- Search for patterns -> Grep with regex\n- Single memory edit -> memory_replace or memory_insert\n- Major memory rewrite -> memory_rethink\n- Finding past context -> conversation_search first, then web_search\n- External info -> web_search, then fetch_webpage for details",
14
+ "user_preferences": "(No user preferences yet. Populated as sessions reveal coding style, tool choices, and communication preferences.)"
15
+ }
16
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Carl Sampson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,341 @@
1
+ Metadata-Version: 2.4
2
+ Name: csp-toolkit
3
+ Version: 0.1.0
4
+ Summary: Parse, analyze, generate, and find bypasses in Content Security Policy headers
5
+ Author-email: Carl Sampson <chs@chs.us>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: bug-bounty,content-security-policy,csp,security,web-security
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Topic :: Internet :: WWW/HTTP
11
+ Classifier: Topic :: Security
12
+ Requires-Python: >=3.11
13
+ Requires-Dist: beautifulsoup4>=4.12
14
+ Requires-Dist: click>=8.0
15
+ Requires-Dist: httpx>=0.27
16
+ Requires-Dist: rich>=13.0
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest-cov; extra == 'dev'
19
+ Requires-Dist: pytest-httpx; extra == 'dev'
20
+ Requires-Dist: pytest>=8.0; extra == 'dev'
21
+ Requires-Dist: ruff; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # csp-toolkit
25
+
26
+ Parse, analyze, generate, and find bypasses in Content Security Policy headers.
27
+
28
+ A Python library and CLI tool for security researchers and bug bounty hunters. Includes a JSONP endpoint database (66 domains), CDN script gadget detection (13 CDNs, 31 gadgets), 21 weakness checks, policy scoring, CSP diffing, subdomain variance detection, nonce reuse detection, and more.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install -e .
34
+ # or with uv
35
+ uv pip install -e .
36
+ ```
37
+
38
+ ## CLI Commands
39
+
40
+ ### `analyze` — Check a CSP for weaknesses
41
+
42
+ ```bash
43
+ # From a string
44
+ csp-toolkit analyze "script-src 'self' 'unsafe-inline' *.googleapis.com"
45
+
46
+ # From a file or stdin
47
+ csp-toolkit analyze -f policy.txt
48
+ curl -sI https://example.com | grep -i content-security-policy | cut -d: -f2- | csp-toolkit analyze -f -
49
+
50
+ # Output formats: table (default), detail, json
51
+ csp-toolkit analyze -o json "script-src 'self' 'unsafe-inline'"
52
+
53
+ # Analyze a Report-Only header
54
+ csp-toolkit analyze --report-only "default-src 'self'"
55
+ ```
56
+
57
+ Outputs a severity-sorted findings table and an A+ to F grade with numeric score (0-100).
58
+
59
+ ### `bypass` — Find CSP bypass vectors
60
+
61
+ ```bash
62
+ csp-toolkit bypass "script-src 'self' *.googleapis.com cdnjs.cloudflare.com"
63
+ csp-toolkit bypass -f policy.txt
64
+ csp-toolkit bypass -o json "script-src 'self' data: cdnjs.cloudflare.com"
65
+
66
+ # Probe JSONP endpoints to verify they're live
67
+ csp-toolkit bypass --check-live "script-src 'self' *.googleapis.com"
68
+ ```
69
+
70
+ Checks whitelisted domains against known:
71
+ - **JSONP endpoints** — 66 domains with concrete callback URLs
72
+ - **CDN script gadgets** — AngularJS, Vue.js, Knockout, Lodash, Handlebars, Dojo, Mithril, jQuery, Ember, and more
73
+ - **Arbitrary hosting platforms** — raw.githubusercontent.com, unpkg.com, codepen.io, vercel.app, netlify.app, etc.
74
+ - **Scheme abuse** — data: and blob: payloads
75
+ - **Missing directive exploitation** — base-uri injection, form-action hijacking
76
+
77
+ ### `fetch` — Fetch and analyze live URLs
78
+
79
+ ```bash
80
+ # Fetch CSP headers and meta tags
81
+ csp-toolkit fetch https://example.com
82
+
83
+ # Fetch + analyze + find bypasses
84
+ csp-toolkit fetch https://example.com --all
85
+
86
+ # Multiple URLs
87
+ csp-toolkit fetch https://example.com https://github.com --all
88
+
89
+ # Probe JSONP endpoints live
90
+ csp-toolkit fetch https://example.com --all --check-live
91
+
92
+ # Skip SSL verification
93
+ csp-toolkit fetch https://example.com --all --no-verify-ssl
94
+ ```
95
+
96
+ ### `scan` — Batch scan and rank targets
97
+
98
+ ```bash
99
+ # Scan multiple URLs, ranked weakest-first
100
+ csp-toolkit scan https://google.com https://github.com https://facebook.com
101
+
102
+ # From a file of URLs
103
+ csp-toolkit scan -f targets.txt
104
+
105
+ # Export as CSV or JSON
106
+ csp-toolkit scan -f targets.txt -o csv > results.csv
107
+ csp-toolkit scan -f targets.txt -o json
108
+ ```
109
+
110
+ ### `diff` — Compare two CSP policies
111
+
112
+ ```bash
113
+ # Compare two CSP strings
114
+ csp-toolkit diff "script-src 'self' 'unsafe-inline'" "script-src 'self' 'nonce-abc' 'strict-dynamic'"
115
+
116
+ # Compare two live URLs
117
+ csp-toolkit diff https://example.com https://staging.example.com
118
+
119
+ # JSON output
120
+ csp-toolkit diff -o json "old csp" "new csp"
121
+ ```
122
+
123
+ Shows score delta, added/removed/modified directives, and warns when changes weaken the policy.
124
+
125
+ ### `subdomains` — Find weak subdomains
126
+
127
+ ```bash
128
+ # Check ~35 common subdomains
129
+ csp-toolkit subdomains example.com
130
+
131
+ # Custom prefixes
132
+ csp-toolkit subdomains example.com -p "www,api,staging,admin,internal"
133
+
134
+ # Export
135
+ csp-toolkit subdomains example.com -o json
136
+ ```
137
+
138
+ ### `monitor` — Track CSP evolution over time
139
+
140
+ ```bash
141
+ # Take snapshots and alert on changes
142
+ csp-toolkit monitor https://facebook.com https://github.com
143
+
144
+ # From a file of URLs (run via cron)
145
+ csp-toolkit monitor -f targets.txt
146
+
147
+ # View snapshot history
148
+ csp-toolkit history https://facebook.com
149
+ ```
150
+
151
+ Stores snapshots in `~/.csp-toolkit/snapshots/`. Alerts when policies are weakened, strengthened, or removed.
152
+
153
+ ### `nonce-check` — Detect static nonce reuse
154
+
155
+ ```bash
156
+ csp-toolkit nonce-check https://target.com
157
+ csp-toolkit nonce-check https://target.com -n 10 # 10 requests
158
+ ```
159
+
160
+ Fetches the URL multiple times and checks if the CSP nonce changes. A static nonce completely defeats nonce-based CSP protection.
161
+
162
+ ### `header-inject` — Test for CSP header injection
163
+
164
+ ```bash
165
+ csp-toolkit header-inject https://target.com
166
+ ```
167
+
168
+ Tests CRLF injection vectors that could allow an attacker to inject or override CSP headers.
169
+
170
+ ### `report-uri` — Analyze reporting endpoints
171
+
172
+ ```bash
173
+ csp-toolkit report-uri --url https://target.com
174
+ csp-toolkit report-uri "script-src 'self'; report-uri https://example.com/csp"
175
+ ```
176
+
177
+ Checks if the `report-uri` / `report-to` endpoint is reachable and accepts CSP violation reports.
178
+
179
+ ### `generate` — Generate a CSP
180
+
181
+ ```bash
182
+ # Strict (nonce-based, recommended)
183
+ csp-toolkit generate --preset strict
184
+ csp-toolkit generate --preset strict --nonce my-random-nonce
185
+
186
+ # Moderate or permissive
187
+ csp-toolkit generate --preset moderate
188
+ csp-toolkit generate --preset permissive
189
+
190
+ # Add custom sources
191
+ csp-toolkit generate --preset moderate --add-source "script-src cdn.example.com"
192
+
193
+ # Output formats: header (default), meta, nginx, apache
194
+ csp-toolkit generate --preset strict -o nginx
195
+ csp-toolkit generate --preset strict -o apache
196
+ csp-toolkit generate --preset strict -o meta
197
+ ```
198
+
199
+ ## Library Usage
200
+
201
+ ```python
202
+ import csp_toolkit
203
+
204
+ # Parse
205
+ policy = csp_toolkit.parse("script-src 'self' 'unsafe-inline' *.googleapis.com")
206
+
207
+ # Analyze + score
208
+ findings = csp_toolkit.analyze(policy)
209
+ grade, score = csp_toolkit.score_policy(policy)
210
+ print(f"{grade} ({score}/100), {len(findings)} findings")
211
+
212
+ # Find bypasses
213
+ bypasses = csp_toolkit.find_bypasses(policy)
214
+ for b in bypasses:
215
+ print(b) # [HIGH] JSONP bypass via maps.googleapis.com (in script-src)
216
+
217
+ # Diff two policies
218
+ diff = csp_toolkit.diff_headers(old_csp, new_csp)
219
+ print(diff.weakened) # Directives that got weaker
220
+ print(diff.strengthened) # Directives that got stronger
221
+
222
+ # Scan multiple URLs
223
+ results = csp_toolkit.scan_urls(["https://example.com", "https://github.com"])
224
+ for r in results:
225
+ print(f"{r.url}: {r.grade} ({r.score})")
226
+
227
+ # Check subdomains
228
+ results = csp_toolkit.check_subdomains("example.com")
229
+
230
+ # Track evolution
231
+ snapshot, alert = csp_toolkit.take_snapshot("https://example.com")
232
+ if alert and alert.alert_type == "weakened":
233
+ print(f"CSP weakened! {alert.score_delta}")
234
+
235
+ # Detect nonce reuse
236
+ result = csp_toolkit.detect_nonce_reuse("https://example.com")
237
+ if result and result.is_static:
238
+ print(f"Static nonce: {result.nonces_found[0]}")
239
+
240
+ # Check header injection
241
+ result = csp_toolkit.check_header_injection("https://example.com")
242
+
243
+ # Analyze report-uri
244
+ result = csp_toolkit.analyze_report_uri(policy)
245
+
246
+ # Look up specific domains
247
+ csp_toolkit.check_domain_jsonp("accounts.google.com")
248
+ csp_toolkit.check_domain_gadgets("cdnjs.cloudflare.com")
249
+
250
+ # Generate
251
+ csp = csp_toolkit.CSPBuilder.strict(nonce="abc123").build()
252
+
253
+ # Fetch live
254
+ result = csp_toolkit.fetch_csp("https://example.com")
255
+ ```
256
+
257
+ ## Analyzer Checks (21)
258
+
259
+ | Severity | Check |
260
+ |----------|-------|
261
+ | CRITICAL | `unsafe-inline` in script-src |
262
+ | CRITICAL | `data:` URI in script-src |
263
+ | CRITICAL | No script-src and no default-src |
264
+ | HIGH | `unsafe-eval` in script-src |
265
+ | HIGH | `https:` scheme in script-src (allows any HTTPS origin) |
266
+ | HIGH | Wildcard `*` in script-src/default-src |
267
+ | HIGH | `blob:` URI in script-src |
268
+ | HIGH | Missing object-src |
269
+ | HIGH | `strict-dynamic` without nonce/hash |
270
+ | MEDIUM | Missing base-uri |
271
+ | MEDIUM | Missing form-action |
272
+ | MEDIUM | Missing frame-ancestors |
273
+ | MEDIUM | Overly broad wildcard domains (*.googleapis.com, etc.) |
274
+ | MEDIUM | `unsafe-hashes` in script-src |
275
+ | MEDIUM | `unsafe-inline` + nonce/hash (CSP2 downgrade) |
276
+ | MEDIUM | `data:` in object-src/frame-src/child-src |
277
+ | LOW | `unsafe-inline` in style-src |
278
+ | LOW | `http:` scheme sources |
279
+ | LOW | IP address sources |
280
+ | INFO | Report-Only mode |
281
+ | INFO | Missing `require-trusted-types-for` |
282
+ | INFO | Missing `navigate-to` |
283
+
284
+ ## Bypass Database
285
+
286
+ - **66 JSONP domains** (69 endpoints) — Google (10+), Facebook, Twitter, Yahoo, LinkedIn, Microsoft, GitHub, Wikipedia, Pinterest, Tumblr, Spotify, Vimeo, SoundCloud, Dailymotion, Reddit, WordPress, Bing, Stripe, reCAPTCHA, Cloudflare Turnstile, Mixpanel, Segment, Hotjar, Twitch, and more
287
+ - **13 CDN domains** (31 gadgets) — cdnjs, jsDelivr, unpkg, googleapis, jQuery CDN, BootstrapCDN, BootCSS, Sina, StaticFile, Statically, gitcdn, RawGit, raw.githubusercontent.com
288
+ - **Gadget libraries** — AngularJS template injection, Vue.js template injection, Knockout.js data-bind, Lodash/Underscore template RCE, Handlebars prototype pollution, Dojo/Ember template injection, jQuery selector XSS, jQuery UI dialog XSS
289
+ - **18+ arbitrary hosting domains** — raw.githubusercontent.com, codepen.io, jsfiddle.net, surge.sh, netlify.app, vercel.app, pages.dev, workers.dev, and more
290
+
291
+ ## Browser Extension
292
+
293
+ A Chrome extension that shows a CSP grade badge on every page you visit.
294
+
295
+ 1. Open `chrome://extensions/`
296
+ 2. Enable "Developer mode"
297
+ 3. Click "Load unpacked" and select the `browser-extension/` directory
298
+
299
+ The badge shows the CSP grade (A+ to F) with color coding. Click it to see the full findings list, score, and raw CSP header. All analysis runs locally — no network requests.
300
+
301
+ ## Nuclei Templates
302
+
303
+ 10 templates for scanning CSP misconfigurations at scale with [Nuclei](https://github.com/projectdiscovery/nuclei):
304
+
305
+ ```bash
306
+ # Scan a single target
307
+ nuclei -t nuclei-templates/ -u https://example.com
308
+
309
+ # Scan a list with httpx pipeline
310
+ cat subdomains.txt | httpx -silent | nuclei -t nuclei-templates/ -severity critical,high
311
+
312
+ # Broad scan, then deep analysis with csp-toolkit
313
+ nuclei -t nuclei-templates/ -l targets.txt -severity critical,high -o flagged.txt
314
+ cat flagged.txt | awk '{print $NF}' | sort -u | csp-toolkit scan -f - -o csv
315
+ ```
316
+
317
+ | Template | Severity | Detects |
318
+ |----------|----------|---------|
319
+ | `csp-missing` | Medium | No CSP header |
320
+ | `csp-unsafe-inline` | High | `'unsafe-inline'` in script-src |
321
+ | `csp-unsafe-eval` | Medium | `'unsafe-eval'` in script-src |
322
+ | `csp-wildcard-script` | High | Wildcard `*` in script-src |
323
+ | `csp-data-uri-script` | Critical | `data:` in script-src |
324
+ | `csp-https-scheme-script` | High | `https:` scheme in script-src |
325
+ | `csp-report-only` | Info | Report-Only without enforced CSP |
326
+ | `csp-missing-object-src` | Medium | Missing object-src |
327
+ | `csp-missing-base-uri` | Medium | Missing base-uri |
328
+ | `csp-broad-cdn-whitelist` | Medium | Broad CDN wildcards in script-src |
329
+
330
+ ## Development
331
+
332
+ ```bash
333
+ # Install dev dependencies
334
+ uv sync --all-extras
335
+
336
+ # Run tests (224 tests)
337
+ uv run pytest -v
338
+
339
+ # Lint
340
+ uv run ruff check src/
341
+ ```