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.
Files changed (43) hide show
  1. package/dist/schema.d.ts +60 -0
  2. package/dist/schema.d.ts.map +1 -1
  3. package/dist/schema.js +12 -0
  4. package/dist/schema.js.map +1 -1
  5. package/package.json +1 -1
  6. package/rules/cookie/no-httponly.yml +5 -1
  7. package/rules/cookie/no-samesite.yml +5 -1
  8. package/rules/cookie/no-secure.yml +8 -5
  9. package/rules/cookie/samesite-none-insecure.yml +22 -0
  10. package/rules/flow/oauth-credential-in-log.yml +97 -0
  11. package/rules/flow/open-redirect.yml +11 -0
  12. package/rules/flow/ssrf.yml +19 -0
  13. package/rules/flow/timing-unsafe-compare.yml +19 -0
  14. package/rules/go/cookie/insecure.yml +15 -6
  15. package/rules/go/flow/oauth-credential-in-log.yml +90 -0
  16. package/rules/go/jwt/untrusted-verify-key.yml +68 -0
  17. package/rules/go/oauth/insecure-token-endpoint.yml +39 -0
  18. package/rules/go/oauth/ropc-grant.yml +64 -0
  19. package/rules/go/oauth/static-state.yml +39 -0
  20. package/rules/go/tls/insecure-skip-verify.yml +14 -3
  21. package/rules/go/tls/min-version.yml +12 -3
  22. package/rules/java/cors/credentialed-wildcard.yml +44 -0
  23. package/rules/java/jwt/none-algorithm.yml +37 -0
  24. package/rules/java/jwt/untrusted-verify-key.yml +54 -0
  25. package/rules/java/oauth/insecure-token-endpoint.yml +39 -0
  26. package/rules/java/oauth/ropc-grant.yml +45 -0
  27. package/rules/java/oauth/static-state.yml +38 -0
  28. package/rules/java/web/permit-all-actuator.yml +43 -0
  29. package/rules/jwt/ignore-expiration.yml +21 -11
  30. package/rules/jwt/untrusted-verify-key.yml +77 -0
  31. package/rules/oauth/insecure-token-endpoint.yml +41 -0
  32. package/rules/oauth/ropc-grant.yml +48 -0
  33. package/rules/oauth/static-state.yml +49 -0
  34. package/rules/py/flow/oauth-credential-in-log.yml +91 -0
  35. package/rules/py/jwt/untrusted-verify-key.yml +75 -0
  36. package/rules/py/oauth/insecure-token-endpoint.yml +40 -0
  37. package/rules/py/oauth/insecure-transport-env.yml +43 -0
  38. package/rules/py/oauth/ropc-grant.yml +58 -0
  39. package/rules/py/oauth/static-state.yml +45 -0
  40. package/rules/py/oauth/token-request-verify-disabled.yml +41 -0
  41. package/rules/rust/oauth/insecure-token-endpoint.yml +39 -0
  42. package/rules/rust/oauth/ropc-grant.yml +57 -0
  43. 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;
@@ -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;AAUxE,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4CpB,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"}
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(),
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauthlint-rules",
3
- "version": "0.2.6",
3
+ "version": "0.3.1",
4
4
  "description": "Semgrep rules catching the OAuth/OIDC/JWT anti-patterns that AI coding tools systematically produce.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -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
- fix: '$RES.cookie($NAME, $VAL, { ...$OPTS, httpOnly: true })'
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
- fix: "$RES.cookie($NAME, $VAL, { ...$OPTS, sameSite: 'strict' })"
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
- # Auto-fix applies to the first (3-arg) pattern: spread the existing
43
- # options object and inject secure:true. Verbose but always-correct;
44
- # users can run prettier afterwards. The 2-arg branch is left alone
45
- # because rewriting it requires inserting a brand-new argument.
46
- fix: '$RES.cookie($NAME, $VAL, { ...$OPTS, secure: true })'
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
@@ -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. Works for `http.Cookie{...}`
18
- # and `&http.Cookie{...}` (the composite literal matches inside the
19
- # address-of).
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-either:
22
- - pattern: 'http.Cookie{..., Secure: false, ...}'
23
- - pattern: 'http.Cookie{..., HttpOnly: false, ...}'
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