diff --git a/rust/ql/lib/codeql/rust/security/HardcodedCryptographicValueExtensions.qll b/rust/ql/lib/codeql/rust/security/HardcodedCryptographicValueExtensions.qll index ea57d9427338..71f2f4cdbe31 100644 --- a/rust/ql/lib/codeql/rust/security/HardcodedCryptographicValueExtensions.qll +++ b/rust/ql/lib/codeql/rust/security/HardcodedCryptographicValueExtensions.qll @@ -62,14 +62,22 @@ module HardcodedCryptographicValue { abstract class Barrier extends DataFlow::Node { } /** - * Holds if `e` is a literal or a combination of literals that is constant. + * Holds if `e` is a literal or an expression that contains a constant. For example: + * ``` + * ["hello", "world", s] + * ``` */ - private predicate isConstant(Expr e) { + private predicate hasConstant(Expr e) { e instanceof LiteralExpr // e.g. `0` or - forex(Expr elem | elem = e.(ArrayListExpr).getExpr(_) | isConstant(elem)) // e.g. `[0, 0, 0, 0]` + exists(Expr elem | elem = e.(ArrayListExpr).getExpr(_) | hasConstant(elem)) // e.g. `[0, 0, 0, 0]` or - isConstant(e.(ArrayRepeatExpr).getRepeatOperand()) // e.g. `[0; 10]` + hasConstant(e.(ArrayRepeatExpr).getRepeatOperand()) // e.g. `[0; 10]` + or + // a match expression with one or more constant arms; taint would reach here + // anyway, but we make it a source to avoid reporting many similar results + // on each match arm. + hasConstant(e.(MatchExpr).getMatchArmList().getAnArm().getExpr()) or // e.g. `const MY_CONST: u64 = ...` // the constant initializer / body is the preferred source location for flow paths, when available. @@ -81,15 +89,15 @@ module HardcodedCryptographicValue { not exists(e.(ConstAccess).getConst().getBody()) or // e.g. `1 << 4` - isConstant(e.(BinaryExpr).getLhs()) and - isConstant(e.(BinaryExpr).getRhs()) + hasConstant(e.(BinaryExpr).getLhs()) and + hasConstant(e.(BinaryExpr).getRhs()) } /** * A constant, considered as a flow source. */ private class ConstantSource extends Source { - ConstantSource() { isConstant(this.asExpr()) } + ConstantSource() { hasConstant(this.asExpr()) } } /** diff --git a/rust/ql/src/change-notes/2026-08-25-hardcoded-cryptographic-match-expressions.md b/rust/ql/src/change-notes/2026-08-25-hardcoded-cryptographic-match-expressions.md new file mode 100644 index 000000000000..b2c71664f87a --- /dev/null +++ b/rust/ql/src/change-notes/2026-08-25-hardcoded-cryptographic-match-expressions.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* The `rust/hard-coded-cryptographic-value` query has been adjusted to produce +fewer results in certain situations where many results were being produced with very similar source locations. diff --git a/rust/ql/test/query-tests/security/CWE-798/HardcodedCryptographicValue.expected b/rust/ql/test/query-tests/security/CWE-798/HardcodedCryptographicValue.expected index 3290d7bca875..039891ffb4d6 100644 --- a/rust/ql/test/query-tests/security/CWE-798/HardcodedCryptographicValue.expected +++ b/rust/ql/test/query-tests/security/CWE-798/HardcodedCryptographicValue.expected @@ -8,8 +8,10 @@ | test_cipher.rs:73:19:73:26 | [0u8; 32] | test_cipher.rs:73:19:73:26 | [0u8; 32] | test_cipher.rs:74:46:74:51 | const2 | This hard-coded value is used as $@. | test_cipher.rs:74:46:74:51 | const2 | a key | | test_cookie.rs:17:28:17:34 | [0; 64] | test_cookie.rs:17:28:17:34 | [0; 64] | test_cookie.rs:18:26:18:32 | &array1 | This hard-coded value is used as $@. | test_cookie.rs:18:26:18:32 | &array1 | a key | | test_cookie.rs:21:28:21:34 | [0; 64] | test_cookie.rs:21:28:21:34 | [0; 64] | test_cookie.rs:22:26:22:32 | &array2 | This hard-coded value is used as $@. | test_cookie.rs:22:26:22:32 | &array2 | a key | -| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:42:34:42:39 | array2 | This hard-coded value is used as $@. | test_cookie.rs:42:34:42:39 | array2 | a key | -| test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:53:34:53:39 | array3 | This hard-coded value is used as $@. | test_cookie.rs:53:34:53:39 | array3 | a key | +| test_cookie.rs:25:16:29:5 | match ... { ... } | test_cookie.rs:25:16:29:5 | match ... { ... } | test_cookie.rs:30:26:30:40 | str3.as_bytes() | This hard-coded value is used as $@. | test_cookie.rs:30:26:30:40 | str3.as_bytes() | a key | +| test_cookie.rs:33:27:37:5 | [...] | test_cookie.rs:33:27:37:5 | [...] | test_cookie.rs:38:26:38:32 | &array4 | This hard-coded value is used as $@. | test_cookie.rs:38:26:38:32 | &array4 | a key | +| test_cookie.rs:54:28:54:36 | [0u8; 64] | test_cookie.rs:54:28:54:36 | [0u8; 64] | test_cookie.rs:58:34:58:39 | array2 | This hard-coded value is used as $@. | test_cookie.rs:58:34:58:39 | array2 | a key | +| test_cookie.rs:65:23:65:25 | 0u8 | test_cookie.rs:65:23:65:25 | 0u8 | test_cookie.rs:69:34:69:39 | array3 | This hard-coded value is used as $@. | test_cookie.rs:69:34:69:39 | array3 | a key | | test_heuristic.rs:38:25:38:30 | 0xFFFF | test_heuristic.rs:38:25:38:30 | 0xFFFF | test_heuristic.rs:81:22:81:31 | MY_CONST_1 | This hard-coded value is used as $@. | test_heuristic.rs:81:22:81:31 | MY_CONST_1 | a salt | | test_heuristic.rs:39:25:39:59 | ... as u64 | test_heuristic.rs:39:25:39:59 | ... as u64 | test_heuristic.rs:82:22:82:31 | MY_CONST_2 | This hard-coded value is used as $@. | test_heuristic.rs:82:22:82:31 | MY_CONST_2 | a salt | | test_heuristic.rs:40:27:40:32 | 0xFFFF | test_heuristic.rs:40:27:40:32 | 0xFFFF | test_heuristic.rs:83:22:83:32 | MY_STATIC_3 | This hard-coded value is used as $@. | test_heuristic.rs:83:22:83:32 | MY_STATIC_3 | a salt | @@ -28,31 +30,31 @@ edges | test_cipher.rs:18:28:18:36 | &... [&ref] | test_cipher.rs:18:9:18:14 | const1 [&ref] | provenance | | | test_cipher.rs:18:29:18:36 | [0u8; 16] | test_cipher.rs:18:28:18:36 | &... [&ref] | provenance | | | test_cipher.rs:19:49:19:79 | ...::from_slice(...) [&ref] | test_cipher.rs:19:49:19:79 | ...::from_slice(...) | provenance | Sink:MaD:2 | -| test_cipher.rs:19:73:19:78 | const1 [&ref] | test_cipher.rs:19:49:19:79 | ...::from_slice(...) [&ref] | provenance | MaD:13 | +| test_cipher.rs:19:73:19:78 | const1 [&ref] | test_cipher.rs:19:49:19:79 | ...::from_slice(...) [&ref] | provenance | MaD:14 | | test_cipher.rs:25:9:25:14 | const4 [&ref] | test_cipher.rs:26:66:26:71 | const4 [&ref] | provenance | | | test_cipher.rs:25:28:25:36 | &... [&ref] | test_cipher.rs:25:9:25:14 | const4 [&ref] | provenance | | | test_cipher.rs:25:29:25:36 | [0u8; 16] | test_cipher.rs:25:28:25:36 | &... [&ref] | provenance | | | test_cipher.rs:26:42:26:72 | ...::from_slice(...) [&ref] | test_cipher.rs:26:42:26:72 | ...::from_slice(...) | provenance | Sink:MaD:3 | -| test_cipher.rs:26:66:26:71 | const4 [&ref] | test_cipher.rs:26:42:26:72 | ...::from_slice(...) [&ref] | provenance | MaD:13 | +| test_cipher.rs:26:66:26:71 | const4 [&ref] | test_cipher.rs:26:42:26:72 | ...::from_slice(...) [&ref] | provenance | MaD:14 | | test_cipher.rs:29:9:29:14 | const5 [&ref] | test_cipher.rs:30:95:30:100 | const5 [&ref] | provenance | | | test_cipher.rs:29:28:29:36 | &... [&ref] | test_cipher.rs:29:9:29:14 | const5 [&ref] | provenance | | | test_cipher.rs:29:29:29:36 | [0u8; 16] | test_cipher.rs:29:28:29:36 | &... [&ref] | provenance | | | test_cipher.rs:30:72:30:101 | ...::from_slice(...) [&ref] | test_cipher.rs:30:72:30:101 | ...::from_slice(...) | provenance | Sink:MaD:4 | -| test_cipher.rs:30:95:30:100 | const5 [&ref] | test_cipher.rs:30:72:30:101 | ...::from_slice(...) [&ref] | provenance | MaD:13 | +| test_cipher.rs:30:95:30:100 | const5 [&ref] | test_cipher.rs:30:72:30:101 | ...::from_slice(...) [&ref] | provenance | MaD:14 | | test_cipher.rs:37:9:37:14 | const7 | test_cipher.rs:38:74:38:79 | const7 | provenance | | | test_cipher.rs:37:27:37:74 | [...] | test_cipher.rs:37:9:37:14 | const7 | provenance | | | test_cipher.rs:38:49:38:80 | ...::from_slice(...) [&ref] | test_cipher.rs:38:49:38:80 | ...::from_slice(...) | provenance | Sink:MaD:2 | -| test_cipher.rs:38:73:38:79 | &const7 [&ref] | test_cipher.rs:38:49:38:80 | ...::from_slice(...) [&ref] | provenance | MaD:13 | +| test_cipher.rs:38:73:38:79 | &const7 [&ref] | test_cipher.rs:38:49:38:80 | ...::from_slice(...) [&ref] | provenance | MaD:14 | | test_cipher.rs:38:74:38:79 | const7 | test_cipher.rs:38:73:38:79 | &const7 [&ref] | provenance | | | test_cipher.rs:41:9:41:14 | const8 [&ref] | test_cipher.rs:42:73:42:78 | const8 [&ref] | provenance | | | test_cipher.rs:41:28:41:76 | &... [&ref] | test_cipher.rs:41:9:41:14 | const8 [&ref] | provenance | | | test_cipher.rs:41:29:41:76 | [...] | test_cipher.rs:41:28:41:76 | &... [&ref] | provenance | | | test_cipher.rs:42:49:42:79 | ...::from_slice(...) [&ref] | test_cipher.rs:42:49:42:79 | ...::from_slice(...) | provenance | Sink:MaD:2 | -| test_cipher.rs:42:73:42:78 | const8 [&ref] | test_cipher.rs:42:49:42:79 | ...::from_slice(...) [&ref] | provenance | MaD:13 | +| test_cipher.rs:42:73:42:78 | const8 [&ref] | test_cipher.rs:42:49:42:79 | ...::from_slice(...) [&ref] | provenance | MaD:14 | | test_cipher.rs:50:9:50:15 | const10 [element] | test_cipher.rs:51:75:51:81 | const10 [element] | provenance | | | test_cipher.rs:50:37:50:54 | ...::zeroed(...) | test_cipher.rs:50:9:50:15 | const10 [element] | provenance | Src:MaD:7 | | test_cipher.rs:51:50:51:82 | ...::from_slice(...) [&ref, element] | test_cipher.rs:51:50:51:82 | ...::from_slice(...) | provenance | Sink:MaD:2 Sink:MaD:2 | -| test_cipher.rs:51:74:51:81 | &const10 [&ref, element] | test_cipher.rs:51:50:51:82 | ...::from_slice(...) [&ref, element] | provenance | MaD:13 | +| test_cipher.rs:51:74:51:81 | &const10 [&ref, element] | test_cipher.rs:51:50:51:82 | ...::from_slice(...) [&ref, element] | provenance | MaD:14 | | test_cipher.rs:51:75:51:81 | const10 [element] | test_cipher.rs:51:74:51:81 | &const10 [&ref, element] | provenance | | | test_cipher.rs:73:9:73:14 | const2 [&ref] | test_cipher.rs:74:46:74:51 | const2 | provenance | Sink:MaD:6 | | test_cipher.rs:73:18:73:26 | &... [&ref] | test_cipher.rs:73:9:73:14 | const2 [&ref] | provenance | | @@ -65,16 +67,24 @@ edges | test_cookie.rs:21:28:21:34 | [0; 64] | test_cookie.rs:21:9:21:14 | array2 | provenance | | | test_cookie.rs:22:26:22:32 | &array2 [&ref] | test_cookie.rs:22:26:22:32 | &array2 | provenance | Sink:MaD:5 | | test_cookie.rs:22:27:22:32 | array2 | test_cookie.rs:22:26:22:32 | &array2 [&ref] | provenance | | -| test_cookie.rs:38:9:38:14 | array2 | test_cookie.rs:42:34:42:39 | array2 | provenance | Sink:MaD:1 | -| test_cookie.rs:38:18:38:37 | ...::from(...) | test_cookie.rs:38:9:38:14 | array2 | provenance | | -| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:8 | -| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:9 | -| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:10 | -| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:11 | -| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:12 | -| test_cookie.rs:49:9:49:14 | array3 [element] | test_cookie.rs:53:34:53:39 | array3 | provenance | Sink:MaD:1 | -| test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | provenance | MaD:14 | -| test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | test_cookie.rs:49:9:49:14 | array3 [element] | provenance | | +| test_cookie.rs:25:9:25:12 | str3 | test_cookie.rs:30:26:30:29 | str3 | provenance | | +| test_cookie.rs:25:16:29:5 | match ... { ... } | test_cookie.rs:25:9:25:12 | str3 | provenance | | +| test_cookie.rs:30:26:30:29 | str3 | test_cookie.rs:30:26:30:40 | str3.as_bytes() [&ref] | provenance | MaD:13 | +| test_cookie.rs:30:26:30:40 | str3.as_bytes() [&ref] | test_cookie.rs:30:26:30:40 | str3.as_bytes() | provenance | Sink:MaD:5 | +| test_cookie.rs:33:9:33:14 | array4 | test_cookie.rs:38:27:38:32 | array4 | provenance | | +| test_cookie.rs:33:27:37:5 | [...] | test_cookie.rs:33:9:33:14 | array4 | provenance | | +| test_cookie.rs:38:26:38:32 | &array4 [&ref] | test_cookie.rs:38:26:38:32 | &array4 | provenance | Sink:MaD:5 | +| test_cookie.rs:38:27:38:32 | array4 | test_cookie.rs:38:26:38:32 | &array4 [&ref] | provenance | | +| test_cookie.rs:54:9:54:14 | array2 | test_cookie.rs:58:34:58:39 | array2 | provenance | Sink:MaD:1 | +| test_cookie.rs:54:18:54:37 | ...::from(...) | test_cookie.rs:54:9:54:14 | array2 | provenance | | +| test_cookie.rs:54:28:54:36 | [0u8; 64] | test_cookie.rs:54:18:54:37 | ...::from(...) | provenance | MaD:8 | +| test_cookie.rs:54:28:54:36 | [0u8; 64] | test_cookie.rs:54:18:54:37 | ...::from(...) | provenance | MaD:9 | +| test_cookie.rs:54:28:54:36 | [0u8; 64] | test_cookie.rs:54:18:54:37 | ...::from(...) | provenance | MaD:10 | +| test_cookie.rs:54:28:54:36 | [0u8; 64] | test_cookie.rs:54:18:54:37 | ...::from(...) | provenance | MaD:11 | +| test_cookie.rs:54:28:54:36 | [0u8; 64] | test_cookie.rs:54:18:54:37 | ...::from(...) | provenance | MaD:12 | +| test_cookie.rs:65:9:65:14 | array3 [element] | test_cookie.rs:69:34:69:39 | array3 | provenance | Sink:MaD:1 | +| test_cookie.rs:65:23:65:25 | 0u8 | test_cookie.rs:65:23:65:29 | ...::from_elem(...) [element] | provenance | MaD:15 | +| test_cookie.rs:65:23:65:29 | ...::from_elem(...) [element] | test_cookie.rs:65:9:65:14 | array3 [element] | provenance | | | test_heuristic.rs:38:25:38:30 | 0xFFFF | test_heuristic.rs:81:22:81:31 | MY_CONST_1 | provenance | | | test_heuristic.rs:39:25:39:59 | ... as u64 | test_heuristic.rs:82:22:82:31 | MY_CONST_2 | provenance | | | test_heuristic.rs:39:62:40:33 | static MY_STATIC_3 | test_heuristic.rs:83:22:83:32 | MY_STATIC_3 | provenance | | @@ -99,8 +109,9 @@ models | 10 | Summary: ::from; Argument[0].Field[alloc::collections::binary_heap::BinaryHeap::data]; ReturnValue; value | | 11 | Summary: ::from; Argument[0].Field[alloc::string::String::vec]; ReturnValue; value | | 12 | Summary: ::from; Argument[0]; ReturnValue; taint | -| 13 | Summary: ::from_slice; Argument[0].Reference; ReturnValue.Reference; value | -| 14 | Summary: alloc::vec::from_elem; Argument[0]; ReturnValue.Element; value | +| 13 | Summary: ::as_bytes; Argument[self].Reference; ReturnValue.Reference; taint | +| 14 | Summary: ::from_slice; Argument[0].Reference; ReturnValue.Reference; value | +| 15 | Summary: alloc::vec::from_elem; Argument[0]; ReturnValue.Element; value | nodes | test_cipher.rs:18:9:18:14 | const1 [&ref] | semmle.label | const1 [&ref] | | test_cipher.rs:18:28:18:36 | &... [&ref] | semmle.label | &... [&ref] | @@ -152,14 +163,24 @@ nodes | test_cookie.rs:22:26:22:32 | &array2 | semmle.label | &array2 | | test_cookie.rs:22:26:22:32 | &array2 [&ref] | semmle.label | &array2 [&ref] | | test_cookie.rs:22:27:22:32 | array2 | semmle.label | array2 | -| test_cookie.rs:38:9:38:14 | array2 | semmle.label | array2 | -| test_cookie.rs:38:18:38:37 | ...::from(...) | semmle.label | ...::from(...) | -| test_cookie.rs:38:28:38:36 | [0u8; 64] | semmle.label | [0u8; 64] | -| test_cookie.rs:42:34:42:39 | array2 | semmle.label | array2 | -| test_cookie.rs:49:9:49:14 | array3 [element] | semmle.label | array3 [element] | -| test_cookie.rs:49:23:49:25 | 0u8 | semmle.label | 0u8 | -| test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | semmle.label | ...::from_elem(...) [element] | -| test_cookie.rs:53:34:53:39 | array3 | semmle.label | array3 | +| test_cookie.rs:25:9:25:12 | str3 | semmle.label | str3 | +| test_cookie.rs:25:16:29:5 | match ... { ... } | semmle.label | match ... { ... } | +| test_cookie.rs:30:26:30:29 | str3 | semmle.label | str3 | +| test_cookie.rs:30:26:30:40 | str3.as_bytes() | semmle.label | str3.as_bytes() | +| test_cookie.rs:30:26:30:40 | str3.as_bytes() [&ref] | semmle.label | str3.as_bytes() [&ref] | +| test_cookie.rs:33:9:33:14 | array4 | semmle.label | array4 | +| test_cookie.rs:33:27:37:5 | [...] | semmle.label | [...] | +| test_cookie.rs:38:26:38:32 | &array4 | semmle.label | &array4 | +| test_cookie.rs:38:26:38:32 | &array4 [&ref] | semmle.label | &array4 [&ref] | +| test_cookie.rs:38:27:38:32 | array4 | semmle.label | array4 | +| test_cookie.rs:54:9:54:14 | array2 | semmle.label | array2 | +| test_cookie.rs:54:18:54:37 | ...::from(...) | semmle.label | ...::from(...) | +| test_cookie.rs:54:28:54:36 | [0u8; 64] | semmle.label | [0u8; 64] | +| test_cookie.rs:58:34:58:39 | array2 | semmle.label | array2 | +| test_cookie.rs:65:9:65:14 | array3 [element] | semmle.label | array3 [element] | +| test_cookie.rs:65:23:65:25 | 0u8 | semmle.label | 0u8 | +| test_cookie.rs:65:23:65:29 | ...::from_elem(...) [element] | semmle.label | ...::from_elem(...) [element] | +| test_cookie.rs:69:34:69:39 | array3 | semmle.label | array3 | | test_heuristic.rs:38:25:38:30 | 0xFFFF | semmle.label | 0xFFFF | | test_heuristic.rs:39:25:39:59 | ... as u64 | semmle.label | ... as u64 | | test_heuristic.rs:39:62:40:33 | static MY_STATIC_3 | semmle.label | static MY_STATIC_3 | diff --git a/rust/ql/test/query-tests/security/CWE-798/test_cookie.rs b/rust/ql/test/query-tests/security/CWE-798/test_cookie.rs index 10852583c678..f28491c713ee 100644 --- a/rust/ql/test/query-tests/security/CWE-798/test_cookie.rs +++ b/rust/ql/test/query-tests/security/CWE-798/test_cookie.rs @@ -3,7 +3,7 @@ use cookie::{CookieJar, SignedJar, PrivateJar, Key}; // --- tests --- -fn test_cookie_jar(array_var: &[u8]) { +fn test_cookie_jar(array_var: &[u8], val: u64) { let mut jar = CookieJar::new(); let key_generate = Key::generate(); // good @@ -21,6 +21,22 @@ fn test_cookie_jar(array_var: &[u8]) { let array2: [u8; 64] = [0; 64]; // $ Alert[rust/hard-coded-cryptographic-value] let key2 = Key::from(&array2); // $ Sink _ = jar.private_mut(&key2); + + let str3 = match(val) { + 0 => "one", + 1 => "two", + _ => "many" + }; // $ Alert[rust/hard-coded-cryptographic-value] + let key3 = Key::from(str3.as_bytes()); // $ Sink + _ = jar.signed_mut(&key3); + + let array4: [u8; 3] = [ + 1, + 2, + val as u8 + ]; // $ Alert[rust/hard-coded-cryptographic-value] + let key4 = Key::from(&array4); // $ Sink + _ = jar.signed_mut(&key4); } fn test_biscotti_crypto(array_var: &[u8]) {