oauthlint-rules 0.3.0 → 0.4.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.
- package/dist/loader.d.ts +1 -1
- package/dist/schema.d.ts +79 -19
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +13 -4
- package/dist/schema.js.map +1 -1
- package/package.json +1 -1
- package/rules/cookie/no-samesite.yml +1 -1
- package/rules/cookie/samesite-none-insecure.yml +23 -1
- package/rules/cors/null-origin.yml +1 -1
- package/rules/cors/reflect-origin.yml +1 -1
- package/rules/flow/basic-auth-in-log.yml +99 -0
- package/rules/flow/open-redirect.yml +53 -21
- package/rules/flow/ssrf.yml +61 -21
- package/rules/flow/timing-unsafe-compare.yml +19 -0
- package/rules/go/cookie/insecure.yml +1 -1
- package/rules/go/cors/allow-all.yml +1 -1
- package/rules/go/flow/open-redirect.yml +16 -6
- package/rules/go/flow/ssrf.yml +16 -7
- package/rules/go/oauth/ropc-grant.yml +15 -0
- package/rules/java/cookie/insecure.yml +1 -1
- package/rules/java/cors/allow-all.yml +1 -1
- package/rules/java/cors/credentialed-wildcard.yml +1 -1
- package/rules/java/flow/ssrf.yml +113 -0
- package/rules/java/session/fixation-disabled.yml +1 -1
- package/rules/java/tls/trust-all-certs.yml +18 -7
- package/rules/oauth/no-state.yml +9 -8
- package/rules/oauth/open-redirect-callback.yml +18 -3
- package/rules/oauth/ropc-grant.yml +8 -3
- package/rules/oauth/static-state.yml +8 -7
- package/rules/py/cookie/insecure-flags.yml +1 -1
- package/rules/py/cors/allow-all.yml +1 -1
- package/rules/py/flow/requests-verify-disabled.yml +1 -1
- package/rules/py/oauth/ropc-grant.yml +12 -4
- package/rules/rust/cookie/insecure.yml +1 -1
- package/rules/rust/cors/permissive.yml +1 -1
- package/rules/rust/flow/ssrf.yml +90 -0
- package/rules/rust/oauth/ropc-grant.yml +17 -0
- package/rules/rust/oauth/static-state.yml +25 -5
- package/rules/tls/reject-unauthorized.yml +1 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: auth.rust.flow.ssrf
|
|
3
|
+
languages:
|
|
4
|
+
- rust
|
|
5
|
+
severity: ERROR
|
|
6
|
+
message: |
|
|
7
|
+
Untrusted request input flows into the URL of an outbound HTTP request.
|
|
8
|
+
Because the destination is attacker-controlled, this is a Server-Side
|
|
9
|
+
Request Forgery (CWE-918): an attacker can point the request at internal
|
|
10
|
+
services behind your firewall, or at the cloud metadata endpoint
|
|
11
|
+
(http://169.254.169.254/...) to steal IAM/instance credentials and pivot
|
|
12
|
+
deeper into your infrastructure.
|
|
13
|
+
|
|
14
|
+
Never pass a request-derived `String` (an axum/actix handler parameter, or
|
|
15
|
+
a value taken from request input) straight into `reqwest::get(...)` or a
|
|
16
|
+
`Client::get(...)` / `Client::post(...).send()`. Validate the destination
|
|
17
|
+
host against an explicit allow-list (parse the URL and check the resolved
|
|
18
|
+
host/scheme, rejecting private/loopback/link-local ranges) before issuing
|
|
19
|
+
the request.
|
|
20
|
+
# Taint mode so indirection (let u = params.url; reqwest::get(u)) is caught,
|
|
21
|
+
# not just the inline form. The source is narrowed to function PARAMETERS
|
|
22
|
+
# (the shape an axum/actix handler takes its request data in) so an
|
|
23
|
+
# arbitrary local `let url = "https://constant"` is not treated as untrusted
|
|
24
|
+
# — only data arriving as a handler/function parameter is. The taint is
|
|
25
|
+
# cleared by an allow-list / host-validation if-guard, mirroring the Python
|
|
26
|
+
# and Go SSRF rules.
|
|
27
|
+
mode: taint
|
|
28
|
+
pattern-sources:
|
|
29
|
+
# A function parameter carrying request data: a bare `String` (the typical
|
|
30
|
+
# axum `String` / extracted field), or an axum `Query`/`Path` extractor
|
|
31
|
+
# (both the type-position and the destructured forms). Focus the parameter
|
|
32
|
+
# so the taint tracks the value rather than the whole function.
|
|
33
|
+
- patterns:
|
|
34
|
+
- pattern-either:
|
|
35
|
+
- pattern: "fn $F(..., $P: String, ...) {...}"
|
|
36
|
+
- pattern: "fn $F(..., $P: Query<$T>, ...) {...}"
|
|
37
|
+
- pattern: "fn $F(..., $P: Path<$T>, ...) {...}"
|
|
38
|
+
- pattern: "fn $F(..., Query($P): Query<$T>, ...) {...}"
|
|
39
|
+
- pattern: "fn $F(..., Path($P): Path<$T>, ...) {...}"
|
|
40
|
+
- focus-metavariable: $P
|
|
41
|
+
pattern-sanitizers:
|
|
42
|
+
# A value used inside an allow-list / host-validation if-guard is treated
|
|
43
|
+
# as vetted, so the guarded request does not fire. A bare `Url::parse(...)`
|
|
44
|
+
# is deliberately NOT a sanitizer: the parsed URL is still
|
|
45
|
+
# attacker-controlled, so parse-then-use without a host check must fire.
|
|
46
|
+
- patterns:
|
|
47
|
+
- pattern: $V
|
|
48
|
+
- pattern-inside: |
|
|
49
|
+
if is_allowed_url(<... $V ...>) {
|
|
50
|
+
...
|
|
51
|
+
}
|
|
52
|
+
- patterns:
|
|
53
|
+
- pattern: $V
|
|
54
|
+
- pattern-inside: |
|
|
55
|
+
if validate_host(<... $V ...>) {
|
|
56
|
+
...
|
|
57
|
+
}
|
|
58
|
+
- patterns:
|
|
59
|
+
- pattern: $V
|
|
60
|
+
- pattern-inside: |
|
|
61
|
+
if $ALLOW.contains(<... $V ...>) {
|
|
62
|
+
...
|
|
63
|
+
}
|
|
64
|
+
pattern-sinks:
|
|
65
|
+
# Focus the URL argument so the finding lands on the tainted destination.
|
|
66
|
+
- patterns:
|
|
67
|
+
- pattern-either:
|
|
68
|
+
- pattern: reqwest::get($URL)
|
|
69
|
+
- pattern: reqwest::blocking::get($URL)
|
|
70
|
+
- pattern: $C.get($URL).send()
|
|
71
|
+
- pattern: $C.post($URL).send()
|
|
72
|
+
- pattern: $C.put($URL).send()
|
|
73
|
+
- pattern: $C.delete($URL).send()
|
|
74
|
+
- pattern: $C.head($URL).send()
|
|
75
|
+
- pattern: $C.request($M, $URL).send()
|
|
76
|
+
- focus-metavariable: $URL
|
|
77
|
+
metadata:
|
|
78
|
+
oauthlint-rule-id: AUTH-RUST-FLOW-002
|
|
79
|
+
oauthlint-doc-url: https://oauthlint.dev/rules/rust-flow-ssrf
|
|
80
|
+
category: security
|
|
81
|
+
cwe: CWE-918
|
|
82
|
+
owasp: API7:2023
|
|
83
|
+
llm-prevalence: HIGH
|
|
84
|
+
technology:
|
|
85
|
+
- reqwest
|
|
86
|
+
- axum
|
|
87
|
+
- actix-web
|
|
88
|
+
references:
|
|
89
|
+
- https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
|
|
90
|
+
- https://cwe.mitre.org/data/definitions/918.html
|
|
@@ -25,6 +25,23 @@ rules:
|
|
|
25
25
|
# URL-encoded request body string: "grant_type=password&username=…".
|
|
26
26
|
- pattern-regex: |-
|
|
27
27
|
[?&"]grant_type=password(?:[&"\s\\]|$)
|
|
28
|
+
# We flag an application sending ROPC, not the example/test code or vendored
|
|
29
|
+
# client libraries that legitimately exercise the grant. Excluding these
|
|
30
|
+
# trees keeps the signal on first-party application code. (`tests.rs` is the
|
|
31
|
+
# Rust convention for a sibling test module file, e.g. oauth2-rs'
|
|
32
|
+
# `src/token/tests.rs`.)
|
|
33
|
+
paths:
|
|
34
|
+
exclude:
|
|
35
|
+
- "**/test/**"
|
|
36
|
+
- "**/tests.rs"
|
|
37
|
+
- "**/*_test.*"
|
|
38
|
+
- "**/*.test.*"
|
|
39
|
+
- "**/example/**"
|
|
40
|
+
- "**/examples/**"
|
|
41
|
+
- "**/mock*/**"
|
|
42
|
+
- "**/vendor/**"
|
|
43
|
+
- "**/node_modules/**"
|
|
44
|
+
- "**/target/**"
|
|
28
45
|
metadata:
|
|
29
46
|
oauthlint-rule-id: AUTH-RUST-OAUTH-001
|
|
30
47
|
oauthlint-doc-url: https://oauthlint.dev/rules/rust-oauth-ropc-grant
|
|
@@ -19,11 +19,31 @@ rules:
|
|
|
19
19
|
# direct argument, so `CsrfToken::new(generate())` or a variable is never
|
|
20
20
|
# flagged. `CsrfToken::new_random()` is a different method and is not
|
|
21
21
|
# matched.
|
|
22
|
-
|
|
23
|
-
- pattern:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
patterns:
|
|
23
|
+
- pattern-either:
|
|
24
|
+
- pattern: CsrfToken::new("...".to_string())
|
|
25
|
+
- pattern: CsrfToken::new("...".to_owned())
|
|
26
|
+
- pattern: CsrfToken::new("...".into())
|
|
27
|
+
- pattern: CsrfToken::new(String::from("..."))
|
|
28
|
+
# Unit tests routinely pass a fixed `state` to assert on the URL — that is
|
|
29
|
+
# the test asserting determinism, not an app shipping a constant CSRF
|
|
30
|
+
# token. The `#[cfg(test)] mod tests { ... }` block is the canonical Rust
|
|
31
|
+
# idiom (e.g. oauth2-rs' own crate tests), so suppress matches inside it.
|
|
32
|
+
- pattern-not-inside: mod tests {...}
|
|
33
|
+
# Belt-and-suspenders: never fire on test/example/vendored/generated trees.
|
|
34
|
+
# `tests.rs` is Rust's sibling test-module file convention.
|
|
35
|
+
paths:
|
|
36
|
+
exclude:
|
|
37
|
+
- "**/test/**"
|
|
38
|
+
- "**/tests.rs"
|
|
39
|
+
- "**/*_test.*"
|
|
40
|
+
- "**/*.test.*"
|
|
41
|
+
- "**/example/**"
|
|
42
|
+
- "**/examples/**"
|
|
43
|
+
- "**/mock*/**"
|
|
44
|
+
- "**/vendor/**"
|
|
45
|
+
- "**/node_modules/**"
|
|
46
|
+
- "**/target/**"
|
|
27
47
|
metadata:
|
|
28
48
|
oauthlint-rule-id: AUTH-RUST-OAUTH-003
|
|
29
49
|
oauthlint-doc-url: https://oauthlint.dev/rules/rust-oauth-static-state
|