From 459f00ab415e1263c0756a03516e51e9963adc52 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 15 Sep 2025 11:25:11 +0000
Subject: [PATCH 01/12] Initial plan
From e630bf86bdd0f9a678b234f04f11f4ba883b5571 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 15 Sep 2025 11:44:05 +0000
Subject: [PATCH 02/12] Implement Rust non-HTTPS URL query (CWE-319)
Co-authored-by: geoffw0 <40627776+geoffw0@users.noreply.github.com>
---
.../rust-code-scanning.qls.expected | 1 +
.../rust-security-and-quality.qls.expected | 1 +
.../rust-security-extended.qls.expected | 1 +
.../rust/security/UseOfHttpExtensions.qll | 60 +
.../change-notes/2025-09-15-non-https-url.md | 4 +
.../queries/security/CWE-319/UseOfHttp.qhelp | 48 +
.../src/queries/security/CWE-319/UseOfHttp.ql | 42 +
.../queries/security/CWE-319/UseOfHttpBad.rs | 10 +
.../queries/security/CWE-319/UseOfHttpGood.rs | 10 +
rust/ql/src/queries/summary/Stats.qll | 1 +
.../query-tests/security/CWE-319/Cargo.lock | 1574 +++++++++++++++++
.../security/CWE-319/UseOfHttp.expected | 78 +
.../security/CWE-319/UseOfHttp.qlref | 4 +
.../test/query-tests/security/CWE-319/main.rs | 65 +
.../query-tests/security/CWE-319/options.yml | 3 +
15 files changed, 1902 insertions(+)
create mode 100644 rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
create mode 100644 rust/ql/src/change-notes/2025-09-15-non-https-url.md
create mode 100644 rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
create mode 100644 rust/ql/src/queries/security/CWE-319/UseOfHttp.ql
create mode 100644 rust/ql/src/queries/security/CWE-319/UseOfHttpBad.rs
create mode 100644 rust/ql/src/queries/security/CWE-319/UseOfHttpGood.rs
create mode 100644 rust/ql/test/query-tests/security/CWE-319/Cargo.lock
create mode 100644 rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
create mode 100644 rust/ql/test/query-tests/security/CWE-319/UseOfHttp.qlref
create mode 100644 rust/ql/test/query-tests/security/CWE-319/main.rs
create mode 100644 rust/ql/test/query-tests/security/CWE-319/options.yml
diff --git a/rust/ql/integration-tests/query-suite/rust-code-scanning.qls.expected b/rust/ql/integration-tests/query-suite/rust-code-scanning.qls.expected
index b601905e6a30..1b8e1015a1ff 100644
--- a/rust/ql/integration-tests/query-suite/rust-code-scanning.qls.expected
+++ b/rust/ql/integration-tests/query-suite/rust-code-scanning.qls.expected
@@ -14,6 +14,7 @@ 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-319/UseOfHttp.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
diff --git a/rust/ql/integration-tests/query-suite/rust-security-and-quality.qls.expected b/rust/ql/integration-tests/query-suite/rust-security-and-quality.qls.expected
index 074cb2ec8888..a2d2e2b820c5 100644
--- a/rust/ql/integration-tests/query-suite/rust-security-and-quality.qls.expected
+++ b/rust/ql/integration-tests/query-suite/rust-security-and-quality.qls.expected
@@ -15,6 +15,7 @@ ql/rust/ql/src/queries/security/CWE-117/LogInjection.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-319/UseOfHttp.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
diff --git a/rust/ql/integration-tests/query-suite/rust-security-extended.qls.expected b/rust/ql/integration-tests/query-suite/rust-security-extended.qls.expected
index 38846e281eb8..9000990ad841 100644
--- a/rust/ql/integration-tests/query-suite/rust-security-extended.qls.expected
+++ b/rust/ql/integration-tests/query-suite/rust-security-extended.qls.expected
@@ -15,6 +15,7 @@ ql/rust/ql/src/queries/security/CWE-117/LogInjection.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-319/UseOfHttp.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
diff --git a/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll b/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
new file mode 100644
index 000000000000..026880785b6c
--- /dev/null
+++ b/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
@@ -0,0 +1,60 @@
+/**
+ * Provides classes and predicates for reasoning about the use of
+ * non-HTTPS URLs in Rust code.
+ */
+
+import rust
+private import codeql.rust.dataflow.DataFlow
+private import codeql.rust.dataflow.FlowSink
+private import codeql.rust.elements.LiteralExprExt
+private import codeql.rust.Concepts
+
+/**
+ * Provides default sources, sinks and barriers for detecting use of
+ * non-HTTPS URLs, as well as extension points for adding your own.
+ */
+module UseOfHttp {
+ /**
+ * A data flow source for use of non-HTTPS URLs.
+ */
+ abstract class Source extends DataFlow::Node { }
+
+ /**
+ * A data flow sink for use of non-HTTPS URLs.
+ */
+ abstract class Sink extends QuerySink::Range {
+ override string getSinkType() { result = "UseOfHttp" }
+ }
+
+ /**
+ * A barrier for use of non-HTTPS URLs.
+ */
+ abstract class Barrier extends DataFlow::Node { }
+
+ /**
+ * A string containing an HTTP URL.
+ */
+ class HttpStringLiteral extends StringLiteralExpr {
+ HttpStringLiteral() {
+ exists(string s | this.getTextValue() = s |
+ // Match HTTP URLs that are not private/local
+ s.regexpMatch("\"http://.*\"") and
+ not s.regexpMatch("\"http://(localhost|127\\.0\\.0\\.1|192\\.168\\.[0-9]+\\.[0-9]+|10\\.[0-9]+\\.[0-9]+\\.[0-9]+|172\\.16\\.[0-9]+\\.[0-9]+|\\[::1\\]|\\[0:0:0:0:0:0:0:1\\]).*\"")
+ )
+ }
+ }
+
+ /**
+ * An HTTP string literal as a source.
+ */
+ private class HttpStringLiteralAsSource extends Source {
+ HttpStringLiteralAsSource() { this.asExpr().getExpr() instanceof HttpStringLiteral }
+ }
+
+ /**
+ * A sink for use of HTTP URLs from model data.
+ */
+ private class ModelsAsDataSink extends Sink {
+ ModelsAsDataSink() { sinkNode(this, "request-url") }
+ }
+}
diff --git a/rust/ql/src/change-notes/2025-09-15-non-https-url.md b/rust/ql/src/change-notes/2025-09-15-non-https-url.md
new file mode 100644
index 000000000000..c4ab664f7324
--- /dev/null
+++ b/rust/ql/src/change-notes/2025-09-15-non-https-url.md
@@ -0,0 +1,4 @@
+---
+category: newQuery
+---
+* Added a new query, `rust/non-https-url`, for detecting the use of non-HTTPS URLs that can be intercepted by third parties.
\ No newline at end of file
diff --git a/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp b/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
new file mode 100644
index 000000000000..a8ca1d9c7c7e
--- /dev/null
+++ b/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
@@ -0,0 +1,48 @@
+
+
+
+
+Constructing URLs with the HTTP protocol can lead to unsecured connections.
+
+Furthermore, constructing URLs with the HTTP protocol can create problems if other parts of the
+code expect HTTPS URLs. A typical pattern is to use libraries that expect secure connections,
+which may fail or fall back to insecure behavior when provided with HTTP URLs instead of HTTPS URLs.
+
+
+
+
+When you construct a URL for network requests, ensure that you use an HTTPS URL rather than an HTTP URL.
+Then, any connections that are made using that URL are secure SSL/TLS connections.
+
+
+
+
+The following example shows two ways of making a network request using a URL. When the request is
+made using an HTTP URL rather than an HTTPS URL, the connection is unsecured and can be intercepted
+by attackers. When the request is made using an HTTPS URL, the connection is a secure SSL/TLS connection.
+
+
+
+A better approach is to use HTTPS:
+
+
+
+
+
+
+
+OWASP:
+Transport Layer Protection Cheat Sheet.
+
+
+OWASP Top 10:
+A08:2021 - Software and Data Integrity Failures.
+
+Rust reqwest documentation:
+reqwest crate.
+
+
+
+
\ No newline at end of file
diff --git a/rust/ql/src/queries/security/CWE-319/UseOfHttp.ql b/rust/ql/src/queries/security/CWE-319/UseOfHttp.ql
new file mode 100644
index 000000000000..4a464d90bbe4
--- /dev/null
+++ b/rust/ql/src/queries/security/CWE-319/UseOfHttp.ql
@@ -0,0 +1,42 @@
+/**
+ * @name Failure to use HTTPS URLs
+ * @description Non-HTTPS connections can be intercepted by third parties.
+ * @kind path-problem
+ * @problem.severity warning
+ * @security-severity 8.1
+ * @precision high
+ * @id rust/non-https-url
+ * @tags security
+ * external/cwe/cwe-319
+ * external/cwe/cwe-345
+ */
+
+import rust
+import codeql.rust.dataflow.DataFlow
+import codeql.rust.dataflow.TaintTracking
+import codeql.rust.security.UseOfHttpExtensions
+
+/**
+ * A taint configuration for HTTP URL strings that flow to URL-using sinks.
+ */
+module UseOfHttpConfig implements DataFlow::ConfigSig {
+ import UseOfHttp
+
+ 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 observeDiffInformedIncrementalMode() { any() }
+}
+
+module UseOfHttpFlow = TaintTracking::Global;
+
+import UseOfHttpFlow::PathGraph
+
+from UseOfHttpFlow::PathNode sourceNode, UseOfHttpFlow::PathNode sinkNode
+where UseOfHttpFlow::flowPath(sourceNode, sinkNode)
+select sinkNode.getNode(), sourceNode, sinkNode,
+ "This URL may be constructed with the HTTP protocol, from $@.", sourceNode.getNode(),
+ "this HTTP URL"
diff --git a/rust/ql/src/queries/security/CWE-319/UseOfHttpBad.rs b/rust/ql/src/queries/security/CWE-319/UseOfHttpBad.rs
new file mode 100644
index 000000000000..ada466cae5c0
--- /dev/null
+++ b/rust/ql/src/queries/security/CWE-319/UseOfHttpBad.rs
@@ -0,0 +1,10 @@
+// BAD: Using HTTP URL which can be intercepted
+use reqwest;
+
+fn main() {
+ let url = "http://example.com/sensitive-data";
+
+ // This makes an insecure HTTP request that can be intercepted
+ let response = reqwest::blocking::get(url).unwrap();
+ println!("Response: {}", response.text().unwrap());
+}
\ No newline at end of file
diff --git a/rust/ql/src/queries/security/CWE-319/UseOfHttpGood.rs b/rust/ql/src/queries/security/CWE-319/UseOfHttpGood.rs
new file mode 100644
index 000000000000..22b94235fa17
--- /dev/null
+++ b/rust/ql/src/queries/security/CWE-319/UseOfHttpGood.rs
@@ -0,0 +1,10 @@
+// GOOD: Using HTTPS URL which provides encryption
+use reqwest;
+
+fn main() {
+ let url = "https://example.com/sensitive-data";
+
+ // This makes a secure HTTPS request that is encrypted
+ let response = reqwest::blocking::get(url).unwrap();
+ println!("Response: {}", response.text().unwrap());
+}
\ No newline at end of file
diff --git a/rust/ql/src/queries/summary/Stats.qll b/rust/ql/src/queries/summary/Stats.qll
index 7a1de4f13144..d49e1fdde5d3 100644
--- a/rust/ql/src/queries/summary/Stats.qll
+++ b/rust/ql/src/queries/summary/Stats.qll
@@ -27,6 +27,7 @@ private import codeql.rust.security.LogInjectionExtensions
private import codeql.rust.security.SqlInjectionExtensions
private import codeql.rust.security.TaintedPathExtensions
private import codeql.rust.security.UncontrolledAllocationSizeExtensions
+private import codeql.rust.security.UseOfHttpExtensions
private import codeql.rust.security.WeakSensitiveDataHashingExtensions
private import codeql.rust.security.HardcodedCryptographicValueExtensions
diff --git a/rust/ql/test/query-tests/security/CWE-319/Cargo.lock b/rust/ql/test/query-tests/security/CWE-319/Cargo.lock
new file mode 100644
index 000000000000..ad4b5cebd226
--- /dev/null
+++ b/rust/ql/test/query-tests/security/CWE-319/Cargo.lock
@@ -0,0 +1,1574 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "backtrace"
+version = "0.3.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets",
+]
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bitflags"
+version = "2.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
+
+[[package]]
+name = "bumpalo"
+version = "3.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+
+[[package]]
+name = "bytes"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+
+[[package]]
+name = "cc"
+version = "1.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.0",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi 0.14.5+wasi-0.2.4",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "h2"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+
+[[package]]
+name = "http"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "hyper"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "pin-utils",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
+dependencies = [
+ "bytes",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
+ "pin-project-lite",
+ "socket2",
+ "system-configuration",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "windows-registry",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
+
+[[package]]
+name = "icu_properties"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "potential_utf",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
+
+[[package]]
+name = "icu_provider"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "io-uring"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
+
+[[package]]
+name = "iri-string"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "js-sys"
+version = "0.3.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.175"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
+
+[[package]]
+name = "litemap"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
+
+[[package]]
+name = "log"
+version = "0.4.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
+
+[[package]]
+name = "memchr"
+version = "2.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
+dependencies = [
+ "libc",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "native-tls"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
+dependencies = [
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "openssl"
+version = "0.10.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "potential_utf"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "reqwest"
+version = "0.12.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb"
+dependencies = [
+ "base64",
+ "bytes",
+ "encoding_rs",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-tls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "mime",
+ "native-tls",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-native-tls",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.16",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
+
+[[package]]
+name = "rustix"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.61.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
+dependencies = [
+ "once_cell",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
+dependencies = [
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5a37813727b78798e53c2bec3f5e8fe12a6d6f8389bf9ca7802add4c9905ad8"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "ryu"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+
+[[package]]
+name = "schannel"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
+dependencies = [
+ "windows-sys 0.61.0",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.223"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a505d71960adde88e293da5cb5eda57093379f64e61cf77bf0e6a63af07a7bac"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.223"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20f57cbd357666aa7b3ac84a90b4ea328f1d4ddb6772b430caa5d9e1309bb9e9"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.223"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d428d07faf17e306e699ec1e91996e5a165ba5d6bce5b5155173e91a8a01a56"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.145"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "slab"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "socket2"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "2.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "system-configuration"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53"
+dependencies = [
+ "fastrand",
+ "getrandom 0.3.3",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.61.0",
+]
+
+[[package]]
+name = "test"
+version = "0.0.1"
+dependencies = [
+ "reqwest",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tokio"
+version = "1.47.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "io-uring",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "slab",
+ "socket2",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tower"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "iri-string",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "url"
+version = "2.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasi"
+version = "0.14.5+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4"
+dependencies = [
+ "wasip2",
+]
+
+[[package]]
+name = "wasip2"
+version = "1.0.0+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
+
+[[package]]
+name = "windows-link"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
+
+[[package]]
+name = "windows-registry"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
+dependencies = [
+ "windows-link 0.1.3",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
+dependencies = [
+ "windows-link 0.2.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "wit-bindgen"
+version = "0.45.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36"
+
+[[package]]
+name = "writeable"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
+
+[[package]]
+name = "yoke"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+
+[[package]]
+name = "zerotrie"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
new file mode 100644
index 000000000000..53cc8606cc82
--- /dev/null
+++ b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
@@ -0,0 +1,78 @@
+#select
+| main.rs:12:22:12:43 | ...::get | main.rs:12:45:12:68 | "http://example.com/api" | main.rs:12:22:12:43 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:12:45:12:68 | "http://example.com/api" | this HTTP URL |
+| main.rs:13:22:13:43 | ...::get | main.rs:13:45:13:73 | "http://api.example.com/data" | main.rs:13:22:13:43 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:13:45:13:73 | "http://api.example.com/data" | this HTTP URL |
+| main.rs:25:21:25:42 | ...::get | main.rs:22:20:22:39 | "http://example.com" | main.rs:25:21:25:42 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:22:20:22:39 | "http://example.com" | this HTTP URL |
+| main.rs:36:30:36:51 | ...::get | main.rs:33:20:33:28 | "http://" | main.rs:36:30:36:51 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:33:20:33:28 | "http://" | this HTTP URL |
+| main.rs:60:21:60:42 | ...::get | main.rs:59:15:59:49 | "http://example.com/sensitive-... | main.rs:60:21:60:42 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:59:15:59:49 | "http://example.com/sensitive-... | this HTTP URL |
+edges
+| main.rs:12:45:12:68 | "http://example.com/api" | main.rs:12:22:12:43 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:13:45:13:73 | "http://api.example.com/data" | main.rs:13:22:13:43 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:22:9:22:16 | base_url | main.rs:24:28:24:53 | MacroExpr | provenance | |
+| main.rs:22:20:22:39 | "http://example.com" | main.rs:22:9:22:16 | base_url | provenance | |
+| main.rs:24:9:24:16 | full_url | main.rs:25:45:25:52 | full_url | provenance | |
+| main.rs:24:20:24:26 | res | main.rs:24:28:24:53 | { ... } | provenance | |
+| main.rs:24:28:24:53 | ...::format(...) | main.rs:24:20:24:26 | res | provenance | |
+| main.rs:24:28:24:53 | ...::must_use(...) | main.rs:24:9:24:16 | full_url | provenance | |
+| main.rs:24:28:24:53 | MacroExpr | main.rs:24:28:24:53 | ...::format(...) | provenance | MaD:2 |
+| main.rs:24:28:24:53 | { ... } | main.rs:24:28:24:53 | ...::must_use(...) | provenance | MaD:3 |
+| main.rs:25:44:25:52 | &full_url [&ref] | main.rs:25:21:25:42 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:25:45:25:52 | full_url | main.rs:25:44:25:52 | &full_url [&ref] | provenance | |
+| main.rs:33:9:33:16 | protocol | main.rs:35:32:35:53 | MacroExpr | provenance | |
+| main.rs:33:20:33:28 | "http://" | main.rs:33:9:33:16 | protocol | provenance | |
+| main.rs:35:9:35:20 | insecure_url | main.rs:36:54:36:65 | insecure_url | provenance | |
+| main.rs:35:24:35:30 | res | main.rs:35:32:35:53 | { ... } | provenance | |
+| main.rs:35:32:35:53 | ...::format(...) | main.rs:35:24:35:30 | res | provenance | |
+| main.rs:35:32:35:53 | ...::must_use(...) | main.rs:35:9:35:20 | insecure_url | provenance | |
+| main.rs:35:32:35:53 | MacroExpr | main.rs:35:32:35:53 | ...::format(...) | provenance | MaD:2 |
+| main.rs:35:32:35:53 | { ... } | main.rs:35:32:35:53 | ...::must_use(...) | provenance | MaD:3 |
+| main.rs:36:53:36:65 | &insecure_url [&ref] | main.rs:36:30:36:51 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:36:54:36:65 | insecure_url | main.rs:36:53:36:65 | &insecure_url [&ref] | provenance | |
+| main.rs:59:9:59:11 | url | main.rs:60:44:60:46 | url | provenance | |
+| main.rs:59:15:59:49 | "http://example.com/sensitive-... | main.rs:59:9:59:11 | url | provenance | |
+| main.rs:60:44:60:46 | url | main.rs:60:21:60:42 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+models
+| 1 | Sink: reqwest::blocking::get; Argument[0]; request-url |
+| 2 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint |
+| 3 | Summary: core::hint::must_use; Argument[0]; ReturnValue; value |
+nodes
+| main.rs:12:22:12:43 | ...::get | semmle.label | ...::get |
+| main.rs:12:45:12:68 | "http://example.com/api" | semmle.label | "http://example.com/api" |
+| main.rs:13:22:13:43 | ...::get | semmle.label | ...::get |
+| main.rs:13:45:13:73 | "http://api.example.com/data" | semmle.label | "http://api.example.com/data" |
+| main.rs:22:9:22:16 | base_url | semmle.label | base_url |
+| main.rs:22:20:22:39 | "http://example.com" | semmle.label | "http://example.com" |
+| main.rs:24:9:24:16 | full_url | semmle.label | full_url |
+| main.rs:24:20:24:26 | res | semmle.label | res |
+| main.rs:24:28:24:53 | ...::format(...) | semmle.label | ...::format(...) |
+| main.rs:24:28:24:53 | ...::must_use(...) | semmle.label | ...::must_use(...) |
+| main.rs:24:28:24:53 | MacroExpr | semmle.label | MacroExpr |
+| main.rs:24:28:24:53 | { ... } | semmle.label | { ... } |
+| main.rs:25:21:25:42 | ...::get | semmle.label | ...::get |
+| main.rs:25:44:25:52 | &full_url [&ref] | semmle.label | &full_url [&ref] |
+| main.rs:25:45:25:52 | full_url | semmle.label | full_url |
+| main.rs:33:9:33:16 | protocol | semmle.label | protocol |
+| main.rs:33:20:33:28 | "http://" | semmle.label | "http://" |
+| main.rs:35:9:35:20 | insecure_url | semmle.label | insecure_url |
+| main.rs:35:24:35:30 | res | semmle.label | res |
+| main.rs:35:32:35:53 | ...::format(...) | semmle.label | ...::format(...) |
+| main.rs:35:32:35:53 | ...::must_use(...) | semmle.label | ...::must_use(...) |
+| main.rs:35:32:35:53 | MacroExpr | semmle.label | MacroExpr |
+| main.rs:35:32:35:53 | { ... } | semmle.label | { ... } |
+| main.rs:36:30:36:51 | ...::get | semmle.label | ...::get |
+| main.rs:36:53:36:65 | &insecure_url [&ref] | semmle.label | &insecure_url [&ref] |
+| main.rs:36:54:36:65 | insecure_url | semmle.label | insecure_url |
+| main.rs:59:9:59:11 | url | semmle.label | url |
+| main.rs:59:15:59:49 | "http://example.com/sensitive-... | semmle.label | "http://example.com/sensitive-... |
+| main.rs:60:21:60:42 | ...::get | semmle.label | ...::get |
+| main.rs:60:44:60:46 | url | semmle.label | url |
+subpaths
+testFailures
+| main.rs:22:20:22:39 | "http://example.com" | Unexpected result: Source |
+| main.rs:22:42:22:71 | //... | Missing result: Alert[rust/non-https-url] |
+| main.rs:25:21:25:42 | ...::get | Unexpected result: Alert |
+| main.rs:33:20:33:28 | "http://" | Unexpected result: Source |
+| main.rs:33:31:33:60 | //... | Missing result: Alert[rust/non-https-url] |
+| main.rs:36:30:36:51 | ...::get | Unexpected result: Alert |
+| main.rs:59:15:59:49 | "http://example.com/sensitive-... | Unexpected result: Source |
+| main.rs:59:52:59:81 | //... | Missing result: Alert[rust/non-https-url] |
+| main.rs:60:21:60:42 | ...::get | Unexpected result: Alert |
diff --git a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.qlref b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.qlref
new file mode 100644
index 000000000000..90b533300191
--- /dev/null
+++ b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.qlref
@@ -0,0 +1,4 @@
+query: queries/security/CWE-319/UseOfHttp.ql
+postprocess:
+ - utils/test/PrettyPrintModels.ql
+ - utils/test/InlineExpectationsTestQuery.ql
\ No newline at end of file
diff --git a/rust/ql/test/query-tests/security/CWE-319/main.rs b/rust/ql/test/query-tests/security/CWE-319/main.rs
new file mode 100644
index 000000000000..ae58967a49bb
--- /dev/null
+++ b/rust/ql/test/query-tests/security/CWE-319/main.rs
@@ -0,0 +1,65 @@
+use reqwest;
+use std::env;
+
+fn main() {
+ test_direct_literals();
+ test_dynamic_urls();
+ test_localhost_exemptions();
+}
+
+fn test_direct_literals() {
+ // BAD: Direct HTTP URLs that should be flagged
+ let _response1 = reqwest::blocking::get("http://example.com/api").unwrap(); // $ Alert[rust/non-https-url]
+ let _response2 = reqwest::blocking::get("http://api.example.com/data").unwrap(); // $ Alert[rust/non-https-url]
+
+ // GOOD: HTTPS URLs that should not be flagged
+ let _response3 = reqwest::blocking::get("https://example.com/api").unwrap();
+ let _response4 = reqwest::blocking::get("https://api.example.com/data").unwrap();
+}
+
+fn test_dynamic_urls() {
+ // BAD: HTTP URLs constructed dynamically
+ let base_url = "http://example.com"; // $ Alert[rust/non-https-url]
+ let endpoint = "/api/users";
+ let full_url = format!("{}{}", base_url, endpoint);
+ let _response = reqwest::blocking::get(&full_url).unwrap();
+
+ // GOOD: HTTPS URLs constructed dynamically
+ let secure_base = "https://example.com";
+ let secure_full = format!("{}{}", secure_base, endpoint);
+ let _secure_response = reqwest::blocking::get(&secure_full).unwrap();
+
+ // BAD: HTTP protocol string
+ let protocol = "http://"; // $ Alert[rust/non-https-url]
+ let host = "api.example.com";
+ let insecure_url = format!("{}{}", protocol, host);
+ let _insecure_response = reqwest::blocking::get(&insecure_url).unwrap();
+
+ // GOOD: HTTPS protocol string
+ let secure_protocol = "https://";
+ let secure_url = format!("{}{}", secure_protocol, host);
+ let _secure_response2 = reqwest::blocking::get(&secure_url).unwrap();
+}
+
+fn test_localhost_exemptions() {
+ // GOOD: localhost URLs should not be flagged (local development)
+ let _local1 = reqwest::blocking::get("http://localhost:8080/api").unwrap();
+ let _local2 = reqwest::blocking::get("http://127.0.0.1:3000/test").unwrap();
+ let _local3 = reqwest::blocking::get("http://192.168.1.100/internal").unwrap();
+ let _local4 = reqwest::blocking::get("http://10.0.0.1/admin").unwrap();
+
+ // Test IPv6 localhost variants
+ let _local5 = reqwest::blocking::get("http://[::1]:8080/api").unwrap();
+ let _local6 = reqwest::blocking::get("http://[0:0:0:0:0:0:0:1]/test").unwrap();
+}
+
+// Additional test cases that mirror the Bad/Good examples
+fn test_examples() {
+ // From UseOfHttpBad.rs - BAD case
+ let url = "http://example.com/sensitive-data"; // $ Alert[rust/non-https-url]
+ let _response = reqwest::blocking::get(url).unwrap();
+
+ // From UseOfHttpGood.rs - GOOD case
+ let secure_url = "https://example.com/sensitive-data";
+ let _secure_response = reqwest::blocking::get(secure_url).unwrap();
+}
\ No newline at end of file
diff --git a/rust/ql/test/query-tests/security/CWE-319/options.yml b/rust/ql/test/query-tests/security/CWE-319/options.yml
new file mode 100644
index 000000000000..aa57719603d6
--- /dev/null
+++ b/rust/ql/test/query-tests/security/CWE-319/options.yml
@@ -0,0 +1,3 @@
+qltest_cargo_check: true
+qltest_dependencies:
+ - reqwest = { version = "0.12.9", features = ["blocking"] }
\ No newline at end of file
From 7b04cf1a73ddb561738e31ba5b31348ced20c626 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 16 Sep 2025 12:15:44 +0100
Subject: [PATCH 03/12] Rust: Fix up the test annotations.
---
.../security/CWE-319/UseOfHttp.expected | 10 -------
.../test/query-tests/security/CWE-319/main.rs | 30 +++++++++----------
2 files changed, 15 insertions(+), 25 deletions(-)
diff --git a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
index 53cc8606cc82..f2a2e7e05f43 100644
--- a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
+++ b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
@@ -66,13 +66,3 @@ nodes
| main.rs:60:21:60:42 | ...::get | semmle.label | ...::get |
| main.rs:60:44:60:46 | url | semmle.label | url |
subpaths
-testFailures
-| main.rs:22:20:22:39 | "http://example.com" | Unexpected result: Source |
-| main.rs:22:42:22:71 | //... | Missing result: Alert[rust/non-https-url] |
-| main.rs:25:21:25:42 | ...::get | Unexpected result: Alert |
-| main.rs:33:20:33:28 | "http://" | Unexpected result: Source |
-| main.rs:33:31:33:60 | //... | Missing result: Alert[rust/non-https-url] |
-| main.rs:36:30:36:51 | ...::get | Unexpected result: Alert |
-| main.rs:59:15:59:49 | "http://example.com/sensitive-... | Unexpected result: Source |
-| main.rs:59:52:59:81 | //... | Missing result: Alert[rust/non-https-url] |
-| main.rs:60:21:60:42 | ...::get | Unexpected result: Alert |
diff --git a/rust/ql/test/query-tests/security/CWE-319/main.rs b/rust/ql/test/query-tests/security/CWE-319/main.rs
index ae58967a49bb..52f744e39a1a 100644
--- a/rust/ql/test/query-tests/security/CWE-319/main.rs
+++ b/rust/ql/test/query-tests/security/CWE-319/main.rs
@@ -11,30 +11,30 @@ fn test_direct_literals() {
// BAD: Direct HTTP URLs that should be flagged
let _response1 = reqwest::blocking::get("http://example.com/api").unwrap(); // $ Alert[rust/non-https-url]
let _response2 = reqwest::blocking::get("http://api.example.com/data").unwrap(); // $ Alert[rust/non-https-url]
-
- // GOOD: HTTPS URLs that should not be flagged
+
+ // GOOD: HTTPS URLs that should not be flagged
let _response3 = reqwest::blocking::get("https://example.com/api").unwrap();
let _response4 = reqwest::blocking::get("https://api.example.com/data").unwrap();
}
fn test_dynamic_urls() {
// BAD: HTTP URLs constructed dynamically
- let base_url = "http://example.com"; // $ Alert[rust/non-https-url]
+ let base_url = "http://example.com"; // $ Source
let endpoint = "/api/users";
let full_url = format!("{}{}", base_url, endpoint);
- let _response = reqwest::blocking::get(&full_url).unwrap();
-
+ let _response = reqwest::blocking::get(&full_url).unwrap(); // $ Alert[rust/non-https-url]
+
// GOOD: HTTPS URLs constructed dynamically
let secure_base = "https://example.com";
let secure_full = format!("{}{}", secure_base, endpoint);
let _secure_response = reqwest::blocking::get(&secure_full).unwrap();
-
+
// BAD: HTTP protocol string
- let protocol = "http://"; // $ Alert[rust/non-https-url]
+ let protocol = "http://"; // $ Source
let host = "api.example.com";
let insecure_url = format!("{}{}", protocol, host);
- let _insecure_response = reqwest::blocking::get(&insecure_url).unwrap();
-
+ let _insecure_response = reqwest::blocking::get(&insecure_url).unwrap(); // $ Alert[rust/non-https-url]
+
// GOOD: HTTPS protocol string
let secure_protocol = "https://";
let secure_url = format!("{}{}", secure_protocol, host);
@@ -47,7 +47,7 @@ fn test_localhost_exemptions() {
let _local2 = reqwest::blocking::get("http://127.0.0.1:3000/test").unwrap();
let _local3 = reqwest::blocking::get("http://192.168.1.100/internal").unwrap();
let _local4 = reqwest::blocking::get("http://10.0.0.1/admin").unwrap();
-
+
// Test IPv6 localhost variants
let _local5 = reqwest::blocking::get("http://[::1]:8080/api").unwrap();
let _local6 = reqwest::blocking::get("http://[0:0:0:0:0:0:0:1]/test").unwrap();
@@ -56,10 +56,10 @@ fn test_localhost_exemptions() {
// Additional test cases that mirror the Bad/Good examples
fn test_examples() {
// From UseOfHttpBad.rs - BAD case
- let url = "http://example.com/sensitive-data"; // $ Alert[rust/non-https-url]
- let _response = reqwest::blocking::get(url).unwrap();
-
- // From UseOfHttpGood.rs - GOOD case
+ let url = "http://example.com/sensitive-data"; // $ Source
+ let _response = reqwest::blocking::get(url).unwrap(); // $ Alert[rust/non-https-url]
+
+ // From UseOfHttpGood.rs - GOOD case
let secure_url = "https://example.com/sensitive-data";
let _secure_response = reqwest::blocking::get(secure_url).unwrap();
-}
\ No newline at end of file
+}
From 0924dec545a0eff7113574d629f4c5f2b99007be Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 16 Sep 2025 11:21:57 +0100
Subject: [PATCH 04/12] Rust: Make the tests of the example code closer to the
actual example code.
---
.../security/CWE-319/UseOfHttp.expected | 16 ++++++++--------
.../test/query-tests/security/CWE-319/main.rs | 18 ++++++++++++++----
2 files changed, 22 insertions(+), 12 deletions(-)
diff --git a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
index f2a2e7e05f43..e8b7d3013359 100644
--- a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
+++ b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
@@ -3,7 +3,7 @@
| main.rs:13:22:13:43 | ...::get | main.rs:13:45:13:73 | "http://api.example.com/data" | main.rs:13:22:13:43 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:13:45:13:73 | "http://api.example.com/data" | this HTTP URL |
| main.rs:25:21:25:42 | ...::get | main.rs:22:20:22:39 | "http://example.com" | main.rs:25:21:25:42 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:22:20:22:39 | "http://example.com" | this HTTP URL |
| main.rs:36:30:36:51 | ...::get | main.rs:33:20:33:28 | "http://" | main.rs:36:30:36:51 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:33:20:33:28 | "http://" | this HTTP URL |
-| main.rs:60:21:60:42 | ...::get | main.rs:59:15:59:49 | "http://example.com/sensitive-... | main.rs:60:21:60:42 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:59:15:59:49 | "http://example.com/sensitive-... | this HTTP URL |
+| main.rs:63:24:63:45 | ...::get | main.rs:60:19:60:53 | "http://example.com/sensitive-... | main.rs:63:24:63:45 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:60:19:60:53 | "http://example.com/sensitive-... | this HTTP URL |
edges
| main.rs:12:45:12:68 | "http://example.com/api" | main.rs:12:22:12:43 | ...::get | provenance | MaD:1 Sink:MaD:1 |
| main.rs:13:45:13:73 | "http://api.example.com/data" | main.rs:13:22:13:43 | ...::get | provenance | MaD:1 Sink:MaD:1 |
@@ -27,9 +27,9 @@ edges
| main.rs:35:32:35:53 | { ... } | main.rs:35:32:35:53 | ...::must_use(...) | provenance | MaD:3 |
| main.rs:36:53:36:65 | &insecure_url [&ref] | main.rs:36:30:36:51 | ...::get | provenance | MaD:1 Sink:MaD:1 |
| main.rs:36:54:36:65 | insecure_url | main.rs:36:53:36:65 | &insecure_url [&ref] | provenance | |
-| main.rs:59:9:59:11 | url | main.rs:60:44:60:46 | url | provenance | |
-| main.rs:59:15:59:49 | "http://example.com/sensitive-... | main.rs:59:9:59:11 | url | provenance | |
-| main.rs:60:44:60:46 | url | main.rs:60:21:60:42 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:60:13:60:15 | url | main.rs:63:47:63:49 | url | provenance | |
+| main.rs:60:19:60:53 | "http://example.com/sensitive-... | main.rs:60:13:60:15 | url | provenance | |
+| main.rs:63:47:63:49 | url | main.rs:63:24:63:45 | ...::get | provenance | MaD:1 Sink:MaD:1 |
models
| 1 | Sink: reqwest::blocking::get; Argument[0]; request-url |
| 2 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint |
@@ -61,8 +61,8 @@ nodes
| main.rs:36:30:36:51 | ...::get | semmle.label | ...::get |
| main.rs:36:53:36:65 | &insecure_url [&ref] | semmle.label | &insecure_url [&ref] |
| main.rs:36:54:36:65 | insecure_url | semmle.label | insecure_url |
-| main.rs:59:9:59:11 | url | semmle.label | url |
-| main.rs:59:15:59:49 | "http://example.com/sensitive-... | semmle.label | "http://example.com/sensitive-... |
-| main.rs:60:21:60:42 | ...::get | semmle.label | ...::get |
-| main.rs:60:44:60:46 | url | semmle.label | url |
+| main.rs:60:13:60:15 | url | semmle.label | url |
+| main.rs:60:19:60:53 | "http://example.com/sensitive-... | semmle.label | "http://example.com/sensitive-... |
+| main.rs:63:24:63:45 | ...::get | semmle.label | ...::get |
+| main.rs:63:47:63:49 | url | semmle.label | url |
subpaths
diff --git a/rust/ql/test/query-tests/security/CWE-319/main.rs b/rust/ql/test/query-tests/security/CWE-319/main.rs
index 52f744e39a1a..cec94840f297 100644
--- a/rust/ql/test/query-tests/security/CWE-319/main.rs
+++ b/rust/ql/test/query-tests/security/CWE-319/main.rs
@@ -56,10 +56,20 @@ fn test_localhost_exemptions() {
// Additional test cases that mirror the Bad/Good examples
fn test_examples() {
// From UseOfHttpBad.rs - BAD case
- let url = "http://example.com/sensitive-data"; // $ Source
- let _response = reqwest::blocking::get(url).unwrap(); // $ Alert[rust/non-https-url]
+ {
+ let url = "http://example.com/sensitive-data"; // $ Source
+
+ // This makes an insecure HTTP request that can be intercepted
+ let response = reqwest::blocking::get(url).unwrap(); // $ Alert[rust/non-https-url]
+ println!("Response: {}", response.text().unwrap());
+ }
// From UseOfHttpGood.rs - GOOD case
- let secure_url = "https://example.com/sensitive-data";
- let _secure_response = reqwest::blocking::get(secure_url).unwrap();
+ {
+ let url = "https://example.com/sensitive-data";
+
+ // This makes a secure HTTPS request that is encrypted
+ let response = reqwest::blocking::get(url).unwrap();
+ println!("Response: {}", response.text().unwrap());
+ }
}
From 9c7fc583373c353f5bf6cf35d055af3b6f28ad0e Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 16 Sep 2025 12:06:05 +0100
Subject: [PATCH 05/12] Rust: Add tests for a few more edge cases.
---
.../security/CWE-319/UseOfHttp.expected | 120 ++++++++++--------
.../test/query-tests/security/CWE-319/main.rs | 22 +++-
2 files changed, 79 insertions(+), 63 deletions(-)
diff --git a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
index e8b7d3013359..216d11b36068 100644
--- a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
+++ b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
@@ -1,35 +1,39 @@
#select
| main.rs:12:22:12:43 | ...::get | main.rs:12:45:12:68 | "http://example.com/api" | main.rs:12:22:12:43 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:12:45:12:68 | "http://example.com/api" | this HTTP URL |
-| main.rs:13:22:13:43 | ...::get | main.rs:13:45:13:73 | "http://api.example.com/data" | main.rs:13:22:13:43 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:13:45:13:73 | "http://api.example.com/data" | this HTTP URL |
-| main.rs:25:21:25:42 | ...::get | main.rs:22:20:22:39 | "http://example.com" | main.rs:25:21:25:42 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:22:20:22:39 | "http://example.com" | this HTTP URL |
-| main.rs:36:30:36:51 | ...::get | main.rs:33:20:33:28 | "http://" | main.rs:36:30:36:51 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:33:20:33:28 | "http://" | this HTTP URL |
-| main.rs:63:24:63:45 | ...::get | main.rs:60:19:60:53 | "http://example.com/sensitive-... | main.rs:63:24:63:45 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:60:19:60:53 | "http://example.com/sensitive-... | this HTTP URL |
+| main.rs:14:22:14:43 | ...::get | main.rs:14:45:14:73 | "http://api.example.com/data" | main.rs:14:22:14:43 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:14:45:14:73 | "http://api.example.com/data" | this HTTP URL |
+| main.rs:26:21:26:42 | ...::get | main.rs:23:20:23:39 | "http://example.com" | main.rs:26:21:26:42 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:23:20:23:39 | "http://example.com" | this HTTP URL |
+| main.rs:37:30:37:51 | ...::get | main.rs:34:20:34:28 | "http://" | main.rs:37:30:37:51 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:34:20:34:28 | "http://" | this HTTP URL |
+| main.rs:53:19:53:40 | ...::get | main.rs:53:42:53:68 | "http://172.31.255.255/bar" | main.rs:53:19:53:40 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:53:42:53:68 | "http://172.31.255.255/bar" | this HTTP URL |
+| main.rs:60:20:60:41 | ...::get | main.rs:60:43:60:65 | "http://172.32.0.0/baz" | main.rs:60:20:60:41 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:60:43:60:65 | "http://172.32.0.0/baz" | this HTTP URL |
+| main.rs:71:24:71:45 | ...::get | main.rs:68:19:68:53 | "http://example.com/sensitive-... | main.rs:71:24:71:45 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:68:19:68:53 | "http://example.com/sensitive-... | this HTTP URL |
edges
| main.rs:12:45:12:68 | "http://example.com/api" | main.rs:12:22:12:43 | ...::get | provenance | MaD:1 Sink:MaD:1 |
-| main.rs:13:45:13:73 | "http://api.example.com/data" | main.rs:13:22:13:43 | ...::get | provenance | MaD:1 Sink:MaD:1 |
-| main.rs:22:9:22:16 | base_url | main.rs:24:28:24:53 | MacroExpr | provenance | |
-| main.rs:22:20:22:39 | "http://example.com" | main.rs:22:9:22:16 | base_url | provenance | |
-| main.rs:24:9:24:16 | full_url | main.rs:25:45:25:52 | full_url | provenance | |
-| main.rs:24:20:24:26 | res | main.rs:24:28:24:53 | { ... } | provenance | |
-| main.rs:24:28:24:53 | ...::format(...) | main.rs:24:20:24:26 | res | provenance | |
-| main.rs:24:28:24:53 | ...::must_use(...) | main.rs:24:9:24:16 | full_url | provenance | |
-| main.rs:24:28:24:53 | MacroExpr | main.rs:24:28:24:53 | ...::format(...) | provenance | MaD:2 |
-| main.rs:24:28:24:53 | { ... } | main.rs:24:28:24:53 | ...::must_use(...) | provenance | MaD:3 |
-| main.rs:25:44:25:52 | &full_url [&ref] | main.rs:25:21:25:42 | ...::get | provenance | MaD:1 Sink:MaD:1 |
-| main.rs:25:45:25:52 | full_url | main.rs:25:44:25:52 | &full_url [&ref] | provenance | |
-| main.rs:33:9:33:16 | protocol | main.rs:35:32:35:53 | MacroExpr | provenance | |
-| main.rs:33:20:33:28 | "http://" | main.rs:33:9:33:16 | protocol | provenance | |
-| main.rs:35:9:35:20 | insecure_url | main.rs:36:54:36:65 | insecure_url | provenance | |
-| main.rs:35:24:35:30 | res | main.rs:35:32:35:53 | { ... } | provenance | |
-| main.rs:35:32:35:53 | ...::format(...) | main.rs:35:24:35:30 | res | provenance | |
-| main.rs:35:32:35:53 | ...::must_use(...) | main.rs:35:9:35:20 | insecure_url | provenance | |
-| main.rs:35:32:35:53 | MacroExpr | main.rs:35:32:35:53 | ...::format(...) | provenance | MaD:2 |
-| main.rs:35:32:35:53 | { ... } | main.rs:35:32:35:53 | ...::must_use(...) | provenance | MaD:3 |
-| main.rs:36:53:36:65 | &insecure_url [&ref] | main.rs:36:30:36:51 | ...::get | provenance | MaD:1 Sink:MaD:1 |
-| main.rs:36:54:36:65 | insecure_url | main.rs:36:53:36:65 | &insecure_url [&ref] | provenance | |
-| main.rs:60:13:60:15 | url | main.rs:63:47:63:49 | url | provenance | |
-| main.rs:60:19:60:53 | "http://example.com/sensitive-... | main.rs:60:13:60:15 | url | provenance | |
-| main.rs:63:47:63:49 | url | main.rs:63:24:63:45 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:14:45:14:73 | "http://api.example.com/data" | main.rs:14:22:14:43 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:23:9:23:16 | base_url | main.rs:25:28:25:53 | MacroExpr | provenance | |
+| main.rs:23:20:23:39 | "http://example.com" | main.rs:23:9:23:16 | base_url | provenance | |
+| main.rs:25:9:25:16 | full_url | main.rs:26:45:26:52 | full_url | provenance | |
+| main.rs:25:20:25:26 | res | main.rs:25:28:25:53 | { ... } | provenance | |
+| main.rs:25:28:25:53 | ...::format(...) | main.rs:25:20:25:26 | res | provenance | |
+| main.rs:25:28:25:53 | ...::must_use(...) | main.rs:25:9:25:16 | full_url | provenance | |
+| main.rs:25:28:25:53 | MacroExpr | main.rs:25:28:25:53 | ...::format(...) | provenance | MaD:2 |
+| main.rs:25:28:25:53 | { ... } | main.rs:25:28:25:53 | ...::must_use(...) | provenance | MaD:3 |
+| main.rs:26:44:26:52 | &full_url [&ref] | main.rs:26:21:26:42 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:26:45:26:52 | full_url | main.rs:26:44:26:52 | &full_url [&ref] | provenance | |
+| main.rs:34:9:34:16 | protocol | main.rs:36:32:36:53 | MacroExpr | provenance | |
+| main.rs:34:20:34:28 | "http://" | main.rs:34:9:34:16 | protocol | provenance | |
+| main.rs:36:9:36:20 | insecure_url | main.rs:37:54:37:65 | insecure_url | provenance | |
+| main.rs:36:24:36:30 | res | main.rs:36:32:36:53 | { ... } | provenance | |
+| main.rs:36:32:36:53 | ...::format(...) | main.rs:36:24:36:30 | res | provenance | |
+| main.rs:36:32:36:53 | ...::must_use(...) | main.rs:36:9:36:20 | insecure_url | provenance | |
+| main.rs:36:32:36:53 | MacroExpr | main.rs:36:32:36:53 | ...::format(...) | provenance | MaD:2 |
+| main.rs:36:32:36:53 | { ... } | main.rs:36:32:36:53 | ...::must_use(...) | provenance | MaD:3 |
+| main.rs:37:53:37:65 | &insecure_url [&ref] | main.rs:37:30:37:51 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:37:54:37:65 | insecure_url | main.rs:37:53:37:65 | &insecure_url [&ref] | provenance | |
+| main.rs:53:42:53:68 | "http://172.31.255.255/bar" | main.rs:53:19:53:40 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:60:43:60:65 | "http://172.32.0.0/baz" | main.rs:60:20:60:41 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:68:13:68:15 | url | main.rs:71:47:71:49 | url | provenance | |
+| main.rs:68:19:68:53 | "http://example.com/sensitive-... | main.rs:68:13:68:15 | url | provenance | |
+| main.rs:71:47:71:49 | url | main.rs:71:24:71:45 | ...::get | provenance | MaD:1 Sink:MaD:1 |
models
| 1 | Sink: reqwest::blocking::get; Argument[0]; request-url |
| 2 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint |
@@ -37,32 +41,36 @@ models
nodes
| main.rs:12:22:12:43 | ...::get | semmle.label | ...::get |
| main.rs:12:45:12:68 | "http://example.com/api" | semmle.label | "http://example.com/api" |
-| main.rs:13:22:13:43 | ...::get | semmle.label | ...::get |
-| main.rs:13:45:13:73 | "http://api.example.com/data" | semmle.label | "http://api.example.com/data" |
-| main.rs:22:9:22:16 | base_url | semmle.label | base_url |
-| main.rs:22:20:22:39 | "http://example.com" | semmle.label | "http://example.com" |
-| main.rs:24:9:24:16 | full_url | semmle.label | full_url |
-| main.rs:24:20:24:26 | res | semmle.label | res |
-| main.rs:24:28:24:53 | ...::format(...) | semmle.label | ...::format(...) |
-| main.rs:24:28:24:53 | ...::must_use(...) | semmle.label | ...::must_use(...) |
-| main.rs:24:28:24:53 | MacroExpr | semmle.label | MacroExpr |
-| main.rs:24:28:24:53 | { ... } | semmle.label | { ... } |
-| main.rs:25:21:25:42 | ...::get | semmle.label | ...::get |
-| main.rs:25:44:25:52 | &full_url [&ref] | semmle.label | &full_url [&ref] |
-| main.rs:25:45:25:52 | full_url | semmle.label | full_url |
-| main.rs:33:9:33:16 | protocol | semmle.label | protocol |
-| main.rs:33:20:33:28 | "http://" | semmle.label | "http://" |
-| main.rs:35:9:35:20 | insecure_url | semmle.label | insecure_url |
-| main.rs:35:24:35:30 | res | semmle.label | res |
-| main.rs:35:32:35:53 | ...::format(...) | semmle.label | ...::format(...) |
-| main.rs:35:32:35:53 | ...::must_use(...) | semmle.label | ...::must_use(...) |
-| main.rs:35:32:35:53 | MacroExpr | semmle.label | MacroExpr |
-| main.rs:35:32:35:53 | { ... } | semmle.label | { ... } |
-| main.rs:36:30:36:51 | ...::get | semmle.label | ...::get |
-| main.rs:36:53:36:65 | &insecure_url [&ref] | semmle.label | &insecure_url [&ref] |
-| main.rs:36:54:36:65 | insecure_url | semmle.label | insecure_url |
-| main.rs:60:13:60:15 | url | semmle.label | url |
-| main.rs:60:19:60:53 | "http://example.com/sensitive-... | semmle.label | "http://example.com/sensitive-... |
-| main.rs:63:24:63:45 | ...::get | semmle.label | ...::get |
-| main.rs:63:47:63:49 | url | semmle.label | url |
+| main.rs:14:22:14:43 | ...::get | semmle.label | ...::get |
+| main.rs:14:45:14:73 | "http://api.example.com/data" | semmle.label | "http://api.example.com/data" |
+| main.rs:23:9:23:16 | base_url | semmle.label | base_url |
+| main.rs:23:20:23:39 | "http://example.com" | semmle.label | "http://example.com" |
+| main.rs:25:9:25:16 | full_url | semmle.label | full_url |
+| main.rs:25:20:25:26 | res | semmle.label | res |
+| main.rs:25:28:25:53 | ...::format(...) | semmle.label | ...::format(...) |
+| main.rs:25:28:25:53 | ...::must_use(...) | semmle.label | ...::must_use(...) |
+| main.rs:25:28:25:53 | MacroExpr | semmle.label | MacroExpr |
+| main.rs:25:28:25:53 | { ... } | semmle.label | { ... } |
+| main.rs:26:21:26:42 | ...::get | semmle.label | ...::get |
+| main.rs:26:44:26:52 | &full_url [&ref] | semmle.label | &full_url [&ref] |
+| main.rs:26:45:26:52 | full_url | semmle.label | full_url |
+| main.rs:34:9:34:16 | protocol | semmle.label | protocol |
+| main.rs:34:20:34:28 | "http://" | semmle.label | "http://" |
+| main.rs:36:9:36:20 | insecure_url | semmle.label | insecure_url |
+| main.rs:36:24:36:30 | res | semmle.label | res |
+| main.rs:36:32:36:53 | ...::format(...) | semmle.label | ...::format(...) |
+| main.rs:36:32:36:53 | ...::must_use(...) | semmle.label | ...::must_use(...) |
+| main.rs:36:32:36:53 | MacroExpr | semmle.label | MacroExpr |
+| main.rs:36:32:36:53 | { ... } | semmle.label | { ... } |
+| main.rs:37:30:37:51 | ...::get | semmle.label | ...::get |
+| main.rs:37:53:37:65 | &insecure_url [&ref] | semmle.label | &insecure_url [&ref] |
+| main.rs:37:54:37:65 | insecure_url | semmle.label | insecure_url |
+| main.rs:53:19:53:40 | ...::get | semmle.label | ...::get |
+| main.rs:53:42:53:68 | "http://172.31.255.255/bar" | semmle.label | "http://172.31.255.255/bar" |
+| main.rs:60:20:60:41 | ...::get | semmle.label | ...::get |
+| main.rs:60:43:60:65 | "http://172.32.0.0/baz" | semmle.label | "http://172.32.0.0/baz" |
+| main.rs:68:13:68:15 | url | semmle.label | url |
+| main.rs:68:19:68:53 | "http://example.com/sensitive-... | semmle.label | "http://example.com/sensitive-... |
+| main.rs:71:24:71:45 | ...::get | semmle.label | ...::get |
+| main.rs:71:47:71:49 | url | semmle.label | url |
subpaths
diff --git a/rust/ql/test/query-tests/security/CWE-319/main.rs b/rust/ql/test/query-tests/security/CWE-319/main.rs
index cec94840f297..0dd59ce0880e 100644
--- a/rust/ql/test/query-tests/security/CWE-319/main.rs
+++ b/rust/ql/test/query-tests/security/CWE-319/main.rs
@@ -10,7 +10,8 @@ fn main() {
fn test_direct_literals() {
// BAD: Direct HTTP URLs that should be flagged
let _response1 = reqwest::blocking::get("http://example.com/api").unwrap(); // $ Alert[rust/non-https-url]
- let _response2 = reqwest::blocking::get("http://api.example.com/data").unwrap(); // $ Alert[rust/non-https-url]
+ let _response2 = reqwest::blocking::get("HTTP://EXAMPLE.COM/API").unwrap(); // $ MISSING: Alert[rust/non-https-url]
+ let _response3 = reqwest::blocking::get("http://api.example.com/data").unwrap(); // $ Alert[rust/non-https-url]
// GOOD: HTTPS URLs that should not be flagged
let _response3 = reqwest::blocking::get("https://example.com/api").unwrap();
@@ -44,13 +45,20 @@ fn test_dynamic_urls() {
fn test_localhost_exemptions() {
// GOOD: localhost URLs should not be flagged (local development)
let _local1 = reqwest::blocking::get("http://localhost:8080/api").unwrap();
- let _local2 = reqwest::blocking::get("http://127.0.0.1:3000/test").unwrap();
- let _local3 = reqwest::blocking::get("http://192.168.1.100/internal").unwrap();
- let _local4 = reqwest::blocking::get("http://10.0.0.1/admin").unwrap();
+ let _local2 = reqwest::blocking::get("HTTP://LOCALHOST:8080/api").unwrap();
+ let _local3 = reqwest::blocking::get("http://127.0.0.1:3000/test").unwrap();
+ let _local4 = reqwest::blocking::get("http://192.168.1.100/internal").unwrap();
+ let _local5 = reqwest::blocking::get("http://10.0.0.1/admin").unwrap();
+ let _local6 = reqwest::blocking::get("http://172.16.0.0/foo").unwrap();
+ let _local7 = reqwest::blocking::get("http://172.31.255.255/bar").unwrap(); // $ SPURIOUS: Alert[rust/non-https-url]
+
+ // GOOD: test IPv6 localhost variants
+ let _local8 = reqwest::blocking::get("http://[::1]:8080/api").unwrap();
+ let _local9 = reqwest::blocking::get("http://[0:0:0:0:0:0:0:1]/test").unwrap();
+
+ // BAD: non-private IP address
+ let _local10 = reqwest::blocking::get("http://172.32.0.0/baz").unwrap(); // $ Alert[rust/non-https-url]
- // Test IPv6 localhost variants
- let _local5 = reqwest::blocking::get("http://[::1]:8080/api").unwrap();
- let _local6 = reqwest::blocking::get("http://[0:0:0:0:0:0:0:1]/test").unwrap();
}
// Additional test cases that mirror the Bad/Good examples
From 0f5aa857b874b22bfb53b67b08be2336e14af351 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 16 Sep 2025 11:32:52 +0100
Subject: [PATCH 06/12] Rust: Remove unnecessary import.
---
rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll | 1 -
1 file changed, 1 deletion(-)
diff --git a/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll b/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
index 026880785b6c..8001e6270dd1 100644
--- a/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
+++ b/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
@@ -6,7 +6,6 @@
import rust
private import codeql.rust.dataflow.DataFlow
private import codeql.rust.dataflow.FlowSink
-private import codeql.rust.elements.LiteralExprExt
private import codeql.rust.Concepts
/**
From 80ce55ab1072fb534776fc7b02a93ca6a2b18ce2 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 16 Sep 2025 11:58:23 +0100
Subject: [PATCH 07/12] Rust: Make the private address spaces URL more
accurate.
---
rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll | 2 +-
rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected | 4 ----
rust/ql/test/query-tests/security/CWE-319/main.rs | 2 +-
3 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll b/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
index 8001e6270dd1..58466dd0a4fb 100644
--- a/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
+++ b/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
@@ -38,7 +38,7 @@ module UseOfHttp {
exists(string s | this.getTextValue() = s |
// Match HTTP URLs that are not private/local
s.regexpMatch("\"http://.*\"") and
- not s.regexpMatch("\"http://(localhost|127\\.0\\.0\\.1|192\\.168\\.[0-9]+\\.[0-9]+|10\\.[0-9]+\\.[0-9]+\\.[0-9]+|172\\.16\\.[0-9]+\\.[0-9]+|\\[::1\\]|\\[0:0:0:0:0:0:0:1\\]).*\"")
+ not s.regexpMatch("\"http://(localhost|127\\.0\\.0\\.1|192\\.168\\.[0-9]+\\.[0-9]+|10\\.[0-9]+\\.[0-9]+\\.[0-9]+|172\\.(1[6-9]|2[0-9]|3[01])\\.[0-9]+|\\[::1\\]|\\[0:0:0:0:0:0:0:1\\]).*\"")
)
}
}
diff --git a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
index 216d11b36068..ef99b001fcf5 100644
--- a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
+++ b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
@@ -3,7 +3,6 @@
| main.rs:14:22:14:43 | ...::get | main.rs:14:45:14:73 | "http://api.example.com/data" | main.rs:14:22:14:43 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:14:45:14:73 | "http://api.example.com/data" | this HTTP URL |
| main.rs:26:21:26:42 | ...::get | main.rs:23:20:23:39 | "http://example.com" | main.rs:26:21:26:42 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:23:20:23:39 | "http://example.com" | this HTTP URL |
| main.rs:37:30:37:51 | ...::get | main.rs:34:20:34:28 | "http://" | main.rs:37:30:37:51 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:34:20:34:28 | "http://" | this HTTP URL |
-| main.rs:53:19:53:40 | ...::get | main.rs:53:42:53:68 | "http://172.31.255.255/bar" | main.rs:53:19:53:40 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:53:42:53:68 | "http://172.31.255.255/bar" | this HTTP URL |
| main.rs:60:20:60:41 | ...::get | main.rs:60:43:60:65 | "http://172.32.0.0/baz" | main.rs:60:20:60:41 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:60:43:60:65 | "http://172.32.0.0/baz" | this HTTP URL |
| main.rs:71:24:71:45 | ...::get | main.rs:68:19:68:53 | "http://example.com/sensitive-... | main.rs:71:24:71:45 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:68:19:68:53 | "http://example.com/sensitive-... | this HTTP URL |
edges
@@ -29,7 +28,6 @@ edges
| main.rs:36:32:36:53 | { ... } | main.rs:36:32:36:53 | ...::must_use(...) | provenance | MaD:3 |
| main.rs:37:53:37:65 | &insecure_url [&ref] | main.rs:37:30:37:51 | ...::get | provenance | MaD:1 Sink:MaD:1 |
| main.rs:37:54:37:65 | insecure_url | main.rs:37:53:37:65 | &insecure_url [&ref] | provenance | |
-| main.rs:53:42:53:68 | "http://172.31.255.255/bar" | main.rs:53:19:53:40 | ...::get | provenance | MaD:1 Sink:MaD:1 |
| main.rs:60:43:60:65 | "http://172.32.0.0/baz" | main.rs:60:20:60:41 | ...::get | provenance | MaD:1 Sink:MaD:1 |
| main.rs:68:13:68:15 | url | main.rs:71:47:71:49 | url | provenance | |
| main.rs:68:19:68:53 | "http://example.com/sensitive-... | main.rs:68:13:68:15 | url | provenance | |
@@ -65,8 +63,6 @@ nodes
| main.rs:37:30:37:51 | ...::get | semmle.label | ...::get |
| main.rs:37:53:37:65 | &insecure_url [&ref] | semmle.label | &insecure_url [&ref] |
| main.rs:37:54:37:65 | insecure_url | semmle.label | insecure_url |
-| main.rs:53:19:53:40 | ...::get | semmle.label | ...::get |
-| main.rs:53:42:53:68 | "http://172.31.255.255/bar" | semmle.label | "http://172.31.255.255/bar" |
| main.rs:60:20:60:41 | ...::get | semmle.label | ...::get |
| main.rs:60:43:60:65 | "http://172.32.0.0/baz" | semmle.label | "http://172.32.0.0/baz" |
| main.rs:68:13:68:15 | url | semmle.label | url |
diff --git a/rust/ql/test/query-tests/security/CWE-319/main.rs b/rust/ql/test/query-tests/security/CWE-319/main.rs
index 0dd59ce0880e..908e6c61c2c1 100644
--- a/rust/ql/test/query-tests/security/CWE-319/main.rs
+++ b/rust/ql/test/query-tests/security/CWE-319/main.rs
@@ -50,7 +50,7 @@ fn test_localhost_exemptions() {
let _local4 = reqwest::blocking::get("http://192.168.1.100/internal").unwrap();
let _local5 = reqwest::blocking::get("http://10.0.0.1/admin").unwrap();
let _local6 = reqwest::blocking::get("http://172.16.0.0/foo").unwrap();
- let _local7 = reqwest::blocking::get("http://172.31.255.255/bar").unwrap(); // $ SPURIOUS: Alert[rust/non-https-url]
+ let _local7 = reqwest::blocking::get("http://172.31.255.255/bar").unwrap();
// GOOD: test IPv6 localhost variants
let _local8 = reqwest::blocking::get("http://[::1]:8080/api").unwrap();
From 4b281fdf12fb8a64cd194aee324ded70855b4695 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 16 Sep 2025 13:02:54 +0100
Subject: [PATCH 08/12] Rust: Use case insensitive regexps.
---
rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll | 4 ++--
rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected | 4 ++++
rust/ql/test/query-tests/security/CWE-319/main.rs | 2 +-
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll b/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
index 58466dd0a4fb..5e0d534fb7d7 100644
--- a/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
+++ b/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
@@ -37,8 +37,8 @@ module UseOfHttp {
HttpStringLiteral() {
exists(string s | this.getTextValue() = s |
// Match HTTP URLs that are not private/local
- s.regexpMatch("\"http://.*\"") and
- not s.regexpMatch("\"http://(localhost|127\\.0\\.0\\.1|192\\.168\\.[0-9]+\\.[0-9]+|10\\.[0-9]+\\.[0-9]+\\.[0-9]+|172\\.(1[6-9]|2[0-9]|3[01])\\.[0-9]+|\\[::1\\]|\\[0:0:0:0:0:0:0:1\\]).*\"")
+ s.regexpMatch("(?i)\"http://.*\"") and
+ not s.regexpMatch("(?i)\"http://(localhost|127\\.0\\.0\\.1|192\\.168\\.[0-9]+\\.[0-9]+|10\\.[0-9]+\\.[0-9]+\\.[0-9]+|172\\.(1[6-9]|2[0-9]|3[01])\\.[0-9]+|\\[::1\\]|\\[0:0:0:0:0:0:0:1\\]).*\"")
)
}
}
diff --git a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
index ef99b001fcf5..952bd741d1c7 100644
--- a/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
+++ b/rust/ql/test/query-tests/security/CWE-319/UseOfHttp.expected
@@ -1,5 +1,6 @@
#select
| main.rs:12:22:12:43 | ...::get | main.rs:12:45:12:68 | "http://example.com/api" | main.rs:12:22:12:43 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:12:45:12:68 | "http://example.com/api" | this HTTP URL |
+| main.rs:13:22:13:43 | ...::get | main.rs:13:45:13:68 | "HTTP://EXAMPLE.COM/API" | main.rs:13:22:13:43 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:13:45:13:68 | "HTTP://EXAMPLE.COM/API" | this HTTP URL |
| main.rs:14:22:14:43 | ...::get | main.rs:14:45:14:73 | "http://api.example.com/data" | main.rs:14:22:14:43 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:14:45:14:73 | "http://api.example.com/data" | this HTTP URL |
| main.rs:26:21:26:42 | ...::get | main.rs:23:20:23:39 | "http://example.com" | main.rs:26:21:26:42 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:23:20:23:39 | "http://example.com" | this HTTP URL |
| main.rs:37:30:37:51 | ...::get | main.rs:34:20:34:28 | "http://" | main.rs:37:30:37:51 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:34:20:34:28 | "http://" | this HTTP URL |
@@ -7,6 +8,7 @@
| main.rs:71:24:71:45 | ...::get | main.rs:68:19:68:53 | "http://example.com/sensitive-... | main.rs:71:24:71:45 | ...::get | This URL may be constructed with the HTTP protocol, from $@. | main.rs:68:19:68:53 | "http://example.com/sensitive-... | this HTTP URL |
edges
| main.rs:12:45:12:68 | "http://example.com/api" | main.rs:12:22:12:43 | ...::get | provenance | MaD:1 Sink:MaD:1 |
+| main.rs:13:45:13:68 | "HTTP://EXAMPLE.COM/API" | main.rs:13:22:13:43 | ...::get | provenance | MaD:1 Sink:MaD:1 |
| main.rs:14:45:14:73 | "http://api.example.com/data" | main.rs:14:22:14:43 | ...::get | provenance | MaD:1 Sink:MaD:1 |
| main.rs:23:9:23:16 | base_url | main.rs:25:28:25:53 | MacroExpr | provenance | |
| main.rs:23:20:23:39 | "http://example.com" | main.rs:23:9:23:16 | base_url | provenance | |
@@ -39,6 +41,8 @@ models
nodes
| main.rs:12:22:12:43 | ...::get | semmle.label | ...::get |
| main.rs:12:45:12:68 | "http://example.com/api" | semmle.label | "http://example.com/api" |
+| main.rs:13:22:13:43 | ...::get | semmle.label | ...::get |
+| main.rs:13:45:13:68 | "HTTP://EXAMPLE.COM/API" | semmle.label | "HTTP://EXAMPLE.COM/API" |
| main.rs:14:22:14:43 | ...::get | semmle.label | ...::get |
| main.rs:14:45:14:73 | "http://api.example.com/data" | semmle.label | "http://api.example.com/data" |
| main.rs:23:9:23:16 | base_url | semmle.label | base_url |
diff --git a/rust/ql/test/query-tests/security/CWE-319/main.rs b/rust/ql/test/query-tests/security/CWE-319/main.rs
index 908e6c61c2c1..0a3539923da6 100644
--- a/rust/ql/test/query-tests/security/CWE-319/main.rs
+++ b/rust/ql/test/query-tests/security/CWE-319/main.rs
@@ -10,7 +10,7 @@ fn main() {
fn test_direct_literals() {
// BAD: Direct HTTP URLs that should be flagged
let _response1 = reqwest::blocking::get("http://example.com/api").unwrap(); // $ Alert[rust/non-https-url]
- let _response2 = reqwest::blocking::get("HTTP://EXAMPLE.COM/API").unwrap(); // $ MISSING: Alert[rust/non-https-url]
+ let _response2 = reqwest::blocking::get("HTTP://EXAMPLE.COM/API").unwrap(); // $ Alert[rust/non-https-url]
let _response3 = reqwest::blocking::get("http://api.example.com/data").unwrap(); // $ Alert[rust/non-https-url]
// GOOD: HTTPS URLs that should not be flagged
From 0eb602aad2991216268e79bbe5db05472f2156c0 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 16 Sep 2025 14:00:43 +0100
Subject: [PATCH 09/12] Rust: Update a redirected URL.
---
.../src/queries/security/CWE-319/UseOfHttp.qhelp | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp b/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
index a8ca1d9c7c7e..a1345b189bb2 100644
--- a/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
+++ b/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
@@ -6,21 +6,21 @@
Constructing URLs with the HTTP protocol can lead to unsecured connections.
-Furthermore, constructing URLs with the HTTP protocol can create problems if other parts of the
-code expect HTTPS URLs. A typical pattern is to use libraries that expect secure connections,
+
Furthermore, constructing URLs with the HTTP protocol can create problems if other parts of the
+code expect HTTPS URLs. A typical pattern is to use libraries that expect secure connections,
which may fail or fall back to insecure behavior when provided with HTTP URLs instead of HTTPS URLs.
-When you construct a URL for network requests, ensure that you use an HTTPS URL rather than an HTTP URL.
+
When you construct a URL for network requests, ensure that you use an HTTPS URL rather than an HTTP URL.
Then, any connections that are made using that URL are secure SSL/TLS connections.
-The following example shows two ways of making a network request using a URL. When the request is
-made using an HTTP URL rather than an HTTPS URL, the connection is unsecured and can be intercepted
+
The following example shows two ways of making a network request using a URL. When the request is
+made using an HTTP URL rather than an HTTPS URL, the connection is unsecured and can be intercepted
by attackers. When the request is made using an HTTPS URL, the connection is a secure SSL/TLS connection.
@@ -34,15 +34,15 @@ by attackers. When the request is made using an HTTPS URL, the connection is a s
OWASP:
-Transport Layer Protection Cheat Sheet.
+Transport Layer Security Cheat Sheet.
OWASP Top 10:
A08:2021 - Software and Data Integrity Failures.
-Rust reqwest documentation:
+Rust reqwest documentation:
reqwest crate.
-
\ No newline at end of file
+
From 31bf86fd1bcb60946c038d1329f132647bc60288 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 16 Sep 2025 14:04:47 +0100
Subject: [PATCH 10/12] Rust: Improve the flow around the qhelp example.
---
rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp b/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
index a1345b189bb2..e4e0fc5eaa90 100644
--- a/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
+++ b/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
@@ -19,13 +19,14 @@ Then, any connections that are made using that URL are secure SSL/TLS connection
-The following example shows two ways of making a network request using a URL. When the request is
+
The following examples show two ways of making a network request using a URL. When the request is
made using an HTTP URL rather than an HTTPS URL, the connection is unsecured and can be intercepted
-by attackers. When the request is made using an HTTPS URL, the connection is a secure SSL/TLS connection.
+by attackers:
-A better approach is to use HTTPS:
+A better approach is to use HTTPS. When the request is made using an HTTPS URL, the connection
+is a secure SSL/TLS connection:
From 6f1fcbf41bf7484025a208af19a19630dc3a4df9 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 16 Sep 2025 17:08:22 +0100
Subject: [PATCH 11/12] Rust: Add IPv6 private address range (and explanatory
comments).
---
rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll b/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
index 5e0d534fb7d7..bd91cde238f3 100644
--- a/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
+++ b/rust/ql/lib/codeql/rust/security/UseOfHttpExtensions.qll
@@ -36,9 +36,12 @@ module UseOfHttp {
class HttpStringLiteral extends StringLiteralExpr {
HttpStringLiteral() {
exists(string s | this.getTextValue() = s |
- // Match HTTP URLs that are not private/local
+ // match HTTP URLs
s.regexpMatch("(?i)\"http://.*\"") and
- not s.regexpMatch("(?i)\"http://(localhost|127\\.0\\.0\\.1|192\\.168\\.[0-9]+\\.[0-9]+|10\\.[0-9]+\\.[0-9]+\\.[0-9]+|172\\.(1[6-9]|2[0-9]|3[01])\\.[0-9]+|\\[::1\\]|\\[0:0:0:0:0:0:0:1\\]).*\"")
+ // exclude private/local addresses:
+ // - IPv4: localhost / 127.0.0.1, 192.168.x.x, 10.x.x.x, 172.16.x.x -> 172.31.x.x
+ // - IPv6 (address inside []): ::1 (or 0:0:0:0:0:0:0:1), fc00::/7 (i.e. anything beginning `fcxx:` or `fdxx:`)
+ not s.regexpMatch("(?i)\"http://(localhost|127\\.0\\.0\\.1|192\\.168\\.[0-9]+\\.[0-9]+|10\\.[0-9]+\\.[0-9]+\\.[0-9]+|172\\.(1[6-9]|2[0-9]|3[01])\\.[0-9]+|\\[::1\\]|\\[0:0:0:0:0:0:0:1\\]|\\[f[cd][0-9a-f]{2}:.*\\]).*\"")
)
}
}
From c26a07bb1008a8122e6f39d8df24a128fc4c6e4c Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Fri, 19 Sep 2025 16:49:54 +0100
Subject: [PATCH 12/12] Apply suggestions from code review
Co-authored-by: Simon Friis Vindum
---
rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp b/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
index e4e0fc5eaa90..088f202965a9 100644
--- a/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
+++ b/rust/ql/src/queries/security/CWE-319/UseOfHttp.qhelp
@@ -4,7 +4,7 @@
-Constructing URLs with the HTTP protocol can lead to unsecured connections.
+Constructing URLs with the HTTP protocol can lead to insecure connections.
Furthermore, constructing URLs with the HTTP protocol can create problems if other parts of the
code expect HTTPS URLs. A typical pattern is to use libraries that expect secure connections,
@@ -14,7 +14,7 @@ which may fail or fall back to insecure behavior when provided with HTTP URLs in
When you construct a URL for network requests, ensure that you use an HTTPS URL rather than an HTTP URL.
-Then, any connections that are made using that URL are secure SSL/TLS connections.
+Then, any connections that are made using that URL are secure TLS connections.
@@ -26,7 +26,7 @@ by attackers:
A better approach is to use HTTPS. When the request is made using an HTTPS URL, the connection
-is a secure SSL/TLS connection:
+is a secure TLS connection: