oauthlint-rules 0.1.0 → 0.2.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.
Files changed (65) hide show
  1. package/dist/schema.d.ts.map +1 -1
  2. package/dist/schema.js +10 -4
  3. package/dist/schema.js.map +1 -1
  4. package/package.json +10 -3
  5. package/rules/cors/null-origin.yml +53 -0
  6. package/rules/cors/reflect-origin.yml +68 -0
  7. package/rules/flow/credentials-in-url.yml +37 -0
  8. package/rules/flow/secret-in-log.yml +95 -0
  9. package/rules/flow/timing-unsafe-compare.yml +62 -16
  10. package/rules/flow/weak-bcrypt-rounds.yml +41 -0
  11. package/rules/flow/weak-password-hash.yml +74 -0
  12. package/rules/go/cookie/insecure.yml +35 -0
  13. package/rules/go/cors/allow-all.yml +44 -0
  14. package/rules/go/crypto/bcrypt-low-cost.yml +37 -0
  15. package/rules/go/crypto/weak-cipher.yml +38 -0
  16. package/rules/go/crypto/weak-password-hash.yml +47 -0
  17. package/rules/go/flow/weak-rand.yml +61 -0
  18. package/rules/go/jwt/hardcoded-secret.yml +37 -0
  19. package/rules/go/jwt/none-algorithm.yml +38 -0
  20. package/rules/go/jwt/parse-unverified.yml +33 -0
  21. package/rules/go/jwt/unchecked-method.yml +72 -0
  22. package/rules/go/tls/insecure-skip-verify.yml +32 -0
  23. package/rules/go/tls/min-version.yml +36 -0
  24. package/rules/java/cookie/insecure.yml +31 -0
  25. package/rules/java/cors/allow-all.yml +47 -0
  26. package/rules/java/crypto/ecb-mode.yml +42 -0
  27. package/rules/java/crypto/insecure-random.yml +62 -0
  28. package/rules/java/crypto/weak-password-hash.yml +47 -0
  29. package/rules/java/jwt/hardcoded-secret.yml +43 -0
  30. package/rules/java/jwt/unsigned-jwt.yml +41 -0
  31. package/rules/java/session/fixation-disabled.yml +34 -0
  32. package/rules/java/tls/trust-all-certs.yml +48 -0
  33. package/rules/java/web/csrf-disabled.yml +35 -0
  34. package/rules/java/web/frame-options-disabled.yml +37 -0
  35. package/rules/java/web/permit-all.yml +36 -0
  36. package/rules/jwt/decode-without-verify.yml +56 -0
  37. package/rules/jwt/no-algorithms-allowlist.yml +38 -0
  38. package/rules/oauth/no-nonce.yml +55 -0
  39. package/rules/oauth/pkce-plain.yml +40 -0
  40. package/rules/py/cookie/insecure-flags.yml +41 -0
  41. package/rules/py/flow/csrf-exempt.yml +39 -0
  42. package/rules/py/flow/debug-enabled.yml +37 -0
  43. package/rules/py/flow/insecure-random-token.yml +51 -0
  44. package/rules/py/flow/requests-verify-disabled.yml +50 -0
  45. package/rules/py/flow/weak-password-hash.yml +64 -0
  46. package/rules/py/jwt/alg-none.yml +41 -0
  47. package/rules/py/jwt/hardcoded-secret.yml +40 -0
  48. package/rules/py/jwt/no-algorithms.yml +45 -0
  49. package/rules/py/jwt/no-verify.yml +37 -0
  50. package/rules/py/secret/django-hardcoded-key.yml +32 -0
  51. package/rules/py/secret/flask-hardcoded-key.yml +33 -0
  52. package/rules/rust/cookie/insecure.yml +35 -0
  53. package/rules/rust/cors/permissive.yml +38 -0
  54. package/rules/rust/crypto/bcrypt-low-cost.yml +40 -0
  55. package/rules/rust/crypto/weak-cipher.yml +41 -0
  56. package/rules/rust/crypto/weak-password-hash.yml +46 -0
  57. package/rules/rust/flow/timing-unsafe-compare.yml +77 -0
  58. package/rules/rust/jwt/disable-signature-validation.yml +31 -0
  59. package/rules/rust/jwt/hardcoded-secret.yml +41 -0
  60. package/rules/rust/jwt/no-aud-validation.yml +34 -0
  61. package/rules/rust/jwt/no-expiration-validation.yml +35 -0
  62. package/rules/rust/tls/accept-invalid-certs.yml +30 -0
  63. package/rules/rust/tls/accept-invalid-hostnames.yml +32 -0
  64. package/rules/secret/public-env-secret.yml +58 -0
  65. package/rules/session/hardcoded-secret.yml +42 -0
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;EAclC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAUxE,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuCpB,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;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"}
package/dist/schema.js CHANGED
@@ -11,8 +11,10 @@ export const SeveritySchema = z.enum(['INFO', 'WARNING', 'ERROR']);
11
11
  * Claude Code, Copilot, Gemini Code Assist).
