Merge pull request #20137 from geoffw0/cleartextstorage

Rust: New Query rust/cleartext-storage-database
This commit is contained in:
Geoffrey White
2025-08-11 12:33:24 +01:00
committed by GitHub
18 changed files with 3077 additions and 7 deletions

View File

@@ -13,6 +13,7 @@ ql/rust/ql/src/queries/security/CWE-022/TaintedPath.ql
ql/rust/ql/src/queries/security/CWE-089/SqlInjection.ql
ql/rust/ql/src/queries/security/CWE-311/CleartextTransmission.ql
ql/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql
ql/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql
ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql
ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql
ql/rust/ql/src/queries/security/CWE-770/UncontrolledAllocationSize.ql

View File

@@ -13,6 +13,7 @@ ql/rust/ql/src/queries/security/CWE-022/TaintedPath.ql
ql/rust/ql/src/queries/security/CWE-089/SqlInjection.ql
ql/rust/ql/src/queries/security/CWE-311/CleartextTransmission.ql
ql/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql
ql/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql
ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql
ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql
ql/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql

View File

@@ -13,6 +13,7 @@ ql/rust/ql/src/queries/security/CWE-022/TaintedPath.ql
ql/rust/ql/src/queries/security/CWE-089/SqlInjection.ql
ql/rust/ql/src/queries/security/CWE-311/CleartextTransmission.ql
ql/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql
ql/rust/ql/src/queries/security/CWE-312/CleartextStorageDatabase.ql
ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql
ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql
ql/rust/ql/src/queries/security/CWE-770/UncontrolledAllocationSize.ql

View File

@@ -0,0 +1,48 @@
/**
* Provides classes and predicates for reasoning about cleartext storage
* of sensitive information in a database.
*/
import rust
private import codeql.rust.dataflow.DataFlow
private import codeql.rust.dataflow.internal.DataFlowImpl
private import codeql.rust.security.SensitiveData
private import codeql.rust.Concepts
/**
* Provides default sources, sinks and barriers for detecting cleartext storage
* of sensitive information in a database, as well as extension points for
* adding your own.
*/
module CleartextStorageDatabase {
/**
* A data flow source for cleartext storage vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for cleartext storage vulnerabilities.
*/
abstract class Sink extends QuerySink::Range {
override string getSinkType() { result = "CleartextStorageDatabase" }
}
/**
* A barrier for cleartext storage vulnerabilities.
*/
abstract class Barrier extends DataFlow::Node { }
/**
* Sensitive data, considered as a flow source.
*/
private class SensitiveDataAsSource extends Source instanceof SensitiveData { }
/**
* A sink for cleartext storage vulnerabilities from model data.
* - SQL commands
* - other database storage operations
*/
private class ModelsAsDataSink extends Sink {
ModelsAsDataSink() { sinkNode(this, ["sql-injection", "database-store"]) }
}
}

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added a new query, `rust/cleartext-storage-database`, for detecting cases where sensitive information is stored non-encrypted in a database.

View File

@@ -0,0 +1,53 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Sensitive information that is stored unencrypted in a database is accessible to an attacker
who gains access to that database. For example, the information could be accessed by any
process or user in a rooted device, or exposed through another vulnerability.
</p>
</overview>
<recommendation>
<p>
Either encrypt the entire database, or ensure that each piece of sensitive information is
encrypted before being stored. In general, decrypt sensitive information only at the point
where it is necessary for it to be used in cleartext. Avoid storing sensitive information
at all if you do not need to keep it.
</p>
</recommendation>
<example>
<p>
The following example stores sensitive information into a database without encryption, using the
SQLx library:
</p>
<sample src="CleartextStorageDatabaseBad.rs"/>
<p>
This is insecure because the sensitive data is stored in cleartext, making it accessible to anyone
with access to the database.
</p>
<p>
To fix this, we can either encrypt the entire database or encrypt just the sensitive data before it
is stored. Take care to select a secure modern encryption algorithm and put suitable key management
practices into place. In the following example, we have encrypted the sensitive data using 256-bit
AES before storing it in the database:
</p>
<sample src="CleartextStorageDatabaseGood.rs"/>
</example>
<references>
<li>
OWASP Top 10:2021:
<a href="https://owasp.org/Top10/A02_2021-Cryptographic_Failures/">A02:2021 - Cryptographic Failures</a>.
</li>
<li>
OWASP:
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html">Key Management Cheat Sheet</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,54 @@
/**
* @name Cleartext storage of sensitive information in a database
* @description Storing sensitive information in a non-encrypted
* database can expose it to an attacker.
* @kind path-problem
* @problem.severity warning
* @security-severity 7.5
* @precision high
* @id rust/cleartext-storage-database
* @tags security
* external/cwe/cwe-312
*/
import rust
import codeql.rust.dataflow.DataFlow
import codeql.rust.dataflow.TaintTracking
import codeql.rust.security.CleartextStorageDatabaseExtensions
/**
* A taint configuration from sensitive information to expressions that are
* stored in a database.
*/
module CleartextStorageDatabaseConfig implements DataFlow::ConfigSig {
import CleartextStorageDatabase
predicate isSource(DataFlow::Node node) { node instanceof Source }
predicate isSink(DataFlow::Node node) { node instanceof Sink }
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof Barrier }
predicate isBarrierIn(DataFlow::Node node) {
// make sources barriers so that we only report the closest instance
isSource(node)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
// flow from `a` to `&a`
node2.asExpr().getExpr().(RefExpr).getExpr() = node1.asExpr().getExpr()
}
predicate observeDiffInformedIncrementalMode() { any() }
}
module CleartextStorageDatabaseFlow = TaintTracking::Global<CleartextStorageDatabaseConfig>;
import CleartextStorageDatabaseFlow::PathGraph
from
CleartextStorageDatabaseFlow::PathNode sourceNode, CleartextStorageDatabaseFlow::PathNode sinkNode
where CleartextStorageDatabaseFlow::flowPath(sourceNode, sinkNode)
select sinkNode.getNode(), sourceNode, sinkNode,
"This database operation may read or write unencrypted sensitive data from $@.", sourceNode,
sourceNode.getNode().toString()

View File

@@ -0,0 +1,6 @@
let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)";
let result = sqlx::query(query)
.bind(id)
.bind(credit_card_number) // BAD: Cleartext storage of sensitive data in the database
.execute(pool)
.await?;

View File

@@ -0,0 +1,41 @@
fn encrypt(text: String, encryption_key: &aes_gcm::Key<Aes256Gcm>) -> String {
// encrypt text -> ciphertext
let cipher = Aes256Gcm::new(&encryption_key);
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let ciphertext = cipher.encrypt(&nonce, text.as_ref()).unwrap();
// append (nonce, ciphertext)
let mut combined = nonce.to_vec();
combined.extend(ciphertext);
// encode to base64 string
BASE64_STANDARD.encode(combined)
}
fn decrypt(data: String, encryption_key: &aes_gcm::Key<Aes256Gcm>) -> String {
let cipher = Aes256Gcm::new(&encryption_key);
// decode base64 string
let decoded = BASE64_STANDARD.decode(data).unwrap();
// split into (nonce, ciphertext)
let nonce_size = <Aes256Gcm as AeadCore>::NonceSize::to_usize();
let (nonce, ciphertext) = decoded.split_at(nonce_size);
// decrypt ciphertext -> plaintext
let plaintext = cipher.decrypt(nonce.into(), ciphertext).unwrap();
String::from_utf8(plaintext).unwrap()
}
...
let encryption_key = Aes256Gcm::generate_key(OsRng);
...
let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)";
let result = sqlx::query(query)
.bind(id)
.bind(encrypt(credit_card_number, &encryption_key)) // GOOD: Encrypted storage of sensitive data in the database
.execute(pool)
.await?;

View File

@@ -20,6 +20,7 @@ private import TaintReach
private import codeql.rust.security.regex.RegexInjectionExtensions
private import codeql.rust.security.AccessInvalidPointerExtensions
private import codeql.rust.security.CleartextLoggingExtensions
private import codeql.rust.security.CleartextStorageDatabaseExtensions
private import codeql.rust.security.CleartextTransmissionExtensions
private import codeql.rust.security.SqlInjectionExtensions
private import codeql.rust.security.TaintedPathExtensions

View File

