From 92e4999fd6d11716ababb7b498cf3ccb1f24e49f Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 19 Dec 2025 08:13:19 +0000 Subject: [PATCH 01/24] Accept MaD sanitizers for existing sink kinds --- .../semmle/code/java/security/AndroidIntentRedirection.qll | 5 +++++ java/ql/lib/semmle/code/java/security/CommandLineQuery.qll | 4 ++++ .../ql/lib/semmle/code/java/security/FragmentInjection.qll | 7 +++++++ .../semmle/code/java/security/FragmentInjectionQuery.qll | 2 ++ java/ql/lib/semmle/code/java/security/GroovyInjection.qll | 7 +++++++ .../lib/semmle/code/java/security/GroovyInjectionQuery.qll | 2 ++ 6 files changed, 27 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirection.qll b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirection.qll index 08a86092afbb..57dfcd29117f 100644 --- a/java/ql/lib/semmle/code/java/security/AndroidIntentRedirection.qll +++ b/java/ql/lib/semmle/code/java/security/AndroidIntentRedirection.qll @@ -35,6 +35,11 @@ private class DefaultIntentRedirectionSink extends IntentRedirectionSink { DefaultIntentRedirectionSink() { sinkNode(this, "intent-redirection") } } +/** External sanitizers for Intent redirection vulnerabilities. */ +private class ExternalIntentRedirectionSanitizer extends IntentRedirectionSanitizer { + ExternalIntentRedirectionSanitizer() { barrierNode(this, "intent-redirection") } +} + /** * A default sanitizer for `Intent` nodes dominated by calls to `ComponentName.getPackageName` * and `ComponentName.getClassName`. These are used to check whether the origin or destination diff --git a/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll b/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll index b6b9d02e289d..273c5360b815 100644 --- a/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll +++ b/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll @@ -37,6 +37,10 @@ private class DefaultCommandInjectionSink extends CommandInjectionSink { DefaultCommandInjectionSink() { sinkNode(this, "command-injection") } } +private class ExternalCommandInjectionSanitizer extends CommandInjectionSanitizer { + ExternalCommandInjectionSanitizer() { barrierNode(this, "command-injection") } +} + private class DefaultCommandInjectionSanitizer extends CommandInjectionSanitizer { DefaultCommandInjectionSanitizer() { this instanceof SimpleTypeSanitizer diff --git a/java/ql/lib/semmle/code/java/security/FragmentInjection.qll b/java/ql/lib/semmle/code/java/security/FragmentInjection.qll index 8cd5e32a5ecd..7e6170ff9283 100644 --- a/java/ql/lib/semmle/code/java/security/FragmentInjection.qll +++ b/java/ql/lib/semmle/code/java/security/FragmentInjection.qll @@ -49,6 +49,13 @@ private class DefaultFragmentInjectionSink extends FragmentInjectionSink { DefaultFragmentInjectionSink() { sinkNode(this, "fragment-injection") } } +/** A barrier for Fragment injection vulnerabilities. */ +abstract class FragmentInjectionSanitizer extends DataFlow::Node { } + +private class ExternalFragmentInjectionSanitizer extends FragmentInjectionSanitizer { + ExternalFragmentInjectionSanitizer() { barrierNode(this, "fragment-injection") } +} + private class DefaultFragmentInjectionAdditionalTaintStep extends FragmentInjectionAdditionalTaintStep { override predicate step(DataFlow::Node n1, DataFlow::Node n2) { diff --git a/java/ql/lib/semmle/code/java/security/FragmentInjectionQuery.qll b/java/ql/lib/semmle/code/java/security/FragmentInjectionQuery.qll index 40636ffd8c25..1cb9f711b6fa 100644 --- a/java/ql/lib/semmle/code/java/security/FragmentInjectionQuery.qll +++ b/java/ql/lib/semmle/code/java/security/FragmentInjectionQuery.qll @@ -14,6 +14,8 @@ module FragmentInjectionTaintConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof FragmentInjectionSink } + predicate isBarrier(DataFlow::Node node) { node instanceof FragmentInjectionSanitizer } + predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) { any(FragmentInjectionAdditionalTaintStep c).step(n1, n2) } diff --git a/java/ql/lib/semmle/code/java/security/GroovyInjection.qll b/java/ql/lib/semmle/code/java/security/GroovyInjection.qll index 45d664897775..d9a5db7b12d3 100644 --- a/java/ql/lib/semmle/code/java/security/GroovyInjection.qll +++ b/java/ql/lib/semmle/code/java/security/GroovyInjection.qll @@ -26,6 +26,13 @@ private class DefaultGroovyInjectionSink extends GroovyInjectionSink { DefaultGroovyInjectionSink() { sinkNode(this, "groovy-injection") } } +/** A data flow sanitizer for Groovy expression injection vulnerabilities. */ +abstract class GroovyInjectionSanitizer extends DataFlow::ExprNode { } + +private class ExternalGroovyInjectionSanitizer extends GroovyInjectionSanitizer { + ExternalGroovyInjectionSanitizer() { barrierNode(this, "groovy-injection") } +} + /** A set of additional taint steps to consider when taint tracking Groovy related data flows. */ private class DefaultGroovyInjectionAdditionalTaintStep extends GroovyInjectionAdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { diff --git a/java/ql/lib/semmle/code/java/security/GroovyInjectionQuery.qll b/java/ql/lib/semmle/code/java/security/GroovyInjectionQuery.qll index b497873b9bb1..af4645b15917 100644 --- a/java/ql/lib/semmle/code/java/security/GroovyInjectionQuery.qll +++ b/java/ql/lib/semmle/code/java/security/GroovyInjectionQuery.qll @@ -14,6 +14,8 @@ module GroovyInjectionConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof GroovyInjectionSink } + predicate isBarrier(DataFlow::Node node) { node instanceof GroovyInjectionSanitizer } + predicate isAdditionalFlowStep(DataFlow::Node fromNode, DataFlow::Node toNode) { any(GroovyInjectionAdditionalTaintStep c).step(fromNode, toNode) } From 8662d73d2071ebbae18d2723d148af7e29bb11b4 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 16:16:49 +0000 Subject: [PATCH 02/24] Allow MaD sanitizers for `java/non-https-url` --- java/ql/lib/semmle/code/java/security/HttpsUrls.qll | 12 ++++++++++++ .../lib/semmle/code/java/security/HttpsUrlsQuery.qll | 3 +-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/HttpsUrls.qll b/java/ql/lib/semmle/code/java/security/HttpsUrls.qll index 071f95b49902..10e4553f9ae8 100644 --- a/java/ql/lib/semmle/code/java/security/HttpsUrls.qll +++ b/java/ql/lib/semmle/code/java/security/HttpsUrls.qll @@ -8,6 +8,7 @@ private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.frameworks.ApacheHttp private import semmle.code.java.frameworks.Networking +private import semmle.code.java.security.Sanitizers /** * String of HTTP URLs not in private domains. @@ -36,6 +37,17 @@ private class DefaultUrlOpenSink extends UrlOpenSink { DefaultUrlOpenSink() { sinkNode(this, "request-forgery") } } +/** + * A sanitizer to URL opening. + */ +abstract class UrlOpenSanitizer extends DataFlow::Node { } + +private class SimpleTypeUrlOpenSanitizer extends UrlOpenSanitizer instanceof SimpleTypeSanitizer { } + +private class ExternalUrlOpenSanitizer extends UrlOpenSanitizer { + ExternalUrlOpenSanitizer() { barrierNode(this, "request-forgery") } +} + /** * A unit class for adding additional taint steps. * diff --git a/java/ql/lib/semmle/code/java/security/HttpsUrlsQuery.qll b/java/ql/lib/semmle/code/java/security/HttpsUrlsQuery.qll index 1e67e3ca59a7..5f4e8489a713 100644 --- a/java/ql/lib/semmle/code/java/security/HttpsUrlsQuery.qll +++ b/java/ql/lib/semmle/code/java/security/HttpsUrlsQuery.qll @@ -4,7 +4,6 @@ import java import semmle.code.java.dataflow.TaintTracking import semmle.code.java.frameworks.Networking import semmle.code.java.security.HttpsUrls -private import semmle.code.java.security.Sanitizers /** * A taint tracking configuration for HTTP connections. @@ -18,7 +17,7 @@ module HttpStringToUrlOpenMethodFlowConfig implements DataFlow::ConfigSig { any(HttpUrlsAdditionalTaintStep c).step(node1, node2) } - predicate isBarrier(DataFlow::Node node) { node instanceof SimpleTypeSanitizer } + predicate isBarrier(DataFlow::Node node) { node instanceof UrlOpenSanitizer } predicate observeDiffInformedIncrementalMode() { any() } } From d5df8dd09195c1bacd432719792c006efe581e05 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 16:29:43 +0000 Subject: [PATCH 03/24] Allow MaD sanitizers for `java/android/implicit-pendingintents` --- .../code/java/security/ImplicitPendingIntents.qll | 12 ++++++++++++ .../java/security/ImplicitPendingIntentsQuery.qll | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll index 94951c10c532..e3ca87b4825e 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll @@ -33,6 +33,9 @@ abstract class ImplicitPendingIntentSource extends ApiSourceNode { } /** A sink that sends an implicit and mutable `PendingIntent` to a third party. */ abstract class ImplicitPendingIntentSink extends DataFlow::Node { } +/** A sanitizer for sending an implicit and mutable `PendingIntent` to a third party. */ +abstract class ImplicitPendingIntentSanitizer extends DataFlow::Node { } + /** * A unit class for adding additional taint steps. * @@ -76,6 +79,15 @@ private class SendPendingIntent extends ImplicitPendingIntentSink { } } +private class ExplicitPendingIntentSanitizer extends ImplicitPendingIntentSanitizer instanceof ExplicitIntentSanitizer +{ } + +private class ExternalIntentRedirectionSanitizer extends ExplicitIntentSanitizer { + ExternalIntentRedirectionSanitizer() { + barrierNode(this, ["intent-redirection", "pending-intents"]) + } +} + private class MutablePendingIntentFlowStep extends ImplicitPendingIntentAdditionalTaintStep { override predicate mutablePendingIntentCreation(DataFlow::Node node1, DataFlow::Node node2) { exists(PendingIntentCreation pic, Argument flagArg | diff --git a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntentsQuery.qll b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntentsQuery.qll index f66309c97bec..0bb96b706aaa 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntentsQuery.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntentsQuery.qll @@ -23,7 +23,9 @@ module ImplicitPendingIntentStartConfig implements DataFlow::StateConfigSig { sink instanceof ImplicitPendingIntentSink and state instanceof MutablePendingIntent } - predicate isBarrier(DataFlow::Node sanitizer) { sanitizer instanceof ExplicitIntentSanitizer } + predicate isBarrier(DataFlow::Node sanitizer) { + sanitizer instanceof ImplicitPendingIntentSanitizer + } predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { any(ImplicitPendingIntentAdditionalTaintStep c).step(node1, node2) From 7b5364909283e2f5435a5e9013213d843912e5dc Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 16:37:51 +0000 Subject: [PATCH 04/24] Allow MaD sanitizers for `java/error-message-exposure` and `java/stack-trace-exposure` --- java/ql/lib/semmle/code/java/security/InformationLeak.qll | 7 +++++++ .../SensitiveDataExposureThroughErrorMessageQuery.qll | 2 ++ .../semmle/code/java/security/StackTraceExposureQuery.qll | 2 ++ 3 files changed, 11 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/InformationLeak.qll b/java/ql/lib/semmle/code/java/security/InformationLeak.qll index ba7a7a52a707..64e979d4036a 100644 --- a/java/ql/lib/semmle/code/java/security/InformationLeak.qll +++ b/java/ql/lib/semmle/code/java/security/InformationLeak.qll @@ -17,3 +17,10 @@ private class DefaultInformationLeakSink extends InformationLeakSink { this instanceof XssSink } } + +/** A sanitizer for information leak. */ +abstract class InformationLeakSanitizer extends DataFlow::Node { } + +private class ExternalInformationLeakSanitizer extends InformationLeakSanitizer { + ExternalInformationLeakSanitizer() { barrierNode(this, "information-leak") } +} diff --git a/java/ql/lib/semmle/code/java/security/SensitiveDataExposureThroughErrorMessageQuery.qll b/java/ql/lib/semmle/code/java/security/SensitiveDataExposureThroughErrorMessageQuery.qll index 8644ef415b3e..df141ddc55c1 100644 --- a/java/ql/lib/semmle/code/java/security/SensitiveDataExposureThroughErrorMessageQuery.qll +++ b/java/ql/lib/semmle/code/java/security/SensitiveDataExposureThroughErrorMessageQuery.qll @@ -21,6 +21,8 @@ private module GetMessageFlowSourceToHttpResponseSinkFlowConfig implements DataF predicate isSource(DataFlow::Node src) { src instanceof GetMessageFlowSource } predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink } + + predicate isBarrier(DataFlow::Node node) { node instanceof InformationLeakSanitizer } } private module GetMessageFlowSourceToHttpResponseSinkFlow = diff --git a/java/ql/lib/semmle/code/java/security/StackTraceExposureQuery.qll b/java/ql/lib/semmle/code/java/security/StackTraceExposureQuery.qll index 0eb069a06c20..b6034107e103 100644 --- a/java/ql/lib/semmle/code/java/security/StackTraceExposureQuery.qll +++ b/java/ql/lib/semmle/code/java/security/StackTraceExposureQuery.qll @@ -70,6 +70,8 @@ private module StackTraceStringToHttpResponseSinkFlowConfig implements DataFlow: predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) } predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink } + + predicate isBarrier(DataFlow::Node node) { node instanceof InformationLeakSanitizer } } private module StackTraceStringToHttpResponseSinkFlow = From e53a28c5673ec8fecedad3f225965507e0238ad0 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 16:39:52 +0000 Subject: [PATCH 05/24] Make abstract class for sinks for `java/insecure-bean-validation` --- .../code/java/security/InsecureBeanValidationQuery.qll | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/InsecureBeanValidationQuery.qll b/java/ql/lib/semmle/code/java/security/InsecureBeanValidationQuery.qll index e1c840ce2642..0eebd69c90e7 100644 --- a/java/ql/lib/semmle/code/java/security/InsecureBeanValidationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/InsecureBeanValidationQuery.qll @@ -60,6 +60,8 @@ module BeanValidationFlow = TaintTracking::Global; * A bean validation sink, such as method `buildConstraintViolationWithTemplate` * declared on a subtype of `javax.validation.ConstraintValidatorContext`. */ -private class BeanValidationSink extends DataFlow::Node { - BeanValidationSink() { sinkNode(this, "bean-validation") } +abstract class BeanValidationSink extends DataFlow::Node { } + +private class ExternalBeanValidationSink extends BeanValidationSink { + ExternalBeanValidationSink() { sinkNode(this, "bean-validation") } } From 7814f0883c8959ebb4dabcf18d4ab2f92c7a3a2c Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 16:43:18 +0000 Subject: [PATCH 06/24] Allow MaD sanitizers for `java/insecure-bean-validation` --- .../code/java/security/InsecureBeanValidationQuery.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/InsecureBeanValidationQuery.qll b/java/ql/lib/semmle/code/java/security/InsecureBeanValidationQuery.qll index 0eebd69c90e7..04876b5d8bb1 100644 --- a/java/ql/lib/semmle/code/java/security/InsecureBeanValidationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/InsecureBeanValidationQuery.qll @@ -50,6 +50,8 @@ module BeanValidationConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof BeanValidationSink } + predicate isBarrier(DataFlow::Node node) { node instanceof BeanValidationSanitizer } + predicate observeDiffInformedIncrementalMode() { any() } } @@ -65,3 +67,10 @@ abstract class BeanValidationSink extends DataFlow::Node { } private class ExternalBeanValidationSink extends BeanValidationSink { ExternalBeanValidationSink() { sinkNode(this, "bean-validation") } } + +/** A bean validation sanitizer. */ +abstract class BeanValidationSanitizer extends DataFlow::Node { } + +private class ExternalBeanValidationSanitizer extends BeanValidationSanitizer { + ExternalBeanValidationSanitizer() { barrierNode(this, "bean-validation") } +} From 8e33d2be7530b8ac831d2d970efd82f41e5b9359 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 16:53:21 +0000 Subject: [PATCH 07/24] Allow MaD sanitizers for `java/jexl-expression-injection` --- .../semmle/code/java/security/JexlInjectionQuery.qll | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/JexlInjectionQuery.qll b/java/ql/lib/semmle/code/java/security/JexlInjectionQuery.qll index 4ad1dd3ba310..c11ebf1ae59f 100644 --- a/java/ql/lib/semmle/code/java/security/JexlInjectionQuery.qll +++ b/java/ql/lib/semmle/code/java/security/JexlInjectionQuery.qll @@ -16,6 +16,16 @@ private class DefaultJexlEvaluationSink extends JexlEvaluationSink { DefaultJexlEvaluationSink() { sinkNode(this, "jexl-injection") } } +/** + * A sink for Expresssion Language injection vulnerabilities via Jexl, + * that is, method calls that run evaluation of a JEXL expression. + */ +abstract class JexlEvaluationSanitizer extends DataFlow::ExprNode { } + +private class ExternalJexlEvaluationSanitizer extends JexlEvaluationSanitizer { + ExternalJexlEvaluationSanitizer() { barrierNode(this, "jexl-injection") } +} + /** * A unit class for adding additional taint steps. * @@ -48,6 +58,8 @@ module JexlInjectionConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink } + predicate isBarrier(DataFlow::Node node) { node instanceof JexlEvaluationSanitizer } + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { any(JexlInjectionAdditionalTaintStep c).step(node1, node2) } From 12eb97942098042e1a963c6a11ed5dcbd99ad9dc Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 16:57:37 +0000 Subject: [PATCH 08/24] Allow MaD sanitizers for `java/jndi-injection` --- java/ql/lib/semmle/code/java/security/JndiInjection.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/JndiInjection.qll b/java/ql/lib/semmle/code/java/security/JndiInjection.qll index 0e61a53c0ab0..cb41c2cf2063 100644 --- a/java/ql/lib/semmle/code/java/security/JndiInjection.qll +++ b/java/ql/lib/semmle/code/java/security/JndiInjection.qll @@ -34,6 +34,10 @@ private class DefaultJndiInjectionSink extends JndiInjectionSink { DefaultJndiInjectionSink() { sinkNode(this, "jndi-injection") } } +private class ExternalJndiInjectionSanitizer extends JndiInjectionSanitizer { + ExternalJndiInjectionSanitizer() { barrierNode(this, "jndi-injection") } +} + /** * A method that does a JNDI lookup when it receives a specific argument set to `true`. */ From 2e685e74a07fca3e0dc0fe9130f6cf46d7c83474 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 16:59:58 +0000 Subject: [PATCH 09/24] Allow MaD sanitizers for `java/ldap-injection` --- java/ql/lib/semmle/code/java/security/LdapInjection.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/LdapInjection.qll b/java/ql/lib/semmle/code/java/security/LdapInjection.qll index ff92d40cf556..ad1cdbe49db2 100644 --- a/java/ql/lib/semmle/code/java/security/LdapInjection.qll +++ b/java/ql/lib/semmle/code/java/security/LdapInjection.qll @@ -38,6 +38,10 @@ private class DefaultLdapInjectionSink extends LdapInjectionSink { /** A sanitizer that clears the taint on (boxed) primitive types. */ private class DefaultLdapSanitizer extends LdapInjectionSanitizer instanceof SimpleTypeSanitizer { } +private class ExternalLdapInjectionSanitizer extends LdapInjectionSanitizer { + ExternalLdapInjectionSanitizer() { barrierNode(this, "ldap-injection") } +} + /** * Holds if `n1` to `n2` is a dataflow step that converts between `String` and `LdapName`, * i.e. `new LdapName(tainted)`. From dc83b62db1613552a5ba62915b19a18fd912ade0 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 22:07:48 +0000 Subject: [PATCH 10/24] Allow MaD sanitizers for `java/log-injection` and `java/sensitive-log` --- java/ql/lib/semmle/code/java/security/LogInjection.qll | 4 ++++ .../lib/semmle/code/java/security/SensitiveLoggingQuery.qll | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/LogInjection.qll b/java/ql/lib/semmle/code/java/security/LogInjection.qll index da5a1dc73a0c..fdde5471e6d9 100644 --- a/java/ql/lib/semmle/code/java/security/LogInjection.qll +++ b/java/ql/lib/semmle/code/java/security/LogInjection.qll @@ -36,6 +36,10 @@ private class DefaultLogInjectionSink extends LogInjectionSink { private class DefaultLogInjectionSanitizer extends LogInjectionSanitizer instanceof SimpleTypeSanitizer { } +private class ExternalLogInjectionSanitizer extends LogInjectionSanitizer { + ExternalLogInjectionSanitizer() { barrierNode(this, "log-injection") } +} + private class LineBreaksLogInjectionSanitizer extends LogInjectionSanitizer { LineBreaksLogInjectionSanitizer() { logInjectionSanitizer(this.asExpr()) diff --git a/java/ql/lib/semmle/code/java/security/SensitiveLoggingQuery.qll b/java/ql/lib/semmle/code/java/security/SensitiveLoggingQuery.qll index 7058b844cbdb..3e256cda12de 100644 --- a/java/ql/lib/semmle/code/java/security/SensitiveLoggingQuery.qll +++ b/java/ql/lib/semmle/code/java/security/SensitiveLoggingQuery.qll @@ -120,6 +120,10 @@ private class DefaultSensitiveLoggerBarrier extends SensitiveLoggerBarrier { } } +private class ExternalSensitiveLoggerBarrier extends SensitiveLoggerBarrier { + ExternalSensitiveLoggerBarrier() { barrierNode(this, "log-injection") } +} + /** A data-flow configuration for identifying potentially-sensitive data flowing to a log output. */ module SensitiveLoggerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source instanceof SensitiveLoggerSource } From 6c458a1bf65868b2dd9a01001e564751a74f4280 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 22:10:06 +0000 Subject: [PATCH 11/24] Allow MaD sanitizers for `java/mvel-expression-injection` --- java/ql/lib/semmle/code/java/security/MvelInjection.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/MvelInjection.qll b/java/ql/lib/semmle/code/java/security/MvelInjection.qll index dc804d4a1854..e6af37f9e4bb 100644 --- a/java/ql/lib/semmle/code/java/security/MvelInjection.qll +++ b/java/ql/lib/semmle/code/java/security/MvelInjection.qll @@ -37,6 +37,10 @@ private class DefaultMvelInjectionSanitizer extends MvelInjectionSanitizer { } } +private class ExternalMvelInjectionSanitizer extends MvelInjectionSanitizer { + ExternalMvelInjectionSanitizer() { barrierNode(this, "mvel-injection") } +} + /** A set of additional taint steps to consider when taint tracking MVEL related data flows. */ private class DefaultMvelInjectionAdditionalTaintStep extends MvelInjectionAdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { From ad8b7669789f4d489b762c6b8dd92db8bce7dab1 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 22:15:57 +0000 Subject: [PATCH 12/24] Allow MaD sanitizers for `java/ognl-injection` --- .../ql/lib/semmle/code/java/security/OgnlInjection.qll | 10 ++++++++++ .../semmle/code/java/security/OgnlInjectionQuery.qll | 3 +-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/OgnlInjection.qll b/java/ql/lib/semmle/code/java/security/OgnlInjection.qll index e3f93b39ece1..c703916fa78d 100644 --- a/java/ql/lib/semmle/code/java/security/OgnlInjection.qll +++ b/java/ql/lib/semmle/code/java/security/OgnlInjection.qll @@ -7,6 +7,7 @@ private import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.frameworks.MyBatis +private import semmle.code.java.security.Sanitizers /** * A data flow sink for unvalidated user input that is used in OGNL EL evaluation. @@ -15,6 +16,8 @@ private import semmle.code.java.frameworks.MyBatis */ abstract class OgnlInjectionSink extends ApiSinkNode { } +abstract class OgnlInjectionSanitizer extends DataFlow::Node { } + /** * A unit class for adding additional taint steps. * @@ -32,6 +35,13 @@ private class DefaultOgnlInjectionSink extends OgnlInjectionSink { DefaultOgnlInjectionSink() { sinkNode(this, "ognl-injection") } } +private class SimpleTypeOgnlInjectionSanitizer extends OgnlInjectionSanitizer instanceof SimpleTypeSanitizer +{ } + +private class ExternalOgnlInjectionSanitizer extends OgnlInjectionSanitizer { + ExternalOgnlInjectionSanitizer() { barrierNode(this, "ognl-injection") } +} + /** The class `org.apache.commons.ognl.Ognl` or `ognl.Ognl`. */ private class TypeOgnl extends Class { TypeOgnl() { this.hasQualifiedName(["org.apache.commons.ognl", "ognl"], "Ognl") } diff --git a/java/ql/lib/semmle/code/java/security/OgnlInjectionQuery.qll b/java/ql/lib/semmle/code/java/security/OgnlInjectionQuery.qll index d9bfad412599..9c1d24ed4ace 100644 --- a/java/ql/lib/semmle/code/java/security/OgnlInjectionQuery.qll +++ b/java/ql/lib/semmle/code/java/security/OgnlInjectionQuery.qll @@ -3,7 +3,6 @@ import java import semmle.code.java.dataflow.FlowSources import semmle.code.java.security.OgnlInjection -private import semmle.code.java.security.Sanitizers /** * A taint-tracking configuration for unvalidated user input that is used in OGNL EL evaluation. @@ -13,7 +12,7 @@ module OgnlInjectionFlowConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof OgnlInjectionSink } - predicate isBarrier(DataFlow::Node node) { node instanceof SimpleTypeSanitizer } + predicate isBarrier(DataFlow::Node node) { node instanceof OgnlInjectionSanitizer } predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { any(OgnlInjectionAdditionalTaintStep c).step(node1, node2) From 3a0815622994667681d6b59e25d6d227c5af907e Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 7 Jan 2026 22:36:25 +0000 Subject: [PATCH 13/24] Allow MaD sanitizers for `java/sql-injection`, `java/concatenated-sql-query` and `java/ip-address-spoofing` --- .../code/java/security/QueryInjection.qll | 11 +++++++++ .../java/security/SqlConcatenatedQuery.qll | 3 +-- .../code/java/security/SqlInjectionQuery.qll | 3 +-- .../ClientSuppliedIpUsedInSecurityCheck.ql | 13 +--------- ...ClientSuppliedIpUsedInSecurityCheckLib.qll | 24 +++++++++++++++++++ 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/QueryInjection.qll b/java/ql/lib/semmle/code/java/security/QueryInjection.qll index 583a41ce9335..509d7d9cebea 100644 --- a/java/ql/lib/semmle/code/java/security/QueryInjection.qll +++ b/java/ql/lib/semmle/code/java/security/QueryInjection.qll @@ -8,10 +8,14 @@ import semmle.code.java.frameworks.javaee.Persistence private import semmle.code.java.frameworks.MyBatis private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.dataflow.FlowSinks +private import semmle.code.java.security.Sanitizers /** A sink for database query language injection vulnerabilities. */ abstract class QueryInjectionSink extends ApiSinkNode { } +/** A sanitizer for query injection vulnerabilities. */ +abstract class QueryInjectionSanitizer extends DataFlow::Node { } + /** * A unit class for adding additional taint steps. * @@ -60,6 +64,13 @@ private class MongoDbInjectionSink extends QueryInjectionSink { } } +private class SimpleTypeQueryInjectionSanitizer extends QueryInjectionSanitizer instanceof SimpleTypeSanitizer +{ } + +private class ExternalQueryInjectionSanitizer extends QueryInjectionSanitizer { + ExternalQueryInjectionSanitizer() { barrierNode(this, "sql-injection") } +} + private class MongoJsonStep extends AdditionalQueryInjectionTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { exists(MethodCall ma | diff --git a/java/ql/lib/semmle/code/java/security/SqlConcatenatedQuery.qll b/java/ql/lib/semmle/code/java/security/SqlConcatenatedQuery.qll index 7cfea41a8d77..0fe876d6686e 100644 --- a/java/ql/lib/semmle/code/java/security/SqlConcatenatedQuery.qll +++ b/java/ql/lib/semmle/code/java/security/SqlConcatenatedQuery.qll @@ -4,7 +4,6 @@ import java private import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.security.SqlConcatenatedLib private import semmle.code.java.security.SqlInjectionQuery -private import semmle.code.java.security.Sanitizers private class UncontrolledStringBuilderSource extends DataFlow::ExprNode { UncontrolledStringBuilderSource() { @@ -23,7 +22,7 @@ module UncontrolledStringBuilderSourceFlowConfig implements DataFlow::ConfigSig predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink } - predicate isBarrier(DataFlow::Node node) { node instanceof SimpleTypeSanitizer } + predicate isBarrier(DataFlow::Node node) { node instanceof QueryInjectionSanitizer } predicate observeDiffInformedIncrementalMode() { any() } diff --git a/java/ql/lib/semmle/code/java/security/SqlInjectionQuery.qll b/java/ql/lib/semmle/code/java/security/SqlInjectionQuery.qll index 67f0f1220433..d2bf1eb10488 100644 --- a/java/ql/lib/semmle/code/java/security/SqlInjectionQuery.qll +++ b/java/ql/lib/semmle/code/java/security/SqlInjectionQuery.qll @@ -8,7 +8,6 @@ import java import semmle.code.java.dataflow.FlowSources -private import semmle.code.java.security.Sanitizers import semmle.code.java.security.QueryInjection /** @@ -19,7 +18,7 @@ module QueryInjectionFlowConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink } - predicate isBarrier(DataFlow::Node node) { node instanceof SimpleTypeSanitizer } + predicate isBarrier(DataFlow::Node node) { node instanceof QueryInjectionSanitizer } predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { any(AdditionalQueryInjectionTaintStep s).step(node1, node2) diff --git a/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql index 05cfd814fc53..64d5b1c66b8f 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql @@ -14,7 +14,6 @@ import java import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources -import semmle.code.java.security.Sanitizers deprecated import ClientSuppliedIpUsedInSecurityCheckLib deprecated import ClientSuppliedIpUsedInSecurityCheckFlow::PathGraph @@ -28,18 +27,8 @@ deprecated module ClientSuppliedIpUsedInSecurityCheckConfig implements DataFlow: predicate isSink(DataFlow::Node sink) { sink instanceof ClientSuppliedIpUsedInSecurityCheckSink } - /** - * Splitting a header value by `,` and taking an entry other than the first is sanitizing, because - * later entries may originate from more-trustworthy intermediate proxies, not the original client. - */ predicate isBarrier(DataFlow::Node node) { - exists(ArrayAccess aa, MethodCall ma | aa.getArray() = ma | - ma.getQualifier() = node.asExpr() and - ma.getMethod() instanceof SplitMethod and - not aa.getIndexExpr().(CompileTimeConstantExpr).getIntValue() = 0 - ) - or - node instanceof SimpleTypeSanitizer + node instanceof ClientSuppliedIpUsedInSecurityCheckSanitizer } } diff --git a/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheckLib.qll b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheckLib.qll index 42c5f989168d..0a12e35b8c98 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheckLib.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheckLib.qll @@ -4,6 +4,7 @@ import java import DataFlow import semmle.code.java.frameworks.Networking import semmle.code.java.security.QueryInjection +import semmle.code.java.security.Sanitizers /** * A data flow source of the client ip obtained according to the remote endpoint identifier specified @@ -28,6 +29,9 @@ class ClientSuppliedIpUsedInSecurityCheck extends DataFlow::Node { /** A data flow sink for ip address forgery vulnerabilities. */ abstract class ClientSuppliedIpUsedInSecurityCheckSink extends DataFlow::Node { } +/** A data flow sanitizer for ip address forgery vulnerabilities. */ +abstract class ClientSuppliedIpUsedInSecurityCheckSanitizer extends DataFlow::Node { } + /** * A data flow sink for remote client ip comparison. * @@ -98,3 +102,23 @@ string getIpAddressRegex() { result = "^((10\\.((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)(\\.)?)|(192\\.168\\.)|172\\.(1[6789]|2[0-9]|3[01])\\.)((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)(\\.)?((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)$" } + +/** + * Splitting a header value by `,` and taking an entry other than the first is sanitizing, because + * later entries may originate from more-trustworthy intermediate proxies, not the original client. + */ +private class EntryAfterFirstSanitizer extends ClientSuppliedIpUsedInSecurityCheckSanitizer { + EntryAfterFirstSanitizer() { + exists(ArrayAccess aa, MethodCall ma | aa.getArray() = ma | + ma.getQualifier() = this.asExpr() and + ma.getMethod() instanceof SplitMethod and + not aa.getIndexExpr().(CompileTimeConstantExpr).getIntValue() = 0 + ) + } +} + +private class SimpleTypeAddressSpoofingSanitizer extends ClientSuppliedIpUsedInSecurityCheckSanitizer instanceof SimpleTypeSanitizer +{ } + +private class SqlOperationSanitizer extends ClientSuppliedIpUsedInSecurityCheckSanitizer instanceof QueryInjectionSanitizer +{ } From 555a2022b978387e9d02cbe7bf7d5de89f4645ea Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Jan 2026 11:04:48 +0000 Subject: [PATCH 14/24] Allow MaD sanitizers for `java/http-response-splitting` --- .../code/java/security/ResponseSplitting.qll | 27 +++++++++++++++++++ .../java/security/ResponseSplittingQuery.qll | 17 +----------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/ResponseSplitting.qll b/java/ql/lib/semmle/code/java/security/ResponseSplitting.qll index 1238793ffd70..877b4d607d80 100644 --- a/java/ql/lib/semmle/code/java/security/ResponseSplitting.qll +++ b/java/ql/lib/semmle/code/java/security/ResponseSplitting.qll @@ -8,6 +8,7 @@ import semmle.code.java.dataflow.FlowSources import semmle.code.java.frameworks.Servlets import semmle.code.java.frameworks.JaxWS private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.security.Sanitizers /** A sink that is vulnerable to an HTTP header splitting attack. */ abstract class HeaderSplittingSink extends DataFlow::Node { } @@ -16,6 +17,32 @@ private class DefaultHeaderSplittingSink extends HeaderSplittingSink { DefaultHeaderSplittingSink() { sinkNode(this, "response-splitting") } } +/** A sanitizer for an HTTP header splitting attack. */ +abstract class HeaderSplittingSanitizer extends DataFlow::Node { } + +private class SimpleTypeHeaderSplittingSanitizer extends HeaderSplittingSanitizer instanceof SimpleTypeSanitizer +{ } + +private class ExternalHeaderSplittingSanitizer extends HeaderSplittingSanitizer { + ExternalHeaderSplittingSanitizer() { barrierNode(this, "response-splitting") } +} + +private class NewlineRemovalHeaderSplittingSanitizer extends HeaderSplittingSanitizer { + NewlineRemovalHeaderSplittingSanitizer() { + exists(MethodCall ma, string methodName, CompileTimeConstantExpr target | + this.asExpr() = ma and + ma.getMethod().hasQualifiedName("java.lang", "String", methodName) and + target = ma.getArgument(0) and + ( + methodName = "replace" and target.getIntValue() = [10, 13] // 10 == "\n", 13 == "\r" + or + methodName = "replaceAll" and + target.getStringValue().regexpMatch(".*([\n\r]|\\[\\^[^\\]\r\n]*\\]).*") + ) + ) + } +} + /** A source that introduces data considered safe to use by a header splitting source. */ abstract class SafeHeaderSplittingSource extends DataFlow::Node instanceof RemoteFlowSource { } diff --git a/java/ql/lib/semmle/code/java/security/ResponseSplittingQuery.qll b/java/ql/lib/semmle/code/java/security/ResponseSplittingQuery.qll index 9bd96a51a68d..327796cf831f 100644 --- a/java/ql/lib/semmle/code/java/security/ResponseSplittingQuery.qll +++ b/java/ql/lib/semmle/code/java/security/ResponseSplittingQuery.qll @@ -2,7 +2,6 @@ import java private import semmle.code.java.dataflow.FlowSources -private import semmle.code.java.security.Sanitizers import semmle.code.java.security.ResponseSplitting /** @@ -16,21 +15,7 @@ module ResponseSplittingConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof HeaderSplittingSink } - predicate isBarrier(DataFlow::Node node) { - node instanceof SimpleTypeSanitizer - or - exists(MethodCall ma, string methodName, CompileTimeConstantExpr target | - node.asExpr() = ma and - ma.getMethod().hasQualifiedName("java.lang", "String", methodName) and - target = ma.getArgument(0) and - ( - methodName = "replace" and target.getIntValue() = [10, 13] // 10 == "\n", 13 == "\r" - or - methodName = "replaceAll" and - target.getStringValue().regexpMatch(".*([\n\r]|\\[\\^[^\\]\r\n]*\\]).*") - ) - ) - } + predicate isBarrier(DataFlow::Node node) { node instanceof HeaderSplittingSanitizer } predicate observeDiffInformedIncrementalMode() { any() } } From a312ea2f4bb7e7870f30dada000deca0012f41ec Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Jan 2026 13:43:17 +0000 Subject: [PATCH 15/24] Allow MaD sanitizers for `java/android/sensitive-notification` --- java/ql/lib/semmle/code/java/security/SensitiveUiQuery.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/SensitiveUiQuery.qll b/java/ql/lib/semmle/code/java/security/SensitiveUiQuery.qll index ea58c08e07ce..57ae83e503d5 100644 --- a/java/ql/lib/semmle/code/java/security/SensitiveUiQuery.qll +++ b/java/ql/lib/semmle/code/java/security/SensitiveUiQuery.qll @@ -14,6 +14,8 @@ private module NotificationTrackingConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sinkNode(sink, "notification") } + predicate isBarrier(DataFlow::Node node) { barrierNode(node, "notification") } + predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) { isSink(node) and exists(c) } From 408872144790c7c3fe9589f5ab2c2e7ab38c006a Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Jan 2026 13:57:36 +0000 Subject: [PATCH 16/24] Allow MaD sanitizers for `java/static-initialization-vector` --- .../code/java/security/StaticInitializationVectorQuery.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/StaticInitializationVectorQuery.qll b/java/ql/lib/semmle/code/java/security/StaticInitializationVectorQuery.qll index a03775990541..35c2ce89aa49 100644 --- a/java/ql/lib/semmle/code/java/security/StaticInitializationVectorQuery.qll +++ b/java/ql/lib/semmle/code/java/security/StaticInitializationVectorQuery.qll @@ -128,6 +128,8 @@ module StaticInitializationVectorConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof EncryptionInitializationSink } + predicate isBarrier(DataFlow::Node node) { barrierNode(node, "encryption-iv") } + predicate observeDiffInformedIncrementalMode() { any() } } From d1f6008e37d7cd752dd80ae1863e7309886f6b33 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Jan 2026 14:00:23 +0000 Subject: [PATCH 17/24] Allow MaD sanitizers for `java/exec-tainted-environment` --- .../code/java/security/TaintedEnvironmentVariableQuery.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/TaintedEnvironmentVariableQuery.qll b/java/ql/lib/semmle/code/java/security/TaintedEnvironmentVariableQuery.qll index 2bc9dba92f01..2fd6080ffa41 100644 --- a/java/ql/lib/semmle/code/java/security/TaintedEnvironmentVariableQuery.qll +++ b/java/ql/lib/semmle/code/java/security/TaintedEnvironmentVariableQuery.qll @@ -22,6 +22,10 @@ private module ProcessBuilderEnvironmentFlow = DataFlow::Global Date: Thu, 8 Jan 2026 14:03:13 +0000 Subject: [PATCH 18/24] Allow MaD sanitizers for `java/server-side-template-injection` --- java/ql/lib/semmle/code/java/security/TemplateInjection.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/TemplateInjection.qll b/java/ql/lib/semmle/code/java/security/TemplateInjection.qll index 58c48bb7f224..9d698e082783 100644 --- a/java/ql/lib/semmle/code/java/security/TemplateInjection.qll +++ b/java/ql/lib/semmle/code/java/security/TemplateInjection.qll @@ -46,3 +46,7 @@ private class DefaultTemplateInjectionSink extends TemplateInjectionSink { private class DefaultTemplateInjectionSanitizer extends TemplateInjectionSanitizer instanceof SimpleTypeSanitizer { } + +private class ExternalTemplateInjectionSanitizer extends TemplateInjectionSanitizer { + ExternalTemplateInjectionSanitizer() { barrierNode(this, "template-injection") } +} From 5118c82690f0eea36b36c6bc1750b938653a9b91 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Jan 2026 14:30:40 +0000 Subject: [PATCH 19/24] Allow MaD sanitizers for `java/unsafe-deserialization` --- .../code/java/security/UnsafeDeserializationQuery.qll | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll b/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll index dc771a466063..2fe6dc8b3349 100644 --- a/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll @@ -145,6 +145,10 @@ private class DefaultUnsafeDeserializationSink extends DataFlow::Node { DefaultUnsafeDeserializationSink() { sinkNode(this, "unsafe-deserialization") } } +private class ExternalUnsafeDeserializationSanitizer extends DataFlow::Node { + ExternalUnsafeDeserializationSanitizer() { barrierNode(this, "unsafe-deserialization") } +} + /** * Holds if `ma` is a call that deserializes data from `sink`. * @@ -308,7 +312,9 @@ private module UnsafeDeserializationConfig implements DataFlow::ConfigSig { isUnsafeDeserializationTaintStep(pred, succ) } - predicate isBarrier(DataFlow::Node node) { isUnsafeDeserializationSanitizer(node) } + predicate isBarrier(DataFlow::Node node) { + isUnsafeDeserializationSanitizer(node) or node instanceof ExternalUnsafeDeserializationSanitizer + } predicate observeDiffInformedIncrementalMode() { any() } From 9d729d0efa852771ccbded44cc10e60c55b91df0 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Jan 2026 14:34:12 +0000 Subject: [PATCH 20/24] Allow MaD sanitizers for `java/unsafe-hostname-verification` --- .../java/security/UnsafeHostnameVerificationQuery.qll | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/UnsafeHostnameVerificationQuery.qll b/java/ql/lib/semmle/code/java/security/UnsafeHostnameVerificationQuery.qll index 60829f426f75..3cf5171af2b5 100644 --- a/java/ql/lib/semmle/code/java/security/UnsafeHostnameVerificationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/UnsafeHostnameVerificationQuery.qll @@ -41,10 +41,11 @@ module TrustAllHostnameVerifierConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof HostnameVerifierSink } - predicate isBarrier(DataFlow::Node barrier) { + predicate isBarrier(DataFlow::Node node) { + node instanceof ExternalHostnameVerifierSanitizer + or // ignore nodes that are in functions that intentionally disable hostname verification - barrier - .getEnclosingCallable() + node.getEnclosingCallable() .getName() /* * Regex: (_)* : @@ -88,6 +89,10 @@ private class HostnameVerifierSink extends DataFlow::Node { HostnameVerifierSink() { sinkNode(this, "hostname-verification") } } +private class ExternalHostnameVerifierSanitizer extends DataFlow::Node { + ExternalHostnameVerifierSanitizer() { barrierNode(this, "hostname-verification") } +} + /** * Flags suggesting a deliberately unsafe `HostnameVerifier` usage. */ From 03a43b81340cb4d9764a8c5d5339cb357757d0c5 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Jan 2026 14:40:31 +0000 Subject: [PATCH 21/24] Allow MaD sanitizers for `java/unvalidated-url-forward` --- java/ql/lib/semmle/code/java/security/UrlForwardQuery.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/UrlForwardQuery.qll b/java/ql/lib/semmle/code/java/security/UrlForwardQuery.qll index 895e824b3dbd..b75a79c49c63 100644 --- a/java/ql/lib/semmle/code/java/security/UrlForwardQuery.qll +++ b/java/ql/lib/semmle/code/java/security/UrlForwardQuery.qll @@ -46,6 +46,10 @@ abstract class UrlForwardBarrier extends DataFlow::Node { } private class PrimitiveBarrier extends UrlForwardBarrier instanceof SimpleTypeSanitizer { } +private class ExternalUrlForwardBarrier extends UrlForwardBarrier { + ExternalUrlForwardBarrier() { barrierNode(this, "url-forward") } +} + /** * A barrier for values appended to a "redirect:" prefix. * These results are excluded because they should be handled From 3dc07ce53239e75bce6e2319bc9902198c0fc057 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Jan 2026 14:43:12 +0000 Subject: [PATCH 22/24] Allow MaD sanitizers for `java/unvalidated-url-redirection` --- java/ql/lib/semmle/code/java/security/UrlRedirect.qll | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/java/ql/lib/semmle/code/java/security/UrlRedirect.qll b/java/ql/lib/semmle/code/java/security/UrlRedirect.qll index be6addfa2529..b3c8d3ff9d14 100644 --- a/java/ql/lib/semmle/code/java/security/UrlRedirect.qll +++ b/java/ql/lib/semmle/code/java/security/UrlRedirect.qll @@ -50,5 +50,9 @@ private class ApacheUrlRedirectSink extends UrlRedirectSink { } } -private class DefaultUrlRedirectSanitizer extends UrlRedirectSanitizer instanceof RequestForgerySanitizer +private class RequestForgeryUrlRedirectSanitizer extends UrlRedirectSanitizer instanceof RequestForgerySanitizer { } + +private class ExternalUrlRedirectSanitizer extends UrlRedirectSanitizer { + ExternalUrlRedirectSanitizer() { barrierNode(this, "url-redirection") } +} From 7175879c9a1c8177b92a026151f2b7412296c556 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Jan 2026 14:45:51 +0000 Subject: [PATCH 23/24] Allow MaD sanitizers for `java/xml/xpath-injection` --- java/ql/lib/semmle/code/java/security/XPath.qll | 7 +++++++ .../lib/semmle/code/java/security/XPathInjectionQuery.qll | 2 ++ 2 files changed, 9 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/XPath.qll b/java/ql/lib/semmle/code/java/security/XPath.qll index cc3fde30091b..5126ba8d8893 100644 --- a/java/ql/lib/semmle/code/java/security/XPath.qll +++ b/java/ql/lib/semmle/code/java/security/XPath.qll @@ -27,3 +27,10 @@ private class DefaultXPathInjectionSink extends XPathInjectionSink { ) } } + +/** A sanitizer for XPath injection. */ +abstract class XPathInjectionSanitizer extends DataFlow::Node { } + +private class ExternalXPathInjectionSanitizer extends XPathInjectionSanitizer { + ExternalXPathInjectionSanitizer() { barrierNode(this, "xpath-injection") } +} diff --git a/java/ql/lib/semmle/code/java/security/XPathInjectionQuery.qll b/java/ql/lib/semmle/code/java/security/XPathInjectionQuery.qll index e387f0d0e118..eb94b4db43ee 100644 --- a/java/ql/lib/semmle/code/java/security/XPathInjectionQuery.qll +++ b/java/ql/lib/semmle/code/java/security/XPathInjectionQuery.qll @@ -13,6 +13,8 @@ module XPathInjectionConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof XPathInjectionSink } + predicate isBarrier(DataFlow::Node node) { node instanceof XPathInjectionSanitizer } + predicate observeDiffInformedIncrementalMode() { any() } } From 4d2a42930a9cec29c8ca8ea8477d58febd90ec93 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 8 Jan 2026 14:49:26 +0000 Subject: [PATCH 24/24] Allow MaD sanitizers for `java/xslt-injection` --- .../lib/semmle/code/java/security/XsltInjection.qll | 11 +++++++++++ .../semmle/code/java/security/XsltInjectionQuery.qll | 3 +-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/XsltInjection.qll b/java/ql/lib/semmle/code/java/security/XsltInjection.qll index d54e92066443..19cebdd93103 100644 --- a/java/ql/lib/semmle/code/java/security/XsltInjection.qll +++ b/java/ql/lib/semmle/code/java/security/XsltInjection.qll @@ -5,6 +5,7 @@ module; import java import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.security.Sanitizers /** * A data flow sink for unvalidated user input that is used in XSLT transformation. @@ -17,6 +18,16 @@ private class DefaultXsltInjectionSink extends XsltInjectionSink { DefaultXsltInjectionSink() { sinkNode(this, "xslt-injection") } } +/** A default sink representing methods susceptible to XSLT Injection attacks. */ +abstract class XsltInjectionSanitizer extends DataFlow::Node { } + +private class SimpleTypeXsltInjectionSanitizer extends XsltInjectionSanitizer instanceof SimpleTypeSanitizer +{ } + +private class ExternalXsltInjectionSanitizer extends XsltInjectionSanitizer { + ExternalXsltInjectionSanitizer() { barrierNode(this, "xslt-injection") } +} + /** * A unit class for adding additional taint steps. * diff --git a/java/ql/lib/semmle/code/java/security/XsltInjectionQuery.qll b/java/ql/lib/semmle/code/java/security/XsltInjectionQuery.qll index 7ff745a057ca..51eef80e9697 100644 --- a/java/ql/lib/semmle/code/java/security/XsltInjectionQuery.qll +++ b/java/ql/lib/semmle/code/java/security/XsltInjectionQuery.qll @@ -5,7 +5,6 @@ import semmle.code.java.dataflow.FlowSources import semmle.code.java.dataflow.TaintTracking import semmle.code.java.security.XmlParsers import semmle.code.java.security.XsltInjection -private import semmle.code.java.security.Sanitizers /** * A taint-tracking configuration for unvalidated user input that is used in XSLT transformation. @@ -15,7 +14,7 @@ module XsltInjectionFlowConfig implements DataFlow::ConfigSig { predicate isSink(DataFlow::Node sink) { sink instanceof XsltInjectionSink } - predicate isBarrier(DataFlow::Node node) { node instanceof SimpleTypeSanitizer } + predicate isBarrier(DataFlow::Node node) { node instanceof XsltInjectionSanitizer } predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { any(XsltInjectionAdditionalTaintStep c).step(node1, node2)