Skip to content

Commit 20e0e1d

Browse files
committed
Rust: Add cleartext transmission query
1 parent 86bd3b8 commit 20e0e1d

File tree

11 files changed

+341
-0
lines changed

11 files changed

+341
-0
lines changed

rust/ql/lib/codeql/rust/frameworks/reqwest.model.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ extensions:
55
data:
66
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "crate::get", "ReturnValue.Field[crate::result::Result::Ok(0)]", "remote", "manual"]
77
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "crate::blocking::get", "ReturnValue.Field[crate::result::Result::Ok(0)]", "remote", "manual"]
8+
- addsTo:
9+
pack: codeql/rust-all
10+
extensible: sinkModel
11+
data:
12+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "crate::blocking::get", "Argument[0]", "transmission", "manual"]
13+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::async_impl::client::Client>::request", "Argument[1]", "transmission", "manual"]
14+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::blocking::client::Client>::request", "Argument[1]", "transmission", "manual"]
815
- addsTo:
916
pack: codeql/rust-all
1017
extensible: summaryModel
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Models for the `url` crate
2+
extensions:
3+
- addsTo:
4+
pack: codeql/rust-all
5+
extensible: summaryModel
6+
data:
7+
- ["repo:https://github.com/servo/rust-url:url", "<crate::Url>::parse", "Argument[0].Reference", "ReturnValue.Field[crate::result::Result::Ok(0)]", "taint", "manual"]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
private import codeql.util.Unit
2+
private import rust
3+
private import codeql.rust.dataflow.DataFlow
4+
private import codeql.rust.dataflow.FlowSink
5+
6+
/**
7+
* A data flow sink for cleartext transmission vulnerabilities. That is,
8+
* a `DataFlow::Node` of something that is transmitted over a network.
9+
*/
10+
abstract class CleartextTransmissionSink extends DataFlow::Node { }
11+
12+
/**
13+
* A barrier for cleartext transmission vulnerabilities.
14+
*/
15+
abstract class CleartextTransmissionBarrier extends DataFlow::Node { }
16+
17+
/**
18+
* A unit class for adding additional flow steps.
19+
*/
20+
class CleartextTransmissionAdditionalFlowStep extends Unit {
21+
/**
22+
* Holds if the step from `node1` to `node2` should be considered a flow
23+
* step for paths related to cleartext transmission vulnerabilities.
24+
*/
25+
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
26+
}
27+
28+
/**
29+
* A sink defined through MaD.
30+
*/
31+
private class MadCleartextTransmissionSink extends CleartextTransmissionSink {
32+
MadCleartextTransmissionSink() { sinkNode(this, "transmission") }
33+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
2+
extensions:
3+
- addsTo:
4+
pack: codeql/rust-all
5+
extensible: sinkModel
6+
data:
7+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::async_impl::client::Client>::delete", "Argument[0]", "transmission", "df-generated"]
8+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::async_impl::client::Client>::get", "Argument[0]", "transmission", "df-generated"]
9+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::async_impl::client::Client>::head", "Argument[0]", "transmission", "df-generated"]
10+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::async_impl::client::Client>::patch", "Argument[0]", "transmission", "df-generated"]
11+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::async_impl::client::Client>::post", "Argument[0]", "transmission", "df-generated"]
12+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::async_impl::client::Client>::put", "Argument[0]", "transmission", "df-generated"]
13+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::connect::Connector as crate::Service>::call", "Argument[0]", "log-injection", "df-generated"]
14+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "<crate::connect::ConnectorService as crate::Service>::call", "Argument[0]", "log-injection", "df-generated"]
15+
- ["repo:https://github.com/seanmonstar/reqwest:reqwest", "crate::get", "Argument[0]", "transmission", "df-generated"]
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
<overview>
4+
5+
<p>
6+
Sensitive information that is transmitted without encryption may be accessible
7+
to an attacker.
8+
</p>
9+
10+
</overview>
11+
<recommendation>
12+
13+
<p>
14+
Ensure that sensitive information is always encrypted before being transmitted
15+
over the network. In general, decrypt sensitive information only at the point
16+
where it is necessary for it to be used in cleartext. Avoid transmitting
17+
sensitive information when it is not necessary to.
18+
</p>
19+
20+
</recommendation>
21+
<example>
22+
23+
<p>
24+
The following example shows three cases of transmitting information. In the
25+
'BAD' case, the data transmitted is sensitive (a password) and is not encrypted
26+
as it occurs as a URL parameter. In the 'GOOD' cases, the data is either not
27+
sensitive, or is protected with encryption. When encryption is used, take care
28+
to select a secure modern encryption algorithm, and put suitable key management
29+
practices into place.
30+
</p>
31+
32+
<sample src="CleartextTransmission.rs" />
33+
34+
</example>
35+
<references>
36+
37+
<li>
38+
OWASP Top 10:2021:
39+
<a href="https://owasp.org/Top10/A02_2021-Cryptographic_Failures/">A02:2021 � Cryptographic Failures</a>.
40+
</li>
41+
<li>
42+
OWASP:
43+
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html">Key Management Cheat Sheet</a>.
44+
</li>
45+
46+
</references>
47+
</qhelp>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* @name Cleartext transmission of sensitive information
3+
* @description Transmitting sensitive information across a network in
4+
* cleartext can expose it to an attacker.
5+
* @kind path-problem
6+
* @problem.severity warning
7+
* @security-severity 7.5
8+
* @precision high
9+
* @id rust/cleartext-transmission
10+
* @tags security
11+
* external/cwe/cwe-319
12+
*/
13+
14+
import rust
15+
import codeql.rust.dataflow.DataFlow
16+
import codeql.rust.security.SensitiveData
17+
import codeql.rust.dataflow.DataFlow
18+
import codeql.rust.dataflow.TaintTracking
19+
import codeql.rust.security.CleartextTransmissionExtensions
20+
21+
/**
22+
* A taint configuration from sensitive information to expressions that are
23+
* transmitted over a network.
24+
*/
25+
module CleartextTransmissionConfig implements DataFlow::ConfigSig {
26+
predicate isSource(DataFlow::Node node) { node instanceof SensitiveData }
27+
28+
predicate isSink(DataFlow::Node node) { node instanceof CleartextTransmissionSink }
29+
30+
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof CleartextTransmissionBarrier }
31+
32+
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
33+
any(CleartextTransmissionAdditionalFlowStep s).step(nodeFrom, nodeTo)
34+
}
35+
36+
predicate isBarrierIn(DataFlow::Node node) {
37+
// make sources barriers so that we only report the closest instance
38+
isSource(node)
39+
}
40+
}
41+
42+
module CleartextTransmissionFlow = TaintTracking::Global<CleartextTransmissionConfig>;
43+
44+
import CleartextTransmissionFlow::PathGraph
45+
46+
from CleartextTransmissionFlow::PathNode sourceNode, CleartextTransmissionFlow::PathNode sinkNode
47+
where CleartextTransmissionFlow::flowPath(sourceNode, sinkNode)
48+
select sinkNode.getNode(), sourceNode, sinkNode,
49+
"The operation '" + sinkNode.getNode().toString() +
50+
"', transmits data which may contain unencrypted sensitive data from $@.", sourceNode,
51+
sourceNode.getNode().toString()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
func getData() {
2+
// ...
3+
4+
// GOOD: not sensitive information
5+
let body = reqwest::get("https://example.com/data").await?.text().await?;
6+
7+
// BAD: sensitive information sent in cleartext
8+
let body = reqwest::get(format!("https://example.com/data?password={password}")).await?.text().await?;
9+
10+
// GOOD: encrypted sensitive information sent
11+
let encryptedPassword = encrypt(password, encryptionKey);
12+
let body = reqwest::get(format!("https://example.com/data?password={encryptedPassword}")).await?.text().await?;
13+
14+
// ...
15+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#select
2+
| main.rs:7:5:7:26 | ...::get | main.rs:6:50:6:57 | password | main.rs:7:5:7:26 | ...::get | The operation '...::get', transmits data which may contain unencrypted sensitive data from $@. | main.rs:6:50:6:57 | password | password |
3+
| main.rs:14:5:14:26 | ...::get | main.rs:12:50:12:57 | password | main.rs:14:5:14:26 | ...::get | The operation '...::get', transmits data which may contain unencrypted sensitive data from $@. | main.rs:12:50:12:57 | password | password |
4+
| main.rs:21:12:21:15 | post | main.rs:19:50:19:57 | password | main.rs:21:12:21:15 | post | The operation 'post', transmits data which may contain unencrypted sensitive data from $@. | main.rs:19:50:19:57 | password | password |
5+
| main.rs:28:12:28:18 | request | main.rs:26:50:26:57 | password | main.rs:28:12:28:18 | request | The operation 'request', transmits data which may contain unencrypted sensitive data from $@. | main.rs:26:50:26:57 | password | password |
6+
| main.rs:35:12:35:18 | request | main.rs:33:50:33:57 | password | main.rs:35:12:35:18 | request | The operation 'request', transmits data which may contain unencrypted sensitive data from $@. | main.rs:33:50:33:57 | password | password |
7+
edges
8+
| main.rs:6:9:6:11 | url | main.rs:7:28:7:30 | url | provenance | |
9+
| main.rs:6:15:6:58 | res | main.rs:6:23:6:57 | { ... } | provenance | |
10+
| main.rs:6:23:6:57 | ...::format(...) | main.rs:6:15:6:58 | res | provenance | |
11+
| main.rs:6:23:6:57 | ...::must_use(...) | main.rs:6:9:6:11 | url | provenance | |
12+
| main.rs:6:23:6:57 | MacroExpr | main.rs:6:23:6:57 | ...::format(...) | provenance | MaD:5 |
13+
| main.rs:6:23:6:57 | { ... } | main.rs:6:23:6:57 | ...::must_use(...) | provenance | MaD:7 |
14+
| main.rs:6:50:6:57 | password | main.rs:6:23:6:57 | MacroExpr | provenance | |
15+
| main.rs:7:28:7:30 | url | main.rs:7:5:7:26 | ...::get | provenance | MaD:4 Sink:MaD:4 |
16+
| main.rs:12:9:12:15 | address | main.rs:13:27:13:33 | address | provenance | |
17+
| main.rs:12:19:12:60 | res | main.rs:12:27:12:59 | { ... } | provenance | |
18+
| main.rs:12:27:12:59 | ...::format(...) | main.rs:12:19:12:60 | res | provenance | |
19+
| main.rs:12:27:12:59 | ...::must_use(...) | main.rs:12:9:12:15 | address | provenance | |
20+
| main.rs:12:27:12:59 | MacroExpr | main.rs:12:27:12:59 | ...::format(...) | provenance | MaD:5 |
21+
| main.rs:12:27:12:59 | { ... } | main.rs:12:27:12:59 | ...::must_use(...) | provenance | MaD:7 |
22+
| main.rs:12:50:12:57 | password | main.rs:12:27:12:59 | MacroExpr | provenance | |
23+
| main.rs:13:9:13:11 | url | main.rs:14:28:14:30 | url | provenance | |
24+
| main.rs:13:15:13:34 | ...::parse(...) [Ok] | main.rs:13:15:13:43 | ... .unwrap(...) | provenance | MaD:6 |
25+
| main.rs:13:15:13:43 | ... .unwrap(...) | main.rs:13:9:13:11 | url | provenance | |
26+
| main.rs:13:26:13:33 | &address [&ref] | main.rs:13:15:13:34 | ...::parse(...) [Ok] | provenance | MaD:8 |
27+
| main.rs:13:27:13:33 | address | main.rs:13:26:13:33 | &address [&ref] | provenance | |
28+
| main.rs:14:28:14:30 | url | main.rs:14:5:14:26 | ...::get | provenance | MaD:4 Sink:MaD:4 |
29+
| main.rs:19:9:19:11 | url | main.rs:21:17:21:19 | url | provenance | |
30+
| main.rs:19:15:19:58 | res | main.rs:19:23:19:57 | { ... } | provenance | |
31+
| main.rs:19:23:19:57 | ...::format(...) | main.rs:19:15:19:58 | res | provenance | |
32+
| main.rs:19:23:19:57 | ...::must_use(...) | main.rs:19:9:19:11 | url | provenance | |
33+
| main.rs:19:23:19:57 | MacroExpr | main.rs:19:23:19:57 | ...::format(...) | provenance | MaD:5 |
34+
| main.rs:19:23:19:57 | { ... } | main.rs:19:23:19:57 | ...::must_use(...) | provenance | MaD:7 |
35+
| main.rs:19:50:19:57 | password | main.rs:19:23:19:57 | MacroExpr | provenance | |
36+
| main.rs:21:17:21:19 | url | main.rs:21:12:21:15 | post | provenance | MaD:1 Sink:MaD:1 |
37+
| main.rs:26:9:26:11 | url | main.rs:28:33:28:35 | url | provenance | |
38+
| main.rs:26:15:26:58 | res | main.rs:26:23:26:57 | { ... } | provenance | |
39+
| main.rs:26:23:26:57 | ...::format(...) | main.rs:26:15:26:58 | res | provenance | |
40+
| main.rs:26:23:26:57 | ...::must_use(...) | main.rs:26:9:26:11 | url | provenance | |
41+
| main.rs:26:23:26:57 | MacroExpr | main.rs:26:23:26:57 | ...::format(...) | provenance | MaD:5 |
42+
| main.rs:26:23:26:57 | { ... } | main.rs:26:23:26:57 | ...::must_use(...) | provenance | MaD:7 |
43+
| main.rs:26:50:26:57 | password | main.rs:26:23:26:57 | MacroExpr | provenance | |
44+
| main.rs:28:33:28:35 | url | main.rs:28:12:28:18 | request | provenance | MaD:3 Sink:MaD:3 |
45+
| main.rs:33:9:33:11 | url | main.rs:35:33:35:35 | url | provenance | |
46+
| main.rs:33:15:33:58 | res | main.rs:33:23:33:57 | { ... } | provenance | |
47+
| main.rs:33:23:33:57 | ...::format(...) | main.rs:33:15:33:58 | res | provenance | |
48+
| main.rs:33:23:33:57 | ...::must_use(...) | main.rs:33:9:33:11 | url | provenance | |
49+
| main.rs:33:23:33:57 | MacroExpr | main.rs:33:23:33:57 | ...::format(...) | provenance | MaD:5 |
50+
| main.rs:33:23:33:57 | { ... } | main.rs:33:23:33:57 | ...::must_use(...) | provenance | MaD:7 |
51+
| main.rs:33:50:33:57 | password | main.rs:33:23:33:57 | MacroExpr | provenance | |
52+
| main.rs:35:33:35:35 | url | main.rs:35:12:35:18 | request | provenance | MaD:2 Sink:MaD:2 |
53+
models
54+
| 1 | Sink: repo:https://github.com/seanmonstar/reqwest:reqwest; <crate::async_impl::client::Client>::post; transmission; Argument[0] |
55+
| 2 | Sink: repo:https://github.com/seanmonstar/reqwest:reqwest; <crate::async_impl::client::Client>::request; transmission; Argument[1] |
56+
| 3 | Sink: repo:https://github.com/seanmonstar/reqwest:reqwest; <crate::blocking::client::Client>::request; transmission; Argument[1] |
57+
| 4 | Sink: repo:https://github.com/seanmonstar/reqwest:reqwest; crate::blocking::get; transmission; Argument[0] |
58+
| 5 | Summary: lang:alloc; crate::fmt::format; Argument[0]; ReturnValue; taint |
59+
| 6 | Summary: lang:core; <crate::result::Result>::unwrap; Argument[self].Field[crate::result::Result::Ok(0)]; ReturnValue; value |
60+
| 7 | Summary: lang:core; crate::hint::must_use; Argument[0]; ReturnValue; value |
61+
| 8 | Summary: repo:https://github.com/servo/rust-url:url; <crate::Url>::parse; Argument[0].Reference; ReturnValue.Field[crate::result::Result::Ok(0)]; taint |
62+
nodes
63+
| main.rs:6:9:6:11 | url | semmle.label | url |
64+
| main.rs:6:15:6:58 | res | semmle.label | res |
65+
| main.rs:6:23:6:57 | ...::format(...) | semmle.label | ...::format(...) |
66+
| main.rs:6:23:6:57 | ...::must_use(...) | semmle.label | ...::must_use(...) |
67+
| main.rs:6:23:6:57 | MacroExpr | semmle.label | MacroExpr |
68+
| main.rs:6:23:6:57 | { ... } | semmle.label | { ... } |
69+
| main.rs:6:50:6:57 | password | semmle.label | password |
70+
| main.rs:7:5:7:26 | ...::get | semmle.label | ...::get |
71+
| main.rs:7:28:7:30 | url | semmle.label | url |
72+
| main.rs:12:9:12:15 | address | semmle.label | address |
73+
| main.rs:12:19:12:60 | res | semmle.label | res |
74+
| main.rs:12:27:12:59 | ...::format(...) | semmle.label | ...::format(...) |
75+
| main.rs:12:27:12:59 | ...::must_use(...) | semmle.label | ...::must_use(...) |
76+
| main.rs:12:27:12:59 | MacroExpr | semmle.label | MacroExpr |
77+
| main.rs:12:27:12:59 | { ... } | semmle.label | { ... } |
78+
| main.rs:12:50:12:57 | password | semmle.label | password |
79+
| main.rs:13:9:13:11 | url | semmle.label | url |
80+
| main.rs:13:15:13:34 | ...::parse(...) [Ok] | semmle.label | ...::parse(...) [Ok] |
81+
| main.rs:13:15:13:43 | ... .unwrap(...) | semmle.label | ... .unwrap(...) |
82+
| main.rs:13:26:13:33 | &address [&ref] | semmle.label | &address [&ref] |
83+
| main.rs:13:27:13:33 | address | semmle.label | address |
84+
| main.rs:14:5:14:26 | ...::get | semmle.label | ...::get |
85+
| main.rs:14:28:14:30 | url | semmle.label | url |
86+
| main.rs:19:9:19:11 | url | semmle.label | url |
87+
| main.rs:19:15:19:58 | res | semmle.label | res |
88+
| main.rs:19:23:19:57 | ...::format(...) | semmle.label | ...::format(...) |
89+
| main.rs:19:23:19:57 | ...::must_use(...) | semmle.label | ...::must_use(...) |
90+
| main.rs:19:23:19:57 | MacroExpr | semmle.label | MacroExpr |
91+
| main.rs:19:23:19:57 | { ... } | semmle.label | { ... } |
92+
| main.rs:19:50:19:57 | password | semmle.label | password |
93+
| main.rs:21:12:21:15 | post | semmle.label | post |
94+
| main.rs:21:17:21:19 | url | semmle.label | url |
95+
| main.rs:26:9:26:11 | url | semmle.label | url |
96+
| main.rs:26:15:26:58 | res | semmle.label | res |
97+
| main.rs:26:23:26:57 | ...::format(...) | semmle.label | ...::format(...) |
98+
| main.rs:26:23:26:57 | ...::must_use(...) | semmle.label | ...::must_use(...) |
99+
| main.rs:26:23:26:57 | MacroExpr | semmle.label | MacroExpr |
100+
| main.rs:26:23:26:57 | { ... } | semmle.label | { ... } |
101+
| main.rs:26:50:26:57 | password | semmle.label | password |
102+
| main.rs:28:12:28:18 | request | semmle.label | request |
103+
| main.rs:28:33:28:35 | url | semmle.label | url |
104+
| main.rs:33:9:33:11 | url | semmle.label | url |
105+
| main.rs:33:15:33:58 | res | semmle.label | res |
106+
| main.rs:33:23:33:57 | ...::format(...) | semmle.label | ...::format(...) |
107+
| main.rs:33:23:33:57 | ...::must_use(...) | semmle.label | ...::must_use(...) |
108+
| main.rs:33:23:33:57 | MacroExpr | semmle.label | MacroExpr |
109+
| main.rs:33:23:33:57 | { ... } | semmle.label | { ... } |
110+
| main.rs:33:50:33:57 | password | semmle.label | password |
111+
| main.rs:35:12:35:18 | request | semmle.label | request |
112+
| main.rs:35:33:35:35 | url | semmle.label | url |
113+
subpaths
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
query: queries/security/CWE-311/CleartextTransmission.ql
2+
postprocess:
3+
- utils/test/PrettyPrintModels.ql
4+
- utils/test/InlineExpectationsTestQuery.ql
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use http::Method;
2+
use url::Url;
3+
4+
fn get_with_password_in_url() {
5+
let password = "Hunter12";
6+
let url = format!("example.com?password={}", password); // $ Source
7+
reqwest::blocking::get(url).unwrap().text().unwrap(); // $ Alert[rust/cleartext-transmission]
8+
}
9+
10+
fn get_with_password_in_constructed_url() {
11+
let password = "Hunter12";
12+
let address = format!("example.com?password={password}"); // $ Source
13+
let url = Url::parse(&address).unwrap();
14+
reqwest::blocking::get(url).unwrap().text().unwrap(); // $ Alert[rust/cleartext-transmission]
15+
}
16+
17+
fn post_with_password_in_url() {
18+
let password = "Hunter12";
19+
let url = format!("example.com?password={}", password); // $ Source
20+
let client = reqwest::Client::new();
21+
client.post(url).body("body").send(); // $ Alert[rust/cleartext-transmission]
22+
}
23+
24+
fn request_blocking_put_with_password_in_url() {
25+
let password = "Hunter12";
26+
let url = format!("example.com?password={}", password); // $ Source
27+
let client = reqwest::blocking::Client::new();
28+
client.request(Method::PUT, url).body("body").send(); // $ Alert[rust/cleartext-transmission]
29+
}
30+
31+
fn request_put_with_password_in_url() {
32+
let password = "Hunter12";
33+
let url = format!("example.com?password={}", password); // $ Source
34+
let client = reqwest::Client::new();
35+
client.request(Method::PUT, url).body("body").send(); // $ Alert[rust/cleartext-transmission]
36+
}
37+
38+
fn main() {
39+
get_with_password_in_url();
40+
get_with_password_in_constructed_url();
41+
post_with_password_in_url();
42+
request_blocking_put_with_password_in_url();
43+
request_put_with_password_in_url();
44+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
qltest_cargo_check: true
2+
qltest_dependencies:
3+
- reqwest = { version = "0.12.9", features = ["blocking"] }
4+
- http = { version = "1.2.0" }
5+
- url = { version = "2.5.4" }

0 commit comments

Comments
 (0)