oauthlint-rules 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauthlint-rules",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
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",
@@ -0,0 +1,41 @@
1
+ rules:
2
+ - id: auth.java.crypto.weak-hash
3
+ languages:
4
+ - java
5
+ severity: WARNING
6
+ message: |
7
+ A broken hash algorithm (MD5 or SHA-1) is being instantiated via JCA
8
+ `MessageDigest.getInstance(...)`. MD5 and SHA-1 are cryptographically
9
+ broken: practical collision attacks exist, so they must NOT be used for
10
+ any security purpose — integrity checks, content/token fingerprints,
11
+ digital signatures, HMAC keys, or deduplication that a trust decision
12
+ depends on (CWE-328 / CWE-327).
13
+
14
+ Use SHA-256 or stronger (SHA-384, SHA-512, SHA-3):
15
+ `MessageDigest.getInstance("SHA-256")`. Note: this rule covers the general
16
+ weak-digest case; storing *passwords* needs a dedicated slow hasher
17
+ (BCrypt/Argon2/PBKDF2), which is enforced separately.
18
+ # Match only the algorithm string passed to MessageDigest.getInstance(...).
19
+ # The metavariable-regex flags the broken digests MD5, SHA-1 and the SHA1
20
+ # alias (case-insensitive, optional hyphen). It is scoped to the
21
+ # getInstance(...) instantiation site on purpose so it is low-FP and does
22
+ # NOT overlap auth.java.crypto.weak-password-hash, which instead anchors on
23
+ # a password-named digest()/update() call (and also flags SHA-256/SHA-512 in
24
+ # that password context). "SHA-256"/"SHA-512" are silent here.
25
+ patterns:
26
+ - pattern: java.security.MessageDigest.getInstance($ALG, ...)
27
+ - metavariable-regex:
28
+ metavariable: $ALG
29
+ regex: (?i)^"(md5|sha-?1)"$
30
+ metadata:
31
+ oauthlint-rule-id: AUTH-JAVA-CRYPTO-004
32
+ oauthlint-doc-url: https://oauthlint.dev/rules/java-crypto-weak-hash
33
+ category: security
34
+ cwe: CWE-328
35
+ owasp: A02:2021
36
+ llm-prevalence: MEDIUM
37
+ technology:
38
+ - java
39
+ references:
40
+ - https://cwe.mitre.org/data/definitions/328.html
41
+ - https://csrc.nist.gov/news/2022/nist-transitioning-away-from-sha-1-for-all-apps
@@ -0,0 +1,39 @@
1
+ rules:
2
+ - id: auth.oauth.access-token-in-url
3
+ languages:
4
+ - javascript
5
+ - typescript
6
+ severity: WARNING
7
+ message: |
8
+ An OAuth `access_token` (or `refresh_token` / `id_token`) is placed in a
9
+ URL query string. URLs leak: the full URL — token included — is recorded
10
+ in server and reverse-proxy access logs, saved in browser history, and
11
+ sent in the `Referer` header to every third-party CDN, analytics, and ad
12
+ script loaded by the destination page. A token in a URL is a leaked token.
13
+
14
+ Send the token in the `Authorization: Bearer …` header, or in a POST
15
+ request body. Never in the URL query string.
16
+
17
+ CWE-598: Use of GET Request Method With Sensitive Query Strings.
18
+ # Anchored to the query-param shape `?<name>=` / `&<name>=` (the `[?&]…=`
19
+ # form) so we fire on token-carrying URLs built as string or template
20
+ # literals, while staying silent on: a token in an `Authorization` header,
21
+ # a POST body / object property `{ access_token: t }` (no leading `?`/`&`
22
+ # and no trailing `=`), `params.set('access_token', …)` builders, and the
23
+ # bare word `access_token` in a comment or non-URL string. Case-insensitive
24
+ # on the param name. This is deliberately distinct from
25
+ # auth.flow.credentials-in-url (which excludes `access_token`) and
26
+ # auth.jwt.in-url (which only matches JWT-shaped `eyJ…` values).
27
+ pattern-regex: '[?&](?i:access_token|refresh_token|id_token)='
28
+ metadata:
29
+ oauthlint-rule-id: AUTH-OAUTH-013
30
+ oauthlint-doc-url: https://oauthlint.dev/rules/oauth-access-token-in-url
31
+ category: security
32
+ cwe: CWE-598
33
+ owasp: A05:2021
34
+ llm-prevalence: MEDIUM
35
+ technology:
36
+ - oauth
37
+ references:
38
+ - https://owasp.org/Top10/A05_2021-Security_Misconfiguration/
39
+ - https://cwe.mitre.org/data/definitions/598.html
@@ -0,0 +1,59 @@
1
+ rules:
2
+ - id: auth.rust.jwt.no-issuer-validation
3
+ languages:
4
+ - rust
5
+ severity: WARNING
6
+ message: |
7
+ A JWT is decoded with a `jsonwebtoken` `Validation` that never sets the
8
+ expected issuer, so `decode` accepts a token minted by ANY issuer. The
9
+ `jsonwebtoken` crate does not validate the `iss` claim unless you opt in,
10
+ so a token signed by an attacker-controlled or otherwise untrusted issuer
11
+ passes validation as long as the signature checks out. For OAuth/OIDC this
12
+ lets a token from the wrong authorization server be replayed against this
13
+ API.
14
+
15
+ Pin the issuer before decoding, e.g.
16
+ `validation.set_issuer(&["https://issuer.example.com"])` (or set
17
+ `validation.iss`), so only tokens whose `iss` claim matches your trusted
18
+ authorization server are accepted.
19
+ # Modeled on auth.rust.jwt.no-aud-validation: detect a `Validation`
20
+ # (`Validation::new(...)` or `Validation::default()`) that flows into
21
+ # `decode(...)` but never has its issuer pinned. Unlike `aud`/`exp` (default
22
+ # `true`, disabled via `validate_* = false`), the issuer check is OFF by
23
+ # default and must be opted into via `set_issuer(...)` / `validation.iss`,
24
+ # so the vulnerable shape is the ABSENCE of that call. We bind the validator
25
+ # to `$V` at construction, require a `decode` that uses it, and suppress the
26
+ # finding with `pattern-not-inside` when `set_issuer` / `validation.iss` is
27
+ # set on the same `$V`. Matching on the construction (not the `decode` call)
28
+ # mirrors the sibling rule's low-FP, AST-only profile.
29
+ patterns:
30
+ - pattern-either:
31
+ - pattern: let mut $V = Validation::new(...);
32
+ - pattern: let mut $V = Validation::default();
33
+ - pattern-not-inside: |
34
+ let mut $V = ...;
35
+ ...
36
+ $V.set_issuer(...);
37
+ ...
38
+ - pattern-not-inside: |
39
+ let mut $V = ...;
40
+ ...
41
+ $V.iss = $X;
42
+ ...
43
+ - pattern-inside: |
44
+ let mut $V = ...;
45
+ ...
46
+ decode($TOKEN, $KEY, &$V)
47
+ metadata:
48
+ oauthlint-rule-id: AUTH-RUST-JWT-005
49
+ oauthlint-doc-url: https://oauthlint.dev/rules/rust-jwt-no-issuer-validation
50
+ category: security
51
+ cwe: CWE-345
52
+ owasp: API2:2023
53
+ llm-prevalence: MEDIUM
54
+ technology:
55
+ - jsonwebtoken
56
+ references:
57
+ - https://docs.rs/jsonwebtoken/latest/jsonwebtoken/struct.Validation.html#method.set_issuer
58
+ - https://cwe.mitre.org/data/definitions/345.html
59
+ - https://cwe.mitre.org/data/definitions/287.html
@@ -16,6 +16,11 @@ rules:
16
16
  # Matches only the literal `true`. `danger_accept_invalid_certs(false)` and
