From 677afff7af4f6f81ec23b9760a139ee89e04717b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Jun 2026 09:12:29 +0000 Subject: [PATCH 1/3] Add CWE-295 C# query for accepting any TLS certificate --- .../CWE-295/AcceptAnyCertificate.cs | 22 ++++ .../CWE-295/AcceptAnyCertificate.qhelp | 52 +++++++++ .../CWE-295/AcceptAnyCertificate.ql | 101 ++++++++++++++++++ .../2026-06-10-accept-any-certificate.md | 4 + .../AcceptAnyCertificate.expected | 24 +++++ .../AcceptAnyCertificate.qlref | 1 + .../CWE-295/AcceptAnyCertificate/Test.cs | 89 +++++++++++++++ .../CWE-295/AcceptAnyCertificate/options | 2 + 8 files changed, 295 insertions(+) create mode 100644 csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.cs create mode 100644 csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.qhelp create mode 100644 csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.ql create mode 100644 csharp/ql/src/change-notes/2026-06-10-accept-any-certificate.md create mode 100644 csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected create mode 100644 csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.qlref create mode 100644 csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs create mode 100644 csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/options diff --git a/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.cs b/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.cs new file mode 100644 index 000000000000..e279798df9a3 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.cs @@ -0,0 +1,22 @@ +using System.Net.Http; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; + +public class CertificateValidation +{ + public void Bad() + { + var handler = new HttpClientHandler(); + // BAD: the callback always returns true, so every certificate is trusted. + handler.ServerCertificateCustomValidationCallback = + (request, certificate, chain, errors) => true; + } + + public void Good() + { + var handler = new HttpClientHandler(); + // GOOD: the certificate is only trusted when there are no validation errors. + handler.ServerCertificateCustomValidationCallback = + (request, certificate, chain, errors) => errors == SslPolicyErrors.None; + } +} diff --git a/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.qhelp b/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.qhelp new file mode 100644 index 000000000000..b499a626acd5 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.qhelp @@ -0,0 +1,52 @@ + + + +

+A TLS/SSL certificate validation callback that always returns true trusts every certificate, +regardless of any validation errors that were detected. This allows an attacker to perform a machine-in-the-middle +attack against the application, therefore breaking any security that Transport Layer Security (TLS) provides. +

+ +

+An attack might look like this: +

+ +
    +
  1. The vulnerable program connects to https://example.com.
  2. +
  3. The attacker intercepts this connection and presents a valid, self-signed certificate for https://example.com.
  4. +
  5. The vulnerable program calls the certificate validation callback to check whether it should trust the certificate.
  6. +
  7. The callback ignores the SslPolicyErrors argument and returns true.
  8. +
  9. The vulnerable program accepts the certificate and proceeds with the connection, since the callback indicated that the certificate is trusted.
  10. +
  11. The attacker can now read the data the program sends to https://example.com and/or alter its replies while the program thinks the connection is secure.
  12. +
+
+ + +

+Do not use a certificate validation callback that unconditionally returns true. +Either rely on the default certificate validation, or implement a callback that inspects the +SslPolicyErrors argument and only trusts a specific, known certificate (for example, when +using a self-signed certificate that has been explicitly pinned). +

+
+ + +

+In the first (bad) example, the callback always returns true and therefore trusts any certificate, +which allows an attacker to perform a machine-in-the-middle attack. In the second (good) example, the callback +returns true only when there are no validation errors. +

