oauthlint-rules 0.1.1 → 0.2.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 (67) 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 +9 -2
  5. package/rules/cors/null-origin.yml +53 -0
  6. package/rules/cors/reflect-origin.yml +78 -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/localstorage.yml +13 -1
  38. package/rules/jwt/no-algorithms-allowlist.yml +38 -0
  39. package/rules/oauth/no-nonce.yml +55 -0
  40. package/rules/oauth/no-state-validation.yml +28 -6
  41. package/rules/oauth/pkce-plain.yml +40 -0
  42. package/rules/py/cookie/insecure-flags.yml +41 -0
  43. package/rules/py/flow/csrf-exempt.yml +39 -0
  44. package/rules/py/flow/debug-enabled.yml +37 -0
  45. package/rules/py/flow/insecure-random-token.yml +51 -0
  46. package/rules/py/flow/requests-verify-disabled.yml +50 -0
  47. package/rules/py/flow/weak-password-hash.yml +64 -0
  48. package/rules/py/jwt/alg-none.yml +41 -0
  49. package/rules/py/jwt/hardcoded-secret.yml +40 -0
  50. package/rules/py/jwt/no-algorithms.yml +45 -0
  51. package/rules/py/jwt/no-verify.yml +37 -0
  52. package/rules/py/secret/django-hardcoded-key.yml +32 -0
  53. package/rules/py/secret/flask-hardcoded-key.yml +33 -0
  54. package/rules/rust/cookie/insecure.yml +35 -0
  55. package/rules/rust/cors/permissive.yml +38 -0
  56. package/rules/rust/crypto/bcrypt-low-cost.yml +40 -0
  57. package/rules/rust/crypto/weak-cipher.yml +41 -0
  58. package/rules/rust/crypto/weak-password-hash.yml +46 -0
  59. package/rules/rust/flow/timing-unsafe-compare.yml +77 -0
  60. package/rules/rust/jwt/disable-signature-validation.yml +31 -0
  61. package/rules/rust/jwt/hardcoded-secret.yml +41 -0
  62. package/rules/rust/jwt/no-aud-validation.yml +34 -0
  63. package/rules/rust/jwt/no-expiration-validation.yml +35 -0
  64. package/rules/rust/tls/accept-invalid-certs.yml +30 -0
  65. package/rules/rust/tls/accept-invalid-hostnames.yml +32 -0
  66. package/rules/secret/public-env-secret.yml +58 -0
  67. package/rules/session/hardcoded-secret.yml +42 -0
