oauthlint-rules 0.2.6 → 0.3.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 (35) hide show
  1. package/package.json +1 -1
  2. package/rules/cookie/no-httponly.yml +5 -1
  3. package/rules/cookie/no-samesite.yml +5 -1
  4. package/rules/cookie/no-secure.yml +8 -5
  5. package/rules/flow/oauth-credential-in-log.yml +97 -0
  6. package/rules/go/cookie/insecure.yml +15 -6
  7. package/rules/go/flow/oauth-credential-in-log.yml +90 -0
  8. package/rules/go/jwt/untrusted-verify-key.yml +68 -0
  9. package/rules/go/oauth/insecure-token-endpoint.yml +39 -0
  10. package/rules/go/oauth/ropc-grant.yml +49 -0
  11. package/rules/go/oauth/static-state.yml +39 -0
  12. package/rules/go/tls/insecure-skip-verify.yml +14 -3
  13. package/rules/go/tls/min-version.yml +12 -3
  14. package/rules/java/cors/credentialed-wildcard.yml +44 -0
  15. package/rules/java/jwt/none-algorithm.yml +37 -0
  16. package/rules/java/jwt/untrusted-verify-key.yml +54 -0
  17. package/rules/java/oauth/insecure-token-endpoint.yml +39 -0
  18. package/rules/java/oauth/ropc-grant.yml +45 -0
  19. package/rules/java/oauth/static-state.yml +38 -0
  20. package/rules/java/web/permit-all-actuator.yml +43 -0
  21. package/rules/jwt/ignore-expiration.yml +21 -11
  22. package/rules/jwt/untrusted-verify-key.yml +77 -0
  23. package/rules/oauth/insecure-token-endpoint.yml +41 -0
  24. package/rules/oauth/ropc-grant.yml +43 -0
  25. package/rules/oauth/static-state.yml +49 -0
  26. package/rules/py/flow/oauth-credential-in-log.yml +91 -0
  27. package/rules/py/jwt/untrusted-verify-key.yml +75 -0
  28. package/rules/py/oauth/insecure-token-endpoint.yml +40 -0
  29. package/rules/py/oauth/insecure-transport-env.yml +43 -0
  30. package/rules/py/oauth/ropc-grant.yml +50 -0
  31. package/rules/py/oauth/static-state.yml +45 -0
  32. package/rules/py/oauth/token-request-verify-disabled.yml +41 -0
  33. package/rules/rust/oauth/insecure-token-endpoint.yml +39 -0
  34. package/rules/rust/oauth/ropc-grant.yml +40 -0
  35. package/rules/rust/oauth/static-state.yml +38 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauthlint-rules",