+ +
+ + +
  • Microsoft Learn: + RemoteCertificateValidationCallback Delegate.
  • +
  • Microsoft Learn: + CA5359: Do not disable certificate validation.
  • +
  • OWASP: + Manipulator-in-the-middle attack.
  • +
    +
    diff --git a/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.ql b/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.ql new file mode 100644 index 000000000000..6c1df334b92d --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.ql @@ -0,0 +1,101 @@ +/** + * @name Accepting any TLS certificate during validation + * @description A certificate validation callback that always accepts any certificate + * allows an attacker to perform a machine-in-the-middle attack. + * @kind path-problem + * @problem.severity error + * @security-severity 7.5 + * @precision high + * @id cs/accept-any-certificate + * @tags security + * external/cwe/cwe-295 + */ + +import csharp +import semmle.code.csharp.dataflow.DataFlow::DataFlow +import AcceptAnyCertificate::PathGraph + +/** + * Holds if `c` always returns `true` and never returns `false`, i.e. it accepts + * every input it is given. + */ +predicate alwaysReturnsTrue(Callable c) { + c.getReturnType() instanceof BoolType and + // There is at least one returned value, and every returned value is the + // constant `true`. + forex(Expr ret | c.canReturn(ret) | ret.getValue() = "true") +} + +/** + * A delegate type used as a TLS/SSL certificate validation callback. Such a + * delegate returns a `bool` (whether the certificate is trusted) and takes a + * `System.Net.Security.SslPolicyErrors` parameter describing any validation + * errors that were found. This covers `RemoteCertificateValidationCallback` as + * well as the `Func<..., SslPolicyErrors, bool>` callbacks used by, for example, + * `HttpClientHandler.ServerCertificateCustomValidationCallback`. + */ +class CertificateValidationCallbackType extends DelegateType { + CertificateValidationCallbackType() { + this.getReturnType() instanceof BoolType and + this.getAParameter().getType().hasFullyQualifiedName("System.Net.Security", "SslPolicyErrors") + } +} + +/** + * Gets a callable that always accepts any certificate, referenced by the + * delegate-producing expression `e`. + */ +Callable getAcceptingCallable(Expr e) { + // A lambda or anonymous method, e.g. `(sender, cert, chain, errors) => true`. + result = e and + alwaysReturnsTrue(e) + or + // A method group, e.g. `AcceptAllCertificates`, possibly wrapped in an + // (implicit or explicit) delegate creation. + result = e.(DelegateCreation).getArgument().(CallableAccess).getTarget() and + alwaysReturnsTrue(result) + or + result = e.(CallableAccess).getTarget() and + alwaysReturnsTrue(result) +} + +module AcceptAnyCertificateConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(getAcceptingCallable(source.asExpr())) + or + // `HttpClientHandler.DangerousAcceptAnyServerCertificateValidator` is a + // built-in callback that accepts every certificate. + source + .asExpr() + .(PropertyAccess) + .getTarget() + .hasName("DangerousAcceptAnyServerCertificateValidator") + } + + predicate isSink(DataFlow::Node sink) { + // The value assigned to a property, field or local of certificate + // validation callback type. + exists(Assignable a | + a.getType() instanceof CertificateValidationCallbackType and + sink.asExpr() = a.getAnAssignedValue() + ) + or + // The value passed as a certificate validation callback argument, e.g. to + // the `SslStream` constructor. + exists(Call call, Parameter p | + p = call.getTarget().getAParameter() and + p.getType() instanceof CertificateValidationCallbackType and + sink.asExpr() = call.getArgumentForParameter(p) + ) + } + + predicate observeDiffInformedIncrementalMode() { any() } +} + +module AcceptAnyCertificate = DataFlow::Global; + +from AcceptAnyCertificate::PathNode source, AcceptAnyCertificate::PathNode sink +where AcceptAnyCertificate::flowPath(source, sink) +select sink.getNode(), source, sink, + "This TLS certificate validation $@, which trusts any certificate.", source.getNode(), + "uses a callback" diff --git a/csharp/ql/src/change-notes/2026-06-10-accept-any-certificate.md b/csharp/ql/src/change-notes/2026-06-10-accept-any-certificate.md new file mode 100644 index 000000000000..c63f53e00c9f --- /dev/null +++ b/csharp/ql/src/change-notes/2026-06-10-accept-any-certificate.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* Added a new query, `cs/accept-any-certificate`, to detect TLS/SSL certificate validation callbacks that always accept any certificate (CWE-295). diff --git a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected new file mode 100644 index 000000000000..8e9becce109a --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected @@ -0,0 +1,24 @@ +edges +| Test.cs:64:45:64:52 | access to local variable callback : (...) => ... | Test.cs:67:48:67:55 | access to local variable callback | provenance | | +| Test.cs:65:13:65:56 | (...) => ... : (...) => ... | Test.cs:64:45:64:52 | access to local variable callback : (...) => ... | provenance | | +nodes +| Test.cs:14:13:14:57 | (...) => ... | semmle.label | (...) => ... | +| Test.cs:22:13:25:13 | (...) => ... | semmle.label | (...) => ... | +| Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | semmle.label | access to property DangerousAcceptAnyServerCertificateValidator | +| Test.cs:40:13:40:56 | (...) => ... | semmle.label | (...) => ... | +| Test.cs:52:67:52:75 | delegate creation of type RemoteCertificateValidationCallback | semmle.label | delegate creation of type RemoteCertificateValidationCallback | +| Test.cs:59:13:59:56 | (...) => ... | semmle.label | (...) => ... | +| Test.cs:64:45:64:52 | access to local variable callback : (...) => ... | semmle.label | access to local variable callback : (...) => ... | +| Test.cs:65:13:65:56 | (...) => ... | semmle.label | (...) => ... | +| Test.cs:65:13:65:56 | (...) => ... : (...) => ... | semmle.label | (...) => ... : (...) => ... | +| Test.cs:67:48:67:55 | access to local variable callback | semmle.label | access to local variable callback | +subpaths +#select +| Test.cs:14:13:14:57 | (...) => ... | Test.cs:14:13:14:57 | (...) => ... | Test.cs:14:13:14:57 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:14:13:14:57 | (...) => ... | uses a callback | +| Test.cs:22:13:25:13 | (...) => ... | Test.cs:22:13:25:13 | (...) => ... | Test.cs:22:13:25:13 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:22:13:25:13 | (...) => ... | uses a callback | +| Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | This TLS certificate validation $@, which trusts any certificate. | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | uses a callback | +| Test.cs:40:13:40:56 | (...) => ... | Test.cs:40:13:40:56 | (...) => ... | Test.cs:40:13:40:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:40:13:40:56 | (...) => ... | uses a callback | +| Test.cs:52:67:52:75 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:52:67:52:75 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:52:67:52:75 | delegate creation of type RemoteCertificateValidationCallback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:52:67:52:75 | delegate creation of type RemoteCertificateValidationCallback | uses a callback | +| Test.cs:59:13:59:56 | (...) => ... | Test.cs:59:13:59:56 | (...) => ... | Test.cs:59:13:59:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:59:13:59:56 | (...) => ... | uses a callback | +| Test.cs:65:13:65:56 | (...) => ... | Test.cs:65:13:65:56 | (...) => ... | Test.cs:65:13:65:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:65:13:65:56 | (...) => ... | uses a callback | +| Test.cs:67:48:67:55 | access to local variable callback | Test.cs:65:13:65:56 | (...) => ... : (...) => ... | Test.cs:67:48:67:55 | access to local variable callback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:65:13:65:56 | (...) => ... | uses a callback | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.qlref b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.qlref new file mode 100644 index 000000000000..3091f848abee --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.qlref @@ -0,0 +1 @@ +Security Features/CWE-295/AcceptAnyCertificate.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs new file mode 100644 index 000000000000..5509abc5a227 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs @@ -0,0 +1,89 @@ +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; + +public class CertificateValidationTests +{ + public void HttpClientHandlerBad() + { + var handler = new HttpClientHandler(); + // BAD: always trusts any certificate. + handler.ServerCertificateCustomValidationCallback = + (request, certificate, chain, errors) => true; + } + + public void HttpClientHandlerBlockBodyBad() + { + var handler = new HttpClientHandler(); + // BAD: always trusts any certificate. + handler.ServerCertificateCustomValidationCallback = + (request, certificate, chain, errors) => + { + return true; + }; + } + + public void HttpClientHandlerDangerousBad() + { + var handler = new HttpClientHandler(); + // BAD: built-in callback that accepts any certificate. + handler.ServerCertificateCustomValidationCallback = + HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; + } + + public void ServicePointManagerBad() + { + // BAD: always trusts any certificate. + ServicePointManager.ServerCertificateValidationCallback = + (sender, certificate, chain, errors) => true; + } + + private static bool AcceptAll(object sender, X509Certificate certificate, X509Chain chain, + SslPolicyErrors errors) + { + return true; + } + + public void MethodGroupBad() + { + // BAD: the referenced method always returns true. + ServicePointManager.ServerCertificateValidationCallback = AcceptAll; + } + + public void SslStreamBad(Stream stream) + { + // BAD: the validation callback always returns true. + var ssl = new SslStream(stream, false, + (sender, certificate, chain, errors) => true); + } + + public void IndirectBad(Stream stream) + { + RemoteCertificateValidationCallback callback = + (sender, certificate, chain, errors) => true; + // BAD: the callback flowing here always returns true. + var ssl = new SslStream(stream, false, callback); + } + + public void HttpClientHandlerGood() + { + var handler = new HttpClientHandler(); + // GOOD: the certificate is only trusted when there are no validation errors. + handler.ServerCertificateCustomValidationCallback = + (request, certificate, chain, errors) => errors == SslPolicyErrors.None; + } + + private static bool Validate(object sender, X509Certificate certificate, X509Chain chain, + SslPolicyErrors errors) + { + return errors == SslPolicyErrors.None; + } + + public void MethodGroupGood() + { + // GOOD: the referenced method performs real validation. + ServicePointManager.ServerCertificateValidationCallback = Validate; + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/options b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/options new file mode 100644 index 000000000000..a5ea8b797c5b --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj From 3d9e8a2886ebf54126b415e940f62890a44c2517 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 19:44:46 +0000 Subject: [PATCH 2/3] Combine best of PR #1643: handle += compound assignment and ignore dead-code returns --- .../CWE-295/AcceptAnyCertificate.ql | 23 ++++++++-- .../AcceptAnyCertificate.expected | 32 ++++++++----- .../CWE-295/AcceptAnyCertificate/Test.cs | 46 ++++++++++++++++++- 3 files changed, 84 insertions(+), 17 deletions(-) diff --git a/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.ql b/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.ql index 6c1df334b92d..eec9b91f3521 100644 --- a/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.ql +++ b/csharp/ql/src/Security Features/CWE-295/AcceptAnyCertificate.ql @@ -21,9 +21,9 @@ import AcceptAnyCertificate::PathGraph */ predicate alwaysReturnsTrue(Callable c) { c.getReturnType() instanceof BoolType and - // There is at least one returned value, and every returned value is the - // constant `true`. - forex(Expr ret | c.canReturn(ret) | ret.getValue() = "true") + // There is at least one live returned value, and every live returned value is + // the constant `true`. Dead (unreachable) returns are ignored. + forex(Expr ret | c.canReturn(ret) and ret.isLive() | ret.getValue() = "true") } /** @@ -59,6 +59,21 @@ Callable getAcceptingCallable(Expr e) { alwaysReturnsTrue(result) } +/** + * Gets the expression that produces the delegate value assigned to `a`, + * handling both simple assignments (`a = ...`) and compound assignments such as + * `a += ...` (used to combine delegates). + */ +Expr getAssignedDelegate(Assignable a) { + exists(Expr source | source = a.getAnAssignedValue() | + // `a += ...` combines delegates; the delegate value is the right operand. + result = source.(AssignOperation).getRightOperand() + or + // `a = ...` assigns the delegate value directly. + result = source and not source instanceof AssignOperation + ) +} + module AcceptAnyCertificateConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { exists(getAcceptingCallable(source.asExpr())) @@ -77,7 +92,7 @@ module AcceptAnyCertificateConfig implements DataFlow::ConfigSig { // validation callback type. exists(Assignable a | a.getType() instanceof CertificateValidationCallbackType and - sink.asExpr() = a.getAnAssignedValue() + sink.asExpr() = getAssignedDelegate(a) ) or // The value passed as a certificate validation callback argument, e.g. to diff --git a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected index 8e9becce109a..001adcdcaa95 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected @@ -1,24 +1,32 @@ edges -| Test.cs:64:45:64:52 | access to local variable callback : (...) => ... | Test.cs:67:48:67:55 | access to local variable callback | provenance | | -| Test.cs:65:13:65:56 | (...) => ... : (...) => ... | Test.cs:64:45:64:52 | access to local variable callback : (...) => ... | provenance | | +| Test.cs:88:45:88:52 | access to local variable callback : (...) => ... | Test.cs:91:48:91:55 | access to local variable callback | provenance | | +| Test.cs:89:13:89:56 | (...) => ... : (...) => ... | Test.cs:88:45:88:52 | access to local variable callback : (...) => ... | provenance | | nodes | Test.cs:14:13:14:57 | (...) => ... | semmle.label | (...) => ... | | Test.cs:22:13:25:13 | (...) => ... | semmle.label | (...) => ... | | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | semmle.label | access to property DangerousAcceptAnyServerCertificateValidator | | Test.cs:40:13:40:56 | (...) => ... | semmle.label | (...) => ... | -| Test.cs:52:67:52:75 | delegate creation of type RemoteCertificateValidationCallback | semmle.label | delegate creation of type RemoteCertificateValidationCallback | -| Test.cs:59:13:59:56 | (...) => ... | semmle.label | (...) => ... | -| Test.cs:64:45:64:52 | access to local variable callback : (...) => ... | semmle.label | access to local variable callback : (...) => ... | -| Test.cs:65:13:65:56 | (...) => ... | semmle.label | (...) => ... | -| Test.cs:65:13:65:56 | (...) => ... : (...) => ... | semmle.label | (...) => ... : (...) => ... | -| Test.cs:67:48:67:55 | access to local variable callback | semmle.label | access to local variable callback | +| Test.cs:47:13:47:61 | (...) => ... | semmle.label | (...) => ... | +| Test.cs:49:68:49:87 | (...) => ... | semmle.label | (...) => ... | +| Test.cs:51:68:51:92 | delegate(...) { ... } | semmle.label | delegate(...) { ... } | +| Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | semmle.label | delegate creation of type RemoteCertificateValidationCallback | +| Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | semmle.label | delegate creation of type RemoteCertificateValidationCallback | +| Test.cs:83:13:83:56 | (...) => ... | semmle.label | (...) => ... | +| Test.cs:88:45:88:52 | access to local variable callback : (...) => ... | semmle.label | access to local variable callback : (...) => ... | +| Test.cs:89:13:89:56 | (...) => ... | semmle.label | (...) => ... | +| Test.cs:89:13:89:56 | (...) => ... : (...) => ... | semmle.label | (...) => ... : (...) => ... | +| Test.cs:91:48:91:55 | access to local variable callback | semmle.label | access to local variable callback | subpaths #select | Test.cs:14:13:14:57 | (...) => ... | Test.cs:14:13:14:57 | (...) => ... | Test.cs:14:13:14:57 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:14:13:14:57 | (...) => ... | uses a callback | | Test.cs:22:13:25:13 | (...) => ... | Test.cs:22:13:25:13 | (...) => ... | Test.cs:22:13:25:13 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:22:13:25:13 | (...) => ... | uses a callback | | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | This TLS certificate validation $@, which trusts any certificate. | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | uses a callback | | Test.cs:40:13:40:56 | (...) => ... | Test.cs:40:13:40:56 | (...) => ... | Test.cs:40:13:40:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:40:13:40:56 | (...) => ... | uses a callback | -| Test.cs:52:67:52:75 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:52:67:52:75 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:52:67:52:75 | delegate creation of type RemoteCertificateValidationCallback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:52:67:52:75 | delegate creation of type RemoteCertificateValidationCallback | uses a callback | -| Test.cs:59:13:59:56 | (...) => ... | Test.cs:59:13:59:56 | (...) => ... | Test.cs:59:13:59:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:59:13:59:56 | (...) => ... | uses a callback | -| Test.cs:65:13:65:56 | (...) => ... | Test.cs:65:13:65:56 | (...) => ... | Test.cs:65:13:65:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:65:13:65:56 | (...) => ... | uses a callback | -| Test.cs:67:48:67:55 | access to local variable callback | Test.cs:65:13:65:56 | (...) => ... : (...) => ... | Test.cs:67:48:67:55 | access to local variable callback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:65:13:65:56 | (...) => ... | uses a callback | +| Test.cs:47:13:47:61 | (...) => ... | Test.cs:47:13:47:61 | (...) => ... | Test.cs:47:13:47:61 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:47:13:47:61 | (...) => ... | uses a callback | +| Test.cs:49:68:49:87 | (...) => ... | Test.cs:49:68:49:87 | (...) => ... | Test.cs:49:68:49:87 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:49:68:49:87 | (...) => ... | uses a callback | +| Test.cs:51:68:51:92 | delegate(...) { ... } | Test.cs:51:68:51:92 | delegate(...) { ... } | Test.cs:51:68:51:92 | delegate(...) { ... } | This TLS certificate validation $@, which trusts any certificate. | Test.cs:51:68:51:92 | delegate(...) { ... } | uses a callback | +| Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | uses a callback | +| Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | uses a callback | +| Test.cs:83:13:83:56 | (...) => ... | Test.cs:83:13:83:56 | (...) => ... | Test.cs:83:13:83:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:83:13:83:56 | (...) => ... | uses a callback | +| Test.cs:89:13:89:56 | (...) => ... | Test.cs:89:13:89:56 | (...) => ... | Test.cs:89:13:89:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:89:13:89:56 | (...) => ... | uses a callback | +| Test.cs:91:48:91:55 | access to local variable callback | Test.cs:89:13:89:56 | (...) => ... : (...) => ... | Test.cs:91:48:91:55 | access to local variable callback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:89:13:89:56 | (...) => ... | uses a callback | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs index 5509abc5a227..c02478caa2fc 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs +++ b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs @@ -40,18 +40,42 @@ public void ServicePointManagerBad() (sender, certificate, chain, errors) => true; } + public void ServicePointManagerCompoundBad() + { + // BAD: always trusts any certificate (compound assignment). + ServicePointManager.ServerCertificateValidationCallback += + (sender, cert, chain, errors) => { return true; }; + // BAD + ServicePointManager.ServerCertificateValidationCallback += (a, b, c, d) => true; + // BAD: parameterless anonymous method. + ServicePointManager.ServerCertificateValidationCallback += delegate { return true; }; + } + private static bool AcceptAll(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { return true; } + public bool AcceptAllNonStatic(object sender, X509Certificate certificate, X509Chain chain, + SslPolicyErrors errors) + { + return true; + } + public void MethodGroupBad() { - // BAD: the referenced method always returns true. + // BAD: the referenced static method always returns true. ServicePointManager.ServerCertificateValidationCallback = AcceptAll; } + public void MethodGroupNonStaticBad() + { + // BAD: the referenced instance method always returns true. + ServicePointManager.ServerCertificateValidationCallback = + new RemoteCertificateValidationCallback(this.AcceptAllNonStatic); + } + public void SslStreamBad(Stream stream) { // BAD: the validation callback always returns true. @@ -75,6 +99,26 @@ public void HttpClientHandlerGood() (request, certificate, chain, errors) => errors == SslPolicyErrors.None; } + public void ControlFlowGood() + { + // GOOD: not every returned value is `true`. + ServicePointManager.ServerCertificateValidationCallback += + (sender, cert, chain, errors) => + { + if (cert == null) + { + return false; + } + + if (errors != SslPolicyErrors.None) + { + return false; + } + + return true; + }; + } + private static bool Validate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { From c7cac9629bd242973da75be97eed1515dcfa7cef Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 3 Jul 2026 09:27:40 +0100 Subject: [PATCH 3/3] Convert to inline expectations test --- .../AcceptAnyCertificate.expected | 26 +++++++++---------- .../AcceptAnyCertificate.qlref | 3 ++- .../CWE-295/AcceptAnyCertificate/Test.cs | 24 ++++++++--------- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected index 001adcdcaa95..14d0bf836c25 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.expected @@ -1,3 +1,16 @@ +#select +| Test.cs:14:13:14:57 | (...) => ... | Test.cs:14:13:14:57 | (...) => ... | Test.cs:14:13:14:57 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:14:13:14:57 | (...) => ... | uses a callback | +| Test.cs:22:13:25:13 | (...) => ... | Test.cs:22:13:25:13 | (...) => ... | Test.cs:22:13:25:13 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:22:13:25:13 | (...) => ... | uses a callback | +| Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | This TLS certificate validation $@, which trusts any certificate. | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | uses a callback | +| Test.cs:40:13:40:56 | (...) => ... | Test.cs:40:13:40:56 | (...) => ... | Test.cs:40:13:40:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:40:13:40:56 | (...) => ... | uses a callback | +| Test.cs:47:13:47:61 | (...) => ... | Test.cs:47:13:47:61 | (...) => ... | Test.cs:47:13:47:61 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:47:13:47:61 | (...) => ... | uses a callback | +| Test.cs:49:68:49:87 | (...) => ... | Test.cs:49:68:49:87 | (...) => ... | Test.cs:49:68:49:87 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:49:68:49:87 | (...) => ... | uses a callback | +| Test.cs:51:68:51:92 | delegate(...) { ... } | Test.cs:51:68:51:92 | delegate(...) { ... } | Test.cs:51:68:51:92 | delegate(...) { ... } | This TLS certificate validation $@, which trusts any certificate. | Test.cs:51:68:51:92 | delegate(...) { ... } | uses a callback | +| Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | uses a callback | +| Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | uses a callback | +| Test.cs:83:13:83:56 | (...) => ... | Test.cs:83:13:83:56 | (...) => ... | Test.cs:83:13:83:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:83:13:83:56 | (...) => ... | uses a callback | +| Test.cs:89:13:89:56 | (...) => ... | Test.cs:89:13:89:56 | (...) => ... | Test.cs:89:13:89:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:89:13:89:56 | (...) => ... | uses a callback | +| Test.cs:91:48:91:55 | access to local variable callback | Test.cs:89:13:89:56 | (...) => ... : (...) => ... | Test.cs:91:48:91:55 | access to local variable callback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:89:13:89:56 | (...) => ... | uses a callback | edges | Test.cs:88:45:88:52 | access to local variable callback : (...) => ... | Test.cs:91:48:91:55 | access to local variable callback | provenance | | | Test.cs:89:13:89:56 | (...) => ... : (...) => ... | Test.cs:88:45:88:52 | access to local variable callback : (...) => ... | provenance | | @@ -17,16 +30,3 @@ nodes | Test.cs:89:13:89:56 | (...) => ... : (...) => ... | semmle.label | (...) => ... : (...) => ... | | Test.cs:91:48:91:55 | access to local variable callback | semmle.label | access to local variable callback | subpaths -#select -| Test.cs:14:13:14:57 | (...) => ... | Test.cs:14:13:14:57 | (...) => ... | Test.cs:14:13:14:57 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:14:13:14:57 | (...) => ... | uses a callback | -| Test.cs:22:13:25:13 | (...) => ... | Test.cs:22:13:25:13 | (...) => ... | Test.cs:22:13:25:13 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:22:13:25:13 | (...) => ... | uses a callback | -| Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | This TLS certificate validation $@, which trusts any certificate. | Test.cs:33:13:33:74 | access to property DangerousAcceptAnyServerCertificateValidator | uses a callback | -| Test.cs:40:13:40:56 | (...) => ... | Test.cs:40:13:40:56 | (...) => ... | Test.cs:40:13:40:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:40:13:40:56 | (...) => ... | uses a callback | -| Test.cs:47:13:47:61 | (...) => ... | Test.cs:47:13:47:61 | (...) => ... | Test.cs:47:13:47:61 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:47:13:47:61 | (...) => ... | uses a callback | -| Test.cs:49:68:49:87 | (...) => ... | Test.cs:49:68:49:87 | (...) => ... | Test.cs:49:68:49:87 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:49:68:49:87 | (...) => ... | uses a callback | -| Test.cs:51:68:51:92 | delegate(...) { ... } | Test.cs:51:68:51:92 | delegate(...) { ... } | Test.cs:51:68:51:92 | delegate(...) { ... } | This TLS certificate validation $@, which trusts any certificate. | Test.cs:51:68:51:92 | delegate(...) { ... } | uses a callback | -| Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:69:67:69:75 | delegate creation of type RemoteCertificateValidationCallback | uses a callback | -| Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:76:13:76:76 | delegate creation of type RemoteCertificateValidationCallback | uses a callback | -| Test.cs:83:13:83:56 | (...) => ... | Test.cs:83:13:83:56 | (...) => ... | Test.cs:83:13:83:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:83:13:83:56 | (...) => ... | uses a callback | -| Test.cs:89:13:89:56 | (...) => ... | Test.cs:89:13:89:56 | (...) => ... | Test.cs:89:13:89:56 | (...) => ... | This TLS certificate validation $@, which trusts any certificate. | Test.cs:89:13:89:56 | (...) => ... | uses a callback | -| Test.cs:91:48:91:55 | access to local variable callback | Test.cs:89:13:89:56 | (...) => ... : (...) => ... | Test.cs:91:48:91:55 | access to local variable callback | This TLS certificate validation $@, which trusts any certificate. | Test.cs:89:13:89:56 | (...) => ... | uses a callback | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.qlref b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.qlref index 3091f848abee..e400be7f8a3b 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.qlref +++ b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/AcceptAnyCertificate.qlref @@ -1 +1,2 @@ -Security Features/CWE-295/AcceptAnyCertificate.ql \ No newline at end of file +query: Security Features/CWE-295/AcceptAnyCertificate.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs index c02478caa2fc..cf267bc0288e 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs +++ b/csharp/ql/test/query-tests/Security Features/CWE-295/AcceptAnyCertificate/Test.cs @@ -11,7 +11,7 @@ public void HttpClientHandlerBad() var handler = new HttpClientHandler(); // BAD: always trusts any certificate. handler.ServerCertificateCustomValidationCallback = - (request, certificate, chain, errors) => true; + (request, certificate, chain, errors) => true; // $ Alert } public void HttpClientHandlerBlockBodyBad() @@ -22,7 +22,7 @@ public void HttpClientHandlerBlockBodyBad() (request, certificate, chain, errors) => { return true; - }; + }; // $ Alert } public void HttpClientHandlerDangerousBad() @@ -30,25 +30,25 @@ public void HttpClientHandlerDangerousBad() var handler = new HttpClientHandler(); // BAD: built-in callback that accepts any certificate. handler.ServerCertificateCustomValidationCallback = - HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; + HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; // $ Alert } public void ServicePointManagerBad() { // BAD: always trusts any certificate. ServicePointManager.ServerCertificateValidationCallback = - (sender, certificate, chain, errors) => true; + (sender, certificate, chain, errors) => true; // $ Alert } public void ServicePointManagerCompoundBad() { // BAD: always trusts any certificate (compound assignment). ServicePointManager.ServerCertificateValidationCallback += - (sender, cert, chain, errors) => { return true; }; + (sender, cert, chain, errors) => { return true; }; // $ Alert // BAD - ServicePointManager.ServerCertificateValidationCallback += (a, b, c, d) => true; + ServicePointManager.ServerCertificateValidationCallback += (a, b, c, d) => true; // $ Alert // BAD: parameterless anonymous method. - ServicePointManager.ServerCertificateValidationCallback += delegate { return true; }; + ServicePointManager.ServerCertificateValidationCallback += delegate { return true; }; // $ Alert } private static bool AcceptAll(object sender, X509Certificate certificate, X509Chain chain, @@ -66,29 +66,29 @@ public bool AcceptAllNonStatic(object sender, X509Certificate certificate, X509C public void MethodGroupBad() { // BAD: the referenced static method always returns true. - ServicePointManager.ServerCertificateValidationCallback = AcceptAll; + ServicePointManager.ServerCertificateValidationCallback = AcceptAll; // $ Alert } public void MethodGroupNonStaticBad() { // BAD: the referenced instance method always returns true. ServicePointManager.ServerCertificateValidationCallback = - new RemoteCertificateValidationCallback(this.AcceptAllNonStatic); + new RemoteCertificateValidationCallback(this.AcceptAllNonStatic); // $ Alert } public void SslStreamBad(Stream stream) { // BAD: the validation callback always returns true. var ssl = new SslStream(stream, false, - (sender, certificate, chain, errors) => true); + (sender, certificate, chain, errors) => true); // $ Alert } public void IndirectBad(Stream stream) { RemoteCertificateValidationCallback callback = - (sender, certificate, chain, errors) => true; + (sender, certificate, chain, errors) => true; // $ Source Alert // BAD: the callback flowing here always returns true. - var ssl = new SslStream(stream, false, callback); + var ssl = new SslStream(stream, false, callback); // $ Alert } public void HttpClientHandlerGood()