@@ -1,10 +1,143 @@
multipleCallTargets
| test_logging.rs:42:5:42:36 | ...::max_level(...) |
| test_logging.rs:43:5:43:36 | ...::max_level(...) |
| test_logging.rs:44:5:44:35 | ...::max_level(...) |
| test_logging.rs:45:5:45:36 | ...::max_level(...) |
| test_logging.rs:46:5:46:35 | ...::max_level(...) |
| test_logging.rs:47:5:47:48 | ...::max_level(...) |
| test_logging.rs:50:5:50:21 | ...::max_level(...) |
| test_logging.rs:51:5:51:36 | ...::max_level(...) |
| test_logging.rs:52:5:52:36 | ...::max_level(...) |
| test_logging.rs:53:5:53:46 | ...::max_level(...) |
| test_logging.rs:54:5:54:49 | ...::max_level(...) |
| test_logging.rs:55:5:55:34 | ...::max_level(...) |
| test_logging.rs:56:5:56:47 | ...::max_level(...) |
| test_logging.rs:57:5:57:34 | ...::max_level(...) |
| test_logging.rs:58:5:58:36 | ...::max_level(...) |
| test_logging.rs:59:5:59:54 | ...::max_level(...) |
| test_logging.rs:60:5:60:54 | ...::max_level(...) |
| test_logging.rs:61:5:61:55 | ...::max_level(...) |
| test_logging.rs:64:5:64:48 | ...::max_level(...) |
| test_logging.rs:65:5:65:48 | ...::max_level(...) |
| test_logging.rs:66:5:66:66 | ...::max_level(...) |
| test_logging.rs:67:5:67:66 | ...::max_level(...) |
| test_logging.rs:68:5:68:67 | ...::max_level(...) |
| test_logging.rs:71:5:71:47 | ...::max_level(...) |
| test_logging.rs:72:5:72:47 | ...::max_level(...) |
| test_logging.rs:73:5:73:50 | ...::max_level(...) |
| test_logging.rs:74:5:74:65 | ...::max_level(...) |
| test_logging.rs:75:5:75:51 | ...::max_level(...) |
| test_logging.rs:76:5:76:47 | ...::max_level(...) |
| test_logging.rs:77:5:77:48 | ...::max_level(...) |
| test_logging.rs:77:20:77:36 | password.as_str() |
| test_logging.rs:78:5:78:50 | ...::max_level(...) |
| test_logging.rs:78:22:78:38 | password.as_str() |
| test_logging.rs:81:5:81:44 | ...::max_level(...) |
| test_logging.rs:82:5:82:44 | ...::max_level(...) |
| test_logging.rs:83:5:83:47 | ...::max_level(...) |
| test_logging.rs:84:5:84:62 | ...::max_level(...) |
| test_logging.rs:85:5:85:48 | ...::max_level(...) |
| test_logging.rs:86:5:86:44 | ...::max_level(...) |
| test_logging.rs:88:18:88:34 | password.as_str() |
| test_logging.rs:89:5:89:29 | ...::max_level(...) |
| test_logging.rs:90:5:90:31 | ...::max_level(...) |
| test_logging.rs:94:5:94:29 | ...::max_level(...) |
| test_logging.rs:97:5:97:19 | ...::max_level(...) |
| test_logging.rs:100:5:100:19 | ...::max_level(...) |
| test_logging.rs:104:5:104:19 | ...::max_level(...) |
| test_logging.rs:108:5:108:19 | ...::max_level(...) |
| test_logging.rs:112:5:112:50 | ...::max_level(...) |
| test_logging.rs:114:9:114:55 | ...::max_level(...) |
| test_logging.rs:118:5:118:42 | ...::max_level(...) |
| test_logging.rs:121:5:121:33 | ...::max_level(...) |
| test_logging.rs:123:5:123:33 | ...::max_level(...) |
| test_logging.rs:126:5:126:33 | ...::max_level(...) |
| test_logging.rs:130:5:130:32 | ...::max_level(...) |
| test_logging.rs:131:5:131:32 | ...::max_level(...) |
| test_logging.rs:132:5:132:32 | ...::max_level(...) |
| test_logging.rs:133:5:133:33 | ...::max_level(...) |
| test_logging.rs:140:5:140:38 | ...::max_level(...) |
| test_logging.rs:141:5:141:38 | ...::max_level(...) |
| test_logging.rs:142:5:142:29 | ...::max_level(...) |
| test_logging.rs:143:5:143:31 | ...::max_level(...) |
| test_logging.rs:144:5:144:32 | ...::max_level(...) |
| test_logging.rs:150:5:150:38 | ...::max_level(...) |
| test_logging.rs:151:5:151:38 | ...::max_level(...) |
| test_logging.rs:152:5:152:29 | ...::max_level(...) |
| test_logging.rs:153:5:153:31 | ...::max_level(...) |
| test_logging.rs:154:5:154:32 | ...::max_level(...) |
| test_logging.rs:192:12:192:37 | ...::_print(...) |
| test_logging.rs:193:14:193:37 | ...::_print(...) |
| test_logging.rs:194:13:194:38 | ...::_eprint(...) |
| test_logging.rs:195:15:195:38 | ...::_eprint(...) |
| test_logging.rs:229:30:229:71 | ... .as_str() |
| test_logging.rs:242:16:242:61 | ... .as_bytes() |
| test_logging.rs:243:5:245:66 | ... .write_all(...) |
| test_logging.rs:245:20:245:65 | ... .as_bytes() |
| test_logging.rs:248:15:248:60 | ... .as_bytes() |
| test_logging.rs:251:15:251:60 | ... .as_bytes() |
| test_storage.rs:13:10:13:33 | ...::from(...) |
| test_storage.rs:17:10:17:35 | ...::from(...) |
| test_storage.rs:21:10:21:35 | ...::from(...) |
| test_storage.rs:25:10:25:32 | ...::from(...) |
| test_storage.rs:29:10:29:35 | ...::from(...) |
| test_storage.rs:36:45:36:57 | text.as_ref() |
| test_storage.rs:68:25:68:74 | ...::from(...) |
| test_storage.rs:69:25:69:76 | ...::from(...) |
| test_storage.rs:70:25:70:82 | ...::from(...) |
| test_storage.rs:71:25:71:79 | ...::from(...) |
| test_storage.rs:72:25:72:70 | ...::from(...) |
| test_storage.rs:73:25:73:67 | ...::from(...) |
| test_storage.rs:75:25:75:65 | ...::from(...) |
| test_storage.rs:76:25:76:65 | ...::from(...) |
| test_storage.rs:77:14:77:24 | s1.as_str() |
| test_storage.rs:78:25:78:65 | ...::from(...) |
| test_storage.rs:79:25:79:65 | ...::from(...) |
| test_storage.rs:80:25:80:70 | ...::from(...) |
| test_storage.rs:81:25:81:72 | ...::from(...) |
| test_storage.rs:82:26:82:77 | ...::from(...) |
| test_storage.rs:85:27:85:48 | select_query1.as_str() |
| test_storage.rs:86:27:86:48 | select_query2.as_str() |
| test_storage.rs:87:27:87:48 | insert_query1.as_str() |
| test_storage.rs:88:27:88:48 | insert_query2.as_str() |
| test_storage.rs:89:27:89:48 | update_query1.as_str() |
| test_storage.rs:90:27:90:48 | update_query2.as_str() |
| test_storage.rs:91:27:91:48 | update_query3.as_str() |
| test_storage.rs:92:27:92:48 | update_query4.as_str() |
| test_storage.rs:93:27:93:48 | update_query5.as_str() |
| test_storage.rs:94:27:94:48 | update_query6.as_str() |
| test_storage.rs:95:27:95:48 | delete_query1.as_str() |
| test_storage.rs:96:27:96:48 | delete_query2.as_str() |
| test_storage.rs:99:25:99:46 | insert_query1.as_str() |
| test_storage.rs:100:25:100:46 | insert_query2.as_str() |
| test_storage.rs:101:25:101:47 | prepared_query.as_str() |
| test_storage.rs:102:25:102:47 | prepared_query.as_str() |
| test_storage.rs:103:25:103:47 | prepared_query.as_str() |
| test_storage.rs:104:25:104:47 | prepared_query.as_str() |
| test_storage.rs:110:27:110:48 | insert_query1.as_str() |
| test_storage.rs:111:27:111:48 | insert_query2.as_str() |
| test_storage.rs:114:27:114:48 | insert_query1.as_str() |
| test_storage.rs:115:27:115:48 | insert_query2.as_str() |
| test_storage.rs:118:25:118:46 | insert_query1.as_str() |
| test_storage.rs:119:25:119:46 | insert_query2.as_str() |
| test_storage.rs:120:25:120:47 | prepared_query.as_str() |
| test_storage.rs:121:25:121:47 | prepared_query.as_str() |
| test_storage.rs:124:25:124:46 | insert_query1.as_str() |
| test_storage.rs:125:25:125:46 | insert_query2.as_str() |
| test_storage.rs:126:25:126:47 | prepared_query.as_str() |
| test_storage.rs:127:25:127:47 | prepared_query.as_str() |
| test_storage.rs:134:27:134:48 | insert_query1.as_str() |
| test_storage.rs:135:27:135:48 | insert_query2.as_str() |
| test_storage.rs:138:25:138:46 | insert_query1.as_str() |
| test_storage.rs:139:25:139:46 | insert_query2.as_str() |
| test_storage.rs:140:25:140:47 | prepared_query.as_str() |
| test_storage.rs:141:25:141:47 | prepared_query.as_str() |
| test_storage.rs:188:29:188:86 | ...::from(...) |
| test_storage.rs:189:28:189:82 | ...::from(...) |
| test_storage.rs:190:28:190:81 | ...::from(...) |
| test_storage.rs:217:14:217:47 | ...::_print(...) |
| test_storage.rs:219:27:219:41 | ...::_print(...) |
| test_storage.rs:220:28:220:43 | ...::_print(...) |
| test_storage.rs:223:14:223:51 | ...::_print(...) |
| test_storage.rs:225:27:225:41 | ...::_print(...) |
| test_storage.rs:226:28:226:43 | ...::_print(...) |

View File

