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.
- csp_toolkit-0.1.0/.claude/settings.local.json +14 -0
- csp_toolkit-0.1.0/.gitignore +11 -0
- csp_toolkit-0.1.0/.letta/claude/conversations.json +6 -0
- csp_toolkit-0.1.0/.letta/claude/session-f4f7c086-5387-46ff-ac96-8982dcb6a33b.json +16 -0
- csp_toolkit-0.1.0/LICENSE +21 -0
- csp_toolkit-0.1.0/PKG-INFO +341 -0
- csp_toolkit-0.1.0/README.md +318 -0
- csp_toolkit-0.1.0/browser-extension/README.md +32 -0
- csp_toolkit-0.1.0/browser-extension/background.js +101 -0
- csp_toolkit-0.1.0/browser-extension/csp-analyzer.js +181 -0
- csp_toolkit-0.1.0/browser-extension/icons/generate-icons.html +62 -0
- csp_toolkit-0.1.0/browser-extension/icons/icon128.png +0 -0
- csp_toolkit-0.1.0/browser-extension/icons/icon16.png +0 -0
- csp_toolkit-0.1.0/browser-extension/icons/icon48.png +0 -0
- csp_toolkit-0.1.0/browser-extension/manifest.json +29 -0
- csp_toolkit-0.1.0/browser-extension/popup.html +130 -0
- csp_toolkit-0.1.0/browser-extension/popup.js +122 -0
- csp_toolkit-0.1.0/nuclei-templates/README.md +57 -0
- csp_toolkit-0.1.0/nuclei-templates/csp-broad-cdn-whitelist.yaml +28 -0
- csp_toolkit-0.1.0/nuclei-templates/csp-data-uri-script.yaml +27 -0
- csp_toolkit-0.1.0/nuclei-templates/csp-https-scheme-script.yaml +28 -0
- csp_toolkit-0.1.0/nuclei-templates/csp-missing-base-uri.yaml +33 -0
- csp_toolkit-0.1.0/nuclei-templates/csp-missing-object-src.yaml +39 -0
- csp_toolkit-0.1.0/nuclei-templates/csp-missing.yaml +31 -0
- csp_toolkit-0.1.0/nuclei-templates/csp-report-only.yaml +33 -0
- csp_toolkit-0.1.0/nuclei-templates/csp-unsafe-eval.yaml +30 -0
- csp_toolkit-0.1.0/nuclei-templates/csp-unsafe-inline.yaml +29 -0
- csp_toolkit-0.1.0/nuclei-templates/csp-wildcard-script.yaml +27 -0
- csp_toolkit-0.1.0/pyproject.toml +46 -0
- csp_toolkit-0.1.0/src/csp_toolkit/__init__.py +59 -0
- csp_toolkit-0.1.0/src/csp_toolkit/__main__.py +5 -0
- csp_toolkit-0.1.0/src/csp_toolkit/analyzer.py +503 -0
- csp_toolkit-0.1.0/src/csp_toolkit/bypass.py +419 -0
- csp_toolkit-0.1.0/src/csp_toolkit/cli.py +632 -0
- csp_toolkit-0.1.0/src/csp_toolkit/data/__init__.py +0 -0
- csp_toolkit-0.1.0/src/csp_toolkit/data/bypass_patterns.json +96 -0
- csp_toolkit-0.1.0/src/csp_toolkit/data/cdn_gadgets.json +307 -0
- csp_toolkit-0.1.0/src/csp_toolkit/data/jsonp_endpoints.json +203 -0
- csp_toolkit-0.1.0/src/csp_toolkit/diff.py +115 -0
- csp_toolkit-0.1.0/src/csp_toolkit/fetcher.py +98 -0
- csp_toolkit-0.1.0/src/csp_toolkit/generator.py +138 -0
- csp_toolkit-0.1.0/src/csp_toolkit/models.py +212 -0
- csp_toolkit-0.1.0/src/csp_toolkit/output.py +157 -0
- csp_toolkit-0.1.0/src/csp_toolkit/parser.py +49 -0
- csp_toolkit-0.1.0/src/csp_toolkit/probes.py +241 -0
- csp_toolkit-0.1.0/src/csp_toolkit/scanner.py +143 -0
- csp_toolkit-0.1.0/src/csp_toolkit/subdomain.py +114 -0
- csp_toolkit-0.1.0/src/csp_toolkit/tracker.py +172 -0
- csp_toolkit-0.1.0/tests/__init__.py +0 -0
- csp_toolkit-0.1.0/tests/conftest.py +63 -0
- csp_toolkit-0.1.0/tests/test_analyzer.py +327 -0
- csp_toolkit-0.1.0/tests/test_bypass.py +231 -0
- csp_toolkit-0.1.0/tests/test_cli.py +264 -0
- csp_toolkit-0.1.0/tests/test_diff.py +117 -0
- csp_toolkit-0.1.0/tests/test_fetcher.py +110 -0
- csp_toolkit-0.1.0/tests/test_generator.py +162 -0
- csp_toolkit-0.1.0/tests/test_models.py +177 -0
- csp_toolkit-0.1.0/tests/test_parser.py +117 -0
- csp_toolkit-0.1.0/tests/test_probes.py +54 -0
- csp_toolkit-0.1.0/tests/test_scanner.py +85 -0
- csp_toolkit-0.1.0/tests/test_subdomain.py +44 -0
- csp_toolkit-0.1.0/tests/test_tracker.py +71 -0
- 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,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
|
+
```
|