@datahogo/core 0.1.0
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.
- package/LICENSE +661 -0
- package/README.md +17 -0
- package/dist/engines/config.d.ts +3 -0
- package/dist/engines/config.d.ts.map +1 -0
- package/dist/engines/config.js +433 -0
- package/dist/engines/config.js.map +1 -0
- package/dist/engines/dast.d.ts +3 -0
- package/dist/engines/dast.d.ts.map +1 -0
- package/dist/engines/dast.js +167 -0
- package/dist/engines/dast.js.map +1 -0
- package/dist/engines/db-rules.d.ts +3 -0
- package/dist/engines/db-rules.d.ts.map +1 -0
- package/dist/engines/db-rules.js +166 -0
- package/dist/engines/db-rules.js.map +1 -0
- package/dist/engines/dependencies.d.ts +3 -0
- package/dist/engines/dependencies.d.ts.map +1 -0
- package/dist/engines/dependencies.js +167 -0
- package/dist/engines/dependencies.js.map +1 -0
- package/dist/engines/github-actions.d.ts +3 -0
- package/dist/engines/github-actions.d.ts.map +1 -0
- package/dist/engines/github-actions.js +215 -0
- package/dist/engines/github-actions.js.map +1 -0
- package/dist/engines/gitleaks.d.ts +3 -0
- package/dist/engines/gitleaks.d.ts.map +1 -0
- package/dist/engines/gitleaks.js +107 -0
- package/dist/engines/gitleaks.js.map +1 -0
- package/dist/engines/npm-audit.d.ts +3 -0
- package/dist/engines/npm-audit.d.ts.map +1 -0
- package/dist/engines/npm-audit.js +113 -0
- package/dist/engines/npm-audit.js.map +1 -0
- package/dist/engines/patterns.d.ts +3 -0
- package/dist/engines/patterns.d.ts.map +1 -0
- package/dist/engines/patterns.js +1330 -0
- package/dist/engines/patterns.js.map +1 -0
- package/dist/engines/secrets.d.ts +4 -0
- package/dist/engines/secrets.d.ts.map +1 -0
- package/dist/engines/secrets.js +260 -0
- package/dist/engines/secrets.js.map +1 -0
- package/dist/engines/semgrep.d.ts +3 -0
- package/dist/engines/semgrep.d.ts.map +1 -0
- package/dist/engines/semgrep.js +173 -0
- package/dist/engines/semgrep.js.map +1 -0
- package/dist/engines/types.d.ts +30 -0
- package/dist/engines/types.d.ts.map +1 -0
- package/dist/engines/types.js +3 -0
- package/dist/engines/types.js.map +1 -0
- package/dist/engines/url-scanner.d.ts +3 -0
- package/dist/engines/url-scanner.d.ts.map +1 -0
- package/dist/engines/url-scanner.js +469 -0
- package/dist/engines/url-scanner.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator.d.ts +32 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +237 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/rules/.gitkeep +0 -0
- package/dist/rules/ai-llm-security.yaml +199 -0
- package/dist/rules/api-security.yaml +489 -0
- package/dist/rules/auth-patterns.yaml +406 -0
- package/dist/rules/crypto-patterns.yaml +227 -0
- package/dist/rules/database-cloud-security.yaml +169 -0
- package/dist/rules/general-security.yaml +588 -0
- package/dist/rules/injection-patterns.yaml +318 -0
- package/dist/rules/nextjs-security.yaml +532 -0
- package/dist/rules/node-security.yaml +380 -0
- package/dist/rules/rules/.gitkeep +0 -0
- package/dist/rules/rules/ai-llm-security.yaml +199 -0
- package/dist/rules/rules/api-security.yaml +489 -0
- package/dist/rules/rules/auth-patterns.yaml +406 -0
- package/dist/rules/rules/crypto-patterns.yaml +227 -0
- package/dist/rules/rules/database-cloud-security.yaml +169 -0
- package/dist/rules/rules/general-security.yaml +588 -0
- package/dist/rules/rules/injection-patterns.yaml +318 -0
- package/dist/rules/rules/nextjs-security.yaml +532 -0
- package/dist/rules/rules/node-security.yaml +380 -0
- package/dist/rules/rules/supabase-security.yaml +387 -0
- package/dist/rules/supabase-security.yaml +387 -0
- package/dist/scanning/agents/csharp-agent.d.ts +12 -0
- package/dist/scanning/agents/csharp-agent.d.ts.map +1 -0
- package/dist/scanning/agents/csharp-agent.js +592 -0
- package/dist/scanning/agents/csharp-agent.js.map +1 -0
- package/dist/scanning/agents/go-agent.d.ts +14 -0
- package/dist/scanning/agents/go-agent.d.ts.map +1 -0
- package/dist/scanning/agents/go-agent.js +641 -0
- package/dist/scanning/agents/go-agent.js.map +1 -0
- package/dist/scanning/agents/java-agent.d.ts +8 -0
- package/dist/scanning/agents/java-agent.d.ts.map +1 -0
- package/dist/scanning/agents/java-agent.js +807 -0
- package/dist/scanning/agents/java-agent.js.map +1 -0
- package/dist/scanning/agents/javascript-agent.d.ts +8 -0
- package/dist/scanning/agents/javascript-agent.d.ts.map +1 -0
- package/dist/scanning/agents/javascript-agent.js +77 -0
- package/dist/scanning/agents/javascript-agent.js.map +1 -0
- package/dist/scanning/agents/mobile-agent.d.ts +8 -0
- package/dist/scanning/agents/mobile-agent.d.ts.map +1 -0
- package/dist/scanning/agents/mobile-agent.js +916 -0
- package/dist/scanning/agents/mobile-agent.js.map +1 -0
- package/dist/scanning/agents/php-agent.d.ts +8 -0
- package/dist/scanning/agents/php-agent.d.ts.map +1 -0
- package/dist/scanning/agents/php-agent.js +679 -0
- package/dist/scanning/agents/php-agent.js.map +1 -0
- package/dist/scanning/agents/python-agent.d.ts +8 -0
- package/dist/scanning/agents/python-agent.d.ts.map +1 -0
- package/dist/scanning/agents/python-agent.js +701 -0
- package/dist/scanning/agents/python-agent.js.map +1 -0
- package/dist/scanning/agents/supabase-agent.d.ts +8 -0
- package/dist/scanning/agents/supabase-agent.d.ts.map +1 -0
- package/dist/scanning/agents/supabase-agent.js +484 -0
- package/dist/scanning/agents/supabase-agent.js.map +1 -0
- package/dist/scanning/orchestrator.d.ts +29 -0
- package/dist/scanning/orchestrator.d.ts.map +1 -0
- package/dist/scanning/orchestrator.js +186 -0
- package/dist/scanning/orchestrator.js.map +1 -0
- package/dist/scanning/tech-detector.d.ts +12 -0
- package/dist/scanning/tech-detector.d.ts.map +1 -0
- package/dist/scanning/tech-detector.js +252 -0
- package/dist/scanning/tech-detector.js.map +1 -0
- package/dist/scanning/types.d.ts +87 -0
- package/dist/scanning/types.d.ts.map +1 -0
- package/dist/scanning/types.js +5 -0
- package/dist/scanning/types.js.map +1 -0
- package/dist/utils/child-process.d.ts +2 -0
- package/dist/utils/child-process.d.ts.map +1 -0
- package/dist/utils/child-process.js +4 -0
- package/dist/utils/child-process.js.map +1 -0
- package/dist/utils/context-classifier.d.ts +11 -0
- package/dist/utils/context-classifier.d.ts.map +1 -0
- package/dist/utils/context-classifier.js +79 -0
- package/dist/utils/context-classifier.js.map +1 -0
- package/dist/utils/exec.d.ts +18 -0
- package/dist/utils/exec.d.ts.map +1 -0
- package/dist/utils/exec.js +51 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/file-filter.d.ts +4 -0
- package/dist/utils/file-filter.d.ts.map +1 -0
- package/dist/utils/file-filter.js +68 -0
- package/dist/utils/file-filter.js.map +1 -0
- package/dist/utils/fs.d.ts +2 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +5 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +27 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/post-processor.d.ts +10 -0
- package/dist/utils/post-processor.d.ts.map +1 -0
- package/dist/utils/post-processor.js +183 -0
- package/dist/utils/post-processor.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
# ============================================================
|
|
3
|
+
# React / Next.js Security Rules
|
|
4
|
+
# ============================================================
|
|
5
|
+
|
|
6
|
+
- id: datahogo-dangerous-inner-html
|
|
7
|
+
pattern: dangerouslySetInnerHTML={{ __html: $VAR }}
|
|
8
|
+
message: "dangerouslySetInnerHTML can lead to XSS if input is not sanitized with DOMPurify. Always sanitize user input before rendering HTML."
|
|
9
|
+
severity: WARNING
|
|
10
|
+
languages: [typescript, javascript, tsx, jsx]
|
|
11
|
+
metadata:
|
|
12
|
+
datahogo_id: 46
|
|
13
|
+
datahogo_category: "react-nextjs"
|
|
14
|
+
owasp_ref: "A03:2021"
|
|
15
|
+
|
|
16
|
+
- id: datahogo-tokens-in-localstorage
|
|
17
|
+
patterns:
|
|
18
|
+
- pattern: localStorage.setItem($KEY, $VALUE)
|
|
19
|
+
- metavariable-regex:
|
|
20
|
+
metavariable: $KEY
|
|
21
|
+
regex: (?i)(token|jwt|auth|session|access_token|refresh_token|api_key)
|
|
22
|
+
message: "Storing authentication tokens in localStorage is insecure. Use httpOnly cookies or secure session management instead."
|
|
23
|
+
severity: WARNING
|
|
24
|
+
languages: [typescript, javascript]
|
|
25
|
+
metadata:
|
|
26
|
+
datahogo_id: 47
|
|
27
|
+
datahogo_category: "react-nextjs"
|
|
28
|
+
owasp_ref: "A07:2021"
|
|
29
|
+
|
|
30
|
+
- id: datahogo-next-data-secrets
|
|
31
|
+
patterns:
|
|
32
|
+
- pattern: |
|
|
33
|
+
export async function getServerSideProps(...) {
|
|
34
|
+
...
|
|
35
|
+
return {
|
|
36
|
+
props: { ...$PROPS... }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
- metavariable-regex:
|
|
40
|
+
metavariable: $PROPS
|
|
41
|
+
regex: (?i)(api_key|secret|password|private_key|token|credentials)
|
|
42
|
+
message: "Sensitive data in getServerSideProps props will be serialized to __NEXT_DATA__ and exposed in the HTML. Never pass secrets to client components."
|
|
43
|
+
severity: WARNING
|
|
44
|
+
languages: [typescript, javascript]
|
|
45
|
+
metadata:
|
|
46
|
+
datahogo_id: 48
|
|
47
|
+
datahogo_category: "react-nextjs"
|
|
48
|
+
owasp_ref: "A01:2021"
|
|
49
|
+
|
|
50
|
+
- id: datahogo-open-cors-api
|
|
51
|
+
patterns:
|
|
52
|
+
- pattern-either:
|
|
53
|
+
- pattern: headers.set("Access-Control-Allow-Origin", "*")
|
|
54
|
+
- pattern: headers.append("Access-Control-Allow-Origin", "*")
|
|
55
|
+
- pattern: res.setHeader("Access-Control-Allow-Origin", "*")
|
|
56
|
+
- pattern-inside: |
|
|
57
|
+
export async function $METHOD(...) {
|
|
58
|
+
...
|
|
59
|
+
}
|
|
60
|
+
message: "Open CORS policy (Access-Control-Allow-Origin: *) allows any origin to access your API. Restrict to specific trusted domains."
|
|
61
|
+
severity: WARNING
|
|
62
|
+
languages: [typescript, javascript]
|
|
63
|
+
metadata:
|
|
64
|
+
datahogo_id: 50
|
|
65
|
+
datahogo_category: "react-nextjs"
|
|
66
|
+
owasp_ref: "A05:2021"
|
|
67
|
+
|
|
68
|
+
- id: datahogo-no-csrf-server-action
|
|
69
|
+
patterns:
|
|
70
|
+
- pattern: |
|
|
71
|
+
"use server";
|
|
72
|
+
export async function $ACTION(...) {
|
|
73
|
+
...
|
|
74
|
+
}
|
|
75
|
+
- pattern-not: |
|
|
76
|
+
"use server";
|
|
77
|
+
export async function $ACTION(...) {
|
|
78
|
+
...
|
|
79
|
+
verifyRequestOrigin(...)
|
|
80
|
+
...
|
|
81
|
+
}
|
|
82
|
+
- pattern-not: |
|
|
83
|
+
"use server";
|
|
84
|
+
export async function $ACTION(...) {
|
|
85
|
+
...
|
|
86
|
+
csrf(...)
|
|
87
|
+
...
|
|
88
|
+
}
|
|
89
|
+
message: "Server Actions should verify request origin to prevent CSRF attacks. Add origin validation or CSRF token checks."
|
|
90
|
+
severity: WARNING
|
|
91
|
+
languages: [typescript, javascript]
|
|
92
|
+
metadata:
|
|
93
|
+
datahogo_id: 51
|
|
94
|
+
datahogo_category: "react-nextjs"
|
|
95
|
+
|
|
96
|
+
- id: datahogo-nextjs-middleware-bypass
|
|
97
|
+
patterns:
|
|
98
|
+
- pattern-either:
|
|
99
|
+
- pattern: |
|
|
100
|
+
export const config = {
|
|
101
|
+
matcher: [..., $PATTERN, ...]
|
|
102
|
+
}
|
|
103
|
+
- pattern: |
|
|
104
|
+
export const config = {
|
|
105
|
+
matcher: $PATTERN
|
|
106
|
+
}
|
|
107
|
+
- metavariable-regex:
|
|
108
|
+
metavariable: $PATTERN
|
|
109
|
+
regex: ^(?!.*_next).*$
|
|
110
|
+
message: "Middleware matcher may be vulnerable to bypass. Ensure paths starting with /_next are excluded to prevent CVE-2025-29927."
|
|
111
|
+
severity: ERROR
|
|
112
|
+
languages: [typescript, javascript]
|
|
113
|
+
metadata:
|
|
114
|
+
datahogo_id: 52
|
|
115
|
+
datahogo_category: "react-nextjs"
|
|
116
|
+
|
|
117
|
+
- id: datahogo-auth-frontend-only
|
|
118
|
+
patterns:
|
|
119
|
+
- pattern: |
|
|
120
|
+
"use client";
|
|
121
|
+
...
|
|
122
|
+
- pattern-either:
|
|
123
|
+
- pattern: if ($USER.role === "admin") { ... }
|
|
124
|
+
- pattern: if ($USER.isAdmin) { ... }
|
|
125
|
+
- pattern: if ($SESSION.user.role === $ROLE) { ... }
|
|
126
|
+
- pattern: |
|
|
127
|
+
$USER.role === $ROLE ? ... : ...
|
|
128
|
+
message: "Auth checks in client components can be bypassed. Always verify permissions on the server (API routes, Server Components)."
|
|
129
|
+
severity: WARNING
|
|
130
|
+
languages: [typescript, javascript, tsx, jsx]
|
|
131
|
+
metadata:
|
|
132
|
+
datahogo_id: 57
|
|
133
|
+
datahogo_category: "vibecoding"
|
|
134
|
+
owasp_ref: "A01:2021"
|
|
135
|
+
|
|
136
|
+
- id: datahogo-api-no-auth-check
|
|
137
|
+
patterns:
|
|
138
|
+
- pattern: |
|
|
139
|
+
export async function $METHOD(req: Request) {
|
|
140
|
+
...
|
|
141
|
+
}
|
|
142
|
+
- metavariable-regex:
|
|
143
|
+
metavariable: $METHOD
|
|
144
|
+
regex: (GET|POST|PUT|PATCH|DELETE)
|
|
145
|
+
- pattern-not: |
|
|
146
|
+
export async function $METHOD(req: Request) {
|
|
147
|
+
...
|
|
148
|
+
$CLIENT.auth.getUser()
|
|
149
|
+
...
|
|
150
|
+
}
|
|
151
|
+
- pattern-not: |
|
|
152
|
+
export async function $METHOD(req: Request) {
|
|
153
|
+
...
|
|
154
|
+
getUser()
|
|
155
|
+
...
|
|
156
|
+
}
|
|
157
|
+
- pattern-not: |
|
|
158
|
+
export async function $METHOD(req: Request) {
|
|
159
|
+
...
|
|
160
|
+
getSession()
|
|
161
|
+
...
|
|
162
|
+
}
|
|
163
|
+
- pattern-not: |
|
|
164
|
+
export async function $METHOD(req: Request) {
|
|
165
|
+
...
|
|
166
|
+
verifyAuth()
|
|
167
|
+
...
|
|
168
|
+
}
|
|
169
|
+
message: "API route handler is missing authentication check. Always verify user session before processing requests."
|
|
170
|
+
severity: ERROR
|
|
171
|
+
languages: [typescript, javascript]
|
|
172
|
+
metadata:
|
|
173
|
+
datahogo_id: 58
|
|
174
|
+
datahogo_category: "vibecoding"
|
|
175
|
+
owasp_ref: "A01:2021"
|
|
176
|
+
|
|
177
|
+
- id: datahogo-dom-xss-document-write
|
|
178
|
+
patterns:
|
|
179
|
+
- pattern-either:
|
|
180
|
+
- pattern: document.write($VAR)
|
|
181
|
+
- pattern: $ELEM.innerHTML = $VAR
|
|
182
|
+
- pattern-either:
|
|
183
|
+
- pattern-regex: location
|
|
184
|
+
- pattern-regex: document\.URL
|
|
185
|
+
- pattern-regex: window\.name
|
|
186
|
+
- pattern-regex: document\.referrer
|
|
187
|
+
message: "DOM-based XSS vulnerability. Using document.write or innerHTML with untrusted input (location, URL, etc.) allows script injection."
|
|
188
|
+
severity: WARNING
|
|
189
|
+
languages: [typescript, javascript]
|
|
190
|
+
metadata:
|
|
191
|
+
datahogo_id: 86
|
|
192
|
+
datahogo_category: "frontend"
|
|
193
|
+
owasp_ref: "A03:2021"
|
|
194
|
+
|
|
195
|
+
- id: datahogo-stored-xss-no-sanitize
|
|
196
|
+
patterns:
|
|
197
|
+
- pattern-either:
|
|
198
|
+
- pattern: dangerouslySetInnerHTML={{ __html: $USER_DATA }}
|
|
199
|
+
- pattern: $ELEM.innerHTML = $USER_DATA
|
|
200
|
+
- pattern-not: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(...) }}
|
|
201
|
+
- pattern-not: $ELEM.innerHTML = DOMPurify.sanitize(...)
|
|
202
|
+
message: "Stored XSS risk. User-generated content rendered without sanitization. Use DOMPurify or avoid innerHTML entirely."
|
|
203
|
+
severity: WARNING
|
|
204
|
+
languages: [typescript, javascript, tsx, jsx]
|
|
205
|
+
metadata:
|
|
206
|
+
datahogo_id: 87
|
|
207
|
+
datahogo_category: "frontend"
|
|
208
|
+
owasp_ref: "A03:2021"
|
|
209
|
+
|
|
210
|
+
- id: datahogo-postmessage-no-origin
|
|
211
|
+
patterns:
|
|
212
|
+
- pattern: |
|
|
213
|
+
window.addEventListener("message", $HANDLER)
|
|
214
|
+
- pattern-not: |
|
|
215
|
+
window.addEventListener("message", ($EVENT) => {
|
|
216
|
+
...
|
|
217
|
+
$EVENT.origin
|
|
218
|
+
...
|
|
219
|
+
})
|
|
220
|
+
- pattern-not: |
|
|
221
|
+
window.addEventListener("message", function($EVENT) {
|
|
222
|
+
...
|
|
223
|
+
$EVENT.origin
|
|
224
|
+
...
|
|
225
|
+
})
|
|
226
|
+
message: "postMessage listener missing origin verification. Always check event.origin to prevent XSS from untrusted domains."
|
|
227
|
+
severity: WARNING
|
|
228
|
+
languages: [typescript, javascript]
|
|
229
|
+
metadata:
|
|
230
|
+
datahogo_id: 88
|
|
231
|
+
datahogo_category: "frontend"
|
|
232
|
+
owasp_ref: "A01:2021"
|
|
233
|
+
|
|
234
|
+
- id: datahogo-no-x-frame-options
|
|
235
|
+
patterns:
|
|
236
|
+
- pattern-either:
|
|
237
|
+
- pattern: |
|
|
238
|
+
export async function $METHOD(...) {
|
|
239
|
+
...
|
|
240
|
+
return new Response(...)
|
|
241
|
+
}
|
|
242
|
+
- pattern: |
|
|
243
|
+
export async function middleware(...) {
|
|
244
|
+
...
|
|
245
|
+
return NextResponse.next()
|
|
246
|
+
}
|
|
247
|
+
- pattern-not: |
|
|
248
|
+
$HEADERS.set("X-Frame-Options", ...)
|
|
249
|
+
- pattern-not: |
|
|
250
|
+
$HEADERS.append("X-Frame-Options", ...)
|
|
251
|
+
message: "Missing X-Frame-Options header allows clickjacking attacks. Set to DENY or SAMEORIGIN in middleware or API responses."
|
|
252
|
+
severity: WARNING
|
|
253
|
+
languages: [typescript, javascript]
|
|
254
|
+
metadata:
|
|
255
|
+
datahogo_id: 89
|
|
256
|
+
datahogo_category: "frontend"
|
|
257
|
+
|
|
258
|
+
- id: datahogo-iframe-no-sandbox
|
|
259
|
+
pattern-regex: <iframe(?:(?!sandbox)[^>])*src=
|
|
260
|
+
message: "Iframe without sandbox attribute can execute scripts and access parent window. Add sandbox='allow-scripts allow-same-origin' with minimal permissions."
|
|
261
|
+
severity: WARNING
|
|
262
|
+
languages: [typescript, javascript, tsx, jsx]
|
|
263
|
+
metadata:
|
|
264
|
+
datahogo_id: 90
|
|
265
|
+
datahogo_category: "frontend"
|
|
266
|
+
|
|
267
|
+
- id: datahogo-cdn-no-integrity
|
|
268
|
+
patterns:
|
|
269
|
+
- pattern-either:
|
|
270
|
+
- pattern-regex: <script\s+src=["']https?://(?:cdn|cdnjs|unpkg|jsdelivr)
|
|
271
|
+
- pattern-regex: <link\s+(?:[^>]*\s+)?href=["']https?://(?:cdn|cdnjs|unpkg|jsdelivr)
|
|
272
|
+
- pattern-not-regex: integrity=["']sha
|
|
273
|
+
message: "CDN resource loaded without Subresource Integrity (SRI). Add integrity attribute to prevent tampering."
|
|
274
|
+
severity: INFO
|
|
275
|
+
languages: [typescript, javascript, tsx, jsx, html]
|
|
276
|
+
metadata:
|
|
277
|
+
datahogo_id: 91
|
|
278
|
+
datahogo_category: "frontend"
|
|
279
|
+
|
|
280
|
+
- id: datahogo-client-redirect-unsafe
|
|
281
|
+
patterns:
|
|
282
|
+
- pattern-either:
|
|
283
|
+
- pattern: window.location = $URL
|
|
284
|
+
- pattern: window.location.href = $URL
|
|
285
|
+
- pattern: location.href = $URL
|
|
286
|
+
- pattern: router.push($URL)
|
|
287
|
+
- pattern-either:
|
|
288
|
+
- pattern: window.location = $PARAMS.get(...)
|
|
289
|
+
- pattern: window.location.href = searchParams.get(...)
|
|
290
|
+
- pattern: router.push(req.query.$PARAM)
|
|
291
|
+
message: "Client-side redirect with unvalidated user input enables open redirect attacks. Validate URL against whitelist before redirecting."
|
|
292
|
+
severity: WARNING
|
|
293
|
+
languages: [typescript, javascript]
|
|
294
|
+
metadata:
|
|
295
|
+
datahogo_id: 92
|
|
296
|
+
datahogo_category: "frontend"
|
|
297
|
+
owasp_ref: "A01:2021"
|
|
298
|
+
|
|
299
|
+
- id: datahogo-sensitive-data-in-url
|
|
300
|
+
patterns:
|
|
301
|
+
- pattern-either:
|
|
302
|
+
- pattern: router.push(`/...?token=${$TOKEN}`)
|
|
303
|
+
- pattern: window.location = `...?password=${$PWD}`
|
|
304
|
+
- pattern: redirect(`...?api_key=${$KEY}`)
|
|
305
|
+
- metavariable-regex:
|
|
306
|
+
metavariable: $TOKEN
|
|
307
|
+
regex: (?i)(token|password|secret|api_key|private_key|credentials)
|
|
308
|
+
message: "Sensitive data in URL query parameters will be logged in browser history and server logs. Use POST body or secure headers instead."
|
|
309
|
+
severity: WARNING
|
|
310
|
+
languages: [typescript, javascript]
|
|
311
|
+
metadata:
|
|
312
|
+
datahogo_id: 93
|
|
313
|
+
datahogo_category: "frontend"
|
|
314
|
+
owasp_ref: "A04:2021"
|
|
315
|
+
|
|
316
|
+
- id: datahogo-no-error-boundary
|
|
317
|
+
patterns:
|
|
318
|
+
- pattern: |
|
|
319
|
+
export default function $LAYOUT({ children }: { children: React.ReactNode }) {
|
|
320
|
+
return (
|
|
321
|
+
...
|
|
322
|
+
{children}
|
|
323
|
+
...
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
- pattern-not: |
|
|
327
|
+
export default function $LAYOUT({ children }: { children: React.ReactNode }) {
|
|
328
|
+
return (
|
|
329
|
+
...
|
|
330
|
+
<ErrorBoundary>
|
|
331
|
+
{children}
|
|
332
|
+
</ErrorBoundary>
|
|
333
|
+
...
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
- pattern-inside: |
|
|
337
|
+
...
|
|
338
|
+
app/$ROUTE/layout.tsx
|
|
339
|
+
message: "No React Error Boundary in layout. Unhandled errors can expose stack traces to users. Wrap children in ErrorBoundary component."
|
|
340
|
+
severity: INFO
|
|
341
|
+
languages: [typescript, javascript, tsx, jsx]
|
|
342
|
+
metadata:
|
|
343
|
+
datahogo_id: 174
|
|
344
|
+
datahogo_category: "best-practices"
|
|
345
|
+
|
|
346
|
+
# ============================================================
|
|
347
|
+
# Additional Next.js Specific Patterns
|
|
348
|
+
# ============================================================
|
|
349
|
+
|
|
350
|
+
- id: datahogo-next-env-client-exposed
|
|
351
|
+
patterns:
|
|
352
|
+
- pattern-either:
|
|
353
|
+
- pattern: process.env.$VAR
|
|
354
|
+
- pattern: env.$VAR
|
|
355
|
+
- metavariable-regex:
|
|
356
|
+
metavariable: $VAR
|
|
357
|
+
regex: ^(?!NEXT_PUBLIC_).*(?i)(secret|key|password|private|token|credential).*$
|
|
358
|
+
- pattern-inside: |
|
|
359
|
+
"use client";
|
|
360
|
+
...
|
|
361
|
+
message: "Server-side environment variable accessed in client component. Only NEXT_PUBLIC_* vars are available in the browser. This will be undefined."
|
|
362
|
+
severity: WARNING
|
|
363
|
+
languages: [typescript, javascript]
|
|
364
|
+
metadata:
|
|
365
|
+
datahogo_id: 134
|
|
366
|
+
datahogo_category: "serverless"
|
|
367
|
+
|
|
368
|
+
- id: datahogo-next-image-unoptimized
|
|
369
|
+
patterns:
|
|
370
|
+
- pattern: <img src={$SRC} ... />
|
|
371
|
+
- pattern-not: <Image src={$SRC} ... />
|
|
372
|
+
- pattern-inside: |
|
|
373
|
+
export default function $COMPONENT(...) {
|
|
374
|
+
...
|
|
375
|
+
}
|
|
376
|
+
message: "Using native <img> tag instead of Next.js <Image>. This bypasses automatic optimization, lazy loading, and responsive images."
|
|
377
|
+
severity: INFO
|
|
378
|
+
languages: [typescript, javascript, tsx, jsx]
|
|
379
|
+
metadata:
|
|
380
|
+
datahogo_id: 165
|
|
381
|
+
datahogo_category: "best-practices"
|
|
382
|
+
|
|
383
|
+
- id: datahogo-use-client-entire-route
|
|
384
|
+
patterns:
|
|
385
|
+
- pattern: |
|
|
386
|
+
"use client";
|
|
387
|
+
...
|
|
388
|
+
export default function Page() {
|
|
389
|
+
...
|
|
390
|
+
}
|
|
391
|
+
- pattern-not: |
|
|
392
|
+
"use client";
|
|
393
|
+
...
|
|
394
|
+
export default function Page() {
|
|
395
|
+
...
|
|
396
|
+
useState(...)
|
|
397
|
+
...
|
|
398
|
+
}
|
|
399
|
+
- pattern-not: |
|
|
400
|
+
"use client";
|
|
401
|
+
...
|
|
402
|
+
export default function Page() {
|
|
403
|
+
...
|
|
404
|
+
useEffect(...)
|
|
405
|
+
...
|
|
406
|
+
}
|
|
407
|
+
- pattern-not: |
|
|
408
|
+
"use client";
|
|
409
|
+
...
|
|
410
|
+
export default function Page() {
|
|
411
|
+
...
|
|
412
|
+
onClick=...
|
|
413
|
+
...
|
|
414
|
+
}
|
|
415
|
+
message: "Entire page marked as 'use client' without obvious client-side features. Consider using Server Components for better performance."
|
|
416
|
+
severity: INFO
|
|
417
|
+
languages: [typescript, javascript, tsx, jsx]
|
|
418
|
+
metadata:
|
|
419
|
+
datahogo_id: 171
|
|
420
|
+
datahogo_category: "best-practices"
|
|
421
|
+
|
|
422
|
+
- id: datahogo-revalidate-with-cache-no-store
|
|
423
|
+
patterns:
|
|
424
|
+
- pattern: |
|
|
425
|
+
fetch($URL, {
|
|
426
|
+
...
|
|
427
|
+
cache: "no-store",
|
|
428
|
+
...
|
|
429
|
+
next: { revalidate: $TIME }
|
|
430
|
+
...
|
|
431
|
+
})
|
|
432
|
+
message: "Using cache: 'no-store' with revalidate is contradictory. Remove cache option or use cache: 'force-cache' with revalidate."
|
|
433
|
+
severity: WARNING
|
|
434
|
+
languages: [typescript, javascript]
|
|
435
|
+
metadata:
|
|
436
|
+
datahogo_id: 171
|
|
437
|
+
datahogo_category: "best-practices"
|
|
438
|
+
|
|
439
|
+
- id: datahogo-server-action-no-revalidate
|
|
440
|
+
patterns:
|
|
441
|
+
- pattern: |
|
|
442
|
+
"use server";
|
|
443
|
+
export async function $ACTION(...) {
|
|
444
|
+
...
|
|
445
|
+
$DB.insert(...)
|
|
446
|
+
...
|
|
447
|
+
}
|
|
448
|
+
- pattern-not: |
|
|
449
|
+
"use server";
|
|
450
|
+
export async function $ACTION(...) {
|
|
451
|
+
...
|
|
452
|
+
revalidatePath(...)
|
|
453
|
+
...
|
|
454
|
+
}
|
|
455
|
+
- pattern-not: |
|
|
456
|
+
"use server";
|
|
457
|
+
export async function $ACTION(...) {
|
|
458
|
+
...
|
|
459
|
+
revalidateTag(...)
|
|
460
|
+
...
|
|
461
|
+
}
|
|
462
|
+
message: "Server Action modifies data without revalidating cache. Call revalidatePath() or revalidateTag() to update stale data."
|
|
463
|
+
severity: WARNING
|
|
464
|
+
languages: [typescript, javascript]
|
|
465
|
+
metadata:
|
|
466
|
+
datahogo_id: 171
|
|
467
|
+
datahogo_category: "best-practices"
|
|
468
|
+
|
|
469
|
+
- id: datahogo-dynamic-force-dynamic-missing
|
|
470
|
+
patterns:
|
|
471
|
+
- pattern: |
|
|
472
|
+
export async function $METHOD(request: Request) {
|
|
473
|
+
...
|
|
474
|
+
$PARAMS.get(...)
|
|
475
|
+
...
|
|
476
|
+
}
|
|
477
|
+
- pattern-not: |
|
|
478
|
+
export const dynamic = "force-dynamic"
|
|
479
|
+
...
|
|
480
|
+
export async function $METHOD(request: Request) {
|
|
481
|
+
...
|
|
482
|
+
}
|
|
483
|
+
- pattern-not: |
|
|
484
|
+
export const revalidate = 0
|
|
485
|
+
...
|
|
486
|
+
export async function $METHOD(request: Request) {
|
|
487
|
+
...
|
|
488
|
+
}
|
|
489
|
+
message: "Route uses dynamic request data (searchParams, cookies, headers) but doesn't set 'export const dynamic = force-dynamic'. May serve stale cached data."
|
|
490
|
+
severity: WARNING
|
|
491
|
+
languages: [typescript, javascript]
|
|
492
|
+
metadata:
|
|
493
|
+
datahogo_id: 171
|
|
494
|
+
datahogo_category: "best-practices"
|
|
495
|
+
|
|
496
|
+
- id: datahogo-console-log-in-production
|
|
497
|
+
patterns:
|
|
498
|
+
- pattern-either:
|
|
499
|
+
- pattern: console.log(...)
|
|
500
|
+
- pattern: console.info(...)
|
|
501
|
+
- pattern: console.warn(...)
|
|
502
|
+
- pattern-inside: |
|
|
503
|
+
export async function $METHOD(...) {
|
|
504
|
+
...
|
|
505
|
+
}
|
|
506
|
+
message: "console.log in production code can leak sensitive data and impacts performance. Use structured logging (Sentry, Winston) instead."
|
|
507
|
+
severity: WARNING
|
|
508
|
+
languages: [typescript, javascript]
|
|
509
|
+
metadata:
|
|
510
|
+
datahogo_id: 172
|
|
511
|
+
datahogo_category: "best-practices"
|
|
512
|
+
|
|
513
|
+
- id: datahogo-api-route-throws-without-try
|
|
514
|
+
patterns:
|
|
515
|
+
- pattern: |
|
|
516
|
+
export async function $METHOD(...) {
|
|
517
|
+
...
|
|
518
|
+
}
|
|
519
|
+
- pattern-not: |
|
|
520
|
+
export async function $METHOD(...) {
|
|
521
|
+
try {
|
|
522
|
+
...
|
|
523
|
+
} catch (...) {
|
|
524
|
+
...
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
message: "API route without try-catch. Unhandled errors will expose stack traces to clients. Wrap in try-catch and return user-friendly errors."
|
|
528
|
+
severity: WARNING
|
|
529
|
+
languages: [typescript, javascript]
|
|
530
|
+
metadata:
|
|
531
|
+
datahogo_id: 173
|
|
532
|
+
datahogo_category: "best-practices"
|