@@ -0,0 +1,4 @@
nonUniqueCertainType
| test_logging.rs:17:8:19:12 | { ... } | E |
| test_logging.rs:29:8:31:12 | { ... } | E |
| test_storage.rs:177:8:179:9 | { ... } | E |

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,146 @@
#select
| test_storage.rs:100:13:100:23 | ...::query | test_storage.rs:71:97:71:114 | get_phone_number(...) | test_storage.rs:100:13:100:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:71:97:71:114 | get_phone_number(...) | get_phone_number(...) |
| test_storage.rs:115:13:115:25 | ...::raw_sql | test_storage.rs:71:97:71:114 | get_phone_number(...) | test_storage.rs:115:13:115:25 | ...::raw_sql | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:71:97:71:114 | get_phone_number(...) | get_phone_number(...) |
| test_storage.rs:119:13:119:23 | ...::query | test_storage.rs:71:97:71:114 | get_phone_number(...) | test_storage.rs:119:13:119:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:71:97:71:114 | get_phone_number(...) | get_phone_number(...) |
| test_storage.rs:125:13:125:23 | ...::query | test_storage.rs:71:97:71:114 | get_phone_number(...) | test_storage.rs:125:13:125:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:71:97:71:114 | get_phone_number(...) | get_phone_number(...) |
| test_storage.rs:139:13:139:23 | ...::query | test_storage.rs:71:97:71:114 | get_phone_number(...) | test_storage.rs:139:13:139:23 | ...::query | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:71:97:71:114 | get_phone_number(...) | get_phone_number(...) |
| test_storage.rs:194:16:194:22 | execute | test_storage.rs:189:100:189:117 | get_phone_number(...) | test_storage.rs:194:16:194:22 | execute | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:189:100:189:117 | get_phone_number(...) | get_phone_number(...) |
| test_storage.rs:196:24:196:32 | query_row | test_storage.rs:190:86:190:103 | get_phone_number(...) | test_storage.rs:196:24:196:32 | query_row | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:190:86:190:103 | get_phone_number(...) | get_phone_number(...) |
| test_storage.rs:204:31:204:37 | prepare | test_storage.rs:190:86:190:103 | get_phone_number(...) | test_storage.rs:204:31:204:37 | prepare | This database operation may read or write unencrypted sensitive data from $@. | test_storage.rs:190:86:190:103 | get_phone_number(...) | get_phone_number(...) |
edges
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:100:25:100:37 | insert_query2 | provenance | |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:100:25:100:46 | insert_query2.as_str() | provenance | MaD:8 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:100:25:100:46 | insert_query2.as_str() | provenance | MaD:7 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:100:25:100:46 | insert_query2.as_str() | provenance | MaD:8 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:115:27:115:39 | insert_query2 | provenance | |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:115:27:115:48 | insert_query2.as_str() | provenance | MaD:8 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:115:27:115:48 | insert_query2.as_str() | provenance | MaD:7 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:115:27:115:48 | insert_query2.as_str() | provenance | MaD:8 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:119:25:119:37 | insert_query2 | provenance | |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:119:25:119:46 | insert_query2.as_str() | provenance | MaD:8 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:119:25:119:46 | insert_query2.as_str() | provenance | MaD:7 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:119:25:119:46 | insert_query2.as_str() | provenance | MaD:8 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:125:25:125:37 | insert_query2 | provenance | |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:125:25:125:46 | insert_query2.as_str() | provenance | MaD:8 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:125:25:125:46 | insert_query2.as_str() | provenance | MaD:7 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:125:25:125:46 | insert_query2.as_str() | provenance | MaD:8 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:139:25:139:37 | insert_query2 | provenance | |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:139:25:139:46 | insert_query2.as_str() | provenance | MaD:8 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:139:25:139:46 | insert_query2.as_str() | provenance | MaD:7 |
| test_storage.rs:71:9:71:21 | insert_query2 | test_storage.rs:139:25:139:46 | insert_query2.as_str() | provenance | MaD:8 |
| test_storage.rs:71:25:71:114 | ... + ... | test_storage.rs:71:9:71:21 | insert_query2 | provenance | |
| test_storage.rs:71:25:71:114 | ... + ... | test_storage.rs:71:25:71:121 | ... + ... | provenance | MaD:6 |
| test_storage.rs:71:25:71:121 | ... + ... | test_storage.rs:71:9:71:21 | insert_query2 | provenance | |
| test_storage.rs:71:96:71:114 | &... | test_storage.rs:71:9:71:21 | insert_query2 | provenance | |
| test_storage.rs:71:96:71:114 | &... | test_storage.rs:71:25:71:114 | ... + ... | provenance | |
| test_storage.rs:71:97:71:114 | get_phone_number(...) | test_storage.rs:71:96:71:114 | &... | provenance | Config |
| test_storage.rs:100:25:100:37 | insert_query2 | test_storage.rs:100:25:100:46 | insert_query2.as_str() [&ref] | provenance | MaD:8 |
| test_storage.rs:100:25:100:37 | insert_query2 | test_storage.rs:100:25:100:46 | insert_query2.as_str() [&ref] | provenance | MaD:7 |
| test_storage.rs:100:25:100:37 | insert_query2 | test_storage.rs:100:25:100:46 | insert_query2.as_str() [&ref] | provenance | MaD:8 |
| test_storage.rs:100:25:100:46 | insert_query2.as_str() | test_storage.rs:100:13:100:23 | ...::query | provenance | MaD:4 Sink:MaD:4 |
| test_storage.rs:100:25:100:46 | insert_query2.as_str() [&ref] | test_storage.rs:100:13:100:23 | ...::query | provenance | MaD:4 Sink:MaD:4 |
| test_storage.rs:115:27:115:39 | insert_query2 | test_storage.rs:115:27:115:48 | insert_query2.as_str() [&ref] | provenance | MaD:8 |
| test_storage.rs:115:27:115:39 | insert_query2 | test_storage.rs:115:27:115:48 | insert_query2.as_str() [&ref] | provenance | MaD:7 |
| test_storage.rs:115:27:115:39 | insert_query2 | test_storage.rs:115:27:115:48 | insert_query2.as_str() [&ref] | provenance | MaD:8 |
| test_storage.rs:115:27:115:48 | insert_query2.as_str() | test_storage.rs:115:13:115:25 | ...::raw_sql | provenance | MaD:5 Sink:MaD:5 |
| test_storage.rs:115:27:115:48 | insert_query2.as_str() [&ref] | test_storage.rs:115:13:115:25 | ...::raw_sql | provenance | MaD:5 Sink:MaD:5 |
| test_storage.rs:119:25:119:37 | insert_query2 | test_storage.rs:119:25:119:46 | insert_query2.as_str() [&ref] | provenance | MaD:8 |
| test_storage.rs:119:25:119:37 | insert_query2 | test_storage.rs:119:25:119:46 | insert_query2.as_str() [&ref] | provenance | MaD:7 |
| test_storage.rs:119:25:119:37 | insert_query2 | test_storage.rs:119:25:119:46 | insert_query2.as_str() [&ref] | provenance | MaD:8 |
| test_storage.rs:119:25:119:46 | insert_query2.as_str() | test_storage.rs:119:13:119:23 | ...::query | provenance | MaD:4 Sink:MaD:4 |
| test_storage.rs:119:25:119:46 | insert_query2.as_str() [&ref] | test_storage.rs:119:13:119:23 | ...::query | provenance | MaD:4 Sink:MaD:4 |
| test_storage.rs:125:25:125:37 | insert_query2 | test_storage.rs:125:25:125:46 | insert_query2.as_str() [&ref] | provenance | MaD:8 |
| test_storage.rs:125:25:125:37 | insert_query2 | test_storage.rs:125:25:125:46 | insert_query2.as_str() [&ref] | provenance | MaD:7 |
| test_storage.rs:125:25:125:37 | insert_query2 | test_storage.rs:125:25:125:46 | insert_query2.as_str() [&ref] | provenance | MaD:8 |
| test_storage.rs:125:25:125:46 | insert_query2.as_str() | test_storage.rs:125:13:125:23 | ...::query | provenance | MaD:4 Sink:MaD:4 |
| test_storage.rs:125:25:125:46 | insert_query2.as_str() [&ref] | test_storage.rs:125:13:125:23 | ...::query | provenance | MaD:4 Sink:MaD:4 |
| test_storage.rs:139:25:139:37 | insert_query2 | test_storage.rs:139:25:139:46 | insert_query2.as_str() [&ref] | provenance | MaD:8 |
| test_storage.rs:139:25:139:37 | insert_query2 | test_storage.rs:139:25:139:46 | insert_query2.as_str() [&ref] | provenance | MaD:7 |
| test_storage.rs:139:25:139:37 | insert_query2 | test_storage.rs:139:25:139:46 | insert_query2.as_str() [&ref] | provenance | MaD:8 |
| test_storage.rs:139:25:139:46 | insert_query2.as_str() | test_storage.rs:139:13:139:23 | ...::query | provenance | MaD:4 Sink:MaD:4 |
| test_storage.rs:139:25:139:46 | insert_query2.as_str() [&ref] | test_storage.rs:139:13:139:23 | ...::query | provenance | MaD:4 Sink:MaD:4 |
| test_storage.rs:189:9:189:24 | insert_query_bad | test_storage.rs:194:25:194:40 | insert_query_bad | provenance | |
| test_storage.rs:189:28:189:117 | ... + ... | test_storage.rs:189:9:189:24 | insert_query_bad | provenance | |
| test_storage.rs:189:28:189:117 | ... + ... | test_storage.rs:189:28:189:124 | ... + ... | provenance | MaD:6 |
| test_storage.rs:189:28:189:124 | ... + ... | test_storage.rs:189:9:189:24 | insert_query_bad | provenance | |
| test_storage.rs:189:99:189:117 | &... | test_storage.rs:189:9:189:24 | insert_query_bad | provenance | |
| test_storage.rs:189:99:189:117 | &... | test_storage.rs:189:28:189:117 | ... + ... | provenance | |
| test_storage.rs:189:100:189:117 | get_phone_number(...) | test_storage.rs:189:99:189:117 | &... | provenance | Config |
| test_storage.rs:190:9:190:24 | select_query_bad | test_storage.rs:196:35:196:50 | select_query_bad | provenance | |
| test_storage.rs:190:28:190:103 | ... + ... | test_storage.rs:190:9:190:24 | select_query_bad | provenance | |
| test_storage.rs:190:28:190:103 | ... + ... | test_storage.rs:190:28:190:109 | ... + ... | provenance | MaD:6 |
| test_storage.rs:190:28:190:109 | ... + ... | test_storage.rs:190:9:190:24 | select_query_bad | provenance | |
| test_storage.rs:190:85:190:103 | &... | test_storage.rs:190:9:190:24 | select_query_bad | provenance | |
| test_storage.rs:190:85:190:103 | &... | test_storage.rs:190:28:190:103 | ... + ... | provenance | |
| test_storage.rs:190:86:190:103 | get_phone_number(...) | test_storage.rs:190:85:190:103 | &... | provenance | Config |
| test_storage.rs:194:24:194:40 | &insert_query_bad | test_storage.rs:194:16:194:22 | execute | provenance | MaD:1 Sink:MaD:1 |
| test_storage.rs:194:24:194:40 | &insert_query_bad [&ref] | test_storage.rs:194:16:194:22 | execute | provenance | MaD:1 Sink:MaD:1 |
| test_storage.rs:194:25:194:40 | insert_query_bad | test_storage.rs:194:24:194:40 | &insert_query_bad | provenance | Config |
| test_storage.rs:194:25:194:40 | insert_query_bad | test_storage.rs:194:24:194:40 | &insert_query_bad [&ref] | provenance | |
| test_storage.rs:196:34:196:50 | &select_query_bad | test_storage.rs:196:24:196:32 | query_row | provenance | MaD:3 Sink:MaD:3 |
| test_storage.rs:196:34:196:50 | &select_query_bad [&ref] | test_storage.rs:196:24:196:32 | query_row | provenance | MaD:3 Sink:MaD:3 |
| test_storage.rs:196:35:196:50 | select_query_bad | test_storage.rs:196:34:196:50 | &select_query_bad | provenance | Config |
| test_storage.rs:196:35:196:50 | select_query_bad | test_storage.rs:196:34:196:50 | &select_query_bad [&ref] | provenance | |
| test_storage.rs:196:35:196:50 | select_query_bad | test_storage.rs:204:40:204:55 | select_query_bad | provenance | |
| test_storage.rs:204:39:204:55 | &select_query_bad | test_storage.rs:204:31:204:37 | prepare | provenance | MaD:2 Sink:MaD:2 |
| test_storage.rs:204:39:204:55 | &select_query_bad [&ref] | test_storage.rs:204:31:204:37 | prepare | provenance | MaD:2 Sink:MaD:2 |
| test_storage.rs:204:40:204:55 | select_query_bad | test_storage.rs:204:39:204:55 | &select_query_bad | provenance | Config |
| test_storage.rs:204:40:204:55 | select_query_bad | test_storage.rs:204:39:204:55 | &select_query_bad [&ref] | provenance | |
models
| 1 | Sink: <rusqlite::Connection>::execute; Argument[0]; sql-injection |
| 2 | Sink: <rusqlite::Connection>::prepare; Argument[0]; sql-injection |
| 3 | Sink: <rusqlite::Connection>::query_row; Argument[0]; sql-injection |
| 4 | Sink: sqlx_core::query::query; Argument[0]; sql-injection |
| 5 | Sink: sqlx_core::raw_sql::raw_sql; Argument[0]; sql-injection |
| 6 | Summary: <alloc::string::String as core::ops::arith::Add>::add; Argument[self]; ReturnValue; value |
| 7 | Summary: <alloc::string::String>::as_str; Argument[self]; ReturnValue; value |
| 8 | Summary: <core::str>::as_str; Argument[self]; ReturnValue; value |
nodes
| test_storage.rs:71:9:71:21 | insert_query2 | semmle.label | insert_query2 |
| test_storage.rs:71:25:71:114 | ... + ... | semmle.label | ... + ... |
| test_storage.rs:71:25:71:121 | ... + ... | semmle.label | ... + ... |
| test_storage.rs:71:96:71:114 | &... | semmle.label | &... |
| test_storage.rs:71:97:71:114 | get_phone_number(...) | semmle.label | get_phone_number(...) |
| test_storage.rs:100:13:100:23 | ...::query | semmle.label | ...::query |
| test_storage.rs:100:25:100:37 | insert_query2 | semmle.label | insert_query2 |
| test_storage.rs:100:25:100:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() |
| test_storage.rs:100:25:100:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] |
| test_storage.rs:115:13:115:25 | ...::raw_sql | semmle.label | ...::raw_sql |
| test_storage.rs:115:27:115:39 | insert_query2 | semmle.label | insert_query2 |
| test_storage.rs:115:27:115:48 | insert_query2.as_str() | semmle.label | insert_query2.as_str() |
| test_storage.rs:115:27:115:48 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] |
| test_storage.rs:119:13:119:23 | ...::query | semmle.label | ...::query |
| test_storage.rs:119:25:119:37 | insert_query2 | semmle.label | insert_query2 |
| test_storage.rs:119:25:119:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() |
| test_storage.rs:119:25:119:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] |
| test_storage.rs:125:13:125:23 | ...::query | semmle.label | ...::query |
| test_storage.rs:125:25:125:37 | insert_query2 | semmle.label | insert_query2 |
| test_storage.rs:125:25:125:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() |
| test_storage.rs:125:25:125:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] |
| test_storage.rs:139:13:139:23 | ...::query | semmle.label | ...::query |
| test_storage.rs:139:25:139:37 | insert_query2 | semmle.label | insert_query2 |
| test_storage.rs:139:25:139:46 | insert_query2.as_str() | semmle.label | insert_query2.as_str() |
| test_storage.rs:139:25:139:46 | insert_query2.as_str() [&ref] | semmle.label | insert_query2.as_str() [&ref] |
| test_storage.rs:189:9:189:24 | insert_query_bad | semmle.label | insert_query_bad |
| test_storage.rs:189:28:189:117 | ... + ... | semmle.label | ... + ... |
| test_storage.rs:189:28:189:124 | ... + ... | semmle.label | ... + ... |
| test_storage.rs:189:99:189:117 | &... | semmle.label | &... |
| test_storage.rs:189:100:189:117 | get_phone_number(...) | semmle.label | get_phone_number(...) |
| test_storage.rs:190:9:190:24 | select_query_bad | semmle.label | select_query_bad |
| test_storage.rs:190:28:190:103 | ... + ... | semmle.label | ... + ... |
| test_storage.rs:190:28:190:109 | ... + ... | semmle.label | ... + ... |
| test_storage.rs:190:85:190:103 | &... | semmle.label | &... |
| test_storage.rs:190:86:190:103 | get_phone_number(...) | semmle.label | get_phone_number(...) |
| test_storage.rs:194:16:194:22 | execute | semmle.label | execute |
| test_storage.rs:194:24:194:40 | &insert_query_bad | semmle.label | &insert_query_bad |
| test_storage.rs:194:24:194:40 | &insert_query_bad [&ref] | semmle.label | &insert_query_bad [&ref] |
| test_storage.rs:194:25:194:40 | insert_query_bad | semmle.label | insert_query_bad |
| test_storage.rs:196:24:196:32 | query_row | semmle.label | query_row |
| test_storage.rs:196:34:196:50 | &select_query_bad | semmle.label | &select_query_bad |
| test_storage.rs:196:34:196:50 | &select_query_bad [&ref] | semmle.label | &select_query_bad [&ref] |
| test_storage.rs:196:35:196:50 | select_query_bad | semmle.label | select_query_bad |
| test_storage.rs:204:31:204:37 | prepare | semmle.label | prepare |
| test_storage.rs:204:39:204:55 | &select_query_bad | semmle.label | &select_query_bad |
| test_storage.rs:204:39:204:55 | &select_query_bad [&ref] | semmle.label | &select_query_bad [&ref] |
| test_storage.rs:204:40:204:55 | select_query_bad | semmle.label | select_query_bad |
subpaths