12
12
  */
13
13
  export const OAuthLintMetadataSchema = z.object({
14
- 'oauthlint-rule-id': z.string().regex(/^AUTH-[A-Z]+-\d{3}$/, {
15
- message: 'oauthlint-rule-id must match AUTH-<CATEGORY>-<NNN>',
14
+ // AUTH-<CATEGORY>-<NNN> for the JS/TS pack (e.g. AUTH-JWT-001); language
15
+ // packs add a language segment (e.g. AUTH-PY-JWT-001).
16
+ 'oauthlint-rule-id': z.string().regex(/^AUTH-[A-Z]+(-[A-Z]+)*-\d{3}$/, {
17
+ message: 'oauthlint-rule-id must match AUTH-<CATEGORY>-<NNN> (optionally AUTH-<LANG>-<CATEGORY>-<NNN>)',
16
18
  }),
17
19
  'oauthlint-doc-url': z.string().url(),
18
20
  category: z.literal('security'),
@@ -32,8 +34,12 @@ export const OAuthLintMetadataSchema = z.object({
32
34
  const PatternEntrySchema = z.lazy(() => z.union([z.string(), z.record(z.string(), z.unknown()), z.array(PatternEntrySchema)]));
33
35
  export const RuleSchema = z
34
36
  .object({
35
- id: z.string().regex(/^auth\.[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*$/, {
36
- message: 'Rule id must follow auth.<category>.<name> (kebab-case)',
37
+ // auth.<category>.<name> for the JS/TS pack; language packs prepend a
38
+ // language segment auth.<lang>.<category>.<name> (e.g. auth.py.jwt.no-verify).
39
+ // The optional middle segment keeps every existing JS/TS id (and its
40
+ // published doc URL) untouched while making the scheme language-scalable.
41
+ id: z.string().regex(/^auth\.([a-z][a-z0-9]*\.)?[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*$/, {
42
+ message: 'Rule id must follow auth.<category>.<name> or auth.<lang>.<category>.<name> (kebab-case)',
37
43
  }),
38
44
  languages: z.array(z.string()).min(1),
39
45
  severity: SeveritySchema,
@@ -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.1.0",
3
+ "version": "0.2.0",
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",
@@ -22,21 +22,28 @@
22
22
  "keywords": [
23
23
  "oauthlint",
24
24
  "semgrep",
25
+ "semgrep-rules",
25
26
  "oauth",
27
+ "oauth2",
26
28
  "oidc",
27
29
  "jwt",
28
30
  "security",
31
+ "appsec",
29
32
  "sast",
33
+ "static-analysis",
34
+ "linter",
35
+ "cwe",
36
+ "owasp",
30
37
  "auth",
31
38
  "ai-code"
32
39
  ],
33
40
  "dependencies": {
34
- "yaml": "2.6.1",
41
+ "fast-glob": "3.3.3",
42
+ "yaml": "2.9.0",
35
43
  "zod": "3.24.1"
36
44
  },
37
45
  "devDependencies": {
38
46
  "@types/node": "22.10.5",
39
- "fast-glob": "3.3.3",
40
47
  "typescript": "5.7.3",
41
48
  "vitest": "2.1.9"
42
49
  },
@@ -0,0 +1,53 @@
1
+ rules:
2
+ - id: auth.cors.null-origin
3
+ languages:
4
+ - javascript
5
+ - typescript
6
+ severity: ERROR
7
+ message: |
8
+ CORS is configured to allow the literal origin `'null'`. Sandboxed
9
+ iframes, documents loaded from `file://`, and certain cross-origin
10
+ redirects send `Origin: null`. Adding the string `'null'` to your
11
+ CORS policy therefore grants cross-origin access to ANY such context
12
+ — including an attacker's sandboxed iframe — which defeats the
13
+ same-origin policy. Combined with credentials this becomes a
14
+ CSRF / data-exfiltration primitive.
15
+
16
+ The `'null'` origin is not a safe sentinel and cannot be trusted:
17
+ it is not bound to any host. Remove it from the allowlist entirely.
18
+
19
+ Use an explicit allowlist of real, trusted origins instead:
20
+ - `cors({ origin: 'https://app.example.com', credentials: true })`
21
+ - `cors({ origin: ['https://app.example.com', 'https://admin.example.com'] })`
22
+
23
+ Never set `Access-Control-Allow-Origin` to the string `'null'` and
24
+ never include `'null'` in a CORS origin allowlist.
25
+ pattern-either:
26
+ # Manual header write: ACAO set to the literal string "null".
27
+ - pattern: $RES.setHeader("Access-Control-Allow-Origin", "null")
28
+ - pattern: $RES.header("Access-Control-Allow-Origin", "null")
29
+ - pattern: $RES.set("Access-Control-Allow-Origin", "null")
30
+ # express cors() middleware: origin is the literal string "null".
31
+ - pattern: 'cors({ ..., origin: "null", ... })'
32
+ # express cors() middleware: allowlist array literal that contains "null".
33
+ - pattern: 'cors({ ..., origin: [..., "null", ...], ... })'
34
+ # An allowlist array variable containing "null" that is then used as the
35
+ # cors() origin (handles the indirected `const allowed = [..., 'null']`).
36
+ - patterns:
37
+ - pattern: 'cors({ ..., origin: $ALLOWED, ... })'
38
+ - pattern-inside: |
39
+ $ALLOWED = [..., "null", ...];
40
+ ...
41
+ metadata:
42
+ oauthlint-rule-id: AUTH-CORS-003
43
+ oauthlint-doc-url: https://oauthlint.dev/rules/cors-null-origin
44
+ category: security
45
+ cwe: CWE-942
46
+ owasp: A05:2021
47
+ llm-prevalence: MEDIUM
48
+ technology:
49
+ - cors
50
+ - express
51
+ references:
52
+ - https://portswigger.net/web-security/cors
53
+ - https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS
@@ -0,0 +1,68 @@
1
+ rules:
2
+ - id: auth.cors.reflect-origin
3
+ languages:
4
+ - javascript
5
+ - typescript
6
+ severity: ERROR
7
+ message: |
8
+ CORS is configured to echo the request's `Origin` back as
9
+ `Access-Control-Allow-Origin`. Reflecting the incoming origin is
10
+ functionally identical to allowing EVERY origin: any site can make
11
+ cross-origin requests and read the responses. Combined with
12
+ credentials this becomes a CSRF / account-takeover primitive, because
13
+ the browser will attach the victim's cookies to the attacker-controlled
14
+ request.
15
+
16
+ This is distinct from a literal `*` wildcard — here the origin is
17
+ reflected dynamically, which silently defeats the same-origin policy
18
+ while looking "scoped" in code review.
19
+
20
+ Use an explicit allowlist of trusted origins instead:
21
+ - `cors({ origin: 'https://app.example.com', credentials: true })`
22
+ - `cors({ origin: ['https://app.example.com', 'https://admin.example.com'] })`
23
+ - a callback that validates against an allowlist before calling
24
+ `cb(null, true)`.
25
+
26
+ Never set `Access-Control-Allow-Origin` to `req.headers.origin`, never
27
+ use `origin: true`, and never write a callback that unconditionally
28
+ returns `cb(null, true)`.
29
+ pattern-either:
30
+ # Manual header write: ACAO set to the request's origin in any form
31
+ # (req.headers.origin, req.header('origin'), req.get('Origin'), ...).
32
+ - pattern: $RES.setHeader("Access-Control-Allow-Origin", $REQ.headers.origin)
33
+ - pattern: $RES.header("Access-Control-Allow-Origin", $REQ.headers.origin)
34
+ - pattern: $RES.set("Access-Control-Allow-Origin", $REQ.headers.origin)
35
+ - pattern: $RES.setHeader("Access-Control-Allow-Origin", $REQ.headers["origin"])
36
+ - pattern: $RES.header("Access-Control-Allow-Origin", $REQ.headers["origin"])
37
+ - pattern: $RES.set("Access-Control-Allow-Origin", $REQ.headers["origin"])
38
+ - pattern: $RES.setHeader("Access-Control-Allow-Origin", $REQ.header("origin"))
39
+ - pattern: $RES.setHeader("Access-Control-Allow-Origin", $REQ.get("origin"))
40
+ - pattern: $RES.setHeader("Access-Control-Allow-Origin", $REQ.get("Origin"))
41
+ # express cors() middleware: origin: true echoes the request origin back.
42
+ - pattern: 'cors({ ..., origin: true, ... })'
43
+ # express cors() middleware: a callback that unconditionally allows
44
+ # every origin (cb(null, true) regardless of the origin argument).
45
+ - pattern: 'cors({ ..., origin: ($O, $CB) => $CB(null, true), ... })'
46
+ - patterns:
47
+ - pattern: |
48
+ cors({ ..., origin: ($O, $CB) => { ... }, ... })
49
+ - pattern-inside: |
50
+ cors({ ..., origin: ($O, $CB) => { ... return $CB(null, true); }, ... })
51
+ - patterns:
52
+ - pattern: |
53
+ cors({ ..., origin: function ($O, $CB) { ... }, ... })
54
+ - pattern-inside: |
55
+ cors({ ..., origin: function ($O, $CB) { ... return $CB(null, true); }, ... })
56
+ metadata:
57
+ oauthlint-rule-id: AUTH-CORS-002
58
+ oauthlint-doc-url: https://oauthlint.dev/rules/cors-reflect-origin
59
+ category: security
60
+ cwe: CWE-942
61
+ owasp: A05:2021
62
+ llm-prevalence: MEDIUM
63
+ technology:
64
+ - cors
65
+ - express
66
+ references:
67
+ - https://portswigger.net/web-security/cors
68
+ - https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
@@ -0,0 +1,37 @@
1
+ rules:
2
+ - id: auth.flow.credentials-in-url
3
+ languages:
4
+ - javascript
5
+ - typescript
6
+ severity: ERROR
7
+ message: |
8
+ A secret credential is placed in a URL query string. URLs leak into
9
+ server access logs, reverse-proxy logs, browser history, and the
10
+ `Referer` header (sent to every third-party CDN, analytics, and ad
11
+ script on the destination page) — so any of `password`, `client_secret`,
12
+ `api_key`, `apikey`, or `secret` in the query is a leaked credential.
13
+
14
+ Send credentials in the POST request body or in the `Authorization`
15
+ header. Never in the URL.
16
+
17
+ OWASP ASVS V3.2 / CWE-598 explicitly forbid credentials in URL
18
+ parameters.
19
+ # `access_token` is deliberately NOT matched: passing it as a query
20
+ # parameter to a provider's userinfo/API endpoint is a legitimate (and
21
+ # sometimes mandated) server-side OAuth pattern — flagging it produces
22
+ # false positives on real provider integrations. Bearer tokens in URLs
23
+ # are still covered by auth.jwt.in-url for JWT-shaped tokens.
24
+ pattern-either:
25
+ # Credential name in a query string (literal or template-built URL).
26
+ - pattern-regex: '[?&](?:password|client_secret|api_key|apikey|secret)='
27
+ # URLSearchParams / params builder: .set('client_secret', ...) / .append('secret', ...)
28
+ - pattern-regex: '\.(?:set|append)\s*\(\s*[''"`](?:password|client_secret|api_key|apikey|secret)[''"`]'
29
+ metadata:
30
+ oauthlint-rule-id: AUTH-FLOW-009
31
+ oauthlint-doc-url: https://oauthlint.dev/rules/flow-credentials-in-url
32
+ category: security
33
+ cwe: CWE-598
34
+ owasp: API2:2023
35
+ llm-prevalence: HIGH
36
+ references:
37
+ - https://owasp.org/www-project-application-security-verification-standard/
@@ -0,0 +1,95 @@
1
+ rules:
2
+ - id: auth.flow.secret-in-log
3
+ languages:
4
+ - javascript
5
+ - typescript
6
+ severity: WARNING
7
+ message: |
8
+ A secret-shaped value (`password`, `token`, `secret`, `apiKey`,
9
+ `accessToken`, `refreshToken`, `privateKey`, `clientSecret`, …) is
10
+ being passed to a logging call (`console.*` or `logger.*`). Logs are
11
+ routinely written to files, shipped to aggregators (Datadog, Splunk,
12
+ CloudWatch) and read by people who should never see the raw secret —
13
+ this is a textbook credential leak.
14
+
15
+ Never log secrets. Redact or mask them before logging
16
+ (`token.slice(0, 4) + '…'`), log a non-sensitive identifier instead
17
+ (a user id, a key id), or drop the field entirely.
18
+ # FP control: we only fire when a logging call receives an *identifier*
19
+ # whose name ends with a secret word. The argument may sit at any position
20
+ # (`$...A, $SECRET, $...B`). The `metavariable-pattern` with a bare-identifier
21
+ # `pattern-regex` forces `$SECRET` to be an identifier (or, in the template
22
+ # arms, an interpolated identifier) — NOT a string literal or member access,
23
+ # so status messages like `console.log('password updated')`,
24
+ # `console.log('reset token sent')` and `console.log(user.id)` are excluded.
25
+ # The outer `metavariable-regex` then requires the identifier name itself to
26
+ # match a secret word (re-using the suffix list from
27
+ # auth.flow.timing-unsafe-compare).
28
+ pattern-either:
29
+ # console.log/info/debug/warn/error(... secretVar ...) — secret at any position
30
+ - patterns:
31
+ - pattern-either:
32
+ - pattern: console.log($...A, $SECRET, $...B)
33
+ - pattern: console.info($...A, $SECRET, $...B)
34
+ - pattern: console.debug($...A, $SECRET, $...B)
35
+ - pattern: console.warn($...A, $SECRET, $...B)
36
+ - pattern: console.error($...A, $SECRET, $...B)
37
+ - metavariable-pattern:
38
+ metavariable: $SECRET
39
+ pattern-regex: ^[A-Za-z_$][A-Za-z0-9_$]*$
40
+ - metavariable-regex:
41
+ metavariable: $SECRET
42
+ regex: (?i)^(?:password|passwd|pwd|secret|token|apikey|api_key|accesstoken|refreshtoken|privatekey|clientsecret)$
43
+ # logger.<level>(... secretVar ...)
44
+ - patterns:
45
+ - pattern: $LOG.$LEVEL($...A, $SECRET, $...B)
46
+ - metavariable-regex:
47
+ metavariable: $LOG
48
+ regex: (?i)^.*log(?:ger)?$
49
+ - metavariable-regex:
50
+ metavariable: $LEVEL
51
+ regex: ^(?:log|info|debug|warn|warning|error|trace|fatal|verbose|silly)$
52
+ - metavariable-pattern:
53
+ metavariable: $SECRET
54
+ pattern-regex: ^[A-Za-z_$][A-Za-z0-9_$]*$
55
+ - metavariable-regex:
56
+ metavariable: $SECRET
57
+ regex: (?i)^(?:password|passwd|pwd|secret|token|apikey|api_key|accesstoken|refreshtoken|privatekey|clientsecret)$
58
+ # Template-literal interpolation: console.log(`token=${token}`)
59
+ - patterns:
60
+ - pattern-either:
61
+ - pattern: console.log(`...${$SECRET}...`)
62
+ - pattern: console.info(`...${$SECRET}...`)
63
+ - pattern: console.debug(`...${$SECRET}...`)
64
+ - pattern: console.warn(`...${$SECRET}...`)
65
+ - pattern: console.error(`...${$SECRET}...`)
66
+ - metavariable-pattern:
67
+ metavariable: $SECRET
68
+ pattern-regex: ^[A-Za-z_$][A-Za-z0-9_$]*$
69
+ - metavariable-regex:
70
+ metavariable: $SECRET
71
+ regex: (?i)^(?:password|passwd|pwd|secret|token|apikey|api_key|accesstoken|refreshtoken|privatekey|clientsecret)$
72
+ - patterns:
73
+ - pattern: $LOG.$LEVEL(`...${$SECRET}...`)
74
+ - metavariable-regex:
75
+ metavariable: $LOG
76
+ regex: (?i)^.*log(?:ger)?$
77
+ - metavariable-regex:
78
+ metavariable: $LEVEL
79
+ regex: ^(?:log|info|debug|warn|warning|error|trace|fatal|verbose|silly)$
80
+ - metavariable-pattern:
81
+ metavariable: $SECRET
82
+ pattern-regex: ^[A-Za-z_$][A-Za-z0-9_$]*$
83
+ - metavariable-regex:
84
+ metavariable: $SECRET
85
+ regex: (?i)^(?:password|passwd|pwd|secret|token|apikey|api_key|accesstoken|refreshtoken|privatekey|clientsecret)$
86
+ metadata:
87
+ oauthlint-rule-id: AUTH-FLOW-008
88
+ oauthlint-doc-url: https://oauthlint.dev/rules/flow-secret-in-log
89
+ category: security
90
+ cwe: CWE-532
91
+ owasp: API8:2023
92
+ llm-prevalence: HIGH
93
+ references:
94
+ - https://cwe.mitre.org/data/definitions/532.html
95
+ - https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/
@@ -38,14 +38,37 @@ rules:
38
38
  - pattern-not: 'typeof $X !== $Y'
39
39
  - pattern-not: 'typeof $X == $Y'
40
40
  - pattern-not: 'typeof $X != $Y'
41
- - pattern-not: '$X === ""'
42
- - pattern-not: "$X === ''"
43
- - pattern-not: '$X !== ""'
44
- - pattern-not: "$X !== ''"
45
- - pattern-not: '$X == ""'
46
- - pattern-not: "$X == ''"
47
- - pattern-not: '$X != ""'
48
- - pattern-not: "$X != ''"
41
+ # A secret-named value compared to a *literal* is never the
42
+ # timing target: a literal baked into source is already public,
43
+ # so there is no secret to leak byte-by-byte. This covers demo
44
+ # passwords (`pw !== "password"`), feature flags
45
+ # (`idToken === false`) and status checks — the dominant FP
46
+ # class on next-auth in real-world validation. `"..."` matches
47
+ # any string literal (including the empty string).
48
+ - pattern-not: '$X === "..."'
49
+ - pattern-not: '$X !== "..."'
50
+ - pattern-not: '$X == "..."'
51
+ - pattern-not: '$X != "..."'
52
+ - pattern-not: '"..." === $X'
53
+ - pattern-not: '"..." !== $X'
54
+ - pattern-not: '"..." == $X'
55
+ - pattern-not: '"..." != $X'
56
+ - pattern-not: '$X === true'
57
+ - pattern-not: '$X !== true'
58
+ - pattern-not: '$X == true'
59
+ - pattern-not: '$X != true'
60
+ - pattern-not: '$X === false'
61
+ - pattern-not: '$X !== false'
62
+ - pattern-not: '$X == false'
63
+ - pattern-not: '$X != false'
64
+ - pattern-not: 'true === $X'
65
+ - pattern-not: 'true !== $X'
66
+ - pattern-not: 'true == $X'
67
+ - pattern-not: 'true != $X'
68
+ - pattern-not: 'false === $X'
69
+ - pattern-not: 'false !== $X'
70
+ - pattern-not: 'false == $X'
71
+ - pattern-not: 'false != $X'
49
72
  - pattern-not: '$X.length === $N'
50
73
  - pattern-not: '$X.length !== $N'
51
74
  - pattern-not: '$X.length == $N'
@@ -71,14 +94,37 @@ rules:
71
94
  - pattern-not: 'typeof $X !== $Y'
72
95
  - pattern-not: 'typeof $X == $Y'
73
96
  - pattern-not: 'typeof $X != $Y'
74
- - pattern-not: '$X === ""'
75
- - pattern-not: "$X === ''"
76
- - pattern-not: '$X !== ""'
77
- - pattern-not: "$X !== ''"
78
- - pattern-not: '$X == ""'
79
- - pattern-not: "$X == ''"
80
- - pattern-not: '$X != ""'
81
- - pattern-not: "$X != ''"
97
+ # A secret-named value compared to a *literal* is never the
98
+ # timing target: a literal baked into source is already public,
99
+ # so there is no secret to leak byte-by-byte. This covers demo
100
+ # passwords (`pw !== "password"`), feature flags
101
+ # (`idToken === false`) and status checks — the dominant FP
102
+ # class on next-auth in real-world validation. `"..."` matches
103
+ # any string literal (including the empty string).
104
+ - pattern-not: '$X === "..."'
105
+ - pattern-not: '$X !== "..."'
106
+ - pattern-not: '$X == "..."'
107
+ - pattern-not: '$X != "..."'
108
+ - pattern-not: '"..." === $X'
109
+ - pattern-not: '"..." !== $X'
110
+ - pattern-not: '"..." == $X'
111
+ - pattern-not: '"..." != $X'
112
+ - pattern-not: '$X === true'
113
+ - pattern-not: '$X !== true'
114
+ - pattern-not: '$X == true'
115
+ - pattern-not: '$X != true'
116
+ - pattern-not: '$X === false'
117
+ - pattern-not: '$X !== false'
118
+ - pattern-not: '$X == false'
119
+ - pattern-not: '$X != false'
120
+ - pattern-not: 'true === $X'
121
+ - pattern-not: 'true !== $X'
122
+ - pattern-not: 'true == $X'
123
+ - pattern-not: 'true != $X'
124
+ - pattern-not: 'false === $X'
125
+ - pattern-not: 'false !== $X'
126
+ - pattern-not: 'false == $X'
127
+ - pattern-not: 'false != $X'
82
128
  - pattern-not: '$X.length === $N'
83
129
  - pattern-not: '$X.length !== $N'
84
130
  - pattern-not: '$X.length == $N'
@@ -0,0 +1,41 @@
1
+ rules:
2
+ - id: auth.flow.weak-bcrypt-rounds
3
+ languages:
4
+ - javascript
5
+ - typescript
6
+ severity: WARNING
7
+ message: |
8
+ A bcrypt cost factor below 10 was used. A low work factor makes the
9
+ hash cheap to compute, which lets an attacker brute-force stolen
10
+ password hashes far too quickly. OWASP recommends a bcrypt cost of at
11
+ least 10, and ≥ 12 for new applications, tuned so a single hash takes
12
+ roughly 250ms on your hardware.
13
+
14
+ Common LLM-generated mistake: `bcrypt.hash(pw, 8)` or
15
+ `bcrypt.genSalt(5)` because the literal "looks fast enough". Raise the
16
+ cost factor to 12 or higher.
17
+ patterns:
18
+ - pattern-either:
19
+ - pattern: bcrypt.hash($PW, $N)
20
+ - pattern: bcrypt.hash($PW, $N, ...)
21
+ - pattern: bcrypt.hashSync($PW, $N)
22
+ - pattern: bcrypt.hashSync($PW, $N, ...)
23
+ - pattern: bcrypt.genSalt($N)
24
+ - pattern: bcrypt.genSalt($N, ...)
25
+ - pattern: bcrypt.genSaltSync($N)
26
+ - pattern: bcrypt.genSaltSync($N, ...)
27
+ - metavariable-comparison:
28
+ metavariable: $N
29
+ comparison: $N < 10
30
+ metadata:
31
+ oauthlint-rule-id: AUTH-FLOW-007
32
+ oauthlint-doc-url: https://oauthlint.dev/rules/flow-weak-bcrypt-rounds
33
+ category: security
34
+ cwe: CWE-916
35
+ owasp: A02:2021
36
+ llm-prevalence: MEDIUM
37
+ technology:
38
+ - bcrypt
39
+ - bcryptjs
40
+ references:
41
+ - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
@@ -0,0 +1,74 @@
1
+ rules:
2
+ - id: auth.flow.weak-password-hash
3
+ languages:
4
+ - javascript
5
+ - typescript
6
+ severity: ERROR
7
+ message: |
8
+ A password is being hashed with a fast, general-purpose hash
9
+ (MD5/SHA-1/SHA-256/SHA-512 via `crypto.createHash`). These functions
10
+ are designed to be FAST, so an attacker who steals the database can
11
+ brute-force billions of candidate passwords per second on commodity
12
+ GPUs. Salting alone does not fix this.
13
+
14
+ Use a dedicated, deliberately-slow password hashing function instead:
15
+ `bcrypt`, `argon2` (argon2id), or `scrypt` (`crypto.scrypt`). These add
16
+ a tunable work factor that makes large-scale brute-forcing infeasible.
17
+ # Anchored on the *name* of the hashed value: we only flag when the value
18
+ # passed to `.update(...)` is named like a password. This avoids the
19
+ # common FP class — checksums, file digests (`createHash('sha256')
20
+ # .update(fileBuffer)`), and HMAC signatures (`createHmac`) are NOT
21
+ # password hashing and must not be flagged.
22
+ pattern-either:
23
+ # Chained form: createHash('md5').update(password).digest(...)
24
+ - patterns:
25
+ - pattern-either:
26
+ - pattern: crypto.createHash($ALGO).update($PW).digest(...)
27
+ - pattern: createHash($ALGO).update($PW).digest(...)
28
+ - pattern: crypto.createHash($ALGO).update($PW, ...).digest(...)
29
+ - pattern: createHash($ALGO).update($PW, ...).digest(...)
30
+ - metavariable-regex:
31
+ metavariable: $ALGO
32
+ regex: ^(["'])(?i)(md5|sha1|sha-1|sha224|sha256|sha-256|sha384|sha512|sha-512)\1$
33
+ - metavariable-pattern:
34
+ metavariable: $PW
35
+ patterns:
36
+ - pattern: $PWNAME
37
+ - metavariable-regex:
38
+ metavariable: $PWNAME
39
+ regex: (?i)^.*(password|passwd|pwd)$
40
+ # Two-step form:
41
+ # const h = crypto.createHash('sha256'); h.update(password); h.digest(...)
42
+ - patterns:
43
+ - pattern-either:
44
+ - pattern: $H.update($PW)
45
+ - pattern: $H.update($PW, ...)
46
+ - metavariable-pattern:
47
+ metavariable: $PW
48
+ patterns:
49
+ - pattern: $PWNAME
50
+ - metavariable-regex:
51
+ metavariable: $PWNAME
52
+ regex: (?i)^.*(password|passwd|pwd)$
53
+ - pattern-either:
54
+ - pattern-inside: |
55
+ $H = crypto.createHash($ALGO);
56
+ ...
57
+ - pattern-inside: |
58
+ $H = createHash($ALGO);
59
+ ...
60
+ - metavariable-regex:
61
+ metavariable: $ALGO
62
+ regex: ^(["'])(?i)(md5|sha1|sha-1|sha224|sha256|sha-256|sha384|sha512|sha-512)\1$
63
+ metadata:
64
+ oauthlint-rule-id: AUTH-FLOW-006
65
+ oauthlint-doc-url: https://oauthlint.dev/rules/flow-weak-password-hash
66
+ category: security
67
+ cwe: CWE-916
68
+ owasp: A02:2021
69
+ llm-prevalence: HIGH
70
+ technology:
71
+ - node:crypto
72
+ references:
73
+ - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
74
+ - https://cwe.mitre.org/data/definitions/916.html
@@ -0,0 +1,35 @@
1
+ rules:
2
+ - id: auth.go.cookie.insecure
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ A session/auth `http.Cookie` is created with a security attribute
8
+ explicitly disabled (`Secure: false` or `HttpOnly: false`). With
9
+ `Secure: false` the cookie is sent over plain HTTP, so a network
10
+ attacker can read the session token. With `HttpOnly: false` the cookie
11
+ is readable from JavaScript, so any XSS can steal it. For OAuth/OIDC
12
+ this exposes session and token cookies to theft and hijacking.
13
+
14
+ Set `Secure: true` and `HttpOnly: true` on auth cookies, and add an
15
+ appropriate `SameSite` mode (for example `SameSite: http.SameSiteLaxMode`).
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).
20
+ patterns:
21
+ - pattern-either:
22
+ - pattern: 'http.Cookie{..., Secure: false, ...}'
23
+ - pattern: 'http.Cookie{..., HttpOnly: false, ...}'
24
+ metadata:
25
+ oauthlint-rule-id: AUTH-GO-COOKIE-001
26
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-cookie-insecure
27
+ category: security
28
+ cwe: CWE-614
29
+ owasp: A05:2021
30
+ llm-prevalence: HIGH
31
+ technology:
32
+ - net/http
33
+ references:
34
+ - https://pkg.go.dev/net/http#Cookie
35
+ - https://cwe.mitre.org/data/definitions/614.html
@@ -0,0 +1,44 @@
1
+ rules:
2
+ - id: auth.go.cors.allow-all
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ CORS is configured to allow every origin with the wildcard `*`. Any
8
+ website can then make cross-origin requests to this endpoint, defeating
9
+ the same-origin policy (CWE-942). This is a common AI-generated mistake —
10
+ `AllowAllOrigins: true`, `AllowedOrigins: []string{"*"}`, or a raw
11
+ `Access-Control-Allow-Origin: *` header is pasted in to "make the browser
12
+ call work" and the intended scope is never added. Combined with
13
+ credentials this becomes an account-takeover primitive that leaks
14
+ OAuth/OIDC tokens cross-origin.
15
+
16
+ Restrict CORS to an explicit allowlist of trusted origins instead, for
17
+ example `AllowOrigins: []string{"https://app.example.com"}` (gin-contrib),
18
+ `AllowedOrigins: []string{"https://app.example.com"}` (rs/cors), or
19
+ `w.Header().Set("Access-Control-Allow-Origin", "https://app.example.com")`.
20
+ # Covers gin-contrib/cors (`cors.Config{..., AllowAllOrigins: true, ...}`),
21
+ # rs/cors (`cors.Options{..., AllowedOrigins: []string{..., "*", ...}, ...}`
22
+ # whose list literal contains the wildcard `"*"`), and the raw header
23
+ # `$W.Header().Set("Access-Control-Allow-Origin", "*")`. Only the wildcard /
24
+ # `AllowAllOrigins: true` is flagged — explicit origins such as
25
+ # `"https://app.example.com"` are not.
26
+ pattern-either:
27
+ - pattern: 'cors.Config{..., AllowAllOrigins: true, ...}'
28
+ - pattern: 'cors.Options{..., AllowedOrigins: []string{..., "*", ...}, ...}'
29
+ - pattern: '$W.Header().Set("Access-Control-Allow-Origin", "*")'
30
+ metadata:
31
+ oauthlint-rule-id: AUTH-GO-CORS-001
32
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-cors-allow-all
33
+ category: security
34
+ cwe: CWE-942
35
+ owasp: A05:2021
36
+ llm-prevalence: HIGH
37
+ technology:
38
+ - gin
39
+ - rs-cors
40
+ - net/http
41
+ references:
42
+ - https://github.com/gin-contrib/cors
43
+ - https://github.com/rs/cors
44
+ - https://cwe.mitre.org/data/definitions/942.html