17
17
  # the method's absence are not flagged. `$B` is any builder expression.
18
18
  pattern: $B.danger_accept_invalid_certs(true)
19
+ # Safe, deterministic autofix: flip the boolean literal to `false`, which is
20
+ # the secure default and fully resolves the finding. `$B` (the builder
21
+ # expression) is preserved verbatim, so the surrounding chain is untouched —
22
+ # only `true` -> `false` changes.
23
+ fix: $B.danger_accept_invalid_certs(false)
19
24
  metadata:
20
25
  oauthlint-rule-id: AUTH-RUST-TLS-001
21
26
  oauthlint-doc-url: https://oauthlint.dev/rules/rust-tls-accept-invalid-certs
@@ -18,6 +18,11 @@ rules:
18
18
  # Matches only the literal `true`. `danger_accept_invalid_hostnames(false)`
19
19
  # and the method's absence are not flagged. `$B` is any builder expression.
20
20
  pattern: $B.danger_accept_invalid_hostnames(true)
21
+ # Safe, deterministic autofix: flip the boolean literal to `false`, which is
22
+ # the secure default and fully resolves the finding. `$B` (the builder
23
+ # expression) is preserved verbatim, so the surrounding chain is untouched —
24
+ # only `true` -> `false` changes.
25
+ fix: $B.danger_accept_invalid_hostnames(false)
21
26
  metadata:
22
27
  oauthlint-rule-id: AUTH-RUST-TLS-002
23
28
  oauthlint-doc-url: https://oauthlint.dev/rules/rust-tls-accept-invalid-hostnames