View File

@@ -0,0 +1,4 @@
query: queries/security/CWE-312/CleartextStorageDatabase.ql
postprocess:
- utils/test/InlineExpectationsTestQuery.ql
- utils/test/PrettyPrintModels.ql

View File

@@ -3,3 +3,9 @@ qltest_dependencies:
- log = { version = "0.4.25", features = ["kv"] }
- simple_logger = { version = "5.0.0" }
- log_err = { version = "1.1.1" }
- sqlx = { version = "0.8.6", features = ["mysql", "sqlite", "postgres", "runtime-async-std", "tls-native-tls"] }
- futures = { version = "0.3" }
- aes = { version = "0.8.4" }
- aes-gcm = { version = "0.10.3" }
- base64 = { version = "0.22.1" }
- rusqlite = { version = "0.32" }

View File

@@ -90,13 +90,13 @@ fn test_log(harmless: String, password: String, encrypted_password: String) {
error!(value2:?; "message"); // $ MISSING: Alert[rust/cleartext-logging]
// pre-formatted
let m1 = &password; // $ Source=m1
let m1 = &password; // $ Source[rust/cleartext-logging]=m1
info!("message = {}", m1); // $ Alert[rust/cleartext-logging]=m1
let m2 = "message = ".to_string() + &password; // $ Source=m2
let m2 = "message = ".to_string() + &password; // $ Source[rust/cleartext-logging]=m2
info!("{}", m2); // $ Alert[rust/cleartext-logging]=m2
let m3 = format!("message = {}", password); // $ Source=m3
let m3 = format!("message = {}", password); // $ Source[rust/cleartext-logging]=m3
info!("{}", m3); // $ Alert[rust/cleartext-logging]=m3
let mut m4 = String::new();
@@ -126,7 +126,7 @@ fn test_log(harmless: String, password: String, encrypted_password: String) {
trace!("message = {}", &str2);
// logging from a tuple
let t1 = (harmless, password); // $ Source=t1
let t1 = (harmless, password); // $ Source[rust/cleartext-logging]=t1
trace!("message = {}", t1.0);
trace!("message = {}", t1.1); // $ Alert[rust/cleartext-logging]=t1
trace!("message = {:?}", t1); // $ MISSING: Alert[rust/cleartext-logging]=t1
@@ -180,11 +180,11 @@ fn test_log(harmless: String, password: String, encrypted_password: String) {
let _ = err_result.log_expect(&format!("Failed with password: {}", password2)); // $ Alert[rust/cleartext-logging]
// test `log_expect` with sensitive `Result.Err`
let err_result2: Result<String, String> = Err(password2.clone()); // $ Source=s3
let err_result2: Result<String, String> = Err(password2.clone()); // $ Source[rust/cleartext-logging]=s3
let _ = err_result2.log_expect(""); // $ Alert[rust/cleartext-logging]=s3
// test `log_unwrap` with sensitive `Result.Err`
let err_result3: Result<String, String> = Err(password2); // $ Source=err_result3
let err_result3: Result<String, String> = Err(password2); // $ Source[rust/cleartext-logging]=err_result3
let _ = err_result3.log_unwrap(); // $ Alert[rust/cleartext-logging]=err_result3
}

View File

@@ -0,0 +1,228 @@
use aes_gcm::aead::{Aead, AeadCore, OsRng};
use aes_gcm::aes::cipher::Unsigned;
use aes_gcm::{Aes256Gcm, KeyInit};
use base64::prelude::*;
use sqlx::Connection;
use sqlx::Executor;
// --- tests ---
fn get_harmless() -> String {
return String::from("harmless");
}
fn get_social_security_number() -> String {
return String::from("1234567890");
}
fn get_phone_number() -> String {
return String::from("1234567890");
}
fn get_email() -> String {
return String::from("a@b.com");
}
fn get_ccn() -> String {
return String::from("1234567890");
}
fn encrypt(text: String, encryption_key: &aes_gcm::Key<Aes256Gcm>) -> String {
// encrypt text -> ciphertext
let cipher = Aes256Gcm::new(&encryption_key);
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let ciphertext = cipher.encrypt(&nonce, text.as_ref()).unwrap();
// append (nonce, ciphertext)
let mut combined = nonce.to_vec();
combined.extend(ciphertext);
// encode to base64 string
BASE64_STANDARD.encode(combined)
}
fn decrypt(data: String, encryption_key: &aes_gcm::Key<Aes256Gcm>) -> String {
let cipher = Aes256Gcm::new(&encryption_key);
// decode base64 string
let decoded = BASE64_STANDARD.decode(data).unwrap();
// split into (nonce, ciphertext)
let nonce_size = <Aes256Gcm as AeadCore>::NonceSize::to_usize();
let (nonce, ciphertext) = decoded.split_at(nonce_size);
// decrypt ciphertext -> plaintext
let plaintext = cipher.decrypt(nonce.into(), ciphertext).unwrap();
String::from_utf8(plaintext).unwrap()
}
async fn test_storage_sqlx_sql_command(url: &str) -> Result<(), sqlx::Error> {
// connect through a MySQL connection pool
let pool1 = sqlx::mysql::MySqlPool::connect(url).await?;
let mut conn1 = pool1.acquire().await?;
// construct queries
let id = "123";
let select_query1 = String::from("SELECT * FROM CONTACTS WHERE ID = ") + id;
let select_query2 = String::from("SELECT * FROM CONTACTS WHERE SSN = '") + &get_social_security_number() + "'";
let insert_query1 = String::from("INSERT INTO CONTACTS(ID, HARMLESS) VALUES(") + id + ", '" + &get_harmless() + "')";
let insert_query2 = String::from("INSERT INTO CONTACTS(ID, PHONE) VALUES(") + id + ", '" + &get_phone_number() + "')"; // $ Source[rust/cleartext-storage-database]
let update_query1 = String::from("UPDATE CONTACTS SET HARMLESS='") + &get_harmless() + "' WHERE ID=" + id;
let update_query2 = String::from("UPDATE CONTACTS SET EMAIL='") + &get_email() + "' WHERE ID=" + id;
let s1 = &get_social_security_number();
let update_query3 = String::from("UPDATE CONTACTS SET SSN='") + s1 + "' WHERE ID=" + id;
let update_query4 = String::from("UPDATE CONTACTS SET SSN='") + &s1 + "' WHERE ID=" + id;
let s2 = s1.as_str();
let update_query5 = String::from("UPDATE CONTACTS SET SSN='") + s2 + "' WHERE ID=" + id;
let update_query6 = String::from("UPDATE CONTACTS SET SSN='") + &s2 + "' WHERE ID=" + id;
let delete_query1 = String::from("DELETE FROM CONTACTS WHERE ID=") + id;
let delete_query2 = String::from("DELETE FROM CONTACTS WHERE SSN='") + &get_social_security_number() + "'";
let prepared_query = String::from("UPDATE CONTACTS SET SSN=? WHERE ID=?");
// execute queries - MySQL, direct
let _ = conn1.execute(select_query1.as_str()).await?;
let _ = conn1.execute(select_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
let _ = conn1.execute(insert_query1.as_str()).await?;
let _ = conn1.execute(insert_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
let _ = conn1.execute(update_query1.as_str()).await?;
let _ = conn1.execute(update_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
let _ = conn1.execute(update_query3.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
let _ = conn1.execute(update_query4.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
let _ = conn1.execute(update_query5.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
let _ = conn1.execute(update_query6.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
let _ = conn1.execute(delete_query1.as_str()).await?;
let _ = conn1.execute(delete_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
// execute queries - MySQL, prepared query
let _ = sqlx::query(insert_query1.as_str()).execute(&pool1).await?;
let _ = sqlx::query(insert_query2.as_str()).execute(&pool1).await?; // $ Alert[rust/cleartext-storage-database]
let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).execute(&pool1).await?;
let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).execute(&pool1).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
let _ = sqlx::query(prepared_query.as_str()).bind(&s1).execute(&pool1).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
let _ = sqlx::query(prepared_query.as_str()).bind(&s2).execute(&pool1).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
// connect through SQLite, no connection pool
let mut conn2 = sqlx::sqlite::SqliteConnection::connect(url).await?;
// execute queries - SQLite, direct
let _ = conn2.execute(insert_query1.as_str()).await?;
let _ = conn2.execute(insert_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
// execute queries - SQLite, direct variant
let _ = sqlx::raw_sql(insert_query1.as_str()).execute(&mut conn2).await?;
let _ = sqlx::raw_sql(insert_query2.as_str()).execute(&mut conn2).await?; // $ Alert[rust/cleartext-storage-database]
// execute queries - SQLite, prepared query
let _ = sqlx::query(insert_query1.as_str()).execute(&mut conn2).await?;
let _ = sqlx::query(insert_query2.as_str()).execute(&mut conn2).await?; // $ Alert[rust/cleartext-storage-database]
let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).bind(id).execute(&mut conn2).await?;
let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).bind(id).execute(&mut conn2).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
// execute queries - SQLite, prepared query variant
let _ = sqlx::query(insert_query1.as_str()).fetch(&mut conn2);
let _ = sqlx::query(insert_query2.as_str()).fetch(&mut conn2); // $ Alert[rust/cleartext-storage-database]
let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).bind(id).fetch(&mut conn2);
let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).bind(id).fetch(&mut conn2); // $ MISSING: Alert[rust/cleartext-storage-database]
// connect through a PostgreSQL connection pool
let pool3 = sqlx::postgres::PgPool::connect(url).await?;
let mut conn3 = pool3.acquire().await?;
// execute queries - PostgreSQL, direct
let _ = conn3.execute(insert_query1.as_str()).await?;
let _ = conn3.execute(insert_query2.as_str()).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
// execute queries - PostgreSQL, prepared query
let _ = sqlx::query(insert_query1.as_str()).execute(&pool3).await?;
let _ = sqlx::query(insert_query2.as_str()).execute(&pool3).await?; // $ Alert[rust/cleartext-storage-database]
let _ = sqlx::query(prepared_query.as_str()).bind(get_harmless()).bind(id).execute(&pool3).await?;
let _ = sqlx::query(prepared_query.as_str()).bind(get_social_security_number()).bind(id).execute(&pool3).await?; // $ MISSING: Alert[rust/cleartext-storage-database]
// "bad" example
{
let pool = &pool1;
let credit_card_number = get_ccn();
let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)";
let result = sqlx::query(query)
.bind(id)
.bind(credit_card_number) // $ MISSING: Alert[rust/cleartext-storage-database]
.execute(pool)
.await?;
}
// "good" example
{
let pool = &pool1;
let credit_card_number = get_ccn();
let encryption_key = Aes256Gcm::generate_key(OsRng);
// ...
let query = "INSERT INTO PAYMENTDETAILS(ID, CARDNUM) VALUES(?, ?)";
let result = sqlx::query(query)
.bind(id)
.bind(encrypt(credit_card_number, &encryption_key))
.execute(pool)
.await?;
}
Ok(())
}
#[derive(Debug)]
struct Contact {
id: i32,
phone: String,
}
async fn test_storage_rusqlite_sql_command(url: &str) -> Result<(), Box<dyn std::error::Error>> {
// connect with rusqlite
let connection = rusqlite::Connection::open_in_memory()?;
// construct queries
let id = "123";
let insert_query_good = String::from("INSERT INTO CONTACTS(ID, HARMLESS) VALUES(") + id + ", '" + &get_harmless() + "')";
let insert_query_bad = String::from("INSERT INTO CONTACTS(ID, PHONE) VALUES(") + id + ", '" + &get_phone_number() + "')"; // $ Source[rust/cleartext-storage-database]
let select_query_bad = String::from("SELECT * FROM CONTACTS WHERE PHONE = '") + &get_phone_number() + "'"; // $ Source[rust/cleartext-storage-database]
// execute queries - rusqlite
connection.execute(&insert_query_good, ())?;
connection.execute(&insert_query_bad, ())?; // $ Alert[rust/cleartext-storage-database]
let _ = connection.query_row(&select_query_bad, (), |row| { // $ Alert[rust/cleartext-storage-database]
let row: &rusqlite::Row<'_> = row;
Ok(Contact {
id: row.get(0)?,
phone: row.get(1)?,
})
})?;
let mut stmt = connection.prepare(&select_query_bad)?; // $ Alert[rust/cleartext-storage-database]
let people = stmt.query_map([], |row| {
let row: &rusqlite::Row<'_> = row;
Ok(Contact {
id: row.get_unwrap(0),
phone: row.get_unwrap(1),
})
})?;
Ok(())
}
fn main() {
println!("test_storage_sqlx_sql_command...");
match futures::executor::block_on(test_storage_sqlx_sql_command("")) {
Ok(_) => println!(" successful!"),
Err(e) => println!(" error: {}", e),
}
println!("test_storage_rusqlite_sql_command...");
match futures::executor::block_on(test_storage_rusqlite_sql_command("")) {
Ok(_) => println!(" successful!"),
Err(e) => println!(" error: {}", e),
}
}