@@ -0,0 +1,37 @@
1
+ rules:
2
+ - id: auth.go.crypto.bcrypt-low-cost
3
+ languages:
4
+ - go
5
+ severity: WARNING
6
+ message: |
7
+ `bcrypt.GenerateFromPassword` is called with a cost factor below 10. A
8
+ low work factor makes each hash cheap to compute, which lets an attacker
9
+ brute-force stolen password hashes far too quickly. OWASP recommends a
10
+ bcrypt cost of at least 10, and ≥ 12 for new applications, tuned so a
11
+ single hash takes roughly 250ms on your hardware.
12
+
13
+ Common LLM-generated mistake: `bcrypt.GenerateFromPassword(pw, 8)`
14
+ because the literal "looks fast enough". Use `bcrypt.DefaultCost` (10)
15
+ or raise the cost factor to 12 or higher.
16
+ # Matches only a numeric literal cost < 10 passed to
17
+ # `bcrypt.GenerateFromPassword`. `metavariable-comparison` constrains $N to
18
+ # numeric literals only, so `bcrypt.DefaultCost`, a cost ≥ 10, or a
19
+ # variable (`bcrypt.GenerateFromPassword(pw, cost)`) are NOT flagged.
20
+ patterns:
21
+ - pattern: bcrypt.GenerateFromPassword($PW, $N)
22
+ - metavariable-comparison:
23
+ metavariable: $N
24
+ comparison: $N < 10
25
+ metadata:
26
+ oauthlint-rule-id: AUTH-GO-CRYPTO-002
27
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-crypto-bcrypt-low-cost
28
+ category: security
29
+ cwe: CWE-916
30
+ owasp: A02:2021
31
+ llm-prevalence: MEDIUM
32
+ technology:
33
+ - bcrypt
34
+ references:
35
+ - https://pkg.go.dev/golang.org/x/crypto/bcrypt#GenerateFromPassword
36
+ - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
37
+ - https://cwe.mitre.org/data/definitions/916.html
@@ -0,0 +1,38 @@
1
+ rules:
2
+ - id: auth.go.crypto.weak-cipher
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ A broken or deprecated block/stream cipher is used to protect data. DES
8
+ (`des.NewCipher`) and 3DES (`des.NewTripleDESCipher`) have a 64-bit block
9
+ and are considered insecure (Sweet32, brute-force), while RC4
10
+ (`rc4.NewCipher`) has well-known keystream biases and is forbidden by RFC
11
+ 7465. For OAuth/OIDC this means tokens, client secrets, and other
12
+ sensitive material are not adequately protected and may be recovered by
13
+ an attacker.
14
+
15
+ Use authenticated AES instead: load the key with `crypto/aes`
16
+ (`aes.NewCipher(key)`) and wrap it in `cipher.NewGCM(block)` to get
17
+ AES-GCM, which provides both confidentiality and integrity.
18
+ # Flags only the broken ciphers DES, 3DES, and RC4. `aes.NewCipher(...)`
19
+ # is intentionally not matched.
20
+ patterns:
21
+ - pattern-either:
22
+ - pattern: des.NewCipher(...)
23
+ - pattern: des.NewTripleDESCipher(...)
24
+ - pattern: rc4.NewCipher(...)
25
+ metadata:
26
+ oauthlint-rule-id: AUTH-GO-CRYPTO-003
27
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-crypto-weak-cipher
28
+ category: security
29
+ cwe: CWE-327
30
+ owasp: A02:2021
31
+ llm-prevalence: MEDIUM
32
+ technology:
33
+ - crypto/des
34
+ - crypto/rc4
35
+ references:
36
+ - https://pkg.go.dev/crypto/aes#NewCipher
37
+ - https://pkg.go.dev/crypto/cipher#NewGCM
38
+ - https://cwe.mitre.org/data/definitions/327.html
@@ -0,0 +1,47 @@
1
+ rules:
2
+ - id: auth.go.crypto.weak-password-hash
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ A password is being hashed with a fast, general-purpose digest from the
8
+ Go standard library (MD5, SHA-1, SHA-256, SHA-512). These algorithms are
9
+ designed to be fast, which makes offline brute-force and rainbow-table
10
+ attacks cheap — they are NOT suitable for storing passwords (CWE-916).
11
+
12
+ Use a dedicated, slow password-hashing function with a per-password salt
13
+ and a tunable work factor: bcrypt
14
+ (`golang.org/x/crypto/bcrypt.GenerateFromPassword`), Argon2
15
+ (`golang.org/x/crypto/argon2.IDKey`), or scrypt
16
+ (`golang.org/x/crypto/scrypt.Key`). These resist brute-force by design.
17
+ # Anchored to the *password* character of the input: the digested/written
18
+ # argument must be named like a password (metavariable-regex on $PW). This
19
+ # avoids flagging `sha256.Sum256(fileBytes)` for file checksums or
20
+ # non-password fingerprints, and does not touch real password hashers
21
+ # (bcrypt/argon2/scrypt). Both the one-shot `Sum`/`Sum256`/`Sum512` form
22
+ # and the streaming `h := md5.New(); h.Write([]byte(password))` writer form
23
+ # are covered.
24
+ patterns:
25
+ - pattern-either:
26
+ - pattern: md5.Sum([]byte($PW))
27
+ - pattern: sha1.Sum([]byte($PW))
28
+ - pattern: sha256.Sum256([]byte($PW))
29
+ - pattern: sha512.Sum512([]byte($PW))
30
+ - pattern: $H.Write([]byte($PW))
31
+ - metavariable-regex:
32
+ metavariable: $PW
33
+ regex: (?i).*(password|passwd|pwd).*
34
+ metadata:
35
+ oauthlint-rule-id: AUTH-GO-CRYPTO-001
36
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-crypto-weak-password-hash
37
+ category: security
38
+ cwe: CWE-916
39
+ owasp: A02:2021
40
+ llm-prevalence: HIGH
41
+ technology:
42
+ - crypto/md5
43
+ - crypto/sha256
44
+ references:
45
+ - https://cwe.mitre.org/data/definitions/916.html
46
+ - https://pkg.go.dev/golang.org/x/crypto/bcrypt
47
+ - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
@@ -0,0 +1,61 @@
1
+ rules:
2
+ - id: auth.go.flow.weak-rand
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ A security-sensitive value — its name indicates a token, secret, key,
8
+ password, nonce, OTP, or salt — is being generated with the `math/rand`
9
+ package. `math/rand` is a deterministic PRNG: its output is predictable
10
+ and an attacker who observes enough values can recover the seed and
11
+ forecast every future token. For OAuth/OIDC this means forgeable
12
+ `state` values, guessable authorization codes, and predictable refresh
13
+ tokens.
14
+
15
+ Use `crypto/rand` instead: allocate a byte slice and fill it with
16
+ `rand.Read(b)` (from `crypto/rand`), then hex- or base64url-encode it.
17
+ Never derive a credential from `math/rand`.
18
+ # We match `rand.$F(...)` where $F is one of the math/rand-EXCLUSIVE
19
+ # generators (Intn/Int/Int31/Int63/Float64/Perm). These do not exist in
20
+ # crypto/rand, so a match is unambiguous. We deliberately do NOT match
21
+ # `rand.Read(...)`, which exists in BOTH packages — the crypto/rand one is
22
+ # the safe path.
23
+ #
24
+ # The result must be assigned (`:=` or `=`) to a secret-shaped identifier;
25
+ # `i := rand.Intn(10)` for a loop or `delay := rand.Intn(500)` for jitter
26
+ # are not flagged. The name constraint uses `metavariable-pattern` +
27
+ # `pattern-regex` (rather than `metavariable-regex`) so that $VAR is
28
+ # recorded as a distinguishing binding — otherwise Semgrep's Go frontend
29
+ # leaves the short-var-decl LHS unbound and collapses two genuinely
30
+ # distinct secrets into a single finding.
31
+ pattern-either:
32
+ - patterns:
33
+ - pattern: '$VAR := rand.$F(...)'
34
+ - metavariable-regex:
35
+ metavariable: $F
36
+ regex: ^(Intn|Int|Int31|Int63|Float64|Perm)$
37
+ - metavariable-pattern:
38
+ metavariable: $VAR
39
+ patterns:
40
+ - pattern-regex: (?i)(token|secret|key|password|nonce|otp|salt)
41
+ - patterns:
42
+ - pattern: '$VAR = rand.$F(...)'
43
+ - metavariable-regex:
44
+ metavariable: $F
45
+ regex: ^(Intn|Int|Int31|Int63|Float64|Perm)$
46
+ - metavariable-pattern:
47
+ metavariable: $VAR
48
+ patterns:
49
+ - pattern-regex: (?i)(token|secret|key|password|nonce|otp|salt)
50
+ metadata:
51
+ oauthlint-rule-id: AUTH-GO-FLOW-001
52
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-flow-weak-rand
53
+ category: security
54
+ cwe: CWE-330
55
+ owasp: A02:2021
56
+ llm-prevalence: HIGH
57
+ technology:
58
+ - math/rand
59
+ references:
60
+ - https://pkg.go.dev/crypto/rand#Read
61
+ - https://cwe.mitre.org/data/definitions/330.html
@@ -0,0 +1,37 @@
1
+ rules:
2
+ - id: auth.go.jwt.hardcoded-secret
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ A JWT HMAC signing/verification key is hardcoded as a string literal in a
8
+ call to golang-jwt. Anyone who can read the source or git history can
9
+ forge or tamper with tokens, which is a complete authentication bypass.
10
+
11
+ Load the secret from the environment or a secret manager instead, e.g.
12
+ `key := []byte(os.Getenv("JWT_SECRET"))` and `token.SignedString(key)`.
13
+ Never commit signing keys to source control.
14
+ # Two shapes are flagged, both requiring the key to be a `[]byte("...")`
15
+ # built directly from a string literal (the `"..."` metavariadic only
16
+ # matches a string-literal AST node): the signing side
17
+ # `tok.SignedString([]byte("x"))`, and a golang-jwt Keyfunc that returns
18
+ # `[]byte("x")` as the verification key. Because the literal must appear
19
+ # in key position, `[]byte(os.Getenv("JWT_SECRET"))` and `[]byte(secret)`
20
+ # (a variable) are NOT matched — only string literals.
21
+ pattern-either:
22
+ - pattern: $TOKEN.SignedString([]byte("..."))
23
+ - patterns:
24
+ - pattern: return []byte("..."), nil
25
+ - pattern-inside: 'func(...) (interface{}, error) { ... }'
26
+ metadata:
27
+ oauthlint-rule-id: AUTH-GO-JWT-003
28
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-jwt-hardcoded-secret
29
+ category: security
30
+ cwe: CWE-798
31
+ owasp: API2:2023
32
+ llm-prevalence: HIGH
33
+ technology:
34
+ - golang-jwt
35
+ references:
36
+ - https://pkg.go.dev/github.com/golang-jwt/jwt/v5#Token.SignedString
37
+ - https://cwe.mitre.org/data/definitions/798.html
@@ -0,0 +1,38 @@
1
+ rules:
2
+ - id: auth.go.jwt.none-algorithm
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ A JWT is created or accepted with the `none` algorithm, which produces an
8
+ unsigned token. Anyone can forge such a token by setting `alg: none` and
9
+ omitting the signature, so the server has no way to verify who issued it —
10
+ a complete authentication bypass (CWE-347). In golang-jwt this happens
11
+ when signing with `jwt.SigningMethodNone` or passing the
12
+ `jwt.UnsafeAllowNoneSignatureType` sentinel to `SignedString`.
13
+
14
+ Never use the `none` algorithm. Sign tokens with a real algorithm such as
15
+ HS256 (`jwt.SigningMethodHS256`), RS256 (`jwt.SigningMethodRS256`), or
16
+ ES256 (`jwt.SigningMethodES256`) and verify them with the matching key.
17
+ # Targets code that ENABLES `none`: building a token with
18
+ # `SigningMethodNone`, or the `UnsafeAllowNoneSignatureType` sentinel (which
19
+ # is only ever used to allow none). A defensive comparison such as
20
+ # `token.Method == jwt.SigningMethodNone` to REJECT none is deliberately not
21
+ # flagged. HS256/RS256/ES256 are never matched.
22
+ pattern-either:
23
+ - pattern: jwt.NewWithClaims(jwt.SigningMethodNone, ...)
24
+ - pattern: jwt.New(jwt.SigningMethodNone, ...)
25
+ - pattern: jwt.New(jwt.SigningMethodNone)
26
+ - pattern: jwt.UnsafeAllowNoneSignatureType
27
+ metadata:
28
+ oauthlint-rule-id: AUTH-GO-JWT-001
29
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-jwt-none-algorithm
30
+ category: security
31
+ cwe: CWE-347
32
+ owasp: API2:2023
33
+ llm-prevalence: HIGH
34
+ technology:
35
+ - golang-jwt
36
+ references:
37
+ - https://pkg.go.dev/github.com/golang-jwt/jwt/v5#pkg-variables
38
+ - https://cwe.mitre.org/data/definitions/347.html
@@ -0,0 +1,33 @@
1
+ rules:
2
+ - id: auth.go.jwt.parse-unverified
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ A JWT is decoded with `ParseUnverified`, which parses the token WITHOUT
8
+ checking its signature. Any claims read from the result are fully
9
+ attacker-controlled — an attacker can forge arbitrary subjects, scopes,
10
+ or expiry and the token will still parse. Trusting these claims for
11
+ authentication or authorization is a complete auth bypass (CWE-347).
12
+
13
+ Verify the signature instead: use `jwt.Parse(tok, keyfunc)` or
14
+ `jwt.ParseWithClaims(tok, claims, keyfunc)` with a `Keyfunc` that returns
15
+ the expected signing key, so a token with a bad or missing signature is
16
+ rejected.
17
+ # Matches the `ParseUnverified` method on any *jwt.Parser receiver:
18
+ # `parser.ParseUnverified(...)`, `jwt.NewParser().ParseUnverified(...)`,
19
+ # `new(jwt.Parser).ParseUnverified(...)`. `jwt.Parse(...)` and
20
+ # `jwt.ParseWithClaims(...)` verify the signature and are NOT matched.
21
+ pattern: $P.ParseUnverified($TOK, $CLAIMS)
22
+ metadata:
23
+ oauthlint-rule-id: AUTH-GO-JWT-002
24
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-jwt-parse-unverified
25
+ category: security
26
+ cwe: CWE-347
27
+ owasp: API2:2023
28
+ llm-prevalence: HIGH
29
+ technology:
30
+ - golang-jwt
31
+ references:
32
+ - https://pkg.go.dev/github.com/golang-jwt/jwt/v5#Parser.ParseUnverified
33
+ - https://cwe.mitre.org/data/definitions/347.html
@@ -0,0 +1,72 @@
1
+ rules:
2
+ - id: auth.go.jwt.unchecked-method
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ A `Keyfunc` passed to `jwt.Parse`/`jwt.ParseWithClaims` returns the
8
+ verification key WITHOUT first checking `token.Method` (the signing
9
+ algorithm). This enables an algorithm-confusion attack: if the server
10
+ verifies RS256 tokens with an RSA public key, an attacker can forge a
11
+ token signed with HS256 using that public key as the HMAC secret, and the
12
+ library will accept it — a complete authentication bypass (CWE-347).
13
+
14
+ Always assert the signing method inside the keyfunc before returning the
15
+ key, e.g.:
16
+ `if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, err }`
17
+ (or `*jwt.SigningMethodRSA` / `*jwt.SigningMethodECDSA` as appropriate),
18
+ so a token signed with an unexpected algorithm is rejected.
19
+ # Matches a keyfunc literal that returns the key directly (`return $KEY, nil`)
20
+ # and whose body does NOT reference `.Method` (neither a type assertion
21
+ # `t.Method.(*jwt.SigningMethod...)` nor a comparison `t.Method != ...`).
22
+ # The `pattern-not` with the `<... $T.Method ...>` deep-expression operator
23
+ # excludes any keyfunc that inspects the signing method, keeping correct
24
+ # method-checking keyfuncs from being flagged.
25
+ patterns:
26
+ - pattern-either:
27
+ - pattern: |
28
+ jwt.Parse($TOK, func($T *jwt.Token) (interface{}, error) {
29
+ ...
30
+ return $KEY, nil
31
+ })
32
+ - pattern: |
33
+ jwt.ParseWithClaims($TOK, $CLAIMS, func($T *jwt.Token) (interface{}, error) {
34
+ ...
35
+ return $KEY, nil
36
+ })
37
+ - pattern-not: |
38
+ jwt.Parse($TOK, func($T *jwt.Token) (interface{}, error) {
39
+ ...
40
+ $T.Method
41
+ ...
42
+ })
43
+ - pattern-not: |
44
+ jwt.ParseWithClaims($TOK, $CLAIMS, func($T *jwt.Token) (interface{}, error) {
45
+ ...
46
+ $T.Method
47
+ ...
48
+ })
49
+ - pattern-not: |
50
+ jwt.Parse($TOK, func($T *jwt.Token) (interface{}, error) {
51
+ ...
52
+ if <... $T.Method ...> { ... }
53
+ ...
54
+ })
55
+ - pattern-not: |
56
+ jwt.ParseWithClaims($TOK, $CLAIMS, func($T *jwt.Token) (interface{}, error) {
57
+ ...
58
+ if <... $T.Method ...> { ... }
59
+ ...
60
+ })
61
+ metadata:
62
+ oauthlint-rule-id: AUTH-GO-JWT-004
63
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-jwt-unchecked-method
64
+ category: security
65
+ cwe: CWE-347
66
+ owasp: API2:2023
67
+ llm-prevalence: HIGH
68
+ technology:
69
+ - golang-jwt
70
+ references:
71
+ - https://pkg.go.dev/github.com/golang-jwt/jwt/v5#Keyfunc
72
+ - https://cwe.mitre.org/data/definitions/347.html
@@ -0,0 +1,32 @@
1
+ rules:
2
+ - id: auth.go.tls.insecure-skip-verify
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ A `tls.Config` is created with `InsecureSkipVerify: true`, which disables
8
+ verification of the server's certificate chain and host name. Any
9
+ attacker who can intercept the connection can present any certificate
10
+ and read or tamper with the traffic — a classic man-in-the-middle hole.
11
+ For OAuth/OIDC this leaks authorization codes, access tokens, and client
12
+ secrets in transit.
13
+
14
+ Never set `InsecureSkipVerify: true`. Leave verification on (the default).
15
+ To trust a private CA in development, set `RootCAs` to a `*x509.CertPool`
16
+ loaded with that CA instead.
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, ...}'
21
+ metadata:
22
+ oauthlint-rule-id: AUTH-GO-TLS-001
23
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-tls-insecure-skip-verify
24
+ category: security
25
+ cwe: CWE-295
26
+ owasp: A02:2021
27
+ llm-prevalence: HIGH
28
+ technology:
29
+ - crypto/tls
30
+ references:
31
+ - https://pkg.go.dev/crypto/tls#Config
32
+ - https://cwe.mitre.org/data/definitions/295.html
@@ -0,0 +1,36 @@
1
+ rules:
2
+ - id: auth.go.tls.min-version
3
+ languages:
4
+ - go
5
+ severity: ERROR
6
+ message: |
7
+ A `tls.Config` is created with `MinVersion` pinned to an obsolete
8
+ protocol — SSL 3.0, TLS 1.0, or TLS 1.1. These versions have known
9
+ cryptographic weaknesses (POODLE, BEAST, downgrade attacks) and are
10
+ deprecated by RFC 8996. Allowing them lets an attacker negotiate a
11
+ broken cipher and intercept or tamper with OAuth/OIDC traffic,
12
+ leaking authorization codes, access tokens, and client secrets.
13
+
14
+ Set `MinVersion` to at least `tls.VersionTLS12`, and ideally
15
+ `tls.VersionTLS13`, so the handshake refuses obsolete protocols.
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 `&`).
19
+ patterns:
20
+ - pattern: 'tls.Config{..., MinVersion: $V, ...}'
21
+ - metavariable-regex:
22
+ metavariable: $V
23
+ regex: ^tls\.(VersionSSL30|VersionTLS10|VersionTLS11)$
24
+ metadata:
25
+ oauthlint-rule-id: AUTH-GO-TLS-002
26
+ oauthlint-doc-url: https://oauthlint.dev/rules/go-tls-min-version
27
+ category: security
28
+ cwe: CWE-326
29
+ owasp: A02:2021
30
+ llm-prevalence: MEDIUM
31
+ technology:
32
+ - crypto/tls
33
+ references:
34
+ - https://pkg.go.dev/crypto/tls#Config
35
+ - https://datatracker.ietf.org/doc/html/rfc8996
36
+ - https://cwe.mitre.org/data/definitions/326.html
@@ -0,0 +1,31 @@
1
+ rules:
2
+ - id: auth.java.cookie.insecure
3
+ languages:
4
+ - java
5
+ severity: ERROR
6
+ message: |
7
+ A servlet Cookie is created with a security attribute explicitly
8
+ disabled. `setSecure(false)` lets the cookie travel over plain HTTP,
9
+ and `setHttpOnly(false)` makes it readable from JavaScript — either way
10
+ a session or auth cookie can be intercepted or stolen (CWE-614). This is
11
+ a common AI-generated mistake where the flag is set to `false` to "make
12
+ it work" over localhost and never switched back.
13
+
14
+ Set `cookie.setSecure(true)` and `cookie.setHttpOnly(true)` on every
15
+ session or authentication cookie, and add `SameSite` (e.g. `Strict` or
16
+ `Lax`) to further limit cross-site exposure.
17
+ pattern-either:
18
+ - pattern: $C.setSecure(false)
19
+ - pattern: $C.setHttpOnly(false)
20
+ metadata:
21
+ oauthlint-rule-id: AUTH-JAVA-COOKIE-001
22
+ oauthlint-doc-url: https://oauthlint.dev/rules/java-cookie-insecure
23
+ category: security
24
+ cwe: CWE-614
25
+ owasp: A05:2021
26
+ llm-prevalence: HIGH
27
+ technology:
28
+ - servlet
29
+ references:
30
+ - https://owasp.org/www-community/controls/SecureCookieAttribute
31
+ - https://cwe.mitre.org/data/definitions/614.html
@@ -0,0 +1,47 @@
1
+ rules:
2
+ - id: auth.java.cors.allow-all
3
+ languages:
4
+ - java
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 Spring
10
+ mistake — `@CrossOrigin(origins = "*")` or `addAllowedOrigin("*")` is
11
+ pasted in to "make the browser call work" and the intended scope is
12
+ never added. A bare `@CrossOrigin` (no arguments) also defaults to all
13
+ origins.
14
+
15
+ Restrict CORS to an explicit allowlist of trusted origins instead, e.g.
16
+ `@CrossOrigin(origins = "https://app.example.com")` or
17
+ `config.setAllowedOrigins(List.of("https://app.example.com"))`. Note that
18
+ `addAllowedOriginPattern("*")` combined with `setAllowCredentials(true)`
19
+ is just as dangerous, because it sends the victim's cookies cross-origin.
20
+ # Covers the Spring MVC annotation (with an explicit `origins = "*"`, with
21
+ # the value alias `@CrossOrigin("*")`, and the bare `@CrossOrigin` which
22
+ # defaults to all origins), plus the programmatic CorsConfiguration API:
23
+ # `addAllowedOrigin("*")` and `setAllowedOrigins(...)` whose argument is a
24
+ # list literal containing `"*"` (`List.of("*")`, `Arrays.asList("*")`,
25
+ # `Collections.singletonList("*")`, ...). Only the literal `"*"` is
26
+ # flagged — explicit origins like `"https://app.example.com"` are not.
27
+ pattern-either:
28
+ - pattern: '@CrossOrigin(origins = "*")'
29
+ - pattern: '@CrossOrigin(origins = {..., "*", ...})'
30
+ - pattern: '@CrossOrigin("*")'
31
+ - pattern: |
32
+ @CrossOrigin
33
+ $RET $M(...) { ... }
34
+ - pattern: $C.addAllowedOrigin("*")
35
+ - pattern: $C.setAllowedOrigins($L(..., "*", ...))
36
+ metadata:
37
+ oauthlint-rule-id: AUTH-JAVA-CORS-001
38
+ oauthlint-doc-url: https://oauthlint.dev/rules/java-cors-allow-all
39
+ category: security
40
+ cwe: CWE-942
41
+ owasp: A05:2021
42
+ llm-prevalence: HIGH
43
+ technology:
44
+ - spring-web
45
+ references:
46
+ - https://docs.spring.io/spring-framework/reference/web/webmvc-cors.html
47
+ - https://cwe.mitre.org/data/definitions/942.html
@@ -0,0 +1,42 @@
1
+ rules:
2
+ - id: auth.java.crypto.ecb-mode
3
+ languages:
4
+ - java
5
+ severity: ERROR
6
+ message: |
7
+ A JCA `Cipher` is being created in ECB mode (or with a bare algorithm
8
+ alias that defaults to ECB). ECB encrypts each block independently, so
9
+ identical plaintext blocks produce identical ciphertext blocks — it is
10
+ deterministic and leaks structure/patterns of the plaintext (CWE-327).
11
+ `Cipher.getInstance("AES")`, `"DES"`, `"DESede"`, or `"Blowfish"` with no
12
+ mode specified silently falls back to ECB as well.
13
+
14
+ Use an authenticated mode: `Cipher.getInstance("AES/GCM/NoPadding")` with
15
+ a unique 12-byte IV per message. At minimum use CBC with a random IV plus
16
+ a separate HMAC (encrypt-then-MAC). Never use ECB or a bare cipher alias.
17
+ # Match only the transformation string passed to Cipher.getInstance(...).
18
+ # The metavariable-regex flags either a bare symmetric algorithm alias
19
+ # (AES / DES / DESede / Blowfish with no mode — these default to ECB) or an
20
+ # explicit ECB mode on one of those block ciphers (any padding). It is
21
+ # scoped to symmetric block ciphers on purpose: "AES/GCM/NoPadding" and
22
+ # "AES/CBC/PKCS5Padding" have a safe mode, and "RSA/ECB/OAEPWith..." is
23
+ # asymmetric (the "/ECB/" token there is a JCA quirk, not block-ECB), so
24
+ # none of them match.
25
+ patterns:
26
+ - pattern: javax.crypto.Cipher.getInstance($TRANSFORM, ...)
27
+ - metavariable-regex:
28
+ metavariable: $TRANSFORM
29
+ regex: (?i)^"(aes|des|desede|blowfish)(/ecb/.*)?"$
30
+ metadata:
31
+ oauthlint-rule-id: AUTH-JAVA-CRYPTO-003
32
+ oauthlint-doc-url: https://oauthlint.dev/rules/java-crypto-ecb-mode
33
+ category: security
34
+ cwe: CWE-327
35
+ owasp: A02:2021
36
+ llm-prevalence: MEDIUM
37
+ technology:
38
+ - javax.crypto
39
+ references:
40
+ - https://cwe.mitre.org/data/definitions/327.html
41
+ - https://owasp.org/Top10/A02_2021-Cryptographic_Failures/
42
+ - https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
@@ -0,0 +1,62 @@
1
+ rules:
2
+ - id: auth.java.crypto.insecure-random
3
+ languages:
4
+ - java
5
+ severity: ERROR
6
+ message: |
7
+ A security-sensitive value (token, secret, key, password, nonce, OTP, or
8
+ salt) is generated with a non-cryptographic PRNG. `java.util.Random` and
9
+ `Math.random()` are predictable: their output is seeded from system time
10
+ and the internal state can be recovered from a few observed values, so an
11
+ attacker can reconstruct the "random" secret (CWE-330). This is a common
12
+ AI-generated mistake — `new Random().nextInt(...)` gets pasted in to
13
+ produce a token because it looks random enough.
14
+
15
+ Use `java.security.SecureRandom` instead. For example,
16
+ `new SecureRandom().nextBytes(buf)` to fill a byte buffer, then encode it
17
+ (Base64/hex) to build the token or secret.
18
+ pattern-either:
19
+ # $VAR = new Random().nextInt(...) / nextLong() / nextDouble() / nextFloat()
20
+ - patterns:
21
+ - pattern-either:
22
+ - pattern: $VAR = new java.util.Random(...).$M(...)
23
+ - pattern: $VAR = new Random(...).$M(...)
24
+ - metavariable-regex:
25
+ metavariable: $VAR
26
+ regex: (?i).*(token|secret|key|password|passwd|nonce|otp|salt).*
27
+ - metavariable-regex:
28
+ metavariable: $M
29
+ regex: ^(nextInt|nextLong|nextDouble|nextFloat|nextBoolean)$
30
+ # $VAR = Math.random()
31
+ - patterns:
32
+ - pattern: $VAR = Math.random()
33
+ - metavariable-regex:
34
+ metavariable: $VAR
35
+ regex: (?i).*(token|secret|key|password|passwd|nonce|otp|salt).*
36
+ # $RND.nextBytes($BUF) where $RND is a java.util.Random (not SecureRandom)
37
+ # and the buffer is a secret.
38
+ - patterns:
39
+ - pattern-either:
40
+ - pattern: |
41
+ java.util.Random $RND = new java.util.Random(...);
42
+ ...
43
+ $RND.nextBytes($BUF);
44
+ - pattern: |
45
+ Random $RND = new Random(...);
46
+ ...
47
+ $RND.nextBytes($BUF);
48
+ - metavariable-regex:
49
+ metavariable: $BUF
50
+ regex: (?i).*(token|secret|key|password|passwd|nonce|otp|salt).*
51
+ metadata:
52
+ oauthlint-rule-id: AUTH-JAVA-CRYPTO-002
53
+ oauthlint-doc-url: https://oauthlint.dev/rules/java-crypto-insecure-random
54
+ category: security
55
+ cwe: CWE-330
56
+ owasp: A02:2021
57
+ llm-prevalence: HIGH
58
+ technology:
59
+ - java.util
60
+ references:
61
+ - https://docs.oracle.com/javase/8/docs/api/java/security/SecureRandom.html
62
+ - https://cwe.mitre.org/data/definitions/330.html