3
- "version": "0.2.6",
3
+ "version": "0.3.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",
@@ -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
@@ -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/
@@ -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
@@ -0,0 +1,39 @@
1
+ rules:
2
+ - id: auth.go.oauth.insecure-token-endpoint
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ An OAuth/OIDC endpoint is being contacted over cleartext `http://`.
8
+ Authorization codes, `client_secret`, access/refresh tokens, and the
9
+ `code_verifier` then travel unencrypted — a network attacker can read
10
+ or rewrite them and take over the flow.
11
+
12
+ RFC 6749 §3.1 / §10.9 require TLS for the authorization and token
13
+ endpoints. Use `https://` for every authorize, token, and userinfo
14
+ URL (including `oauth2.Endpoint{AuthURL, TokenURL}`).
15
+ `http://localhost` is fine for local development and is not flagged.
16
+ # A string literal that targets an OAuth/OIDC endpoint over http://.
17
+ # Required OAuth markers keep this precise: a generic http URL is NOT
18
+ # flagged, only one carrying an authorize/token request or an /oauth path.
19
+ # `https://` cannot match (the scheme is `http://` literally), and the
20
+ # localhost / loopback dev hosts are subtracted below.
21
+ patterns:
22
+ - pattern-regex: |-
23
+ "http://[^"\s]+(?:response_type=|client_id=|client_secret=|grant_type=|code_challenge=|/oauth2?/|/connect/token|/o/oauth2|/authorize|/oauth/token)[^"]*"
24
+ - pattern-not-regex: |-
25
+ http://(?:localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])
26
+ metadata:
27
+ oauthlint-rule-id: AUTH-GO-OAUTH-002
28
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-oauth-insecure-token-endpoint
29
+ category: security
30
+ cwe: CWE-319
31
+ owasp: A02:2021
32
+ llm-prevalence: MEDIUM
33
+ technology:
34
+ - oauth2
35
+ - oidc
36
+ references:
37
+ - https://datatracker.ietf.org/doc/html/rfc6749#section-3.1
38
+ - https://datatracker.ietf.org/doc/html/rfc6749#section-10.9
39
+ - https://cwe.mitre.org/data/definitions/319.html
@@ -0,0 +1,49 @@
1
+ rules:
2
+ - id: auth.go.oauth.ropc-grant
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ OAuth token request uses the Resource Owner Password Credentials
8
+ grant (`grant_type=password`). The app collects the user's password
9
+ and replays it to the authorization server — exactly what OAuth was
10
+ designed to avoid. It cannot support federation, MFA, or step-up
11
+ auth, and any compromise of your service exposes raw user passwords.
12
+
13
+ The OAuth 2.0 Security BCP (RFC 9700 §2.4) forbids ROPC and OAuth 2.1
14
+ removes it entirely. Use the authorization-code flow with PKCE
15
+ (`grant_type=authorization_code`) for user login, or
16
+ `client_credentials` for machine-to-machine. With `golang.org/x/oauth2`,
17
+ avoid `Config.PasswordCredentialsToken` and use `AuthCodeURL` /
18
+ `Exchange` instead.
19
+ pattern-either:
20
+ # golang.org/x/oauth2: Config.PasswordCredentialsToken(ctx, user, pass)
21
+ # is the ROPC helper — its only purpose is the password grant.
22
+ - pattern: $C.PasswordCredentialsToken(...)
23
+ # url.Values builders: v.Set("grant_type", "password") /
24
+ # v.Add("grant_type", "password"). The value is bounded so
25
+ # `password_reset` and friends are not matched.
26
+ - pattern-regex: |-
27
+ "grant_type"\s*,\s*"password"
28
+ # url.Values / map composite literal: {"grant_type": {"password"}} or
29
+ # {"grant_type": "password"}.
30
+ - pattern-regex: |-
31
+ "grant_type"\s*:\s*\[?\s*\{?\s*"password"
32
+ # URL-encoded request body string: "grant_type=password&username=…"
33
+ # (double-quoted or raw backtick string). The trailing class bounds the
34
+ # value so only the password grant matches.
35
+ - pattern-regex: |-
36
+ [?&"`]grant_type=password(?:[&"`\s\\]|$)
37
+ metadata:
38
+ oauthlint-rule-id: AUTH-GO-OAUTH-001
39
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-oauth-ropc-grant
40
+ category: security
41
+ cwe: CWE-522
42
+ owasp: API2:2023
43
+ llm-prevalence: MEDIUM
44
+ technology:
45
+ - oauth2
46
+ references:
47
+ - https://datatracker.ietf.org/doc/html/rfc9700#section-2.4
48
+ - https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-2.4
49
+ - https://cwe.mitre.org/data/definitions/522.html
@@ -0,0 +1,39 @@
1
+ rules:
2
+ - id: auth.go.oauth.static-state
3
+ languages:
4
+ - go
5
+ severity: WARNING
6
+ message: |
7
+ OAuth authorization request is built with a hardcoded, constant `state`
8
+ value. A static `state` provides ZERO CSRF protection — the whole point
9
+ is an unguessable, per-request value that you store and then compare on
10
+ the callback. A literal that ships in your source is known to everyone
11
+ and identical on every request, so an attacker can forge a matching
12
+ callback.
13
+
14
+ Generate `state` fresh per request from a CSPRNG (e.g.
15
+ `crypto/rand` -> `base64.URLEncoding.EncodeToString(b)`), persist it in
16
+ the session/cookie, and verify it when the provider redirects back. With
17
+ `golang.org/x/oauth2`, pass that random value as the first argument to
18
+ `Config.AuthCodeURL(state, ...)`.
19
+ # `Config.AuthCodeURL(state, ...)` is the golang.org/x/oauth2 authorize-URL
20
+ # builder; its first argument is the CSRF `state`. A string LITERAL there is
21
+ # constant on every request and cannot provide CSRF protection. A
22
+ # per-request value is a variable (not a quoted literal) and does not match.
23
+ # The empty-string form is excluded — that is a missing state, covered
24
+ # elsewhere — so this fires only on a genuinely hardcoded value.
25
+ patterns:
26
+ - pattern: $C.AuthCodeURL("...", ...)
27
+ - pattern-not: $C.AuthCodeURL("", ...)
28
+ metadata:
29
+ oauthlint-rule-id: AUTH-GO-OAUTH-003
30
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-oauth-static-state
31
+ category: security
32
+ cwe: CWE-330
33
+ owasp: API1:2023
34
+ llm-prevalence: MEDIUM
35
+ technology:
36
+ - oauth2
37
+ references:
38
+ - https://datatracker.ietf.org/doc/html/rfc6749#section-10.12
39
+ - https://cwe.mitre.org/data/definitions/330.html
@@ -15,9 +15,20 @@ rules:
15
15
  To trust a private CA in development, set `RootCAs` to a `*x509.CertPool`
16
16
  loaded with that CA instead.
17
17
  # Matches only the literal `true`. `InsecureSkipVerify: false` and the
18
- # field's absence are not flagged. Works for `tls.Config{...}` and
19
- # `&tls.Config{...}` (the composite literal matches inside the address-of).
20
- pattern: 'tls.Config{..., InsecureSkipVerify: true, ...}'
18
+ # field's absence are not flagged. The `pattern-inside` keeps detection
19
+ # scoped to a `tls.Config{...}` (and `&tls.Config{...}`) composite literal,
20
+ # so an unrelated struct that happens to expose an `InsecureSkipVerify`
21
+ # field is never matched. Narrowing the matched node to the field itself
22
+ # (rather than the whole literal) is what lets the autofix below rewrite
23
+ # just that field and leave the surrounding `...` fields untouched.
24
+ patterns:
25
+ - pattern-inside: 'tls.Config{...}'
26
+ - pattern: 'InsecureSkipVerify: true'
27
+ # Safe, deterministic autofix: flip the boolean to `false`, the secure
28
+ # default that restores certificate verification and fully resolves the
29
+ # finding. Only the `true` literal changes; every other field in the
30
+ # `tls.Config` is preserved verbatim.
31
+ fix: 'InsecureSkipVerify: false'
21
32
  metadata:
22
33
  oauthlint-rule-id: AUTH-GO-TLS-001
23
34
  oauthlint-doc-url: https://oauthlint.dev/rules/go-tls-insecure-skip-verify
@@ -14,13 +14,22 @@ rules:
14
14
  Set `MinVersion` to at least `tls.VersionTLS12`, and ideally
15
15
  `tls.VersionTLS13`, so the handshake refuses obsolete protocols.
16
16
  # Matches the obsolete constants only. `tls.VersionTLS12` and
17
- # `tls.VersionTLS13` are not flagged. Works for both `tls.Config{...}`
18
- # and `&tls.Config{...}` (the composite literal matches inside `&`).
17
+ # `tls.VersionTLS13` are not flagged. The `pattern-inside` keeps detection
18
+ # scoped to a `tls.Config{...}` (and `&tls.Config{...}`) composite literal;
19
+ # narrowing the matched node to the `MinVersion` field itself is what lets
20
+ # the autofix below rewrite just that field and leave neighbours untouched.
19
21
  patterns:
20
- - pattern: 'tls.Config{..., MinVersion: $V, ...}'
22
+ - pattern-inside: 'tls.Config{...}'
23
+ - pattern: 'MinVersion: $V'
21
24
  - metavariable-regex:
22
25
  metavariable: $V
23
26
  regex: ^tls\.(VersionSSL30|VersionTLS10|VersionTLS11)$
27
+ # Safe, deterministic autofix: raise the floor to `tls.VersionTLS12`, the
28
+ # lowest version RFC 8996 still permits. This is the minimal correct upgrade
29
+ # — it fully resolves the finding (TLS 1.2+ is no longer flagged) without
30
+ # presuming the stricter `tls.VersionTLS13`, which can break interop with
31
+ # peers that don't yet speak 1.3.
32
+ fix: 'MinVersion: tls.VersionTLS12'
24
33
  metadata:
25
34
  oauthlint-rule-id: AUTH-GO-TLS-002
26
35
  oauthlint-doc-url: https://oauthlint.dev/rules/go-tls-min-version
@@ -0,0 +1,44 @@
1
+ rules:
2
+ - id: auth.java.cors.credentialed-wildcard
3
+ languages:
4
+ - java
5
+ severity: ERROR
6
+ message: |
7
+ CORS is configured to allow any origin together with credentials. A
8
+ wildcard origin combined with `allowCredentials = true` tells the browser
9
+ to send the victim's cookies and authorization headers to a response that
10
+ any site can read — a cross-origin account-takeover primitive (CWE-942).
11
+ Because browsers reject a literal `*` when credentials are allowed,
12
+ `addAllowedOriginPattern("*")` exists specifically to re-enable this unsafe
13
+ combination, so its very use is the smell.
14
+
15
+ Never pair a wildcard origin with credentials. List the exact trusted
16
+ origins — `setAllowedOrigins(List.of("https://app.example.com"))` with
17
+ `setAllowCredentials(true)` — or drop credentials if you genuinely need a
18
+ public, anonymous API.
19
+ # Annotation form: @CrossOrigin with a wildcard origin AND allowCredentials
20
+ # = "true" (both attribute orderings, `origins` and the `value` alias).
21
+ # Programmatic form: the origin-PATTERN wildcard API, which only exists to
22
+ # allow `*` with credentials — flagged on its own. The plain
23
+ # `addAllowedOrigin`/`setAllowedOrigins` wildcards are covered by
24
+ # auth.java.cors.allow-all and are intentionally not duplicated here.
25
+ pattern-either:
26
+ - pattern: '@CrossOrigin(..., origins = "*", ..., allowCredentials = "true", ...)'
27
+ - pattern: '@CrossOrigin(..., allowCredentials = "true", ..., origins = "*", ...)'
28
+ - pattern: '@CrossOrigin(..., value = "*", ..., allowCredentials = "true", ...)'
29
+ - pattern: '@CrossOrigin(..., allowCredentials = "true", ..., value = "*", ...)'
30
+ - pattern: $C.addAllowedOriginPattern("*")
31
+ - pattern: $C.setAllowedOriginPatterns($L(..., "*", ...))
32
+ metadata:
33
+ oauthlint-rule-id: AUTH-JAVA-CORS-002
34
+ oauthlint-doc-url: https://oauthlint.dev/rules/java-cors-credentialed-wildcard
35
+ category: security
36
+ cwe: CWE-942
37
+ owasp: A05:2021
38
+ llm-prevalence: MEDIUM
39
+ technology:
40
+ - spring-web
41
+ references:
42
+ - https://docs.spring.io/spring-framework/reference/web/webmvc-cors.html
43
+ - https://portswigger.net/web-security/cors
44
+ - https://cwe.mitre.org/data/definitions/942.html
@@ -0,0 +1,37 @@
1
+ rules:
2
+ - id: auth.java.jwt.none-algorithm
3
+ languages:
4
+ - java
5
+ severity: ERROR
6
+ message: |
7
+ A JWT is created or verified with the `none` algorithm, which means there
8
+ is no signature at all. An `alg=none` token can be forged by anyone —
9
+ changing the subject, roles, or expiry costs nothing because there is no
10
+ signature to verify (CWE-347). This is a common AI-generated mistake: the
11
+ "no signature" algorithm is reached for during prototyping or testing and
12
+ never swapped for a real signing key.
13
+
14
+ Always sign and verify with a real algorithm. With jjwt use
15
+ `Jwts.builder()...signWith(key)` and a keyed parser; with Auth0 java-jwt
16
+ use `Algorithm.HMAC256(secret)` / `Algorithm.RSA256(...)` instead of
17
+ `Algorithm.none()`.
18
+ # jjwt (io.jsonwebtoken): the SignatureAlgorithm.NONE enum constant.
19
+ # Auth0 java-jwt (com.auth0.jwt): the Algorithm.none() factory.
20
+ pattern-either:
21
+ - pattern: io.jsonwebtoken.SignatureAlgorithm.NONE
22
+ - pattern: SignatureAlgorithm.NONE
23
+ - pattern: com.auth0.jwt.algorithms.Algorithm.none()
24
+ - pattern: Algorithm.none()
25
+ metadata:
26
+ oauthlint-rule-id: AUTH-JAVA-JWT-004
27
+ oauthlint-doc-url: https://oauthlint.dev/rules/java-jwt-none-algorithm
28
+ category: security
29
+ cwe: CWE-347
30
+ owasp: API2:2023
31
+ llm-prevalence: MEDIUM
32
+ technology:
33
+ - jjwt
34
+ - java-jwt
35
+ references:
36
+ - https://datatracker.ietf.org/doc/html/rfc8725#section-2.1
37
+ - https://cwe.mitre.org/data/definitions/347.html