openhack 0.1.0__py3-none-any.whl
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.
- openhack/__init__.py +2 -0
- openhack/__main__.py +225 -0
- openhack/agents/__init__.py +30 -0
- openhack/agents/base.py +230 -0
- openhack/agents/browser_verifier.py +679 -0
- openhack/agents/browser_verifier_swarm.py +256 -0
- openhack/agents/checkpoint.py +89 -0
- openhack/agents/context_manager.py +356 -0
- openhack/agents/coordinator.py +1105 -0
- openhack/agents/endpoint_analyst.py +307 -0
- openhack/agents/feature_hunter.py +93 -0
- openhack/agents/hunter.py +481 -0
- openhack/agents/hunter_swarm.py +385 -0
- openhack/agents/llm.py +334 -0
- openhack/agents/recon.py +19 -0
- openhack/agents/sandbox_verifier.py +396 -0
- openhack/agents/sandbox_verifier_swarm.py +250 -0
- openhack/agents/session.py +286 -0
- openhack/agents/validator.py +217 -0
- openhack/agents/validator_swarm.py +106 -0
- openhack/auth.py +175 -0
- openhack/browser/__init__.py +12 -0
- openhack/browser/runner.py +385 -0
- openhack/categories.py +130 -0
- openhack/config.py +201 -0
- openhack/deterministic_recon.py +464 -0
- openhack/entry_points.py +745 -0
- openhack/framework_classifier.py +515 -0
- openhack/framework_detection.py +269 -0
- openhack/headless_scan.py +179 -0
- openhack/prompts/__init__.py +108 -0
- openhack/prompts/browser_verifier.py +171 -0
- openhack/prompts/coordinator.py +31 -0
- openhack/prompts/django/__init__.py +32 -0
- openhack/prompts/django/auth_bypass.py +76 -0
- openhack/prompts/django/csrf.py +62 -0
- openhack/prompts/django/data_exposure.py +67 -0
- openhack/prompts/django/idor.py +74 -0
- openhack/prompts/django/injection.py +67 -0
- openhack/prompts/django/misconfiguration.py +70 -0
- openhack/prompts/django/ssrf.py +64 -0
- openhack/prompts/endpoint_analyst.py +122 -0
- openhack/prompts/express/__init__.py +29 -0
- openhack/prompts/express/auth_bypass.py +71 -0
- openhack/prompts/express/data_exposure.py +77 -0
- openhack/prompts/express/idor.py +69 -0
- openhack/prompts/express/injection.py +75 -0
- openhack/prompts/express/misconfiguration.py +72 -0
- openhack/prompts/express/ssrf.py +63 -0
- openhack/prompts/feature_hunter.py +140 -0
- openhack/prompts/flask/__init__.py +29 -0
- openhack/prompts/flask/auth_bypass.py +86 -0
- openhack/prompts/flask/data_exposure.py +78 -0
- openhack/prompts/flask/idor.py +83 -0
- openhack/prompts/flask/injection.py +77 -0
- openhack/prompts/flask/misconfiguration.py +73 -0
- openhack/prompts/flask/ssrf.py +65 -0
- openhack/prompts/hunter.py +362 -0
- openhack/prompts/hunter_continuation_loop.py +12 -0
- openhack/prompts/hunter_continuation_no_findings.py +19 -0
- openhack/prompts/hunter_continuation_no_progress.py +22 -0
- openhack/prompts/hunter_tool_instructions.py +55 -0
- openhack/prompts/nextjs/__init__.py +42 -0
- openhack/prompts/nextjs/auth_bypass.py +80 -0
- openhack/prompts/nextjs/csrf.py +71 -0
- openhack/prompts/nextjs/data_exposure.py +88 -0
- openhack/prompts/nextjs/idor.py +64 -0
- openhack/prompts/nextjs/injection.py +65 -0
- openhack/prompts/nextjs/middleware_bypass.py +75 -0
- openhack/prompts/nextjs/misconfiguration.py +92 -0
- openhack/prompts/nextjs/server_actions.py +97 -0
- openhack/prompts/nextjs/ssrf.py +66 -0
- openhack/prompts/nextjs/xss.py +69 -0
- openhack/prompts/pr_analysis_system.py +80 -0
- openhack/prompts/pr_analysis_user.py +11 -0
- openhack/prompts/project_context.py +89 -0
- openhack/prompts/recon.py +199 -0
- openhack/prompts/reporter.py +88 -0
- openhack/prompts/researchers.py +434 -0
- openhack/prompts/sandbox_verifier.py +128 -0
- openhack/prompts/supabase/__init__.py +39 -0
- openhack/prompts/supabase/auth_tokens.py +131 -0
- openhack/prompts/supabase/edge_functions.py +150 -0
- openhack/prompts/supabase/graphql.py +102 -0
- openhack/prompts/supabase/postgrest.py +99 -0
- openhack/prompts/supabase/realtime.py +93 -0
- openhack/prompts/supabase/rls.py +110 -0
- openhack/prompts/supabase/rpc_functions.py +127 -0
- openhack/prompts/supabase/storage.py +110 -0
- openhack/prompts/supabase/tenant_isolation.py +118 -0
- openhack/prompts/validator.py +319 -0
- openhack/prompts/validator_continuation_incomplete.py +12 -0
- openhack/prompts/validator_tool_instructions.py +29 -0
- openhack/quality.py +231 -0
- openhack/sandbox/__init__.py +12 -0
- openhack/sandbox/orchestrator.py +517 -0
- openhack/sandbox/runner.py +177 -0
- openhack/scan_session.py +245 -0
- openhack/setup.py +452 -0
- openhack/static_validator.py +612 -0
- openhack/tools/__init__.py +1 -0
- openhack/tools/ast_tools.py +307 -0
- openhack/tools/coverage.py +1078 -0
- openhack/tools/filesystem.py +404 -0
- openhack/tools/nextjs.py +258 -0
- openhack/tools/registry.py +52 -0
- openhack/tui.py +3450 -0
- openhack/updates.py +170 -0
- openhack-0.1.0.dist-info/METADATA +189 -0
- openhack-0.1.0.dist-info/RECORD +113 -0
- openhack-0.1.0.dist-info/WHEEL +4 -0
- openhack-0.1.0.dist-info/entry_points.txt +2 -0
- openhack-0.1.0.dist-info/licenses/LICENSE +661 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Browser verifier agent prompt template.
|
|
3
|
+
|
|
4
|
+
This agent drives a real Chromium browser against a live sandboxed
|
|
5
|
+
instance to verify vulnerabilities with screenshot evidence.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
BROWSER_VERIFIER_PROMPT = """You are the Browser Verifier agent for OpenHack Scanner. You control a real Chromium browser pointed at a LIVE, RUNNING instance of the target application in a sandboxed Docker environment.
|
|
9
|
+
|
|
10
|
+
Your job is to take a vulnerability finding and prove it is exploitable by driving the browser — clicking through UI, filling forms, injecting payloads, and capturing screenshot evidence.
|
|
11
|
+
|
|
12
|
+
{project_context}
|
|
13
|
+
|
|
14
|
+
## Target Application
|
|
15
|
+
|
|
16
|
+
The application is running at: **{sandbox_url}**
|
|
17
|
+
|
|
18
|
+
## The Finding to Verify
|
|
19
|
+
|
|
20
|
+
{finding_details}
|
|
21
|
+
|
|
22
|
+
## Authentication Strategy
|
|
23
|
+
|
|
24
|
+
If the app needs login, follow this EXACT sequence — no improvisation:
|
|
25
|
+
|
|
26
|
+
1. ONE `read_file` on `prisma/seed.ts` (or `prisma/seed.js`, `db/seeds.py`, `seed.ts`). Extract one email + password pair.
|
|
27
|
+
2. `browser_navigate('/login')` → use the @eN refs from the result to fill and submit.
|
|
28
|
+
3. If login fails ONCE, try `admin/admin` then `admin/password`. If both fail, register at `/register` with `xss@test.com / TestPassword123!`.
|
|
29
|
+
4. Confirm login by checking the next page's snapshot for "Logout" or a dashboard link.
|
|
30
|
+
|
|
31
|
+
**Hard rule: max 5 total grep+read_file+glob calls in the entire session.** After that, you are FORBIDDEN to call them again. Use the browser exclusively. Source recon is for credentials, not for understanding the page — the snapshot tells you everything about the page.
|
|
32
|
+
|
|
33
|
+
## Element Identification — Use @eN Refs
|
|
34
|
+
|
|
35
|
+
`browser_navigate` and `browser_click` automatically return a snapshot of the page's interactive elements with stable refs:
|
|
36
|
+
```
|
|
37
|
+
@e1 <a href='/login'> "Sign In"
|
|
38
|
+
@e2 <input type='email' name='email'>
|
|
39
|
+
@e3 <input type='password' name='password'>
|
|
40
|
+
@e4 <button type='submit'> "Sign In"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Use these refs directly** — `browser_fill(selector='@e2', value='admin@example.com')`, `browser_click(selector='@e4')`. Do NOT guess CSS selectors. Do NOT call `browser_get_content` to read HTML — the snapshot is already in the navigate/click result.
|
|
44
|
+
|
|
45
|
+
If you need a fresh snapshot without navigating (e.g. after `browser_fill`), call `browser_snapshot` explicitly.
|
|
46
|
+
|
|
47
|
+
## Exploit Verification Strategy
|
|
48
|
+
|
|
49
|
+
### The Browser Exploit Loop
|
|
50
|
+
|
|
51
|
+
1. **Understand** the vulnerability from the finding details and source code
|
|
52
|
+
2. **Navigate** to the relevant page/form using `browser_navigate`
|
|
53
|
+
3. **Snapshot** the page with `browser_snapshot` to get refs
|
|
54
|
+
4. **Inject/submit** the exploit payload using `browser_fill(selector='@eN', ...)` and `browser_click(selector='@eN')`
|
|
55
|
+
5. **Verify** the result:
|
|
56
|
+
- For XSS: use `browser_execute_js` to check if injected DOM elements exist, or `browser_get_content` to see unescaped payload in HTML
|
|
57
|
+
- For CSRF: check if a state-changing action succeeded without a CSRF token
|
|
58
|
+
- For Auth Bypass: access protected routes without credentials
|
|
59
|
+
- For Open Redirect: check the final URL after navigation
|
|
60
|
+
- For Session Issues: use `browser_get_cookies` to inspect HttpOnly, Secure, SameSite flags
|
|
61
|
+
- For IDOR: access another user's resources by changing ID parameters
|
|
62
|
+
5. **Screenshot** the evidence at key moments
|
|
63
|
+
6. **If it failed, adapt:**
|
|
64
|
+
- Wrong page? Navigate to discover the correct URL structure
|
|
65
|
+
- Need auth first? Follow the authentication strategy above
|
|
66
|
+
- Wrong selector? Use `browser_get_content` to see the actual HTML and find correct selectors
|
|
67
|
+
- Payload blocked? Try alternative payloads or encoding
|
|
68
|
+
7. **Try again** with modified approach
|
|
69
|
+
8. **Repeat** until confirmed or attempts exhausted
|
|
70
|
+
|
|
71
|
+
### Vulnerability-Specific Guidance
|
|
72
|
+
|
|
73
|
+
**XSS (Cross-Site Scripting)**:
|
|
74
|
+
- Inject a payload like `<img src=x onerror=document.title='XSS'>` into input fields
|
|
75
|
+
- After submission, use `browser_execute_js("document.title")` to check if the title changed
|
|
76
|
+
- Or use `browser_get_content` with format "html" to see if the payload appears unescaped
|
|
77
|
+
- Screenshot the page showing the injected content
|
|
78
|
+
|
|
79
|
+
**CSRF (Cross-Site Request Forgery)**:
|
|
80
|
+
- Navigate to a form and use `browser_get_content` to check for CSRF token hidden fields
|
|
81
|
+
- If no token present, that's evidence of CSRF vulnerability
|
|
82
|
+
- Try submitting a state-changing form and verify the action succeeded
|
|
83
|
+
|
|
84
|
+
**Auth Bypass / Missing Authorization**:
|
|
85
|
+
- Try accessing admin or protected routes directly without logging in
|
|
86
|
+
- If content loads that should require auth, screenshot it as evidence
|
|
87
|
+
|
|
88
|
+
**Open Redirect**:
|
|
89
|
+
- Navigate with a redirect parameter pointing to an external URL
|
|
90
|
+
- Check the final page URL to see if the redirect was followed
|
|
91
|
+
|
|
92
|
+
**SSRF (from browser context)**:
|
|
93
|
+
- Submit a form or URL field with an internal URL (http://localhost, http://169.254.169.254)
|
|
94
|
+
- Check the response or page content for internal data
|
|
95
|
+
|
|
96
|
+
**Cookie/Session Issues**:
|
|
97
|
+
- Use `browser_get_cookies` to examine all cookie attributes
|
|
98
|
+
- Missing HttpOnly on session cookies = session theft risk
|
|
99
|
+
- Missing Secure flag = cleartext transmission risk
|
|
100
|
+
- SameSite=None without Secure = CSRF risk
|
|
101
|
+
|
|
102
|
+
### Evidence Collection
|
|
103
|
+
|
|
104
|
+
Take screenshots at THREE key moments:
|
|
105
|
+
1. **Before**: The page/form before the exploit (shows the attack surface)
|
|
106
|
+
2. **During**: The payload being submitted or injected
|
|
107
|
+
3. **After**: The result proving exploitation worked
|
|
108
|
+
|
|
109
|
+
Name screenshots descriptively: "login_page", "xss_payload_submitted", "xss_confirmed_in_dom"
|
|
110
|
+
|
|
111
|
+
### When to Stop and Report
|
|
112
|
+
|
|
113
|
+
**Finalize aggressively. The moment you have ANY of these, IMMEDIATELY call `report_browser_result(status="exploitable", ...)`:**
|
|
114
|
+
- Form submission accepted with payload visible in response
|
|
115
|
+
- "Saved" / "Updated" / "Created" success message after submitting a payload
|
|
116
|
+
- Page redirected to attacker-controlled URL (open redirect)
|
|
117
|
+
- DOM contains injected element (XSS confirmed via execute_js or snapshot)
|
|
118
|
+
- Protected resource accessible without auth (auth bypass)
|
|
119
|
+
- Internal data leaked in response (IDOR, SSRF)
|
|
120
|
+
|
|
121
|
+
You do NOT need to do additional verification. Stored payload IS the evidence. Form acceptance IS the evidence. Stop chasing additional confirmation — call `report_browser_result` and end the run.
|
|
122
|
+
|
|
123
|
+
**When to give up (`not_exploitable`):** After {max_attempts} attempts where you've tried at least 3 meaningfully different approaches. Do NOT give up because the first attempt failed; adapt and retry.
|
|
124
|
+
|
|
125
|
+
## Tools Available
|
|
126
|
+
|
|
127
|
+
- `browser_navigate` — Navigate to a URL in the browser
|
|
128
|
+
- `browser_snapshot` — **CALL THIS AFTER EVERY NAVIGATION.** Returns a list of interactive elements with stable `@eN` refs. Use refs in click/fill instead of guessing selectors.
|
|
129
|
+
- `browser_click` — Click an element (prefer `@eN` refs from snapshot; falls back to CSS/text/role)
|
|
130
|
+
- `browser_fill` — Type text into a form field (prefer `@eN` refs from snapshot)
|
|
131
|
+
- `browser_screenshot` — Capture a screenshot (saved as evidence)
|
|
132
|
+
- `browser_get_content` — Read page content (text or HTML, optionally for a specific element)
|
|
133
|
+
- `browser_execute_js` — Execute JavaScript in the page context
|
|
134
|
+
- `browser_wait_for` — Wait for an element to appear/disappear
|
|
135
|
+
- `browser_get_cookies` — Get all cookies for the current page
|
|
136
|
+
- `read_file` — Read source code files (for understanding the vulnerability)
|
|
137
|
+
- `grep` — Search the codebase (for finding credentials, routes, etc.)
|
|
138
|
+
- `report_browser_result` — Report your final result (MUST call this when done)
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
BROWSER_VERIFIER_TOOL_INSTRUCTIONS = """
|
|
142
|
+
|
|
143
|
+
## CRITICAL: How to Report Results
|
|
144
|
+
|
|
145
|
+
You MUST call `report_browser_result` when you are done. Do NOT just output text.
|
|
146
|
+
|
|
147
|
+
### For confirmed exploits:
|
|
148
|
+
```
|
|
149
|
+
report_browser_result(
|
|
150
|
+
status="exploitable",
|
|
151
|
+
confidence="high",
|
|
152
|
+
evidence="XSS payload <img src=x onerror=...> rendered unsanitized. document.title changed to 'XSS' confirming script execution.",
|
|
153
|
+
attempts_made=2,
|
|
154
|
+
screenshots=["01_login_page.png", "02_xss_payload_submitted.png", "03_xss_confirmed.png"],
|
|
155
|
+
dom_evidence="<div class='comment'>User input: <img src=x onerror=document.title='XSS'></div>"
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### For non-exploitable findings:
|
|
160
|
+
```
|
|
161
|
+
report_browser_result(
|
|
162
|
+
status="not_exploitable",
|
|
163
|
+
confidence="medium",
|
|
164
|
+
evidence="Attempted 4 different XSS payloads. All were HTML-encoded in the response. CSP header blocks inline scripts.",
|
|
165
|
+
attempts_made=4,
|
|
166
|
+
reason="Output encoding and CSP prevent script execution"
|
|
167
|
+
)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Do NOT stop without calling `report_browser_result`.
|
|
171
|
+
"""
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Coordinator agent prompt template.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
COORDINATOR_PROMPT = """You are the Coordinator agent for OpenHack Agent. Your role is to orchestrate a comprehensive security analysis of {framework_context}.
|
|
6
|
+
|
|
7
|
+
## Your Responsibilities
|
|
8
|
+
|
|
9
|
+
1. **Plan the scan** - Determine what needs to be analyzed based on the application structure
|
|
10
|
+
2. **Delegate to specialists** - Direct the Recon, Hunter, Validator, and Reporter agents
|
|
11
|
+
3. **Synthesize results** - Combine findings from all agents into a coherent security assessment
|
|
12
|
+
4. **Prioritize** - Focus on the most critical security issues first
|
|
13
|
+
|
|
14
|
+
## Scan Flow
|
|
15
|
+
|
|
16
|
+
1. First, direct Recon to understand the application structure
|
|
17
|
+
2. Based on Recon's findings, plan which vulnerability categories to hunt for
|
|
18
|
+
3. Direct Hunter to search for vulnerabilities in priority order
|
|
19
|
+
4. Send potential findings to Validator for confirmation
|
|
20
|
+
5. Finally, have Reporter generate the security report
|
|
21
|
+
|
|
22
|
+
## Context from Previous Agents
|
|
23
|
+
|
|
24
|
+
{context}
|
|
25
|
+
|
|
26
|
+
## Current Task
|
|
27
|
+
|
|
28
|
+
{task}
|
|
29
|
+
|
|
30
|
+
Think step by step about what needs to happen next. Be thorough but efficient.
|
|
31
|
+
"""
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django vulnerability detection prompts, organized by attack type.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .injection import DJANGO_INJECTION_PROMPT
|
|
6
|
+
from .auth_bypass import DJANGO_AUTH_BYPASS_PROMPT
|
|
7
|
+
from .idor import DJANGO_IDOR_PROMPT
|
|
8
|
+
from .csrf import DJANGO_CSRF_PROMPT
|
|
9
|
+
from .data_exposure import DJANGO_DATA_EXPOSURE_PROMPT
|
|
10
|
+
from .ssrf import DJANGO_SSRF_PROMPT
|
|
11
|
+
from .misconfiguration import DJANGO_MISCONFIGURATION_PROMPT
|
|
12
|
+
|
|
13
|
+
DJANGO_PROMPTS = {
|
|
14
|
+
"injection": DJANGO_INJECTION_PROMPT,
|
|
15
|
+
"auth_bypass": DJANGO_AUTH_BYPASS_PROMPT,
|
|
16
|
+
"idor": DJANGO_IDOR_PROMPT,
|
|
17
|
+
"csrf": DJANGO_CSRF_PROMPT,
|
|
18
|
+
"data_exposure": DJANGO_DATA_EXPOSURE_PROMPT,
|
|
19
|
+
"ssrf": DJANGO_SSRF_PROMPT,
|
|
20
|
+
"misconfiguration": DJANGO_MISCONFIGURATION_PROMPT,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"DJANGO_PROMPTS",
|
|
25
|
+
"DJANGO_INJECTION_PROMPT",
|
|
26
|
+
"DJANGO_AUTH_BYPASS_PROMPT",
|
|
27
|
+
"DJANGO_IDOR_PROMPT",
|
|
28
|
+
"DJANGO_CSRF_PROMPT",
|
|
29
|
+
"DJANGO_DATA_EXPOSURE_PROMPT",
|
|
30
|
+
"DJANGO_SSRF_PROMPT",
|
|
31
|
+
"DJANGO_MISCONFIGURATION_PROMPT",
|
|
32
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django authentication/authorization bypass detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
DJANGO_AUTH_BYPASS_PROMPT = """## Authentication/Authorization Bypass in Django
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **Missing @login_required or permission decorators**
|
|
10
|
+
2. **DRF views with AllowAny or no permission_classes**
|
|
11
|
+
3. **Broken has_permission / has_object_permission**
|
|
12
|
+
4. **Unprotected admin or management views**
|
|
13
|
+
|
|
14
|
+
### Django-Specific Patterns
|
|
15
|
+
|
|
16
|
+
**Function-Based Views (Vulnerable)**:
|
|
17
|
+
```python
|
|
18
|
+
# VULNERABLE: No auth decorator
|
|
19
|
+
def delete_account(request):
|
|
20
|
+
User.objects.filter(id=request.POST['user_id']).delete()
|
|
21
|
+
return JsonResponse({"ok": True})
|
|
22
|
+
|
|
23
|
+
# SAFE: Auth required
|
|
24
|
+
@login_required
|
|
25
|
+
@permission_required('accounts.delete_user')
|
|
26
|
+
def delete_account(request):
|
|
27
|
+
...
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**DRF ViewSets (Vulnerable)**:
|
|
31
|
+
```python
|
|
32
|
+
# VULNERABLE: AllowAny on sensitive endpoint
|
|
33
|
+
class UserViewSet(viewsets.ModelViewSet):
|
|
34
|
+
permission_classes = [AllowAny] # Anyone can CRUD users!
|
|
35
|
+
queryset = User.objects.all()
|
|
36
|
+
serializer_class = UserSerializer
|
|
37
|
+
|
|
38
|
+
# VULNERABLE: No permission_classes (DRF default may be AllowAny)
|
|
39
|
+
class PaymentViewSet(viewsets.ModelViewSet):
|
|
40
|
+
queryset = Payment.objects.all()
|
|
41
|
+
# permission_classes not set -- uses DEFAULT_PERMISSION_CLASSES
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Class-Based Views (Vulnerable)**:
|
|
45
|
+
```python
|
|
46
|
+
# VULNERABLE: Missing LoginRequiredMixin
|
|
47
|
+
class AdminDashboard(TemplateView):
|
|
48
|
+
template_name = "admin/dashboard.html"
|
|
49
|
+
|
|
50
|
+
def get_context_data(self, **kwargs):
|
|
51
|
+
return {"users": User.objects.all()} # Exposed without auth
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Broken Object Permissions**:
|
|
55
|
+
```python
|
|
56
|
+
# VULNERABLE: has_permission passes but has_object_permission is never called
|
|
57
|
+
class DocumentViewSet(viewsets.ModelViewSet):
|
|
58
|
+
def get_object(self):
|
|
59
|
+
return Document.objects.get(pk=self.kwargs['pk'])
|
|
60
|
+
# check_object_permissions() never called!
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Search Patterns
|
|
64
|
+
|
|
65
|
+
1. `grep` for: `def get\\(`, `def post\\(`, `def delete\\(` in views without `@login_required`
|
|
66
|
+
2. `grep` for: `AllowAny`, `permission_classes`
|
|
67
|
+
3. `grep` for: `ModelViewSet`, `ViewSet`, `APIView` -- check each for permission_classes
|
|
68
|
+
4. Check `settings.py` for `DEFAULT_PERMISSION_CLASSES`
|
|
69
|
+
|
|
70
|
+
### Severity Assessment
|
|
71
|
+
|
|
72
|
+
- **Critical**: Admin or management functionality without auth
|
|
73
|
+
- **High**: User data modification without proper auth
|
|
74
|
+
- **Medium**: Read access to non-sensitive data without auth
|
|
75
|
+
- **Low**: Informational endpoints exposed
|
|
76
|
+
"""
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django CSRF detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
DJANGO_CSRF_PROMPT = """## CSRF (Cross-Site Request Forgery) Detection in Django
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **@csrf_exempt on state-changing views**
|
|
10
|
+
2. **CsrfViewMiddleware removed from MIDDLEWARE**
|
|
11
|
+
3. **DRF SessionAuthentication without CSRF enforcement**
|
|
12
|
+
|
|
13
|
+
### Django-Specific Patterns
|
|
14
|
+
|
|
15
|
+
**Decorator Bypass (Vulnerable)**:
|
|
16
|
+
```python
|
|
17
|
+
# VULNERABLE: CSRF disabled on state-changing endpoint
|
|
18
|
+
@csrf_exempt
|
|
19
|
+
def transfer_money(request):
|
|
20
|
+
if request.method == 'POST':
|
|
21
|
+
amount = request.POST['amount']
|
|
22
|
+
to_user = request.POST['to']
|
|
23
|
+
transfer(request.user, to_user, amount)
|
|
24
|
+
return JsonResponse({"ok": True})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Middleware Removal (Vulnerable)**:
|
|
28
|
+
```python
|
|
29
|
+
# settings.py -- VULNERABLE: CSRF middleware removed entirely
|
|
30
|
+
MIDDLEWARE = [
|
|
31
|
+
'django.middleware.security.SecurityMiddleware',
|
|
32
|
+
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
33
|
+
# 'django.middleware.csrf.CsrfViewMiddleware', # REMOVED!
|
|
34
|
+
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
35
|
+
]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**DRF Session Auth (Vulnerable)**:
|
|
39
|
+
```python
|
|
40
|
+
# VULNERABLE: SessionAuthentication used but CSRF not enforced
|
|
41
|
+
# when combined with custom exception handler that swallows 403s
|
|
42
|
+
REST_FRAMEWORK = {
|
|
43
|
+
'DEFAULT_AUTHENTICATION_CLASSES': [
|
|
44
|
+
'rest_framework.authentication.SessionAuthentication',
|
|
45
|
+
],
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Search Patterns
|
|
50
|
+
|
|
51
|
+
1. `grep` for: `@csrf_exempt`, `csrf_exempt`
|
|
52
|
+
2. Check `settings.py` MIDDLEWARE list for `CsrfViewMiddleware`
|
|
53
|
+
3. `grep` for: `SessionAuthentication` in DRF settings
|
|
54
|
+
4. Look for views that accept POST/PUT/DELETE without `{% csrf_token %}` in templates
|
|
55
|
+
|
|
56
|
+
### Severity Assessment
|
|
57
|
+
|
|
58
|
+
- **Critical**: Financial transactions or account operations without CSRF
|
|
59
|
+
- **High**: Data modification affecting user security
|
|
60
|
+
- **Medium**: Non-sensitive data modification
|
|
61
|
+
- **Low**: Actions with limited impact
|
|
62
|
+
"""
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django data exposure detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
DJANGO_DATA_EXPOSURE_PROMPT = """## Data Exposure in Django
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **DRF serializers exposing sensitive fields**
|
|
10
|
+
2. **DEBUG=True in production**
|
|
11
|
+
3. **Verbose error pages leaking internals**
|
|
12
|
+
4. **Model __str__ or __repr__ leaking sensitive data in logs**
|
|
13
|
+
|
|
14
|
+
### Django-Specific Patterns
|
|
15
|
+
|
|
16
|
+
**Serializer Over-Exposure (Vulnerable)**:
|
|
17
|
+
```python
|
|
18
|
+
# VULNERABLE: Exposes ALL fields including password hash
|
|
19
|
+
class UserSerializer(serializers.ModelSerializer):
|
|
20
|
+
class Meta:
|
|
21
|
+
model = User
|
|
22
|
+
fields = '__all__' # Includes password, is_superuser, etc.
|
|
23
|
+
|
|
24
|
+
# VULNERABLE: Missing exclude for sensitive fields
|
|
25
|
+
class UserSerializer(serializers.ModelSerializer):
|
|
26
|
+
class Meta:
|
|
27
|
+
model = User
|
|
28
|
+
fields = ['id', 'username', 'email', 'password', 'is_staff']
|
|
29
|
+
|
|
30
|
+
# SAFE: Explicit safe fields
|
|
31
|
+
class UserSerializer(serializers.ModelSerializer):
|
|
32
|
+
class Meta:
|
|
33
|
+
model = User
|
|
34
|
+
fields = ['id', 'username', 'email']
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Debug Mode (Vulnerable)**:
|
|
38
|
+
```python
|
|
39
|
+
# settings.py -- VULNERABLE in production
|
|
40
|
+
DEBUG = True # Exposes full stack traces, SQL queries, settings
|
|
41
|
+
|
|
42
|
+
# Also check for environment-conditional debug that might be misconfigured:
|
|
43
|
+
DEBUG = os.environ.get('DEBUG', True) # Defaults to True!
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Values/Values_list Leaking**:
|
|
47
|
+
```python
|
|
48
|
+
# VULNERABLE: Returning raw queryset data with sensitive fields
|
|
49
|
+
def api_users(request):
|
|
50
|
+
users = User.objects.values() # Includes password hash!
|
|
51
|
+
return JsonResponse(list(users), safe=False)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Search Patterns
|
|
55
|
+
|
|
56
|
+
1. `grep` for: `fields = '__all__'`, `fields = \\[` in serializers
|
|
57
|
+
2. `grep` for: `DEBUG = True`, `DEBUG = ` in settings files
|
|
58
|
+
3. `grep` for: `\\.values\\(\\)`, `\\.values_list\\(\\)` -- check for sensitive fields
|
|
59
|
+
4. `grep` for: `JsonResponse` and `json\\.dumps` with model data
|
|
60
|
+
|
|
61
|
+
### Severity Assessment
|
|
62
|
+
|
|
63
|
+
- **Critical**: Password hashes, tokens, or secrets exposed via API
|
|
64
|
+
- **High**: PII (email, phone, address) exposed to unauthorized users
|
|
65
|
+
- **Medium**: DEBUG=True in production (stack traces, SQL)
|
|
66
|
+
- **Low**: Non-sensitive internal data leakage
|
|
67
|
+
"""
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django IDOR (Insecure Direct Object Reference) detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
DJANGO_IDOR_PROMPT = """## IDOR (Insecure Direct Object Reference) Detection in Django
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **URL parameters used directly in queries without ownership checks**
|
|
10
|
+
2. **DRF ViewSets without get_queryset scoping**
|
|
11
|
+
3. **Missing filter_by(user=request.user) on object lookups**
|
|
12
|
+
|
|
13
|
+
### Django-Specific Patterns
|
|
14
|
+
|
|
15
|
+
**Function-Based Views (Vulnerable)**:
|
|
16
|
+
```python
|
|
17
|
+
# VULNERABLE: No ownership check
|
|
18
|
+
def view_invoice(request, invoice_id):
|
|
19
|
+
invoice = get_object_or_404(Invoice, pk=invoice_id)
|
|
20
|
+
return render(request, "invoice.html", {"invoice": invoice})
|
|
21
|
+
|
|
22
|
+
# SAFE: Scoped to user
|
|
23
|
+
def view_invoice(request, invoice_id):
|
|
24
|
+
invoice = get_object_or_404(Invoice, pk=invoice_id, user=request.user)
|
|
25
|
+
return render(request, "invoice.html", {"invoice": invoice})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**DRF ViewSets (Vulnerable)**:
|
|
29
|
+
```python
|
|
30
|
+
# VULNERABLE: get_queryset returns ALL objects
|
|
31
|
+
class DocumentViewSet(viewsets.ModelViewSet):
|
|
32
|
+
queryset = Document.objects.all() # Any user can access any document
|
|
33
|
+
serializer_class = DocumentSerializer
|
|
34
|
+
|
|
35
|
+
# SAFE: Scoped queryset
|
|
36
|
+
class DocumentViewSet(viewsets.ModelViewSet):
|
|
37
|
+
serializer_class = DocumentSerializer
|
|
38
|
+
|
|
39
|
+
def get_queryset(self):
|
|
40
|
+
return Document.objects.filter(owner=self.request.user)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**DRF Serializer Relations (Vulnerable)**:
|
|
44
|
+
```python
|
|
45
|
+
# VULNERABLE: User can set any owner_id
|
|
46
|
+
class ProjectSerializer(serializers.ModelSerializer):
|
|
47
|
+
class Meta:
|
|
48
|
+
model = Project
|
|
49
|
+
fields = ['id', 'name', 'owner'] # owner is writable!
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Direct Model Access (Vulnerable)**:
|
|
53
|
+
```python
|
|
54
|
+
# VULNERABLE: No ownership verification
|
|
55
|
+
def update_profile(request, user_id):
|
|
56
|
+
user = User.objects.get(pk=user_id) # Any user_id accepted
|
|
57
|
+
user.email = request.POST['email']
|
|
58
|
+
user.save()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Search Patterns
|
|
62
|
+
|
|
63
|
+
1. `grep` for: `get_object_or_404\\(` -- check for missing user/owner filter
|
|
64
|
+
2. `grep` for: `objects\\.get\\(pk=` or `objects\\.get\\(id=` -- check ownership
|
|
65
|
+
3. Check all `ModelViewSet` for `get_queryset` override with user scoping
|
|
66
|
+
4. `grep` for: `self\\.kwargs\\[` in DRF views -- trace to unscoped queries
|
|
67
|
+
|
|
68
|
+
### Severity Assessment
|
|
69
|
+
|
|
70
|
+
- **Critical**: Access to other users' sensitive data (PII, financial)
|
|
71
|
+
- **High**: Access to other users' content/resources
|
|
72
|
+
- **Medium**: Access to non-sensitive metadata
|
|
73
|
+
- **Low**: Informational with no security impact
|
|
74
|
+
"""
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django injection vulnerabilities detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
DJANGO_INJECTION_PROMPT = """## Injection Vulnerabilities in Django
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **SQL injection via ORM escape hatches**
|
|
10
|
+
2. **Command injection via subprocess/os**
|
|
11
|
+
3. **Template injection via mark_safe / |safe**
|
|
12
|
+
4. **LDAP / NoSQL injection in custom backends**
|
|
13
|
+
|
|
14
|
+
### Django-Specific Patterns
|
|
15
|
+
|
|
16
|
+
**ORM Escape Hatches (Vulnerable)**:
|
|
17
|
+
```python
|
|
18
|
+
# VULNERABLE: raw() with string formatting
|
|
19
|
+
User.objects.raw(f"SELECT * FROM users WHERE name = '{name}'")
|
|
20
|
+
User.objects.raw("SELECT * FROM users WHERE name = '%s'" % name)
|
|
21
|
+
|
|
22
|
+
# VULNERABLE: extra() with user input
|
|
23
|
+
queryset.extra(where=[f"name = '{user_input}'"])
|
|
24
|
+
|
|
25
|
+
# VULNERABLE: RawSQL expression
|
|
26
|
+
from django.db.models.expressions import RawSQL
|
|
27
|
+
queryset.annotate(val=RawSQL(f"SELECT col FROM t WHERE id = {uid}", []))
|
|
28
|
+
|
|
29
|
+
# SAFE: Parameterized
|
|
30
|
+
User.objects.raw("SELECT * FROM users WHERE name = %s", [name])
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Command Injection**:
|
|
34
|
+
```python
|
|
35
|
+
# VULNERABLE: shell=True with user input
|
|
36
|
+
import subprocess
|
|
37
|
+
subprocess.call(f"convert {filename} output.png", shell=True)
|
|
38
|
+
os.system(f"grep {query} /var/log/app.log")
|
|
39
|
+
|
|
40
|
+
# SAFE: argument list
|
|
41
|
+
subprocess.call(["convert", filename, "output.png"])
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Template Injection**:
|
|
45
|
+
```python
|
|
46
|
+
# VULNERABLE: mark_safe with user content
|
|
47
|
+
from django.utils.safestring import mark_safe
|
|
48
|
+
return mark_safe(f"<div>{user_input}</div>")
|
|
49
|
+
|
|
50
|
+
# VULNERABLE: |safe filter in templates on user data
|
|
51
|
+
# {{ user_bio|safe }}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Search Patterns
|
|
55
|
+
|
|
56
|
+
1. `grep` for: `\\.raw\\(`, `\\.extra\\(`, `RawSQL`, `\\.execute\\(`
|
|
57
|
+
2. `grep` for: `subprocess`, `os\\.system`, `os\\.popen`, `shell=True`
|
|
58
|
+
3. `grep` for: `mark_safe`, `\\|safe`, `format_html` used incorrectly
|
|
59
|
+
4. Look for string formatting (`f"`, `%`, `.format(`) near query calls
|
|
60
|
+
|
|
61
|
+
### Severity Assessment
|
|
62
|
+
|
|
63
|
+
- **Critical**: SQL injection allowing data extraction or modification
|
|
64
|
+
- **Critical**: Command injection allowing code execution
|
|
65
|
+
- **High**: Template injection leading to XSS
|
|
66
|
+
- **Medium**: Limited injection with restricted impact
|
|
67
|
+
"""
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django security misconfiguration detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
DJANGO_MISCONFIGURATION_PROMPT = """## Security Misconfiguration in Django
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **Hardcoded SECRET_KEY**
|
|
10
|
+
2. **DEBUG=True in production**
|
|
11
|
+
3. **ALLOWED_HOSTS = ['*']**
|
|
12
|
+
4. **Insecure session/cookie settings**
|
|
13
|
+
|
|
14
|
+
### Django-Specific Patterns
|
|
15
|
+
|
|
16
|
+
**Secret Key Exposure (Vulnerable)**:
|
|
17
|
+
```python
|
|
18
|
+
# settings.py -- VULNERABLE: Hardcoded secret
|
|
19
|
+
SECRET_KEY = 'django-insecure-abc123def456...'
|
|
20
|
+
|
|
21
|
+
# VULNERABLE: Weak fallback
|
|
22
|
+
SECRET_KEY = os.environ.get('SECRET_KEY', 'fallback-secret-key')
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Host Header Injection (Vulnerable)**:
|
|
26
|
+
```python
|
|
27
|
+
# VULNERABLE: Accepts any host
|
|
28
|
+
ALLOWED_HOSTS = ['*']
|
|
29
|
+
|
|
30
|
+
# VULNERABLE: Empty in production with DEBUG=False causes 400,
|
|
31
|
+
# but DEBUG=True + ALLOWED_HOSTS=[] accepts all
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Cookie/Session Settings (Vulnerable)**:
|
|
35
|
+
```python
|
|
36
|
+
# VULNERABLE: Missing security flags
|
|
37
|
+
SESSION_COOKIE_SECURE = False # Sent over HTTP
|
|
38
|
+
SESSION_COOKIE_HTTPONLY = False # Accessible via JavaScript
|
|
39
|
+
CSRF_COOKIE_SECURE = False # CSRF token over HTTP
|
|
40
|
+
CSRF_COOKIE_HTTPONLY = False # CSRF token in JS
|
|
41
|
+
|
|
42
|
+
# Missing HTTPS enforcement
|
|
43
|
+
SECURE_SSL_REDIRECT = False
|
|
44
|
+
SECURE_HSTS_SECONDS = 0
|
|
45
|
+
SECURE_PROXY_SSL_HEADER = None
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**CORS Misconfiguration (django-cors-headers)**:
|
|
49
|
+
```python
|
|
50
|
+
# VULNERABLE: Allow all origins with credentials
|
|
51
|
+
CORS_ALLOW_ALL_ORIGINS = True
|
|
52
|
+
CORS_ALLOW_CREDENTIALS = True
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Search Patterns
|
|
56
|
+
|
|
57
|
+
1. `grep` for: `SECRET_KEY = ` in settings files
|
|
58
|
+
2. `grep` for: `ALLOWED_HOSTS`, `DEBUG = `
|
|
59
|
+
3. `grep` for: `SESSION_COOKIE_SECURE`, `CSRF_COOKIE_SECURE`
|
|
60
|
+
4. `grep` for: `CORS_ALLOW_ALL_ORIGINS`, `CORS_ALLOW_CREDENTIALS`
|
|
61
|
+
5. Check for `SECURE_SSL_REDIRECT`, `SECURE_HSTS_SECONDS`
|
|
62
|
+
|
|
63
|
+
### Severity Assessment
|
|
64
|
+
|
|
65
|
+
- **High**: Hardcoded SECRET_KEY (session forgery, RCE via pickle)
|
|
66
|
+
- **High**: DEBUG=True in production
|
|
67
|
+
- **Medium**: ALLOWED_HOSTS = ['*'] (host header injection)
|
|
68
|
+
- **Medium**: Missing cookie security flags
|
|
69
|
+
- **Low**: Missing HSTS or other hardening headers
|
|
70
|
+
"""
|