mirror of
https://github.com/github/codeql.git
synced 2025-12-16 08:43:11 +01:00
Merge pull request #20829 from geoffw0/cert-checks
Rust: New Query rust/disabled-certificate-check
This commit is contained in:
@@ -11,6 +11,7 @@ ql/rust/ql/src/queries/diagnostics/UnresolvedMacroCalls.ql
|
||||
ql/rust/ql/src/queries/security/CWE-020/RegexInjection.ql
|
||||
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-295/DisabledCertificateCheck.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
|
||||
|
||||
@@ -12,6 +12,7 @@ ql/rust/ql/src/queries/security/CWE-020/RegexInjection.ql
|
||||
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-117/LogInjection.ql
|
||||
ql/rust/ql/src/queries/security/CWE-295/DisabledCertificateCheck.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
|
||||
|
||||
@@ -12,6 +12,7 @@ ql/rust/ql/src/queries/security/CWE-020/RegexInjection.ql
|
||||
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-117/LogInjection.ql
|
||||
ql/rust/ql/src/queries/security/CWE-295/DisabledCertificateCheck.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
|
||||
|
||||
4
rust/ql/lib/change-notes/2025-11-21-fs.md
Normal file
4
rust/ql/lib/change-notes/2025-11-21-fs.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added more detailed models for `std::fs` and `std::path`.
|
||||
9
rust/ql/lib/codeql/rust/frameworks/native-tls.model.yml
Normal file
9
rust/ql/lib/codeql/rust/frameworks/native-tls.model.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/rust-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["<native_tls::TlsConnectorBuilder>::danger_accept_invalid_certs", "Argument[0]", "disable-certificate", "manual"]
|
||||
- ["<native_tls::TlsConnectorBuilder>::danger_accept_invalid_hostnames", "Argument[0]", "disable-certificate", "manual"]
|
||||
- ["<async_native_tls::connect::TlsConnector>::danger_accept_invalid_certs", "Argument[0]", "disable-certificate", "manual"]
|
||||
- ["<async_native_tls::connect::TlsConnector>::danger_accept_invalid_hostnames", "Argument[0]", "disable-certificate", "manual"]
|
||||
@@ -11,6 +11,10 @@ extensions:
|
||||
data:
|
||||
- ["<reqwest::async_impl::client::Client>::request", "Argument[1]", "request-url", "manual"]
|
||||
- ["<reqwest::blocking::client::Client>::request", "Argument[1]", "request-url", "manual"]
|
||||
- ["<reqwest::async_impl::client::ClientBuilder>::danger_accept_invalid_certs", "Argument[0]", "disable-certificate", "manual"]
|
||||
- ["<reqwest::async_impl::client::ClientBuilder>::danger_accept_invalid_hostnames", "Argument[0]", "disable-certificate", "manual"]
|
||||
- ["<reqwest::blocking::client::ClientBuilder>::danger_accept_invalid_certs", "Argument[0]", "disable-certificate", "manual"]
|
||||
- ["<reqwest::blocking::client::ClientBuilder>::danger_accept_invalid_hostnames", "Argument[0]", "disable-certificate", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/rust-all
|
||||
extensible: summaryModel
|
||||
|
||||
@@ -3,14 +3,27 @@ extensions:
|
||||
pack: codeql/rust-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["std::fs::exists", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["std::fs::read", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["std::fs::read_dir", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["std::fs::read_to_string", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["std::fs::read_link", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["std::fs::metadata", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["std::fs::symlink_metadata", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["<std::fs::DirEntry>::path", "ReturnValue", "file", "manual"]
|
||||
- ["<std::fs::DirEntry>::file_name", "ReturnValue", "file", "manual"]
|
||||
- ["<std::fs::File>::open", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["<std::fs::File>::open_buffered", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["<std::fs::OpenOptions>::open", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["<std::path::Path>::exists", "ReturnValue", "file", "manual"]
|
||||
- ["<std::path::Path>::try_exists", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["<std::path::Path>::is_file", "ReturnValue", "file", "manual"]
|
||||
- ["<std::path::Path>::is_dir", "ReturnValue", "file", "manual"]
|
||||
- ["<std::path::Path>::is_symlink", "ReturnValue", "file", "manual"]
|
||||
- ["<std::path::Path>::metadata", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["<std::path::Path>::symlink_metadata", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["<std::path::Path>::read_dir", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- ["<std::path::Path>::read_link", "ReturnValue.Field[core::result::Result::Ok(0)]", "file", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/rust-all
|
||||
extensible: sinkModel
|
||||
@@ -68,3 +81,12 @@ extensions:
|
||||
- ["<std::path::Path>::with_extension", "Argument[Self].Reference", "ReturnValue", "taint", "manual"]
|
||||
- ["<std::path::Path>::with_file_name", "Argument[Self].Reference", "ReturnValue", "taint", "manual"]
|
||||
- ["<std::path::Path>::with_file_name", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["<std::fs::Metadata>::accessed", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
|
||||
- ["<std::fs::Metadata>::created", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
|
||||
- ["<std::fs::Metadata>::file_type", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
|
||||
- ["<std::fs::Metadata>::is_file", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
|
||||
- ["<std::fs::Metadata>::is_dir", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
|
||||
- ["<std::fs::Metadata>::is_symlink", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
|
||||
- ["<std::fs::Metadata>::len", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
|
||||
- ["<std::fs::Metadata>::modified", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
|
||||
- ["<std::fs::Metadata>::permissions", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Provides classes and predicates for reasoning about disabled certificate
|
||||
* check vulnerabilities.
|
||||
*/
|
||||
|
||||
import rust
|
||||
private import codeql.rust.dataflow.DataFlow
|
||||
private import codeql.rust.dataflow.FlowSink
|
||||
private import codeql.rust.Concepts
|
||||
private import codeql.rust.dataflow.internal.Node as Node
|
||||
|
||||
/**
|
||||
* Provides default sinks for detecting disabled certificate check
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
module DisabledCertificateCheckExtensions {
|
||||
/**
|
||||
* A data flow sink for disabled certificate check vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends QuerySink::Range {
|
||||
override string getSinkType() { result = "DisabledCertificateCheck" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for disabled certificate check vulnerabilities from model data.
|
||||
*/
|
||||
private class ModelsAsDataSink extends Sink {
|
||||
ModelsAsDataSink() { sinkNode(this, "disable-certificate") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A heuristic sink for disabled certificate check vulnerabilities based on function names.
|
||||
*/
|
||||
private class HeuristicSink extends Sink {
|
||||
HeuristicSink() {
|
||||
exists(CallExprBase fc |
|
||||
fc.getStaticTarget().(Function).getName().getText() =
|
||||
["danger_accept_invalid_certs", "danger_accept_invalid_hostnames"] and
|
||||
fc.getArg(0) = this.asExpr() and
|
||||
// don't duplicate modeled sinks
|
||||
not exists(ModelsAsDataSink s | s.(Node::FlowSummaryNode).getSinkElement().getCall() = fc)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query `rust/disabled-certificate-check`, to detect disabled TLS certificate checks.
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
The <code>danger_accept_invalid_certs</code> option on TLS connectors and HTTP clients controls whether certificate verification is performed. If this option is set to <code>true</code>, the client will accept any certificate, making it susceptible to man-in-the-middle attacks.
|
||||
</p>
|
||||
<p>
|
||||
Similarly, the <code>danger_accept_invalid_hostnames</code> option controls whether hostname verification is performed. If this option is set to <code>true</code>, the client will accept any valid certificate regardless of the site that certificate is for, again making it susceptible to man-in-the-middle attacks.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Do not set <code>danger_accept_invalid_certs</code> or <code>danger_accept_invalid_hostnames</code> to <code>true</code>, except in controlled environments such as tests. In production, always ensure certificate and hostname verification is enabled to prevent security risks.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following code snippet shows a function that creates an HTTP client with certificate verification disabled:
|
||||
</p>
|
||||
<sample src="DisabledCertificateCheckBad.rs"/>
|
||||
<p>
|
||||
In production code, always configure clients to verify certificates:
|
||||
</p>
|
||||
<sample src="DisabledCertificateCheckGood.rs"/>
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
Rust native-tls crate: <a href="https://docs.rs/native-tls/latest/native_tls/struct.TlsConnectorBuilder.html">TlsConnectorBuilder</a>.
|
||||
</li>
|
||||
<li>
|
||||
Rust reqwest crate: <a href="https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html">ClientBuilder</a>.
|
||||
</li>
|
||||
<li>
|
||||
SSL.com: <a href="https://www.ssl.com/article/browsers-and-certificate-validation/">Browsers and Certificate Validation</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @name Disabled TLS certificate check
|
||||
* @description An application that disables TLS certificate checking is more vulnerable to
|
||||
* man-in-the-middle attacks.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @id rust/disabled-certificate-check
|
||||
* @tags security
|
||||
* external/cwe/cwe-295
|
||||
*/
|
||||
|
||||
import rust
|
||||
import codeql.rust.dataflow.DataFlow
|
||||
import codeql.rust.dataflow.TaintTracking
|
||||
import codeql.rust.security.DisabledCertificateCheckExtensions
|
||||
import codeql.rust.Concepts
|
||||
|
||||
/**
|
||||
* A taint configuration for disabled TLS certificate checks.
|
||||
*/
|
||||
module DisabledCertificateCheckConfig implements DataFlow::ConfigSig {
|
||||
import DisabledCertificateCheckExtensions
|
||||
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
// the constant `true`
|
||||
node.asExpr().(BooleanLiteralExpr).getTextValue() = "true"
|
||||
or
|
||||
// a value controlled by a potential attacker
|
||||
node instanceof ActiveThreatModelSource
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) { node instanceof Sink }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
}
|
||||
|
||||
module DisabledCertificateCheckFlow = TaintTracking::Global<DisabledCertificateCheckConfig>;
|
||||
|
||||
import DisabledCertificateCheckFlow::PathGraph
|
||||
|
||||
from
|
||||
DisabledCertificateCheckFlow::PathNode sourceNode, DisabledCertificateCheckFlow::PathNode sinkNode
|
||||
where DisabledCertificateCheckFlow::flowPath(sourceNode, sinkNode)
|
||||
select sinkNode.getNode(), sourceNode, sinkNode,
|
||||
"Disabling TLS certificate validation can expose the application to man-in-the-middle attacks."
|
||||
@@ -0,0 +1,6 @@
|
||||
// BAD: Disabling certificate validation in Rust
|
||||
|
||||
let _client = reqwest::Client::builder()
|
||||
.danger_accept_invalid_certs(true) // disables certificate validation
|
||||
.build()
|
||||
.unwrap();
|
||||
@@ -0,0 +1,10 @@
|
||||
// GOOD: Certificate validation is enabled (default)
|
||||
|
||||
let _client = reqwest::Client::builder()
|
||||
.danger_accept_invalid_certs(false) // certificate validation enabled explicitly
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let _client = native_tls::TlsConnector::builder() // certificate validation enabled by default
|
||||
.build()
|
||||
.unwrap();
|
||||
@@ -22,6 +22,7 @@ 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.DisabledCertificateCheckExtensions
|
||||
private import codeql.rust.security.HardcodedCryptographicValueExtensions
|
||||
private import codeql.rust.security.InsecureCookieExtensions
|
||||
private import codeql.rust.security.LogInjectionExtensions
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
| test.rs:17:31:17:38 | ...::read | Flow source 'FileSource' of type file (DEFAULT). |
|
||||
| test.rs:22:22:22:39 | ...::read_to_string | Flow source 'FileSource' of type file (DEFAULT). |
|
||||
| test.rs:22:22:22:39 | ...::read_to_string | Flow source 'FileSource' of type file (DEFAULT). |
|
||||
| test.rs:26:18:26:29 | ...::read_dir | Flow source 'FileSource' of type file (DEFAULT). |
|
||||
| test.rs:29:22:29:25 | path | Flow source 'FileSource' of type file (DEFAULT). |
|
||||
| test.rs:43:27:43:35 | file_name | Flow source 'FileSource' of type file (DEFAULT). |
|
||||
| test.rs:51:52:51:59 | read_dir | Flow source 'FileSource' of type file (DEFAULT). |
|
||||
| test.rs:54:22:54:25 | path | Flow source 'FileSource' of type file (DEFAULT). |
|
||||
| test.rs:55:27:55:35 | file_name | Flow source 'FileSource' of type file (DEFAULT). |
|
||||
| test.rs:65:22:65:34 | ...::read_link | Flow source 'FileSource' of type file (DEFAULT). |
|
||||
|
||||
@@ -23,7 +23,7 @@ fn test_fs() -> Result<(), Box<dyn std::error::Error>> {
|
||||
sink(buffer); // $ hasTaintFlow="file.txt"
|
||||
}
|
||||
|
||||
for entry in fs::read_dir("directory")? {
|
||||
for entry in fs::read_dir("directory")? { // $ Alert[rust/summary/taint-sources]
|
||||
let e = entry?;
|
||||
|
||||
let path = e.path(); // $ Alert[rust/summary/taint-sources]
|
||||
@@ -48,7 +48,7 @@ fn test_fs() -> Result<(), Box<dyn std::error::Error>> {
|
||||
sink(file_name.clone().as_encoded_bytes()); // $ MISSING: hasTaintFlow
|
||||
sink(file_name); // $ hasTaintFlow
|
||||
}
|
||||
for entry in std::path::Path::new("directory").read_dir()? {
|
||||
for entry in std::path::Path::new("directory").read_dir()? { // $ Alert[rust/summary/taint-sources]
|
||||
let e = entry?;
|
||||
|
||||
let path = e.path(); // $ Alert[rust/summary/taint-sources]
|
||||
|
||||
1596
rust/ql/test/query-tests/security/CWE-295/Cargo.lock
generated
Normal file
1596
rust/ql/test/query-tests/security/CWE-295/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,129 @@
|
||||
#select
|
||||
| main.rs:4:4:4:30 | danger_accept_invalid_certs | main.rs:4:32:4:35 | true | main.rs:4:4:4:30 | danger_accept_invalid_certs | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:9:4:9:34 | danger_accept_invalid_hostnames | main.rs:9:36:9:39 | true | main.rs:9:4:9:34 | danger_accept_invalid_hostnames | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:16:4:16:30 | danger_accept_invalid_certs | main.rs:16:32:16:35 | true | main.rs:16:4:16:30 | danger_accept_invalid_certs | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:17:4:17:34 | danger_accept_invalid_hostnames | main.rs:17:36:17:39 | true | main.rs:17:4:17:34 | danger_accept_invalid_hostnames | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:37:4:37:30 | danger_accept_invalid_certs | main.rs:37:32:37:35 | true | main.rs:37:4:37:30 | danger_accept_invalid_certs | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:42:4:42:34 | danger_accept_invalid_hostnames | main.rs:42:36:42:39 | true | main.rs:42:4:42:34 | danger_accept_invalid_hostnames | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:47:4:47:30 | danger_accept_invalid_certs | main.rs:47:32:47:35 | true | main.rs:47:4:47:30 | danger_accept_invalid_certs | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:48:4:48:34 | danger_accept_invalid_hostnames | main.rs:48:36:48:39 | true | main.rs:48:4:48:34 | danger_accept_invalid_hostnames | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:55:4:55:30 | danger_accept_invalid_certs | main.rs:55:32:55:35 | true | main.rs:55:4:55:30 | danger_accept_invalid_certs | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:56:4:56:34 | danger_accept_invalid_hostnames | main.rs:56:36:56:39 | true | main.rs:56:4:56:34 | danger_accept_invalid_hostnames | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:83:4:83:30 | danger_accept_invalid_certs | main.rs:74:15:74:18 | true | main.rs:83:4:83:30 | danger_accept_invalid_certs | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:88:4:88:30 | danger_accept_invalid_certs | main.rs:75:22:75:25 | true | main.rs:88:4:88:30 | danger_accept_invalid_certs | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:93:4:93:30 | danger_accept_invalid_certs | main.rs:154:17:154:20 | true | main.rs:93:4:93:30 | danger_accept_invalid_certs | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:109:4:109:34 | danger_accept_invalid_hostnames | main.rs:107:17:107:31 | ...::exists | main.rs:109:4:109:34 | danger_accept_invalid_hostnames | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:115:4:115:34 | danger_accept_invalid_hostnames | main.rs:113:43:113:50 | metadata | main.rs:115:4:115:34 | danger_accept_invalid_hostnames | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:121:4:121:34 | danger_accept_invalid_hostnames | main.rs:119:11:119:27 | ...::metadata | main.rs:121:4:121:34 | danger_accept_invalid_hostnames | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
| main.rs:146:4:146:34 | danger_accept_invalid_hostnames | main.rs:144:39:144:42 | true | main.rs:146:4:146:34 | danger_accept_invalid_hostnames | Disabling TLS certificate validation can expose the application to man-in-the-middle attacks. |
|
||||
edges
|
||||
| main.rs:4:32:4:35 | true | main.rs:4:4:4:30 | danger_accept_invalid_certs | provenance | MaD:1 Sink:MaD:1 |
|
||||
| main.rs:9:36:9:39 | true | main.rs:9:4:9:34 | danger_accept_invalid_hostnames | provenance | MaD:2 Sink:MaD:2 |
|
||||
| main.rs:16:32:16:35 | true | main.rs:16:4:16:30 | danger_accept_invalid_certs | provenance | MaD:1 Sink:MaD:1 |
|
||||
| main.rs:17:36:17:39 | true | main.rs:17:4:17:34 | danger_accept_invalid_hostnames | provenance | MaD:2 Sink:MaD:2 |
|
||||
| main.rs:37:32:37:35 | true | main.rs:37:4:37:30 | danger_accept_invalid_certs | provenance | MaD:3 Sink:MaD:3 |
|
||||
| main.rs:42:36:42:39 | true | main.rs:42:4:42:34 | danger_accept_invalid_hostnames | provenance | MaD:6 Sink:MaD:6 |
|
||||
| main.rs:47:32:47:35 | true | main.rs:47:4:47:30 | danger_accept_invalid_certs | provenance | MaD:3 Sink:MaD:3 |
|
||||
| main.rs:48:36:48:39 | true | main.rs:48:4:48:34 | danger_accept_invalid_hostnames | provenance | MaD:4 Sink:MaD:4 |
|
||||
| main.rs:55:32:55:35 | true | main.rs:55:4:55:30 | danger_accept_invalid_certs | provenance | MaD:5 Sink:MaD:5 |
|
||||
| main.rs:56:36:56:39 | true | main.rs:56:4:56:34 | danger_accept_invalid_hostnames | provenance | MaD:6 Sink:MaD:6 |
|
||||
| main.rs:73:19:73:40 | ...: bool | main.rs:93:32:93:47 | sometimes_global | provenance | |
|
||||
| main.rs:74:6:74:11 | always | main.rs:83:32:83:37 | always | provenance | |
|
||||
| main.rs:74:15:74:18 | true | main.rs:74:6:74:11 | always | provenance | |
|
||||
| main.rs:75:6:75:18 | mut sometimes | main.rs:88:32:88:40 | sometimes | provenance | |
|
||||
| main.rs:75:22:75:25 | true | main.rs:75:6:75:18 | mut sometimes | provenance | |
|
||||
| main.rs:83:32:83:37 | always | main.rs:83:4:83:30 | danger_accept_invalid_certs | provenance | MaD:1 Sink:MaD:1 |
|
||||
| main.rs:88:32:88:40 | sometimes | main.rs:88:4:88:30 | danger_accept_invalid_certs | provenance | MaD:1 Sink:MaD:1 |
|
||||
| main.rs:93:32:93:47 | sometimes_global | main.rs:93:4:93:30 | danger_accept_invalid_certs | provenance | MaD:1 Sink:MaD:1 |
|
||||
| main.rs:107:6:107:7 | b1 | main.rs:109:36:109:37 | b1 | provenance | |
|
||||
| main.rs:107:17:107:31 | ...::exists | main.rs:107:17:107:42 | ...::exists(...) [Ok] | provenance | Src:MaD:8 |
|
||||
| main.rs:107:17:107:42 | ...::exists(...) [Ok] | main.rs:107:17:107:51 | ... .unwrap() | provenance | MaD:10 |
|
||||
| main.rs:107:17:107:51 | ... .unwrap() | main.rs:107:6:107:7 | b1 | provenance | |
|
||||
| main.rs:109:36:109:37 | b1 | main.rs:109:4:109:34 | danger_accept_invalid_hostnames | provenance | MaD:2 Sink:MaD:2 |
|
||||
| main.rs:113:6:113:7 | b2 | main.rs:115:36:115:37 | b2 | provenance | |
|
||||
| main.rs:113:11:113:52 | ... .metadata() [Ok] | main.rs:113:11:113:61 | ... .unwrap() | provenance | MaD:10 |
|
||||
| main.rs:113:11:113:61 | ... .unwrap() | main.rs:113:11:113:71 | ... .is_file() | provenance | MaD:12 |
|
||||
| main.rs:113:11:113:71 | ... .is_file() | main.rs:113:6:113:7 | b2 | provenance | |
|
||||
| main.rs:113:43:113:50 | metadata | main.rs:113:11:113:52 | ... .metadata() [Ok] | provenance | Src:MaD:7 |
|
||||
| main.rs:115:36:115:37 | b2 | main.rs:115:4:115:34 | danger_accept_invalid_hostnames | provenance | MaD:2 Sink:MaD:2 |
|
||||
| main.rs:119:6:119:7 | b3 | main.rs:121:36:121:37 | b3 | provenance | |
|
||||
| main.rs:119:11:119:27 | ...::metadata | main.rs:119:11:119:38 | ...::metadata(...) [Ok] | provenance | Src:MaD:9 |
|
||||
| main.rs:119:11:119:38 | ...::metadata(...) [Ok] | main.rs:119:11:119:47 | ... .unwrap() | provenance | MaD:10 |
|
||||
| main.rs:119:11:119:47 | ... .unwrap() | main.rs:119:11:119:56 | ... .is_dir() | provenance | MaD:11 |
|
||||
| main.rs:119:11:119:56 | ... .is_dir() | main.rs:119:6:119:7 | b3 | provenance | |
|
||||
| main.rs:121:36:121:37 | b3 | main.rs:121:4:121:34 | danger_accept_invalid_hostnames | provenance | MaD:2 Sink:MaD:2 |
|
||||
| main.rs:144:6:144:7 | b6 | main.rs:146:36:146:37 | b6 | provenance | |
|
||||
| main.rs:144:39:144:42 | true | main.rs:144:6:144:7 | b6 | provenance | |
|
||||
| main.rs:146:36:146:37 | b6 | main.rs:146:4:146:34 | danger_accept_invalid_hostnames | provenance | MaD:2 Sink:MaD:2 |
|
||||
| main.rs:154:17:154:20 | true | main.rs:73:19:73:40 | ...: bool | provenance | |
|
||||
models
|
||||
| 1 | Sink: <native_tls::TlsConnectorBuilder>::danger_accept_invalid_certs; Argument[0]; disable-certificate |
|
||||
| 2 | Sink: <native_tls::TlsConnectorBuilder>::danger_accept_invalid_hostnames; Argument[0]; disable-certificate |
|
||||
| 3 | Sink: <reqwest::async_impl::client::ClientBuilder>::danger_accept_invalid_certs; Argument[0]; disable-certificate |
|
||||
| 4 | Sink: <reqwest::async_impl::client::ClientBuilder>::danger_accept_invalid_hostnames; Argument[0]; disable-certificate |
|
||||
| 5 | Sink: <reqwest::blocking::client::ClientBuilder>::danger_accept_invalid_certs; Argument[0]; disable-certificate |
|
||||
| 6 | Sink: <reqwest::blocking::client::ClientBuilder>::danger_accept_invalid_hostnames; Argument[0]; disable-certificate |
|
||||
| 7 | Source: <std::path::Path>::metadata; ReturnValue.Field[core::result::Result::Ok(0)]; file |
|
||||
| 8 | Source: std::fs::exists; ReturnValue.Field[core::result::Result::Ok(0)]; file |
|
||||
| 9 | Source: std::fs::metadata; ReturnValue.Field[core::result::Result::Ok(0)]; file |
|
||||
| 10 | Summary: <core::result::Result>::unwrap; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value |
|
||||
| 11 | Summary: <std::fs::Metadata>::is_dir; Argument[self].Reference; ReturnValue; taint |
|
||||
| 12 | Summary: <std::fs::Metadata>::is_file; Argument[self].Reference; ReturnValue; taint |
|
||||
nodes
|
||||
| main.rs:4:4:4:30 | danger_accept_invalid_certs | semmle.label | danger_accept_invalid_certs |
|
||||
| main.rs:4:32:4:35 | true | semmle.label | true |
|
||||
| main.rs:9:4:9:34 | danger_accept_invalid_hostnames | semmle.label | danger_accept_invalid_hostnames |
|
||||
| main.rs:9:36:9:39 | true | semmle.label | true |
|
||||
| main.rs:16:4:16:30 | danger_accept_invalid_certs | semmle.label | danger_accept_invalid_certs |
|
||||
| main.rs:16:32:16:35 | true | semmle.label | true |
|
||||
| main.rs:17:4:17:34 | danger_accept_invalid_hostnames | semmle.label | danger_accept_invalid_hostnames |
|
||||
| main.rs:17:36:17:39 | true | semmle.label | true |
|
||||
| main.rs:37:4:37:30 | danger_accept_invalid_certs | semmle.label | danger_accept_invalid_certs |
|
||||
| main.rs:37:32:37:35 | true | semmle.label | true |
|
||||
| main.rs:42:4:42:34 | danger_accept_invalid_hostnames | semmle.label | danger_accept_invalid_hostnames |
|
||||
| main.rs:42:36:42:39 | true | semmle.label | true |
|
||||
| main.rs:47:4:47:30 | danger_accept_invalid_certs | semmle.label | danger_accept_invalid_certs |
|
||||
| main.rs:47:32:47:35 | true | semmle.label | true |
|
||||
| main.rs:48:4:48:34 | danger_accept_invalid_hostnames | semmle.label | danger_accept_invalid_hostnames |
|
||||
| main.rs:48:36:48:39 | true | semmle.label | true |
|
||||
| main.rs:55:4:55:30 | danger_accept_invalid_certs | semmle.label | danger_accept_invalid_certs |
|
||||
| main.rs:55:32:55:35 | true | semmle.label | true |
|
||||
| main.rs:56:4:56:34 | danger_accept_invalid_hostnames | semmle.label | danger_accept_invalid_hostnames |
|
||||
| main.rs:56:36:56:39 | true | semmle.label | true |
|
||||
| main.rs:73:19:73:40 | ...: bool | semmle.label | ...: bool |
|
||||
| main.rs:74:6:74:11 | always | semmle.label | always |
|
||||
| main.rs:74:15:74:18 | true | semmle.label | true |
|
||||
| main.rs:75:6:75:18 | mut sometimes | semmle.label | mut sometimes |
|
||||
| main.rs:75:22:75:25 | true | semmle.label | true |
|
||||
| main.rs:83:4:83:30 | danger_accept_invalid_certs | semmle.label | danger_accept_invalid_certs |
|
||||
| main.rs:83:32:83:37 | always | semmle.label | always |
|
||||
| main.rs:88:4:88:30 | danger_accept_invalid_certs | semmle.label | danger_accept_invalid_certs |
|
||||
| main.rs:88:32:88:40 | sometimes | semmle.label | sometimes |
|
||||
| main.rs:93:4:93:30 | danger_accept_invalid_certs | semmle.label | danger_accept_invalid_certs |
|
||||
| main.rs:93:32:93:47 | sometimes_global | semmle.label | sometimes_global |
|
||||
| main.rs:107:6:107:7 | b1 | semmle.label | b1 |
|
||||
| main.rs:107:17:107:31 | ...::exists | semmle.label | ...::exists |
|
||||
| main.rs:107:17:107:42 | ...::exists(...) [Ok] | semmle.label | ...::exists(...) [Ok] |
|
||||
| main.rs:107:17:107:51 | ... .unwrap() | semmle.label | ... .unwrap() |
|
||||
| main.rs:109:4:109:34 | danger_accept_invalid_hostnames | semmle.label | danger_accept_invalid_hostnames |
|
||||
| main.rs:109:36:109:37 | b1 | semmle.label | b1 |
|
||||
| main.rs:113:6:113:7 | b2 | semmle.label | b2 |
|
||||
| main.rs:113:11:113:52 | ... .metadata() [Ok] | semmle.label | ... .metadata() [Ok] |
|
||||
| main.rs:113:11:113:61 | ... .unwrap() | semmle.label | ... .unwrap() |
|
||||
| main.rs:113:11:113:71 | ... .is_file() | semmle.label | ... .is_file() |
|
||||
| main.rs:113:43:113:50 | metadata | semmle.label | metadata |
|
||||
| main.rs:115:4:115:34 | danger_accept_invalid_hostnames | semmle.label | danger_accept_invalid_hostnames |
|
||||
| main.rs:115:36:115:37 | b2 | semmle.label | b2 |
|
||||
| main.rs:119:6:119:7 | b3 | semmle.label | b3 |
|
||||
| main.rs:119:11:119:27 | ...::metadata | semmle.label | ...::metadata |
|
||||
| main.rs:119:11:119:38 | ...::metadata(...) [Ok] | semmle.label | ...::metadata(...) [Ok] |
|
||||
| main.rs:119:11:119:47 | ... .unwrap() | semmle.label | ... .unwrap() |
|
||||
| main.rs:119:11:119:56 | ... .is_dir() | semmle.label | ... .is_dir() |
|
||||
| main.rs:121:4:121:34 | danger_accept_invalid_hostnames | semmle.label | danger_accept_invalid_hostnames |
|
||||
| main.rs:121:36:121:37 | b3 | semmle.label | b3 |
|
||||
| main.rs:144:6:144:7 | b6 | semmle.label | b6 |
|
||||
| main.rs:144:39:144:42 | true | semmle.label | true |
|
||||
| main.rs:146:4:146:34 | danger_accept_invalid_hostnames | semmle.label | danger_accept_invalid_hostnames |
|
||||
| main.rs:146:36:146:37 | b6 | semmle.label | b6 |
|
||||
| main.rs:154:17:154:20 | true | semmle.label | true |
|
||||
subpaths
|
||||
@@ -0,0 +1,4 @@
|
||||
query: queries/security/CWE-295/DisabledCertificateCheck.ql
|
||||
postprocess:
|
||||
- utils/test/PrettyPrintModels.ql
|
||||
- utils/test/InlineExpectationsTestQuery.ql
|
||||
157
rust/ql/test/query-tests/security/CWE-295/main.rs
Normal file
157
rust/ql/test/query-tests/security/CWE-295/main.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
fn test_native_tls() {
|
||||
// unsafe
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_certs(true) // $ Alert[rust/disabled-certificate-check]
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_hostnames(true) // $ Alert[rust/disabled-certificate-check]
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.min_protocol_version(Some(native_tls::Protocol::Tlsv12))
|
||||
.use_sni(true)
|
||||
.danger_accept_invalid_certs(true) // $ Alert[rust/disabled-certificate-check]
|
||||
.danger_accept_invalid_hostnames(true) // $ Alert[rust/disabled-certificate-check]
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// safe
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_certs(false) // good
|
||||
.danger_accept_invalid_hostnames(false) // good
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// default (safe)
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn test_reqwest() {
|
||||
// unsafe
|
||||
let _client = reqwest::Client::builder()
|
||||
.danger_accept_invalid_certs(true) // $ Alert[rust/disabled-certificate-check]
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let _client = reqwest::blocking::ClientBuilder::new()
|
||||
.danger_accept_invalid_hostnames(true) // $ Alert[rust/disabled-certificate-check]
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let _client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true) // $ Alert[rust/disabled-certificate-check]
|
||||
.danger_accept_invalid_hostnames(true) // $ Alert[rust/disabled-certificate-check]
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let _client = reqwest::blocking::Client::builder()
|
||||
.tcp_keepalive(std::time::Duration::from_secs(30))
|
||||
.https_only(true)
|
||||
.danger_accept_invalid_certs(true) // $ Alert[rust/disabled-certificate-check]
|
||||
.danger_accept_invalid_hostnames(true) // $ Alert[rust/disabled-certificate-check]
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// safe
|
||||
let _client = reqwest::blocking::Client::builder()
|
||||
.danger_accept_invalid_certs(false) // good
|
||||
.danger_accept_invalid_hostnames(false) // good
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// default (safe)
|
||||
let _client = reqwest::blocking::Client::builder()
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn test_data_flow(sometimes_global: bool) {
|
||||
let always = true; // $ Source=always
|
||||
let mut sometimes = true; // $ Source=sometimes
|
||||
let never = false;
|
||||
|
||||
if rand::random_range(0 .. 2) == 0 {
|
||||
sometimes = false;
|
||||
}
|
||||
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_certs(always) // $ Alert[rust/disabled-certificate-check]=always
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_certs(sometimes) // $ Alert[rust/disabled-certificate-check]=sometimes
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_certs(sometimes_global) // $ Alert[rust/disabled-certificate-check]=arg
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_certs(never) // good
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn test_threat_model_source() {
|
||||
// hostname setting from `fs` functions returning `bool` directly
|
||||
// (these are highly unnatural but serve to create simple tests)
|
||||
|
||||
let b1: bool = std::fs::exists("main.rs").unwrap(); // $ Source=exists
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_hostnames(b1) // $ Alert[rust/disabled-certificate-check]=exists
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let b2 = std::path::Path::new("main.rs").metadata().unwrap().is_file(); // $ Source=is_file
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_hostnames(b2) // $ Alert[rust/disabled-certificate-check]=is_file
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let b3 = std::fs::metadata("main.rs").unwrap().is_dir(); // $ Source=is_dir
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_hostnames(b3) // $ Alert[rust/disabled-certificate-check]=is_dir
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// hostname setting from `stdin`, parsed to `bool`
|
||||
// (these are a little closer to something real)
|
||||
|
||||
let mut input_line = String::new();
|
||||
let input = std::io::stdin();
|
||||
input.read_line(&mut input_line).unwrap();
|
||||
|
||||
let b4: bool = input_line.parse::<bool>().unwrap_or(false);
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_hostnames(b4) // $ MISSING: Alert[rust/disabled-certificate-check]=stdin
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let b5 = std::str::FromStr::from_str(&input_line).unwrap_or(false);
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_hostnames(b5) // $ MISSING: Alert[rust/disabled-certificate-check]=stdin
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let b6 = if (input_line == "true") { true } else { false }; // $ Source=true
|
||||
let _client = native_tls::TlsConnector::builder()
|
||||
.danger_accept_invalid_hostnames(b6) // $ Alert[rust/disabled-certificate-check]=true
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_native_tls();
|
||||
test_reqwest();
|
||||
test_data_flow(true); // $ Source=arg
|
||||
test_data_flow(false);
|
||||
test_threat_model_source();
|
||||
}
|
||||
5
rust/ql/test/query-tests/security/CWE-295/options.yml
Normal file
5
rust/ql/test/query-tests/security/CWE-295/options.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
qltest_cargo_check: true
|
||||
qltest_dependencies:
|
||||
- reqwest = { version = "0.12.9", features = ["blocking"] }
|
||||
- native-tls = { version = "0.2.14" }
|
||||
- rand = { version = "0.9.2" }
|
||||
Reference in New Issue
Block a user