-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Rust: New Query rust/disabled-certificate-check #20829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
QHelp previews: rust/ql/src/queries/security/CWE-295/DisabledCertificateCheck.qhelpDisabled TLS certificate checkThe Similarly, the RecommendationDo not set ExampleThe following code snippet shows a function that creates an HTTP client with certificate verification disabled: // BAD: Disabling certificate validation in Rust
let _client = reqwest::Client::builder()
.danger_accept_invalid_certs(true) // disables certificate validation
.build()
.unwrap();In production code, always configure clients to verify certificates: // GOOD: Certificate validation is enabled (default)
let _client = reqwest::Client::builder()
.danger_accept_invalid_certs(false) // certificate validation enabled explicitly
.build()
.unwrap();
let _client = native_tls::TlsConnector::builder() // certificate validation enabled by default
.build()
.unwrap();References
|
| exists(CallExprBase fc | | ||
| fc.getStaticTarget().(Function).getName().getText() = | ||
| ["danger_accept_invalid_certs", "danger_accept_invalid_hostnames"] and | ||
| fc.getArg(0) = this.asExpr().getExpr() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did try defining the sinks using models-as-data, but it proved to be less reliable in the absence of full modelling of the classes involved. For example in a chain Builder.a().b().sink(true), we need summaries for a() and b() to understand that sink(true) is being called on a Builder. With the QL-defined sinks that only match the function names, these models are not required - and given the very specific names of these functions, matching with just names is likely to be very accurate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I follow this. Since the sink here uses getStaticTarget it only works when we can resolve a target. Wouldn't we then also be able to find the MaD in the same cases as MaD works by finding the target and checking its canonical name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I think you're right, maybe something slightly different is going on. I will investigate...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I must've made a mistake in my initial MaD implementation. My second attempt (now pushed) works fine. I did find a couple of additional sinks with a matching name and purpose in the MRVA-1000, so I've modelled them as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... after experimenting with this some more, we still lose quite a lot of sinks with the modelled sinks compared to the old name matching (e.g. the MRVA-1000 alone would require 10+ more models above what I'd already added). And the name matching is quite precise due to those long and specific function names. So I've reinstated that, but I'm calling it a heuristic sink now. If you disable the heuristic sinks, we do still have reliable MaD sinks for the common cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces a new security query rust/disabled-certificate-check to detect when TLS certificate validation is disabled in Rust code. The query identifies calls to danger_accept_invalid_certs and danger_accept_invalid_hostnames methods that accept true as an argument, which disables important security checks.
- Implements data flow analysis to track
trueliterals flowing into certificate validation configuration methods - Supports detection across popular Rust HTTP/TLS libraries (reqwest, native-tls)
- Includes comprehensive test cases with expected results
Reviewed Changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| rust/ql/test/query-tests/security/CWE-295/options.yml | Configures test dependencies for reqwest, native-tls, and rand crates |
| rust/ql/test/query-tests/security/CWE-295/main.rs | Test cases covering native-tls, reqwest, and data flow scenarios |
| rust/ql/test/query-tests/security/CWE-295/DisabledCertificateCheck.qlref | Query reference for running the test |
| rust/ql/test/query-tests/security/CWE-295/DisabledCertificateCheck.expected | Expected test results with 14 findings |
| rust/ql/test/query-tests/security/CWE-295/Cargo.lock | Generated lock file with resolved dependencies |
| rust/ql/src/queries/summary/Stats.qll | Adds import for new security extension module |
| rust/ql/src/queries/security/CWE-295/DisabledCertificateCheckGood.rs | Example of safe certificate validation configuration |
| rust/ql/src/queries/security/CWE-295/DisabledCertificateCheckBad.rs | Example of unsafe certificate validation disabling |
| rust/ql/src/queries/security/CWE-295/DisabledCertificateCheck.ql | Main query implementation using data flow analysis |
| rust/ql/src/queries/security/CWE-295/DisabledCertificateCheck.qhelp | Help documentation explaining the vulnerability and remediation |
| rust/ql/src/change-notes/2025-11-12-disabled-certificate-check.md | Change note documenting the new query |
| rust/ql/lib/codeql/rust/security/DisabledCertificateCheckExtensions.qll | Extension library defining sinks for certificate check configuration |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
I'll review this on behalf of Docs today. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@geoffw0 - this LGTM. I made a few minor editorial suggestions. As usual, feel free to ignore anything you don't agree with.
Approving on behalf of Docs.
rust/ql/src/queries/security/CWE-295/DisabledCertificateCheck.qhelp
Outdated
Show resolved
Hide resolved
rust/ql/src/queries/security/CWE-295/DisabledCertificateCheck.qhelp
Outdated
Show resolved
Hide resolved
rust/ql/src/queries/security/CWE-295/DisabledCertificateCheck.qhelp
Outdated
Show resolved
Hide resolved
rust/ql/src/queries/security/CWE-295/DisabledCertificateCheck.ql
Outdated
Show resolved
Hide resolved
Co-authored-by: mc <[email protected]>
|
Thanks for the review and suggestions @mchammer01 ! |
|
Thanks for looking at my suggestions and for improving the qhelp file further @geoffw0 🙇🏻♀️ |
paldepind
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks really good!
| exists(CallExprBase fc | | ||
| fc.getStaticTarget().(Function).getName().getText() = | ||
| ["danger_accept_invalid_certs", "danger_accept_invalid_hostnames"] and | ||
| fc.getArg(0) = this.asExpr().getExpr() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I follow this. Since the sink here uses getStaticTarget it only works when we can resolve a target. Wouldn't we then also be able to find the MaD in the same cases as MaD works by finding the target and checking its canonical name?
| import DisabledCertificateCheckExtensions | ||
|
|
||
| predicate isSource(DataFlow::Node node) { | ||
| node.asExpr().getExpr().(BooleanLiteralExpr).getTextValue() = "true" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to also have tainted user input as a sink? In such cases a user could make the value true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, makes sense - I'm not sure if it will add many more results in practice but I'll add taint sources as a flow source here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. It was a bit tricky to test as we didn't have any (direct) bool taint sources until now, but we've ended up with a bunch of new models and a second change note coming from that. I'll do a second DCA run as well - I think the effect on results for this query is going to be quite marginal, but best to check...
| fc.getStaticTarget().(Function).getName().getText() = | ||
| ["danger_accept_invalid_certs", "danger_accept_invalid_hostnames"] and | ||
| fc.getArg(0) = this.asExpr() and | ||
| // don't duplicate modelled sinks |
Check warning
Code scanning / CodeQL
Misspelling Warning
Adds a new Rust query
rust/disabled-certificate-check, to detect disabled TLS certificate checks. Based ongo/disabled-certificate-checkthough there are some differences in both the sinks and query logic. Includes tests and.qhelpwith examples.On the MRVA-1000, we find 73 results across 29 projects. A sample of these results LGTM (though a good number are only in tests; only a minority needed data flow to be found).