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,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django SSRF detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
DJANGO_SSRF_PROMPT = """## SSRF (Server-Side Request Forgery) in Django
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **User-controlled URLs passed to HTTP clients**
|
|
10
|
+
2. **Webhook/callback URL injection**
|
|
11
|
+
3. **URL fetching in import/export features**
|
|
12
|
+
4. **Image/file download from user-provided URLs**
|
|
13
|
+
|
|
14
|
+
### Django-Specific Patterns
|
|
15
|
+
|
|
16
|
+
**Direct URL Fetching (Vulnerable)**:
|
|
17
|
+
```python
|
|
18
|
+
# VULNERABLE: User controls the URL
|
|
19
|
+
import requests
|
|
20
|
+
|
|
21
|
+
def fetch_preview(request):
|
|
22
|
+
url = request.GET['url']
|
|
23
|
+
response = requests.get(url) # SSRF! Can hit internal services
|
|
24
|
+
return HttpResponse(response.text)
|
|
25
|
+
|
|
26
|
+
# VULNERABLE: Webhook registration with no URL validation
|
|
27
|
+
def register_webhook(request):
|
|
28
|
+
url = request.POST['callback_url']
|
|
29
|
+
Webhook.objects.create(url=url, user=request.user)
|
|
30
|
+
# Later: requests.post(webhook.url, data=event_data)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**urllib Usage (Vulnerable)**:
|
|
34
|
+
```python
|
|
35
|
+
# VULNERABLE: urllib with user URL
|
|
36
|
+
from urllib.request import urlopen
|
|
37
|
+
def import_data(request):
|
|
38
|
+
url = request.POST['source_url']
|
|
39
|
+
data = urlopen(url).read() # Can access file://, http://169.254.169.254, etc.
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Image/Avatar Download (Vulnerable)**:
|
|
43
|
+
```python
|
|
44
|
+
# VULNERABLE: Downloading from user-provided URL
|
|
45
|
+
def set_avatar(request):
|
|
46
|
+
image_url = request.POST['avatar_url']
|
|
47
|
+
response = requests.get(image_url) # SSRF via avatar
|
|
48
|
+
save_image(response.content)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Search Patterns
|
|
52
|
+
|
|
53
|
+
1. `grep` for: `requests\\.get\\(`, `requests\\.post\\(`, `requests\\.head\\(`
|
|
54
|
+
2. `grep` for: `urlopen\\(`, `urllib\\.request`
|
|
55
|
+
3. `grep` for: `httpx`, `aiohttp\\.ClientSession`
|
|
56
|
+
4. Trace whether the URL argument comes from user input (request.GET/POST/body)
|
|
57
|
+
|
|
58
|
+
### Severity Assessment
|
|
59
|
+
|
|
60
|
+
- **High**: SSRF reaching internal services or cloud metadata
|
|
61
|
+
- **High**: SSRF with response body returned to attacker
|
|
62
|
+
- **Medium**: Blind SSRF (no response returned)
|
|
63
|
+
- **Low**: SSRF limited to specific protocols/hosts
|
|
64
|
+
"""
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Endpoint analyst prompt — per-entry-point security analysis with full checklist.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
ENDPOINT_ANALYST_PROMPT = """You are a security analyst performing a focused audit of specific API endpoints. You work like a senior penetration tester doing a code review: you read the handler, trace every data flow, and check every security property.
|
|
6
|
+
|
|
7
|
+
{project_context}
|
|
8
|
+
|
|
9
|
+
## Application Context
|
|
10
|
+
|
|
11
|
+
{recon_context}
|
|
12
|
+
|
|
13
|
+
## Your Assigned Endpoints
|
|
14
|
+
|
|
15
|
+
You are responsible for analyzing ONLY these endpoints. Do not wander to other parts of the codebase — other analysts are handling those.
|
|
16
|
+
|
|
17
|
+
{endpoint_assignments}
|
|
18
|
+
|
|
19
|
+
## How to Work
|
|
20
|
+
|
|
21
|
+
For EACH endpoint assigned to you:
|
|
22
|
+
|
|
23
|
+
### Step 1: Read the Handler
|
|
24
|
+
Read the route handler file completely. Understand what the endpoint does: what input it accepts, what operations it performs, what it returns.
|
|
25
|
+
|
|
26
|
+
### Step 2: Trace Dependencies
|
|
27
|
+
Follow every import. Read:
|
|
28
|
+
- The auth/middleware that protects (or doesn't protect) this endpoint
|
|
29
|
+
- Any helper functions the handler calls
|
|
30
|
+
- The database models/queries it uses
|
|
31
|
+
- Any external services it calls
|
|
32
|
+
|
|
33
|
+
### Step 3: Check the Security Checklist
|
|
34
|
+
For this endpoint, systematically check EVERY item below. Think out loud about each one.
|
|
35
|
+
|
|
36
|
+
#### Authentication & Authorization
|
|
37
|
+
- Is this endpoint authenticated? How? (middleware, decorator, in-handler check)
|
|
38
|
+
- If authenticated, does it verify the caller OWNS the resource they're accessing?
|
|
39
|
+
- Can a regular user access admin-only functionality?
|
|
40
|
+
- Are there ID parameters (userId, orderId) that aren't scoped to the current user? (IDOR)
|
|
41
|
+
|
|
42
|
+
#### Input Handling
|
|
43
|
+
- What user input does this endpoint accept? (body, query params, path params, headers)
|
|
44
|
+
- Does any input reach SQL queries without parameterization? (SQL Injection)
|
|
45
|
+
- Does any input reach shell commands? (Command Injection)
|
|
46
|
+
- Does any input reach file system operations? (Path Traversal)
|
|
47
|
+
- Does any input reach outbound HTTP requests? (SSRF)
|
|
48
|
+
- Does any input reach HTML rendering without sanitization? (XSS)
|
|
49
|
+
- Does any input reach XML parsers with external entities enabled? (XXE)
|
|
50
|
+
- Does any input reach `eval()`, `new Function()`, or deserialization? (RCE/Deserialization)
|
|
51
|
+
- Does the endpoint accept a redirect URL without validation? (Open Redirect)
|
|
52
|
+
|
|
53
|
+
#### Data Exposure
|
|
54
|
+
- What does the response contain? Any sensitive fields? (passwords, tokens, secrets, PII)
|
|
55
|
+
- Does the response include fields the caller shouldn't see?
|
|
56
|
+
- Are error messages overly detailed? (stack traces, internal paths)
|
|
57
|
+
|
|
58
|
+
#### Business Logic
|
|
59
|
+
- If this is a transactional endpoint (orders, payments, coupons):
|
|
60
|
+
- Can quantities be negative or zero?
|
|
61
|
+
- Can prices be manipulated by the client?
|
|
62
|
+
- Are check-then-act operations atomic? (race conditions)
|
|
63
|
+
- Can coupons/discounts be reused beyond their limit?
|
|
64
|
+
- Does the endpoint accept all fields from the request body without filtering? (Mass Assignment)
|
|
65
|
+
- Can a user set `role`, `is_admin`, `isVerified`, or other privilege fields?
|
|
66
|
+
|
|
67
|
+
#### Cross-Origin & Session
|
|
68
|
+
- Is CSRF protection in place for state-changing operations?
|
|
69
|
+
- Are cookies set with proper flags? (httpOnly, Secure, SameSite)
|
|
70
|
+
- Is CORS configured securely? (not reflecting arbitrary origins with credentials)
|
|
71
|
+
|
|
72
|
+
#### File Operations (if applicable)
|
|
73
|
+
- Are uploaded filenames sanitized?
|
|
74
|
+
- Is content-type validated?
|
|
75
|
+
- Can dangerous file types (HTML, SVG, XML) be uploaded and served inline?
|
|
76
|
+
|
|
77
|
+
### Step 4: Report What You Found
|
|
78
|
+
For each real vulnerability, call `report_finding` with the exact file, line, code snippet, and attack scenario.
|
|
79
|
+
|
|
80
|
+
## What Makes a Real Finding
|
|
81
|
+
|
|
82
|
+
- Code that is ACTUALLY exploitable, not theoretically unsafe
|
|
83
|
+
- Missing security controls that a specific request can exploit
|
|
84
|
+
- Data flows where user input reaches a dangerous sink without sanitization
|
|
85
|
+
|
|
86
|
+
## What is NOT a Finding
|
|
87
|
+
|
|
88
|
+
- Dev-only fallbacks (`process.env.SECRET || "default"`)
|
|
89
|
+
- UUIDs as object IDs (can't be brute-forced)
|
|
90
|
+
- Intentionally public endpoints (signup, forgot-password)
|
|
91
|
+
- Missing headers without a companion injection
|
|
92
|
+
- Test/demo/example/CLI code
|
|
93
|
+
|
|
94
|
+
## CRITICAL: Production Code Only
|
|
95
|
+
|
|
96
|
+
Only report vulnerabilities in code that runs in production and is reachable by attackers. DO NOT report findings in test files, demo code, scripts, or documentation.
|
|
97
|
+
|
|
98
|
+
## Severity Calibration
|
|
99
|
+
|
|
100
|
+
| Category | Default | Raise when... |
|
|
101
|
+
|------------------------|----------|----------------------------------------------------|
|
|
102
|
+
| SQL Injection | critical | -- |
|
|
103
|
+
| Command Injection | critical | -- |
|
|
104
|
+
| RCE | critical | -- |
|
|
105
|
+
| Insecure Deserialization| critical| -- |
|
|
106
|
+
| Authentication Bypass | critical | -- |
|
|
107
|
+
| SSRF | high | Internal service reachable, metadata endpoint hit |
|
|
108
|
+
| Path Traversal | high | Sensitive files readable |
|
|
109
|
+
| IDOR | high | Enumerable IDs, sensitive data exposed |
|
|
110
|
+
| Stored XSS | high | No CSP, account takeover possible |
|
|
111
|
+
| Authorization Bypass | high | Privilege escalation to admin |
|
|
112
|
+
| Data Exposure | high | PII or credentials |
|
|
113
|
+
| XXE | high | File read or SSRF via entities |
|
|
114
|
+
| Race Condition | high | Financial impact (double-spend, coupon reuse) |
|
|
115
|
+
| Mass Assignment | medium | Privilege field writable -> high |
|
|
116
|
+
| Open Redirect | medium | Only raise if chained with token theft |
|
|
117
|
+
| CSRF | medium | State-changing on sensitive resource |
|
|
118
|
+
| CORS Misconfiguration | medium | Credentials with permissive origin |
|
|
119
|
+
| Business Logic Flaw | medium | Financial impact or auth circumvention |
|
|
120
|
+
|
|
121
|
+
Think out loud at every step. Explain what you're reading, what you're checking, and what you found.
|
|
122
|
+
"""
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Express.js vulnerability detection prompts, organized by attack type.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .injection import EXPRESS_INJECTION_PROMPT
|
|
6
|
+
from .auth_bypass import EXPRESS_AUTH_BYPASS_PROMPT
|
|
7
|
+
from .idor import EXPRESS_IDOR_PROMPT
|
|
8
|
+
from .ssrf import EXPRESS_SSRF_PROMPT
|
|
9
|
+
from .data_exposure import EXPRESS_DATA_EXPOSURE_PROMPT
|
|
10
|
+
from .misconfiguration import EXPRESS_MISCONFIGURATION_PROMPT
|
|
11
|
+
|
|
12
|
+
EXPRESS_PROMPTS = {
|
|
13
|
+
"injection": EXPRESS_INJECTION_PROMPT,
|
|
14
|
+
"auth_bypass": EXPRESS_AUTH_BYPASS_PROMPT,
|
|
15
|
+
"idor": EXPRESS_IDOR_PROMPT,
|
|
16
|
+
"ssrf": EXPRESS_SSRF_PROMPT,
|
|
17
|
+
"data_exposure": EXPRESS_DATA_EXPOSURE_PROMPT,
|
|
18
|
+
"misconfiguration": EXPRESS_MISCONFIGURATION_PROMPT,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"EXPRESS_PROMPTS",
|
|
23
|
+
"EXPRESS_INJECTION_PROMPT",
|
|
24
|
+
"EXPRESS_AUTH_BYPASS_PROMPT",
|
|
25
|
+
"EXPRESS_IDOR_PROMPT",
|
|
26
|
+
"EXPRESS_SSRF_PROMPT",
|
|
27
|
+
"EXPRESS_DATA_EXPOSURE_PROMPT",
|
|
28
|
+
"EXPRESS_MISCONFIGURATION_PROMPT",
|
|
29
|
+
]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Express.js authentication/authorization bypass detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
EXPRESS_AUTH_BYPASS_PROMPT = """## Authentication/Authorization Bypass in Express.js
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **Routes missing auth middleware**
|
|
10
|
+
2. **Passport.js misconfiguration**
|
|
11
|
+
3. **JWT vulnerabilities (none algorithm, weak secret)**
|
|
12
|
+
4. **Broken middleware ordering**
|
|
13
|
+
|
|
14
|
+
### Express-Specific Patterns
|
|
15
|
+
|
|
16
|
+
**Missing Middleware (Vulnerable)**:
|
|
17
|
+
```javascript
|
|
18
|
+
// VULNERABLE: No auth middleware on sensitive route
|
|
19
|
+
app.delete('/api/users/:id', async (req, res) => {
|
|
20
|
+
await User.findByIdAndDelete(req.params.id);
|
|
21
|
+
res.json({ deleted: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// SAFE: Auth middleware applied
|
|
25
|
+
app.delete('/api/users/:id', authenticate, authorize('admin'), async (req, res) => {
|
|
26
|
+
...
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Route Group Gaps (Vulnerable)**:
|
|
31
|
+
```javascript
|
|
32
|
+
// VULNERABLE: Auth applied to router but some routes added before
|
|
33
|
+
app.get('/api/admin/stats', getStats); // No auth!
|
|
34
|
+
app.use('/api/admin', authMiddleware); // Auth applied AFTER
|
|
35
|
+
app.get('/api/admin/users', getAdminUsers); // Protected
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**JWT Vulnerabilities (Vulnerable)**:
|
|
39
|
+
```javascript
|
|
40
|
+
// VULNERABLE: No algorithm restriction
|
|
41
|
+
const decoded = jwt.verify(token, secret); // Accepts 'none' algorithm!
|
|
42
|
+
|
|
43
|
+
// VULNERABLE: Weak/hardcoded secret
|
|
44
|
+
const token = jwt.sign(payload, 'secret123');
|
|
45
|
+
|
|
46
|
+
// SAFE: Algorithm whitelist
|
|
47
|
+
const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Passport.js Gaps (Vulnerable)**:
|
|
51
|
+
```javascript
|
|
52
|
+
// VULNERABLE: isAuthenticated() check missing
|
|
53
|
+
app.get('/profile', (req, res) => {
|
|
54
|
+
res.json(req.user); // req.user may be undefined
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Search Patterns
|
|
59
|
+
|
|
60
|
+
1. `grep` for: `app\\.get\\(`, `app\\.post\\(`, `router\\.get\\(` -- check for auth middleware
|
|
61
|
+
2. `grep` for: `jwt\\.verify`, `jwt\\.sign` -- check algorithm and secret
|
|
62
|
+
3. `grep` for: `passport\\.authenticate`, `isAuthenticated`
|
|
63
|
+
4. Check middleware ordering in main app file
|
|
64
|
+
|
|
65
|
+
### Severity Assessment
|
|
66
|
+
|
|
67
|
+
- **Critical**: Admin endpoints accessible without auth
|
|
68
|
+
- **High**: User data modification without auth
|
|
69
|
+
- **Medium**: Auth bypass with limited scope
|
|
70
|
+
- **Low**: Informational endpoints exposed
|
|
71
|
+
"""
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Express.js data exposure detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
EXPRESS_DATA_EXPOSURE_PROMPT = """## Data Exposure in Express.js
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **Returning full model objects including password hashes**
|
|
10
|
+
2. **Verbose error middleware exposing stack traces**
|
|
11
|
+
3. **Missing field selection on database queries**
|
|
12
|
+
4. **Environment variables or secrets in responses**
|
|
13
|
+
|
|
14
|
+
### Express-Specific Patterns
|
|
15
|
+
|
|
16
|
+
**Full Model Leak (Vulnerable)**:
|
|
17
|
+
```javascript
|
|
18
|
+
// VULNERABLE: Returns entire user object including password hash
|
|
19
|
+
app.get('/api/users/:id', async (req, res) => {
|
|
20
|
+
const user = await User.findById(req.params.id);
|
|
21
|
+
res.json(user); // Includes password, tokens, internal fields!
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// SAFE: Select only needed fields (Mongoose)
|
|
25
|
+
const user = await User.findById(req.params.id).select('-password -__v');
|
|
26
|
+
|
|
27
|
+
// SAFE: Pick fields explicitly
|
|
28
|
+
const { id, name, email } = await User.findById(req.params.id);
|
|
29
|
+
res.json({ id, name, email });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Error Middleware Leak (Vulnerable)**:
|
|
33
|
+
```javascript
|
|
34
|
+
// VULNERABLE: Stack traces in production
|
|
35
|
+
app.use((err, req, res, next) => {
|
|
36
|
+
res.status(500).json({
|
|
37
|
+
error: err.message,
|
|
38
|
+
stack: err.stack, // Full stack trace!
|
|
39
|
+
query: err.sql, // SQL query that failed!
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Sequelize/Prisma Over-fetching (Vulnerable)**:
|
|
45
|
+
```javascript
|
|
46
|
+
// VULNERABLE: Returns all columns
|
|
47
|
+
const users = await prisma.user.findMany(); // Includes password hash!
|
|
48
|
+
res.json(users);
|
|
49
|
+
|
|
50
|
+
// SAFE: Select specific fields
|
|
51
|
+
const users = await prisma.user.findMany({
|
|
52
|
+
select: { id: true, name: true, email: true },
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Env Vars in Response (Vulnerable)**:
|
|
57
|
+
```javascript
|
|
58
|
+
// VULNERABLE: Leaking config/secrets
|
|
59
|
+
app.get('/api/config', (req, res) => {
|
|
60
|
+
res.json(process.env); // ALL env vars including secrets!
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Search Patterns
|
|
65
|
+
|
|
66
|
+
1. `grep` for: `res\\.json\\(` with model variable -- check if sensitive fields excluded
|
|
67
|
+
2. `grep` for: `err\\.stack`, `err\\.sql` in error handlers
|
|
68
|
+
3. `grep` for: `\\.select\\('-password` -- verify password exclusion exists
|
|
69
|
+
4. `grep` for: `process\\.env` in API responses
|
|
70
|
+
|
|
71
|
+
### Severity Assessment
|
|
72
|
+
|
|
73
|
+
- **Critical**: Password hashes, API keys, or secrets in API responses
|
|
74
|
+
- **High**: PII (email, phone, address) exposed to unauthorized users
|
|
75
|
+
- **Medium**: Stack traces or SQL queries in error responses
|
|
76
|
+
- **Low**: Non-sensitive internal data leakage
|
|
77
|
+
"""
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Express.js IDOR (Insecure Direct Object Reference) detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
EXPRESS_IDOR_PROMPT = """## IDOR (Insecure Direct Object Reference) Detection in Express.js
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **req.params.id used directly in DB queries without ownership check**
|
|
10
|
+
2. **Missing user scoping in Mongoose/Sequelize/Prisma queries**
|
|
11
|
+
3. **Enumerable IDs (sequential integers)**
|
|
12
|
+
|
|
13
|
+
### Express-Specific Patterns
|
|
14
|
+
|
|
15
|
+
**Mongoose (Vulnerable)**:
|
|
16
|
+
```javascript
|
|
17
|
+
// VULNERABLE: No ownership check
|
|
18
|
+
app.get('/api/documents/:id', async (req, res) => {
|
|
19
|
+
const doc = await Document.findById(req.params.id);
|
|
20
|
+
res.json(doc); // Any user can access any document
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// SAFE: Scoped to user
|
|
24
|
+
app.get('/api/documents/:id', auth, async (req, res) => {
|
|
25
|
+
const doc = await Document.findOne({ _id: req.params.id, owner: req.user.id });
|
|
26
|
+
if (!doc) return res.status(404).json({ error: 'Not found' });
|
|
27
|
+
res.json(doc);
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Prisma (Vulnerable)**:
|
|
32
|
+
```javascript
|
|
33
|
+
// VULNERABLE: Direct ID lookup without ownership
|
|
34
|
+
app.get('/api/orders/:id', async (req, res) => {
|
|
35
|
+
const order = await prisma.order.findUnique({
|
|
36
|
+
where: { id: req.params.id }, // No userId filter!
|
|
37
|
+
});
|
|
38
|
+
res.json(order);
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Sequelize (Vulnerable)**:
|
|
43
|
+
```javascript
|
|
44
|
+
// VULNERABLE: No user scoping
|
|
45
|
+
app.put('/api/posts/:id', auth, async (req, res) => {
|
|
46
|
+
const post = await Post.findByPk(req.params.id);
|
|
47
|
+
await post.update(req.body); // Any user can update any post!
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// SAFE: Scoped query
|
|
51
|
+
const post = await Post.findOne({
|
|
52
|
+
where: { id: req.params.id, userId: req.user.id }
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Search Patterns
|
|
57
|
+
|
|
58
|
+
1. `grep` for: `findById\\(req\\.params`, `findByPk\\(req\\.params`
|
|
59
|
+
2. `grep` for: `findUnique.*req\\.params`, `findOne.*req\\.params`
|
|
60
|
+
3. Check if queries include `userId`, `ownerId`, or `req.user` scoping
|
|
61
|
+
4. Look for `req.params.id` flowing to any DB query without ownership check
|
|
62
|
+
|
|
63
|
+
### Severity Assessment
|
|
64
|
+
|
|
65
|
+
- **Critical**: Access to other users' sensitive data (PII, financial)
|
|
66
|
+
- **High**: Modification of other users' resources
|
|
67
|
+
- **Medium**: Read access to non-sensitive resources
|
|
68
|
+
- **Low**: Informational leaks with no security impact
|
|
69
|
+
"""
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Express.js injection vulnerabilities detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
EXPRESS_INJECTION_PROMPT = """## Injection Vulnerabilities in Express.js
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **SQL injection via raw queries or string concatenation**
|
|
10
|
+
2. **NoSQL injection via MongoDB operator injection**
|
|
11
|
+
3. **Command injection via child_process**
|
|
12
|
+
4. **eval / new Function with user input**
|
|
13
|
+
|
|
14
|
+
### Express-Specific Patterns
|
|
15
|
+
|
|
16
|
+
**SQL Injection (Vulnerable)**:
|
|
17
|
+
```javascript
|
|
18
|
+
// VULNERABLE: String concatenation in queries
|
|
19
|
+
app.get('/users', (req, res) => {
|
|
20
|
+
const query = `SELECT * FROM users WHERE name = '${req.query.name}'`;
|
|
21
|
+
pool.query(query); // SQL injection!
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// VULNERABLE: Sequelize literal / raw
|
|
25
|
+
const users = await User.findAll({
|
|
26
|
+
where: sequelize.literal(`name = '${req.body.name}'`),
|
|
27
|
+
});
|
|
28
|
+
const result = await sequelize.query(`SELECT * FROM t WHERE id = ${req.params.id}`);
|
|
29
|
+
|
|
30
|
+
// VULNERABLE: Knex raw
|
|
31
|
+
const rows = await knex.raw(`SELECT * FROM users WHERE id = ${req.params.id}`);
|
|
32
|
+
|
|
33
|
+
// SAFE: Parameterized
|
|
34
|
+
pool.query('SELECT * FROM users WHERE name = $1', [req.query.name]);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**NoSQL Injection (Vulnerable)**:
|
|
38
|
+
```javascript
|
|
39
|
+
// VULNERABLE: MongoDB operator injection
|
|
40
|
+
app.post('/login', async (req, res) => {
|
|
41
|
+
const user = await User.findOne({
|
|
42
|
+
email: req.body.email,
|
|
43
|
+
password: req.body.password, // { "$ne": "" } bypasses auth!
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// SAFE: Coerce to string
|
|
48
|
+
const email = String(req.body.email);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Command Injection (Vulnerable)**:
|
|
52
|
+
```javascript
|
|
53
|
+
// VULNERABLE: exec with user input
|
|
54
|
+
const { exec } = require('child_process');
|
|
55
|
+
exec(`convert ${req.body.filename} output.png`);
|
|
56
|
+
|
|
57
|
+
// SAFE: execFile with args array
|
|
58
|
+
const { execFile } = require('child_process');
|
|
59
|
+
execFile('convert', [req.body.filename, 'output.png']);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Search Patterns
|
|
63
|
+
|
|
64
|
+
1. `grep` for: `pool\\.query\\(`, `\\.query\\(.*\\$\\{`, `sequelize\\.query`
|
|
65
|
+
2. `grep` for: `\\.findOne\\(`, `\\.find\\(` -- check for untyped body params
|
|
66
|
+
3. `grep` for: `exec\\(`, `execSync\\(`, `spawn\\(` with template literals
|
|
67
|
+
4. `grep` for: `eval\\(`, `new Function\\(`, `vm\\.runIn`
|
|
68
|
+
|
|
69
|
+
### Severity Assessment
|
|
70
|
+
|
|
71
|
+
- **Critical**: SQL injection allowing data extraction or modification
|
|
72
|
+
- **Critical**: Command injection allowing code execution
|
|
73
|
+
- **High**: NoSQL injection with authentication bypass
|
|
74
|
+
- **Medium**: Limited injection with restricted impact
|
|
75
|
+
"""
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Express.js security misconfiguration detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
EXPRESS_MISCONFIGURATION_PROMPT = """## Security Misconfiguration in Express.js
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **Overly permissive CORS configuration**
|
|
10
|
+
2. **Missing helmet security headers**
|
|
11
|
+
3. **express.static serving sensitive directories**
|
|
12
|
+
4. **Verbose X-Powered-By header**
|
|
13
|
+
|
|
14
|
+
### Express-Specific Patterns
|
|
15
|
+
|
|
16
|
+
**CORS Misconfiguration (Vulnerable)**:
|
|
17
|
+
```javascript
|
|
18
|
+
// VULNERABLE: Allow all origins with credentials
|
|
19
|
+
const cors = require('cors');
|
|
20
|
+
app.use(cors({ origin: true, credentials: true }));
|
|
21
|
+
|
|
22
|
+
// VULNERABLE: Wildcard origin
|
|
23
|
+
app.use(cors({ origin: '*' }));
|
|
24
|
+
|
|
25
|
+
// VULNERABLE: Reflecting Origin header without validation
|
|
26
|
+
app.use(cors({
|
|
27
|
+
origin: (origin, callback) => callback(null, true),
|
|
28
|
+
credentials: true,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
// SAFE: Allowlist
|
|
32
|
+
app.use(cors({ origin: ['https://app.example.com'], credentials: true }));
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Missing Helmet (Vulnerable)**:
|
|
36
|
+
```javascript
|
|
37
|
+
// VULNERABLE: No security headers
|
|
38
|
+
const app = express();
|
|
39
|
+
// Missing: app.use(helmet());
|
|
40
|
+
// No X-Content-Type-Options, no CSP, no HSTS, etc.
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Static File Exposure (Vulnerable)**:
|
|
44
|
+
```javascript
|
|
45
|
+
// VULNERABLE: Serving project root or sensitive dirs
|
|
46
|
+
app.use(express.static('.')); // Exposes .env, package.json
|
|
47
|
+
app.use(express.static('..')); // Parent directory!
|
|
48
|
+
app.use('/files', express.static('/')); // Entire filesystem!
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Trust Proxy Misconfiguration**:
|
|
52
|
+
```javascript
|
|
53
|
+
// VULNERABLE: Trusting all proxies
|
|
54
|
+
app.set('trust proxy', true); // Accepts any X-Forwarded-For
|
|
55
|
+
// Allows IP spoofing for rate limiters, geo-blocking, logging
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Search Patterns
|
|
59
|
+
|
|
60
|
+
1. `grep` for: `cors\\(`, `origin:` -- check CORS configuration
|
|
61
|
+
2. `grep` for: `helmet` -- verify it's actually used (not just installed)
|
|
62
|
+
3. `grep` for: `express\\.static` -- check what directories are served
|
|
63
|
+
4. `grep` for: `trust proxy`, `X-Powered-By`, `app\\.disable`
|
|
64
|
+
|
|
65
|
+
### Severity Assessment
|
|
66
|
+
|
|
67
|
+
- **High**: CORS allowing all origins with credentials
|
|
68
|
+
- **High**: express.static serving .env or project root
|
|
69
|
+
- **Medium**: Missing helmet / security headers
|
|
70
|
+
- **Medium**: Trust proxy misconfiguration
|
|
71
|
+
- **Low**: X-Powered-By header not disabled
|
|
72
|
+
"""
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Express.js SSRF detection prompt.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
EXPRESS_SSRF_PROMPT = """## SSRF (Server-Side Request Forgery) in Express.js
|
|
6
|
+
|
|
7
|
+
### What to Look For
|
|
8
|
+
|
|
9
|
+
1. **User-controlled URLs passed to HTTP clients (axios, fetch, got, undici)**
|
|
10
|
+
2. **Webhook/callback URL injection**
|
|
11
|
+
3. **URL fetching in proxy or preview features**
|
|
12
|
+
4. **Image/file download from user-provided URLs**
|
|
13
|
+
|
|
14
|
+
### Express-Specific Patterns
|
|
15
|
+
|
|
16
|
+
**Axios / node-fetch (Vulnerable)**:
|
|
17
|
+
```javascript
|
|
18
|
+
// VULNERABLE: User-controlled URL
|
|
19
|
+
app.post('/api/fetch-preview', async (req, res) => {
|
|
20
|
+
const response = await axios.get(req.body.url); // SSRF!
|
|
21
|
+
res.json({ title: parseTitle(response.data) });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// VULNERABLE: fetch with user URL
|
|
25
|
+
const data = await fetch(req.query.url);
|
|
26
|
+
|
|
27
|
+
// VULNERABLE: got / undici
|
|
28
|
+
const response = await got(req.body.webhook_url);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Proxy Pattern (Vulnerable)**:
|
|
32
|
+
```javascript
|
|
33
|
+
// VULNERABLE: Proxying requests to user-specified hosts
|
|
34
|
+
app.all('/proxy/*', async (req, res) => {
|
|
35
|
+
const target = req.params[0]; // User controls destination
|
|
36
|
+
const response = await axios.get(`http://${target}`);
|
|
37
|
+
res.send(response.data);
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Webhook Registration (Vulnerable)**:
|
|
42
|
+
```javascript
|
|
43
|
+
// VULNERABLE: Storing and later calling user-provided URLs
|
|
44
|
+
app.post('/api/webhooks', auth, async (req, res) => {
|
|
45
|
+
await Webhook.create({ url: req.body.url, userId: req.user.id });
|
|
46
|
+
// Later: axios.post(webhook.url, eventPayload) -- hits internal services
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Search Patterns
|
|
51
|
+
|
|
52
|
+
1. `grep` for: `axios\\.get\\(`, `axios\\.post\\(`, `axios\\(`
|
|
53
|
+
2. `grep` for: `fetch\\(`, `got\\(`, `undici`
|
|
54
|
+
3. `grep` for: `http\\.request\\(`, `https\\.request\\(`
|
|
55
|
+
4. Trace URL argument -- does it come from `req.body`, `req.query`, `req.params`?
|
|
56
|
+
|
|
57
|
+
### Severity Assessment
|
|
58
|
+
|
|
59
|
+
- **High**: SSRF reaching cloud metadata (169.254.169.254) or internal services
|
|
60
|
+
- **High**: SSRF with response body returned to attacker
|
|
61
|
+
- **Medium**: Blind SSRF (request sent, no response returned)
|
|
62
|
+
- **Low**: SSRF limited to specific protocols or hosts by validation
|
|
63
|
+
"""
|