diff --git a/rust/ql/lib/codeql/rust/security/HardcodedCryptographicValueExtensions.qll b/rust/ql/lib/codeql/rust/security/HardcodedCryptographicValueExtensions.qll index a7fb35d138b..1f8c14dd6f9 100644 --- a/rust/ql/lib/codeql/rust/security/HardcodedCryptographicValueExtensions.qll +++ b/rust/ql/lib/codeql/rust/security/HardcodedCryptographicValueExtensions.qll @@ -9,6 +9,7 @@ private import codeql.rust.dataflow.FlowSource private import codeql.rust.dataflow.FlowSink private import codeql.rust.Concepts private import codeql.rust.security.SensitiveData +private import codeql.rust.dataflow.internal.Node as Node /** * A kind of cryptographic value. @@ -98,6 +99,37 @@ module HardcodedCryptographicValue { override CryptographicValueKind getKind() { result = kind } } + /** + * A heuristic sink for hard-coded cryptographic value vulnerabilities. + */ + private class HeuristicSinks extends Sink { + CryptographicValueKind kind; + + HeuristicSinks() { + // any argument going to a parameter whose name matches a credential name + exists(CallExprBase fc, Function f, int argIndex, string argName | + fc.getArg(argIndex) = this.asExpr() and + fc.getStaticTarget() = f and + f.getParam(argIndex).getPat().(IdentPat).getName().getText() = argName and + ( + argName = "password" and kind = "password" + or + argName = "key" and kind = "key" + or + argName = "iv" and kind = "iv" + or + argName = "nonce" and kind = "nonce" + or + argName = "salt" and kind = "salt" + ) and + // don't duplicate modeled sinks + not exists(ModelsAsDataSinks s | s.(Node::FlowSummaryNode).getSinkElement().getCall() = fc) + ) + } + + override CryptographicValueKind getKind() { result = kind } + } + /** * A call to `getrandom` that is a barrier. */ 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 e4657a426ea..34140c1f249 100644 --- a/rust/ql/test/query-tests/security/CWE-798/HardcodedCryptographicValue.expected +++ b/rust/ql/test/query-tests/security/CWE-798/HardcodedCryptographicValue.expected @@ -10,6 +10,11 @@ | test_cookie.rs:21:28:21:34 | [0; 64] | test_cookie.rs:21:28:21:34 | [0; 64] | test_cookie.rs:22:16:22:24 | ...::from | This hard-coded value is used as $@. | test_cookie.rs:22:16:22:24 | ...::from | a key | | test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:42:14:42:32 | ...::from | This hard-coded value is used as $@. | test_cookie.rs:42:14:42:32 | ...::from | a key | | test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:53:14:53:32 | ...::from | This hard-coded value is used as $@. | test_cookie.rs:53:14:53:32 | ...::from | a key | +| test_heuristic.rs:37:32:37:39 | [0u8; 16] | test_heuristic.rs:37:32:37:39 | [0u8; 16] | test_heuristic.rs:38:31:38:39 | const_key | This hard-coded value is used as $@. | test_heuristic.rs:38:31:38:39 | const_key | a key | +| test_heuristic.rs:40:31:40:38 | [0u8; 16] | test_heuristic.rs:40:31:40:38 | [0u8; 16] | test_heuristic.rs:41:41:41:48 | const_iv | This hard-coded value is used as $@. | test_heuristic.rs:41:41:41:48 | const_iv | an initialization vector | +| test_heuristic.rs:59:30:59:37 | "secret" | test_heuristic.rs:59:30:59:37 | "secret" | test_heuristic.rs:59:30:59:37 | "secret" | This hard-coded value is used as $@. | test_heuristic.rs:59:30:59:37 | "secret" | a password | +| test_heuristic.rs:60:20:60:27 | [0u8; 16] | test_heuristic.rs:60:20:60:27 | [0u8; 16] | test_heuristic.rs:60:19:60:27 | &... | This hard-coded value is used as $@. | test_heuristic.rs:60:19:60:27 | &... | a nonce | +| test_heuristic.rs:61:31:61:38 | [0u8; 16] | test_heuristic.rs:61:31:61:38 | [0u8; 16] | test_heuristic.rs:61:30:61:38 | &... | This hard-coded value is used as $@. | test_heuristic.rs:61:30:61:38 | &... | a salt | edges | test_cipher.rs:18:9:18:14 | const1 [&ref] | test_cipher.rs:19:73:19:78 | const1 [&ref] | provenance | | | test_cipher.rs:18:28:18:36 | &... [&ref] | test_cipher.rs:18:9:18:14 | const1 [&ref] | provenance | | @@ -62,6 +67,14 @@ edges | test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | provenance | MaD:10 | | test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | test_cookie.rs:49:9:49:14 | array3 [element] | provenance | | | test_cookie.rs:53:34:53:39 | array3 [element] | test_cookie.rs:53:14:53:32 | ...::from | provenance | MaD:2 Sink:MaD:2 | +| test_heuristic.rs:37:9:37:17 | const_key [&ref] | test_heuristic.rs:38:31:38:39 | const_key | provenance | | +| test_heuristic.rs:37:31:37:39 | &... [&ref] | test_heuristic.rs:37:9:37:17 | const_key [&ref] | provenance | | +| test_heuristic.rs:37:32:37:39 | [0u8; 16] | test_heuristic.rs:37:31:37:39 | &... [&ref] | provenance | | +| test_heuristic.rs:40:9:40:16 | const_iv [&ref] | test_heuristic.rs:41:41:41:48 | const_iv | provenance | | +| test_heuristic.rs:40:30:40:38 | &... [&ref] | test_heuristic.rs:40:9:40:16 | const_iv [&ref] | provenance | | +| test_heuristic.rs:40:31:40:38 | [0u8; 16] | test_heuristic.rs:40:30:40:38 | &... [&ref] | provenance | | +| test_heuristic.rs:60:20:60:27 | [0u8; 16] | test_heuristic.rs:60:19:60:27 | &... | provenance | | +| test_heuristic.rs:61:31:61:38 | [0u8; 16] | test_heuristic.rs:61:30:61:38 | &... | provenance | | models | 1 | Sink: <_ as crypto_common::KeyInit>::new_from_slice; Argument[0]; credentials-key | | 2 | Sink: ::from; Argument[0]; credentials-key | @@ -136,4 +149,17 @@ nodes | test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | semmle.label | ...::from_elem(...) [element] | | test_cookie.rs:53:14:53:32 | ...::from | semmle.label | ...::from | | test_cookie.rs:53:34:53:39 | array3 [element] | semmle.label | array3 [element] | +| test_heuristic.rs:37:9:37:17 | const_key [&ref] | semmle.label | const_key [&ref] | +| test_heuristic.rs:37:31:37:39 | &... [&ref] | semmle.label | &... [&ref] | +| test_heuristic.rs:37:32:37:39 | [0u8; 16] | semmle.label | [0u8; 16] | +| test_heuristic.rs:38:31:38:39 | const_key | semmle.label | const_key | +| test_heuristic.rs:40:9:40:16 | const_iv [&ref] | semmle.label | const_iv [&ref] | +| test_heuristic.rs:40:30:40:38 | &... [&ref] | semmle.label | &... [&ref] | +| test_heuristic.rs:40:31:40:38 | [0u8; 16] | semmle.label | [0u8; 16] | +| test_heuristic.rs:41:41:41:48 | const_iv | semmle.label | const_iv | +| test_heuristic.rs:59:30:59:37 | "secret" | semmle.label | "secret" | +| test_heuristic.rs:60:19:60:27 | &... | semmle.label | &... | +| test_heuristic.rs:60:20:60:27 | [0u8; 16] | semmle.label | [0u8; 16] | +| test_heuristic.rs:61:30:61:38 | &... | semmle.label | &... | +| test_heuristic.rs:61:31:61:38 | [0u8; 16] | semmle.label | [0u8; 16] | subpaths diff --git a/rust/ql/test/query-tests/security/CWE-798/test_heuristic.rs b/rust/ql/test/query-tests/security/CWE-798/test_heuristic.rs index 6ecfa492a41..55474c76c4a 100644 --- a/rust/ql/test/query-tests/security/CWE-798/test_heuristic.rs +++ b/rust/ql/test/query-tests/security/CWE-798/test_heuristic.rs @@ -32,15 +32,15 @@ impl MyCryptor { } fn test(var_string: &str, var_data: &[u8;16]) { - encrypt_with("plaintext", var_data, var_data); // $ MISSING: Sink + encrypt_with("plaintext", var_data, var_data); - let const_key: &[u8;16] = &[0u8;16]; // $ MISSING: Alert[rust/hard-coded-cryptographic-value] - encrypt_with("plaintext", const_key, var_data); // $ MISSING: Sink + let const_key: &[u8;16] = &[0u8;16]; // $ Alert[rust/hard-coded-cryptographic-value] + encrypt_with("plaintext", const_key, var_data); // $ Sink - let const_iv: &[u8;16] = &[0u8;16]; // $ MISSING: Alert[rust/hard-coded-cryptographic-value] - encrypt_with("plaintext", var_data, const_iv); // $ MISSING: Sink + let const_iv: &[u8;16] = &[0u8;16]; // $ Alert[rust/hard-coded-cryptographic-value] + encrypt_with("plaintext", var_data, const_iv); // $ Sink - encrypt2("plaintext", var_data, var_data); // $ MISSING: Sink + encrypt2("plaintext", var_data, var_data); let const_key2: &[u8;16] = &[1u8;16]; // $ MISSING: Alert[rust/hard-coded-cryptographic-value] encrypt2("plaintext", const_key2, var_data); // $ MISSING: Sink @@ -56,7 +56,7 @@ fn test(var_string: &str, var_data: &[u8;16]) { mc1.set_nonce(var_data); mc1.encrypt("plaintext", var_data); - let mc2 = MyCryptor::new("secret"); // $ MISSING: Alert[rust/hard-coded-cryptographic-value] - mc2.set_nonce(&[0u8;16]); // $ MISSING: Alert[rust/hard-coded-cryptographic-value] - mc2.encrypt("plaintext", &[0u8;16]); // $ MISSING: Alert[rust/hard-coded-cryptographic-value] + let mc2 = MyCryptor::new("secret"); // $ Alert[rust/hard-coded-cryptographic-value] + mc2.set_nonce(&[0u8;16]); // $ Alert[rust/hard-coded-cryptographic-value] + mc2.encrypt("plaintext", &[0u8;16]); // $ Alert[rust/hard-coded-cryptographic-value] }