oauthlint-rules 0.2.6 → 0.3.1
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/dist/schema.d.ts +60 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +12 -0
- package/dist/schema.js.map +1 -1
- package/package.json +1 -1
- package/rules/cookie/no-httponly.yml +5 -1
- package/rules/cookie/no-samesite.yml +5 -1
- package/rules/cookie/no-secure.yml +8 -5
- package/rules/cookie/samesite-none-insecure.yml +22 -0
- package/rules/flow/oauth-credential-in-log.yml +97 -0
- package/rules/flow/open-redirect.yml +11 -0
- package/rules/flow/ssrf.yml +19 -0
- package/rules/flow/timing-unsafe-compare.yml +19 -0
- package/rules/go/cookie/insecure.yml +15 -6
- package/rules/go/flow/oauth-credential-in-log.yml +90 -0
- package/rules/go/jwt/untrusted-verify-key.yml +68 -0
- package/rules/go/oauth/insecure-token-endpoint.yml +39 -0
- package/rules/go/oauth/ropc-grant.yml +64 -0
- package/rules/go/oauth/static-state.yml +39 -0
- package/rules/go/tls/insecure-skip-verify.yml +14 -3
- package/rules/go/tls/min-version.yml +12 -3
- package/rules/java/cors/credentialed-wildcard.yml +44 -0
- package/rules/java/jwt/none-algorithm.yml +37 -0
- package/rules/java/jwt/untrusted-verify-key.yml +54 -0
- package/rules/java/oauth/insecure-token-endpoint.yml +39 -0
- package/rules/java/oauth/ropc-grant.yml +45 -0
- package/rules/java/oauth/static-state.yml +38 -0
- package/rules/java/web/permit-all-actuator.yml +43 -0
- package/rules/jwt/ignore-expiration.yml +21 -11
- package/rules/jwt/untrusted-verify-key.yml +77 -0
- package/rules/oauth/insecure-token-endpoint.yml +41 -0
- package/rules/oauth/ropc-grant.yml +48 -0
- package/rules/oauth/static-state.yml +49 -0
- package/rules/py/flow/oauth-credential-in-log.yml +91 -0
- package/rules/py/jwt/untrusted-verify-key.yml +75 -0
- package/rules/py/oauth/insecure-token-endpoint.yml +40 -0
- package/rules/py/oauth/insecure-transport-env.yml +43 -0
- package/rules/py/oauth/ropc-grant.yml +58 -0
- package/rules/py/oauth/static-state.yml +45 -0
- package/rules/py/oauth/token-request-verify-disabled.yml +41 -0
- package/rules/rust/oauth/insecure-token-endpoint.yml +39 -0
- package/rules/rust/oauth/ropc-grant.yml +57 -0
- package/rules/rust/oauth/static-state.yml +58 -0
package/dist/schema.d.ts
CHANGED
|
@@ -81,6 +81,16 @@ export declare const RuleSchema: z.ZodEffects<z.ZodObject<{
|
|
|
81
81
|
'pattern-either-regex': z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
82
82
|
patterns: z.ZodOptional<z.ZodArray<z.ZodType<unknown, z.ZodTypeDef, unknown>, "many">>;
|
|
83
83
|
fix: z.ZodOptional<z.ZodString>;
|
|
84
|
+
paths: z.ZodOptional<z.ZodObject<{
|
|
85
|
+
include: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
86
|
+
exclude: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
87
|
+
}, "strip", z.ZodTypeAny, {
|
|
88
|
+
include?: string[] | undefined;
|
|
89
|
+
exclude?: string[] | undefined;
|
|
90
|
+
}, {
|
|
91
|
+
include?: string[] | undefined;
|
|
92
|
+
exclude?: string[] | undefined;
|
|
93
|
+
}>>;
|
|
84
94
|
mode: z.ZodOptional<z.ZodEnum<["search", "taint"]>>;
|
|
85
95
|
'pattern-sources': z.ZodOptional<z.ZodArray<z.ZodType<unknown, z.ZodTypeDef, unknown>, "many">>;
|
|
86
96
|
'pattern-sinks': z.ZodOptional<z.ZodArray<z.ZodType<unknown, z.ZodTypeDef, unknown>, "many">>;
|
|
@@ -109,6 +119,10 @@ export declare const RuleSchema: z.ZodEffects<z.ZodObject<{
|
|
|
109
119
|
'pattern-either-regex'?: string[] | undefined;
|
|
110
120
|
patterns?: unknown[] | undefined;
|
|
111
121
|
fix?: string | undefined;
|
|
122
|
+
paths?: {
|
|
123
|
+
include?: string[] | undefined;
|
|
124
|
+
exclude?: string[] | undefined;
|
|
125
|
+
} | undefined;
|
|
112
126
|
mode?: "search" | "taint" | undefined;
|
|
113
127
|
'pattern-sources'?: unknown[] | undefined;
|
|
114
128
|
'pattern-sinks'?: unknown[] | undefined;
|
|
@@ -137,6 +151,10 @@ export declare const RuleSchema: z.ZodEffects<z.ZodObject<{
|
|
|
137
151
|
'pattern-either-regex'?: string[] | undefined;
|
|
138
152
|
patterns?: unknown[] | undefined;
|
|
139
153
|
fix?: string | undefined;
|
|
154
|
+
paths?: {
|
|
155
|
+
include?: string[] | undefined;
|
|
156
|
+
exclude?: string[] | undefined;
|
|
157
|
+
} | undefined;
|
|
140
158
|
mode?: "search" | "taint" | undefined;
|
|
141
159
|
'pattern-sources'?: unknown[] | undefined;
|
|
142
160
|
'pattern-sinks'?: unknown[] | undefined;
|
|
@@ -165,6 +183,10 @@ export declare const RuleSchema: z.ZodEffects<z.ZodObject<{
|
|
|
165
183
|
'pattern-either-regex'?: string[] | undefined;
|
|
166
184
|
patterns?: unknown[] | undefined;
|
|
167
185
|
fix?: string | undefined;
|
|
186
|
+
paths?: {
|
|
187
|
+
include?: string[] | undefined;
|
|
188
|
+
exclude?: string[] | undefined;
|
|
189
|
+
} | undefined;
|
|
168
190
|
mode?: "search" | "taint" | undefined;
|
|
169
191
|
'pattern-sources'?: unknown[] | undefined;
|
|
170
192
|
'pattern-sinks'?: unknown[] | undefined;
|
|
@@ -193,6 +215,10 @@ export declare const RuleSchema: z.ZodEffects<z.ZodObject<{
|
|
|
193
215
|
'pattern-either-regex'?: string[] | undefined;
|
|
194
216
|
patterns?: unknown[] | undefined;
|
|
195
217
|
fix?: string | undefined;
|
|
218
|
+
paths?: {
|
|
219
|
+
include?: string[] | undefined;
|
|
220
|
+
exclude?: string[] | undefined;
|
|
221
|
+
} | undefined;
|
|
196
222
|
mode?: "search" | "taint" | undefined;
|
|
197
223
|
'pattern-sources'?: unknown[] | undefined;
|
|
198
224
|
'pattern-sinks'?: unknown[] | undefined;
|
|
@@ -242,6 +268,16 @@ export declare const RuleFileSchema: z.ZodObject<{
|
|
|
242
268
|
'pattern-either-regex': z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
243
269
|
patterns: z.ZodOptional<z.ZodArray<z.ZodType<unknown, z.ZodTypeDef, unknown>, "many">>;
|
|
244
270
|
fix: z.ZodOptional<z.ZodString>;
|
|
271
|
+
paths: z.ZodOptional<z.ZodObject<{
|
|
272
|
+
include: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
273
|
+
exclude: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
274
|
+
}, "strip", z.ZodTypeAny, {
|
|
275
|
+
include?: string[] | undefined;
|
|
276
|
+
exclude?: string[] | undefined;
|
|
277
|
+
}, {
|
|
278
|
+
include?: string[] | undefined;
|
|
279
|
+
exclude?: string[] | undefined;
|
|
280
|
+
}>>;
|
|
245
281
|
mode: z.ZodOptional<z.ZodEnum<["search", "taint"]>>;
|
|
246
282
|
'pattern-sources': z.ZodOptional<z.ZodArray<z.ZodType<unknown, z.ZodTypeDef, unknown>, "many">>;
|
|
247
283
|
'pattern-sinks': z.ZodOptional<z.ZodArray<z.ZodType<unknown, z.ZodTypeDef, unknown>, "many">>;
|
|
@@ -270,6 +306,10 @@ export declare const RuleFileSchema: z.ZodObject<{
|
|
|
270
306
|
'pattern-either-regex'?: string[] | undefined;
|
|
271
307
|
patterns?: unknown[] | undefined;
|
|
272
308
|
fix?: string | undefined;
|
|
309
|
+
paths?: {
|
|
310
|
+
include?: string[] | undefined;
|
|
311
|
+
exclude?: string[] | undefined;
|
|
312
|
+
} | undefined;
|
|
273
313
|
mode?: "search" | "taint" | undefined;
|
|
274
314
|
'pattern-sources'?: unknown[] | undefined;
|
|
275
315
|
'pattern-sinks'?: unknown[] | undefined;
|
|
@@ -298,6 +338,10 @@ export declare const RuleFileSchema: z.ZodObject<{
|
|
|
298
338
|
'pattern-either-regex'?: string[] | undefined;
|
|
299
339
|
patterns?: unknown[] | undefined;
|
|
300
340
|
fix?: string | undefined;
|
|
341
|
+
paths?: {
|
|
342
|
+
include?: string[] | undefined;
|
|
343
|
+
exclude?: string[] | undefined;
|
|
344
|
+
} | undefined;
|
|
301
345
|
mode?: "search" | "taint" | undefined;
|
|
302
346
|
'pattern-sources'?: unknown[] | undefined;
|
|
303
347
|
'pattern-sinks'?: unknown[] | undefined;
|
|
@@ -326,6 +370,10 @@ export declare const RuleFileSchema: z.ZodObject<{
|
|
|
326
370
|
'pattern-either-regex'?: string[] | undefined;
|
|
327
371
|
patterns?: unknown[] | undefined;
|
|
328
372
|
fix?: string | undefined;
|
|
373
|
+
paths?: {
|
|
374
|
+
include?: string[] | undefined;
|
|
375
|
+
exclude?: string[] | undefined;
|
|
376
|
+
} | undefined;
|
|
329
377
|
mode?: "search" | "taint" | undefined;
|
|
330
378
|
'pattern-sources'?: unknown[] | undefined;
|
|
331
379
|
'pattern-sinks'?: unknown[] | undefined;
|
|
@@ -354,6 +402,10 @@ export declare const RuleFileSchema: z.ZodObject<{
|
|
|
354
402
|
'pattern-either-regex'?: string[] | undefined;
|
|
355
403
|
patterns?: unknown[] | undefined;
|
|
356
404
|
fix?: string | undefined;
|
|
405
|
+
paths?: {
|
|
406
|
+
include?: string[] | undefined;
|
|
407
|
+
exclude?: string[] | undefined;
|
|
408
|
+
} | undefined;
|
|
357
409
|
mode?: "search" | "taint" | undefined;
|
|
358
410
|
'pattern-sources'?: unknown[] | undefined;
|
|
359
411
|
'pattern-sinks'?: unknown[] | undefined;
|
|
@@ -384,6 +436,10 @@ export declare const RuleFileSchema: z.ZodObject<{
|
|
|
384
436
|
'pattern-either-regex'?: string[] | undefined;
|
|
385
437
|
patterns?: unknown[] | undefined;
|
|
386
438
|
fix?: string | undefined;
|
|
439
|
+
paths?: {
|
|
440
|
+
include?: string[] | undefined;
|
|
441
|
+
exclude?: string[] | undefined;
|
|
442
|
+
} | undefined;
|
|
387
443
|
mode?: "search" | "taint" | undefined;
|
|
388
444
|
'pattern-sources'?: unknown[] | undefined;
|
|
389
445
|
'pattern-sinks'?: unknown[] | undefined;
|
|
@@ -414,6 +470,10 @@ export declare const RuleFileSchema: z.ZodObject<{
|
|
|
414
470
|
'pattern-either-regex'?: string[] | undefined;
|
|
415
471
|
patterns?: unknown[] | undefined;
|
|
416
472
|
fix?: string | undefined;
|
|
473
|
+
paths?: {
|
|
474
|
+
include?: string[] | undefined;
|
|
475
|
+
exclude?: string[] | undefined;
|
|
476
|
+
} | undefined;
|
|
417
477
|
mode?: "search" | "taint" | undefined;
|
|
418
478
|
'pattern-sources'?: unknown[] | undefined;
|
|
419
479
|
'pattern-sinks'?: unknown[] | undefined;
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,cAAc,yCAAuC,CAAC;AACnE,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,cAAc,yCAAuC,CAAC;AACnE,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAqBxE,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CpB,CAAC;AAEJ,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEzB,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
|
package/dist/schema.js
CHANGED
|
@@ -32,6 +32,16 @@ export const OAuthLintMetadataSchema = z.object({
|
|
|
32
32
|
* Semgrep accepts many shapes; we only validate the fields we rely on.
|
|
33
33
|
*/
|
|
34
34
|
const PatternEntrySchema = z.lazy(() => z.union([z.string(), z.record(z.string(), z.unknown()), z.array(PatternEntrySchema)]));
|
|
35
|
+
/**
|
|
36
|
+
* Semgrep `paths:` filter. Lets a rule scope itself to (or away from) files by
|
|
37
|
+
* glob. We use `exclude` to keep production-grant rules from firing on test,
|
|
38
|
+
* example, vendored, or generated code — locations that should never carry a
|
|
39
|
+
* real application's OAuth misuse. Passed straight through to Semgrep.
|
|
40
|
+
*/
|
|
41
|
+
const PathsSchema = z.object({
|
|
42
|
+
include: z.array(z.string()).optional(),
|
|
43
|
+
exclude: z.array(z.string()).optional(),
|
|
44
|
+
});
|
|
35
45
|
export const RuleSchema = z
|
|
36
46
|
.object({
|
|
37
47
|
// auth.<category>.<name> for the JS/TS pack; language packs prepend a
|
|
@@ -53,6 +63,8 @@ export const RuleSchema = z
|
|
|
53
63
|
'pattern-either-regex': z.array(z.string()).optional(),
|
|
54
64
|
patterns: z.array(PatternEntrySchema).optional(),
|
|
55
65
|
fix: z.string().optional(),
|
|
66
|
+
// Scope a rule to/away from files by glob (passed through to Semgrep).
|
|
67
|
+
paths: PathsSchema.optional(),
|
|
56
68
|
// Taint-mode rules (Semgrep dataflow).
|
|
57
69
|
mode: z.enum(['search', 'taint']).optional(),
|
|
58
70
|
'pattern-sources': z.array(PatternEntrySchema).optional(),
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -39,7 +39,11 @@ rules:
|
|
|
39
39
|
- metavariable-regex:
|
|
40
40
|
metavariable: $NAME
|
|
41
41
|
regex: ^(['"])(?:[a-zA-Z0-9_-]*(?:session|sid|sess|auth|token|jwt|refresh|access)[a-zA-Z0-9_-]*)\1$
|
|
42
|
-
|
|
42
|
+
# No autofix. As with `auth.cookie.no-secure`, a single rule-level `fix:`
|
|
43
|
+
# cannot cover all three matched shapes safely: a spread template corrupts
|
|
44
|
+
# the 2-arg form (unbound `$OPTS` is emitted literally) and does not resolve
|
|
45
|
+
# an explicit `httpOnly: false`. Inserting a missing key is not a clean
|
|
46
|
+
# literal replacement, so this rule deliberately ships no `fix:`.
|
|
43
47
|
metadata:
|
|
44
48
|
oauthlint-rule-id: AUTH-COOKIE-002
|
|
45
49
|
oauthlint-doc-url: https://oauthlint.dev/rules/cookie-no-httponly
|
|
@@ -41,7 +41,11 @@ rules:
|
|
|
41
41
|
- pattern: "{..., sameSite: 'None', ...}"
|
|
42
42
|
- pattern: '{..., sameSite: "None", ...}'
|
|
43
43
|
- pattern-not: '{..., secure: true, ...}'
|
|
44
|
-
|
|
44
|
+
# No autofix. The correct `SameSite` value is context-dependent — `Strict`
|
|
45
|
+
# suits most auth flows but breaks OAuth callbacks, which need `Lax`, while a
|
|
46
|
+
# cookie deliberately set cross-site needs `None` plus `Secure`. The tool
|
|
47
|
+
# cannot know which, and a spread template would also corrupt the 2-arg form
|
|
48
|
+
# (unbound `$OPTS`). So this rule deliberately ships no `fix:`.
|
|
45
49
|
metadata:
|
|
46
50
|
oauthlint-rule-id: AUTH-COOKIE-003
|
|
47
51
|
oauthlint-doc-url: https://oauthlint.dev/rules/cookie-no-samesite
|
|
@@ -39,11 +39,14 @@ rules:
|
|
|
39
39
|
- metavariable-regex:
|
|
40
40
|
metavariable: $NAME
|
|
41
41
|
regex: ^(['"])(?:[a-zA-Z0-9_-]*(?:session|sid|sess|auth|token|jwt|refresh|access)[a-zA-Z0-9_-]*)\1$
|
|
42
|
-
#
|
|
43
|
-
# options object
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
|
|
42
|
+
# No autofix. A single rule-level `fix:` cannot be made safe here: this rule
|
|
43
|
+
# matches three shapes — an options object missing `secure`, the 2-arg form
|
|
44
|
+
# with no options object at all, and an explicit `secure: false`. A spread
|
|
45
|
+
# template like `{ ...$OPTS, secure: true }` corrupts the 2-arg form (`$OPTS`
|
|
46
|
+
# is unbound, so Semgrep emits the literal text `$OPTS`) and leaves an
|
|
47
|
+
# explicit `secure: false` key in place (the finding re-fires). Inserting a
|
|
48
|
+
# brand-new argument/key is not a clean literal replacement, so we ship no
|
|
49
|
+
# `fix:` rather than a rewrite that can break source.
|
|
47
50
|
metadata:
|
|
48
51
|
oauthlint-rule-id: AUTH-COOKIE-001
|
|
49
52
|
oauthlint-doc-url: https://oauthlint.dev/rules/cookie-no-secure
|
|
@@ -4,6 +4,28 @@ rules:
|
|
|
4
4
|
- javascript
|
|
5
5
|
- typescript
|
|
6
6
|
severity: WARNING
|
|
7
|
+
# Non-production code (example apps, demos, docs, vendored copies, tests)
|
|
8
|
+
# is not the library surface users ship, so findings there are noise for a
|
|
9
|
+
# low-FP linter. In particular this skips vendored third-party cookie
|
|
10
|
+
# serializers (e.g. packages/core/src/lib/vendored/cookie.ts) whose
|
|
11
|
+
# incremental `"; SameSite=None"` attribute fragment is not a complete
|
|
12
|
+
# insecure cookie. Globs intentionally omit `**/tests/**` / `**/fixtures/**`
|
|
13
|
+
# so the rule still fires on its own fixtures under rules/tests/fixtures/.
|
|
14
|
+
paths:
|
|
15
|
+
exclude:
|
|
16
|
+
- "**/test/**"
|
|
17
|
+
- "**/__tests__/**"
|
|
18
|
+
- "**/*.test.*"
|
|
19
|
+
- "**/*.spec.*"
|
|
20
|
+
- "**/example/**"
|
|
21
|
+
- "**/examples/**"
|
|
22
|
+
- "**/apps/**"
|
|
23
|
+
- "**/docs/**"
|
|
24
|
+
- "**/__mocks__/**"
|
|
25
|
+
- "**/mocks/**"
|
|
26
|
+
- "**/vendored/**"
|
|
27
|
+
- "**/node_modules/**"
|
|
28
|
+
- "**/*.stories.*"
|
|
7
29
|
message: |
|
|
8
30
|
A cookie is being set with `SameSite=None` but WITHOUT `Secure`.
|
|
9
31
|
`SameSite=None` *requires* `Secure`: modern browsers reject a
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: auth.flow.oauth-credential-in-log
|
|
3
|
+
languages:
|
|
4
|
+
- javascript
|
|
5
|
+
- typescript
|
|
6
|
+
severity: ERROR
|
|
7
|
+
message: |
|
|
8
|
+
An OAuth/OIDC credential taken from the request — an authorization
|
|
9
|
+
`code`, an `access_token` / `refresh_token` / `id_token`, a bearer
|
|
10
|
+
`token`, a `client_secret`, or the raw `Authorization` header — flows
|
|
11
|
+
into a logging call (`console.*` or `logger.*`). Logs are written to
|
|
12
|
+
files, shipped to aggregators (Datadog, Splunk, CloudWatch) and read by
|
|
13
|
+
people and systems that should never see live credentials. A leaked
|
|
14
|
+
authorization code or token can be replayed to impersonate the user or
|
|
15
|
+
complete the OAuth exchange (CWE-532).
|
|
16
|
+
|
|
17
|
+
Never log the raw credential. Redact or mask it before logging
|
|
18
|
+
(`token.slice(0, 4) + '…'`), log a non-sensitive identifier instead
|
|
19
|
+
(a user id, a key id), or drop the field entirely.
|
|
20
|
+
# Taint mode so indirection is caught — `const at = req.query.access_token;
|
|
21
|
+
# logger.info(at)` flags, not just the inline `console.log(req.query.code)`
|
|
22
|
+
# form. Distinct from auth.flow.secret-in-log (a search-mode rule keyed on
|
|
23
|
+
# the NAME of the logged identifier): this is a dataflow rule keyed on the
|
|
24
|
+
# request SOURCE, so it fires even when the credential is carried through an
|
|
25
|
+
# arbitrarily-named intermediate variable. The source list is narrowed to
|
|
26
|
+
# OAuth/OIDC credential fields (and the Authorization header), so logging a
|
|
27
|
+
# benign request field such as `req.query.page` does not fire. Routing the
|
|
28
|
+
# value through a redaction/masking helper or a truncating slice clears the
|
|
29
|
+
# taint.
|
|
30
|
+
mode: taint
|
|
31
|
+
pattern-sources:
|
|
32
|
+
# Dotted accessor: req.query.code / req.body.access_token / …
|
|
33
|
+
- patterns:
|
|
34
|
+
- pattern-either:
|
|
35
|
+
- pattern: $REQ.query.$K
|
|
36
|
+
- pattern: $REQ.body.$K
|
|
37
|
+
- pattern: $REQ.params.$K
|
|
38
|
+
- metavariable-regex:
|
|
39
|
+
metavariable: $K
|
|
40
|
+
regex: (?i)^(?:code|access[_-]?token|accesstoken|refresh[_-]?token|refreshtoken|id[_-]?token|idtoken|token|client[_-]?secret|clientsecret)$
|
|
41
|
+
# Index accessor: req.query['id_token'] / req.body["code"] / …
|
|
42
|
+
- patterns:
|
|
43
|
+
- pattern-either:
|
|
44
|
+
- pattern: $REQ.query[$K]
|
|
45
|
+
- pattern: $REQ.body[$K]
|
|
46
|
+
- pattern: $REQ.params[$K]
|
|
47
|
+
- metavariable-regex:
|
|
48
|
+
metavariable: $K
|
|
49
|
+
regex: (?i)^["'](?:code|access[_-]?token|accesstoken|refresh[_-]?token|refreshtoken|id[_-]?token|idtoken|token|client[_-]?secret|clientsecret)["']$
|
|
50
|
+
# Raw Authorization header (carries the bearer token / basic credentials).
|
|
51
|
+
- pattern: $REQ.headers.authorization
|
|
52
|
+
- pattern: $REQ.headers['authorization']
|
|
53
|
+
- pattern: $REQ.headers["authorization"]
|
|
54
|
+
- pattern: $REQ.get('authorization')
|
|
55
|
+
- pattern: $REQ.get('Authorization')
|
|
56
|
+
- pattern: $REQ.header('authorization')
|
|
57
|
+
- pattern: $REQ.header('Authorization')
|
|
58
|
+
pattern-sanitizers:
|
|
59
|
+
# Redaction / masking helpers, or a truncating slice/substring — the value
|
|
60
|
+
# that reaches the log is no longer the live credential.
|
|
61
|
+
- pattern: redact(...)
|
|
62
|
+
- pattern: mask(...)
|
|
63
|
+
- pattern: maskToken(...)
|
|
64
|
+
- pattern: $S.slice(...)
|
|
65
|
+
- pattern: $S.substring(...)
|
|
66
|
+
- pattern: $S.substr(...)
|
|
67
|
+
pattern-sinks:
|
|
68
|
+
# console.log/info/debug/warn/error(...) — any tainted argument fires.
|
|
69
|
+
- patterns:
|
|
70
|
+
- pattern-either:
|
|
71
|
+
- pattern: console.log(...)
|
|
72
|
+
- pattern: console.info(...)
|
|
73
|
+
- pattern: console.debug(...)
|
|
74
|
+
- pattern: console.warn(...)
|
|
75
|
+
- pattern: console.error(...)
|
|
76
|
+
# logger.<level>(...) — a log-named receiver with a log-level method.
|
|
77
|
+
- patterns:
|
|
78
|
+
- pattern: $LOG.$LEVEL(...)
|
|
79
|
+
- metavariable-regex:
|
|
80
|
+
metavariable: $LOG
|
|
81
|
+
regex: (?i)^.*log(?:ger)?$
|
|
82
|
+
- metavariable-regex:
|
|
83
|
+
metavariable: $LEVEL
|
|
84
|
+
regex: ^(?:log|info|debug|warn|warning|error|trace|fatal|verbose|silly)$
|
|
85
|
+
metadata:
|
|
86
|
+
oauthlint-rule-id: AUTH-FLOW-013
|
|
87
|
+
oauthlint-doc-url: https://oauthlint.dev/rules/flow-oauth-credential-in-log
|
|
88
|
+
category: security
|
|
89
|
+
cwe: CWE-532
|
|
90
|
+
owasp: API8:2023
|
|
91
|
+
llm-prevalence: MEDIUM
|
|
92
|
+
technology:
|
|
93
|
+
- express
|
|
94
|
+
references:
|
|
95
|
+
- https://cwe.mitre.org/data/definitions/532.html
|
|
96
|
+
- https://datatracker.ietf.org/doc/html/rfc6749#section-10.3
|
|
97
|
+
- https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/
|
|
@@ -61,6 +61,17 @@ rules:
|
|
|
61
61
|
- pattern: "$RES.setHeader('Location', $SINK)"
|
|
62
62
|
- pattern: '$RES.setHeader("Location", $SINK)'
|
|
63
63
|
- pattern: "$RES.writeHead($S, {..., Location: $SINK, ...})"
|
|
64
|
+
# `$RES.redirect(...)` / `$RES.location(...)` also match a bare,
|
|
65
|
+
# receiver-less `redirect(...)` / `location(...)` call. Those are the
|
|
66
|
+
# framework navigation primitives exported by Next.js
|
|
67
|
+
# (`next/navigation`), SvelteKit and Remix — they send the user to a
|
|
68
|
+
# server-constructed application URL, not the Express `res.redirect`
|
|
69
|
+
# open-redirect sink this rule targets. Requiring an explicit receiver
|
|
70
|
+
# removes that over-match (the dominant FP class seen on next-auth)
|
|
71
|
+
# without affecting any `res.redirect` / `res.location` detection.
|
|
72
|
+
- pattern-not: redirect($SINK)
|
|
73
|
+
- pattern-not: redirect($CODE, $SINK)
|
|
74
|
+
- pattern-not: location($SINK)
|
|
64
75
|
- focus-metavariable: $SINK
|
|
65
76
|
metadata:
|
|
66
77
|
oauthlint-rule-id: AUTH-FLOW-010
|
package/rules/flow/ssrf.yml
CHANGED
|
@@ -4,6 +4,25 @@ rules:
|
|
|
4
4
|
- javascript
|
|
5
5
|
- typescript
|
|
6
6
|
severity: ERROR
|
|
7
|
+
# Non-production code (example apps, demos, docs, vendored copies, tests)
|
|
8
|
+
# is not the library surface users ship, so findings there are noise for a
|
|
9
|
+
# low-FP linter. Globs intentionally omit `**/tests/**` / `**/fixtures/**`
|
|
10
|
+
# so the rule still fires on its own fixtures under rules/tests/fixtures/.
|
|
11
|
+
paths:
|
|
12
|
+
exclude:
|
|
13
|
+
- "**/test/**"
|
|
14
|
+
- "**/__tests__/**"
|
|
15
|
+
- "**/*.test.*"
|
|
16
|
+
- "**/*.spec.*"
|
|
17
|
+
- "**/example/**"
|
|
18
|
+
- "**/examples/**"
|
|
19
|
+
- "**/apps/**"
|
|
20
|
+
- "**/docs/**"
|
|
21
|
+
- "**/__mocks__/**"
|
|
22
|
+
- "**/mocks/**"
|
|
23
|
+
- "**/vendored/**"
|
|
24
|
+
- "**/node_modules/**"
|
|
25
|
+
- "**/*.stories.*"
|
|
7
26
|
message: |
|
|
8
27
|
Untrusted request input flows into the URL of an outbound HTTP request.
|
|
9
28
|
Because the destination is attacker-controlled, this is a Server-Side
|
|
@@ -4,6 +4,25 @@ rules:
|
|
|
4
4
|
- javascript
|
|
5
5
|
- typescript
|
|
6
6
|
severity: WARNING
|
|
7
|
+
# Non-production code (example apps, demos, docs, vendored copies, tests)
|
|
8
|
+
# is not the library surface users ship, so findings there are noise for a
|
|
9
|
+
# low-FP linter. Globs intentionally omit `**/tests/**` / `**/fixtures/**`
|
|
10
|
+
# so the rule still fires on its own fixtures under rules/tests/fixtures/.
|
|
11
|
+
paths:
|
|
12
|
+
exclude:
|
|
13
|
+
- "**/test/**"
|
|
14
|
+
- "**/__tests__/**"
|
|
15
|
+
- "**/*.test.*"
|
|
16
|
+
- "**/*.spec.*"
|
|
17
|
+
- "**/example/**"
|
|
18
|
+
- "**/examples/**"
|
|
19
|
+
- "**/apps/**"
|
|
20
|
+
- "**/docs/**"
|
|
21
|
+
- "**/__mocks__/**"
|
|
22
|
+
- "**/mocks/**"
|
|
23
|
+
- "**/vendored/**"
|
|
24
|
+
- "**/node_modules/**"
|
|
25
|
+
- "**/*.stories.*"
|
|
7
26
|
message: |
|
|
8
27
|
A secret-shaped value (`password`, `token`, `secret`, `apiKey`,
|
|
9
28
|
`csrf`, `hmac`) is being compared with `===` / `!==` /
|
|
@@ -14,13 +14,22 @@ rules:
|
|
|
14
14
|
Set `Secure: true` and `HttpOnly: true` on auth cookies, and add an
|
|
15
15
|
appropriate `SameSite` mode (for example `SameSite: http.SameSiteLaxMode`).
|
|
16
16
|
# Matches only the literal `false`. `Secure: true`, `HttpOnly: true`, and
|
|
17
|
-
# the absence of the field are not flagged.
|
|
18
|
-
#
|
|
19
|
-
#
|
|
17
|
+
# the absence of the field are not flagged. The `pattern-inside` keeps
|
|
18
|
+
# detection scoped to an `http.Cookie{...}` (and `&http.Cookie{...}`)
|
|
19
|
+
# composite literal, so an unrelated struct exposing a `Secure`/`HttpOnly`
|
|
20
|
+
# bool is never matched. `$FIELD` captures the offending field name so the
|
|
21
|
+
# narrow match — and the autofix below — targets just that field.
|
|
20
22
|
patterns:
|
|
21
|
-
- pattern-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
- pattern-inside: 'http.Cookie{...}'
|
|
24
|
+
- pattern: '$FIELD: false'
|
|
25
|
+
- metavariable-regex:
|
|
26
|
+
metavariable: $FIELD
|
|
27
|
+
regex: ^(Secure|HttpOnly)$
|
|
28
|
+
# Safe, deterministic autofix: flip the disabled flag to `true`. `$FIELD` is
|
|
29
|
+
# preserved, so `Secure: false` becomes `Secure: true` and `HttpOnly: false`
|
|
30
|
+
# becomes `HttpOnly: true` — each the secure value that resolves the finding,
|
|
31
|
+
# with every other field in the literal left untouched.
|
|
32
|
+
fix: '$FIELD: true'
|
|
24
33
|
metadata:
|
|
25
34
|
oauthlint-rule-id: AUTH-GO-COOKIE-001
|
|
26
35
|
oauthlint-doc-url: https://oauthlint.dev/rules/go-cookie-insecure
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: auth.go.flow.oauth-credential-in-log
|
|
3
|
+
languages:
|
|
4
|
+
- go
|
|
5
|
+
severity: ERROR
|
|
6
|
+
message: |
|
|
7
|
+
An OAuth/OIDC credential taken from the HTTP request — an authorization
|
|
8
|
+
`code`, an `access_token` / `refresh_token` / `id_token`, a bearer
|
|
9
|
+
`token`, a `client_secret`, or the raw `Authorization` header — flows
|
|
10
|
+
into a logging call (`log.*`, `slog.*`, `fmt.Print*`, or a `logger.*`
|
|
11
|
+
method). Logs are written to files, shipped to aggregators (Datadog,
|
|
12
|
+
Splunk, CloudWatch) and read by people and systems that should never see
|
|
13
|
+
live credentials. A leaked authorization code or token can be replayed
|
|
14
|
+
to impersonate the user or complete the OAuth exchange (CWE-532).
|
|
15
|
+
|
|
16
|
+
Never log the raw credential. Redact or mask it before logging, log a
|
|
17
|
+
non-sensitive identifier instead (a user id, a key id), or drop the
|
|
18
|
+
field entirely.
|
|
19
|
+
# Taint mode so indirection is caught — `at := r.FormValue("access_token");
|
|
20
|
+
# log.Println(at)` flags, not just the inline form. The source list is
|
|
21
|
+
# narrowed to OAuth/OIDC credential field names (and the Authorization
|
|
22
|
+
# header), so logging a benign request field such as `r.FormValue("page")`
|
|
23
|
+
# does not fire. Routing the value through a redaction/masking helper clears
|
|
24
|
+
# the taint.
|
|
25
|
+
mode: taint
|
|
26
|
+
pattern-sources:
|
|
27
|
+
# Request getters keyed by a credential-looking parameter name.
|
|
28
|
+
- patterns:
|
|
29
|
+
- pattern-either:
|
|
30
|
+
- pattern: $R.URL.Query().Get($K)
|
|
31
|
+
- pattern: $R.FormValue($K)
|
|
32
|
+
- pattern: $R.PostFormValue($K)
|
|
33
|
+
- metavariable-regex:
|
|
34
|
+
metavariable: $K
|
|
35
|
+
regex: (?i)^"(code|access[_-]?token|accesstoken|refresh[_-]?token|refreshtoken|id[_-]?token|idtoken|token|client[_-]?secret|clientsecret)"$
|
|
36
|
+
# The raw Authorization header carries the bearer / basic credential.
|
|
37
|
+
- patterns:
|
|
38
|
+
- pattern: $R.Header.Get($K)
|
|
39
|
+
- metavariable-regex:
|
|
40
|
+
metavariable: $K
|
|
41
|
+
regex: (?i)^"authorization"$
|
|
42
|
+
pattern-sanitizers:
|
|
43
|
+
# Redaction / masking helpers — the value reaching the log is no longer
|
|
44
|
+
# the live credential.
|
|
45
|
+
- pattern: redact(...)
|
|
46
|
+
- pattern: mask(...)
|
|
47
|
+
- pattern: maskToken(...)
|
|
48
|
+
pattern-sinks:
|
|
49
|
+
# Standard library log / slog / fmt print sinks — any tainted argument.
|
|
50
|
+
- patterns:
|
|
51
|
+
- pattern-either:
|
|
52
|
+
- pattern: log.Print(...)
|
|
53
|
+
- pattern: log.Printf(...)
|
|
54
|
+
- pattern: log.Println(...)
|
|
55
|
+
- pattern: log.Fatal(...)
|
|
56
|
+
- pattern: log.Fatalf(...)
|
|
57
|
+
- pattern: log.Fatalln(...)
|
|
58
|
+
- pattern: log.Panic(...)
|
|
59
|
+
- pattern: log.Panicf(...)
|
|
60
|
+
- pattern: slog.Info(...)
|
|
61
|
+
- pattern: slog.Debug(...)
|
|
62
|
+
- pattern: slog.Warn(...)
|
|
63
|
+
- pattern: slog.Error(...)
|
|
64
|
+
- pattern: fmt.Print(...)
|
|
65
|
+
- pattern: fmt.Printf(...)
|
|
66
|
+
- pattern: fmt.Println(...)
|
|
67
|
+
# A log-named receiver with a log-level method: logger.Info(...),
|
|
68
|
+
# myLog.Printf(...). The receiver-name constraint keeps this off
|
|
69
|
+
# unrelated method calls.
|
|
70
|
+
- patterns:
|
|
71
|
+
- pattern: $LOG.$LEVEL(...)
|
|
72
|
+
- metavariable-regex:
|
|
73
|
+
metavariable: $LOG
|
|
74
|
+
regex: (?i)^.*log(ger)?$
|
|
75
|
+
- metavariable-regex:
|
|
76
|
+
metavariable: $LEVEL
|
|
77
|
+
regex: ^(Print|Printf|Println|Info|Infof|Infow|Debug|Debugf|Debugw|Warn|Warnf|Warning|Error|Errorf|Errorw|Fatal|Fatalf|Panic|Panicf|Trace|Tracef)$
|
|
78
|
+
metadata:
|
|
79
|
+
oauthlint-rule-id: AUTH-GO-FLOW-005
|
|
80
|
+
oauthlint-doc-url: https://oauthlint.dev/rules/go-flow-oauth-credential-in-log
|
|
81
|
+
category: security
|
|
82
|
+
cwe: CWE-532
|
|
83
|
+
owasp: API8:2023
|
|
84
|
+
llm-prevalence: MEDIUM
|
|
85
|
+
technology:
|
|
86
|
+
- net/http
|
|
87
|
+
references:
|
|
88
|
+
- https://cwe.mitre.org/data/definitions/532.html
|
|
89
|
+
- https://datatracker.ietf.org/doc/html/rfc6749#section-10.3
|
|
90
|
+
- https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: auth.go.jwt.untrusted-verify-key
|
|
3
|
+
languages:
|
|
4
|
+
- go
|
|
5
|
+
severity: ERROR
|
|
6
|
+
message: |
|
|
7
|
+
Untrusted request input flows into the verification key returned by a
|
|
8
|
+
`golang-jwt` `Keyfunc` (or into the `WithValidMethods` allowlist). When
|
|
9
|
+
the attacker controls the key, they sign their own forged token and
|
|
10
|
+
supply the matching key, so every token "verifies" — a complete
|
|
11
|
+
authentication bypass. When the attacker controls the accepted methods,
|
|
12
|
+
they can downgrade verification and defeat the signature check (CWE-347,
|
|
13
|
+
Improper Verification of Cryptographic Signature).
|
|
14
|
+
|
|
15
|
+
The verification key and the accepted algorithms must be fixed
|
|
16
|
+
server-side. Return the key from trusted configuration or a vetted key
|
|
17
|
+
set keyed by a validated `kid`, and pin accepted methods to a constant
|
|
18
|
+
allowlist — never resolve them from `r.URL.Query()`, `r.FormValue`, or a
|
|
19
|
+
request header.
|
|
20
|
+
# Taint mode. The source is request input; the sink is the value RETURNED
|
|
21
|
+
# from a keyfunc (focused on the key expression, not the token) or the
|
|
22
|
+
# argument to `jwt.WithValidMethods`. Focusing on the key/methods — never
|
|
23
|
+
# the token, which is supposed to come from the request — keeps this from
|
|
24
|
+
# firing on every correct call. Distinct from auth.go.jwt.unchecked-method
|
|
25
|
+
# (a keyfunc that skips the `token.Method` check): this is about a
|
|
26
|
+
# request-CONTROLLED key or method list. Routing the value through an
|
|
27
|
+
# allow-list / validation helper clears the taint.
|
|
28
|
+
mode: taint
|
|
29
|
+
pattern-sources:
|
|
30
|
+
- pattern: $R.URL.Query().Get(...)
|
|
31
|
+
- pattern: $R.URL.Query()[$K]
|
|
32
|
+
- pattern: $R.FormValue(...)
|
|
33
|
+
- pattern: $R.PostFormValue(...)
|
|
34
|
+
- pattern: $R.Header.Get(...)
|
|
35
|
+
- pattern: $R.PathValue(...)
|
|
36
|
+
pattern-sanitizers:
|
|
37
|
+
- pattern: isAllowedKey(...)
|
|
38
|
+
- pattern: validateKey(...)
|
|
39
|
+
- pattern: isAllowedAlgorithm(...)
|
|
40
|
+
- pattern: validateAlgorithm(...)
|
|
41
|
+
pattern-sinks:
|
|
42
|
+
# The key returned from a golang-jwt Keyfunc literal. Scoped with
|
|
43
|
+
# pattern-inside so only a keyfunc return is a sink, and focused on the
|
|
44
|
+
# returned key expression so the token argument is never the trigger.
|
|
45
|
+
- patterns:
|
|
46
|
+
- pattern-inside: |
|
|
47
|
+
func($T *jwt.Token) ($RET, error) {
|
|
48
|
+
...
|
|
49
|
+
}
|
|
50
|
+
- pattern: return $SINK, $E
|
|
51
|
+
- focus-metavariable: $SINK
|
|
52
|
+
# Request input used as the accepted signing methods.
|
|
53
|
+
- patterns:
|
|
54
|
+
- pattern: jwt.WithValidMethods($SINK)
|
|
55
|
+
- focus-metavariable: $SINK
|
|
56
|
+
metadata:
|
|
57
|
+
oauthlint-rule-id: AUTH-GO-JWT-006
|
|
58
|
+
oauthlint-doc-url: https://oauthlint.dev/rules/go-jwt-untrusted-verify-key
|
|
59
|
+
category: security
|
|
60
|
+
cwe: CWE-347
|
|
61
|
+
owasp: API2:2023
|
|
62
|
+
llm-prevalence: LOW
|
|
63
|
+
technology:
|
|
64
|
+
- golang-jwt
|
|
65
|
+
references:
|
|
66
|
+
- https://cwe.mitre.org/data/definitions/347.html
|
|
67
|
+
- https://pkg.go.dev/github.com/golang-jwt/jwt/v5#Keyfunc
|
|
68
|
+
- https://datatracker.ietf.org/doc/html/rfc7518#section-3.1
|