Rust: Add heuristic sinks for rust/hard-coded-cryptographic-value.

This commit is contained in:
Geoffrey White
2025-11-28 17:02:01 +00:00
parent 8e099480ab
commit bb50e9fb40
3 changed files with 67 additions and 9 deletions

View File

@@ -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.
*/

View File

@@ -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: <biscotti::crypto::master::Key>::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

View File

@@ -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]
}