diff --git a/java/ql/lib/change-notes/2025-06-14-log-injection-improvements.md b/java/ql/lib/change-notes/2025-06-14-log-injection-improvements.md new file mode 100644 index 000000000000..1db28332043f --- /dev/null +++ b/java/ql/lib/change-notes/2025-06-14-log-injection-improvements.md @@ -0,0 +1,5 @@ +--- +category: majorAnalysis +--- +* The `java/log-injection` query now excludes calls to DEBUG and TRACE level logging methods (`debug`, `trace`, `fine`, `finer`, `finest`) from its sinks, since these log levels are typically disabled in production. +* Added `java.net.URLEncoder.encode()` as a sanitizer for the `java/log-injection` query, since URL encoding replaces line breaks with percent-encoded equivalents. diff --git a/java/ql/lib/semmle/code/java/security/LogInjection.qll b/java/ql/lib/semmle/code/java/security/LogInjection.qll index b585c249d1eb..8df9477059c2 100644 --- a/java/ql/lib/semmle/code/java/security/LogInjection.qll +++ b/java/ql/lib/semmle/code/java/security/LogInjection.qll @@ -30,12 +30,36 @@ class LogInjectionAdditionalTaintStep extends Unit { } private class DefaultLogInjectionSink extends LogInjectionSink { - DefaultLogInjectionSink() { sinkNode(this, "log-injection") } + DefaultLogInjectionSink() { + sinkNode(this, "log-injection") and + not exists(MethodCall mc | + this.asExpr() = mc.getAnArgument() and + mc.getMethod().getName() in [ + "debug", "trace", // SLF4J, Log4j, Commons Logging, JBoss Logging + "fine", "finer", "finest" // java.util.logging + ] + ) + } } private class DefaultLogInjectionSanitizer extends LogInjectionSanitizer instanceof SimpleTypeSanitizer { } +/** + * A call to `java.net.URLEncoder.encode(...)` is considered a sanitizer for log injection, + * since URL encoding replaces line breaks and other special characters with percent-encoded + * equivalents that cannot be used to forge log entries. + */ +private class UrlEncoderSanitizer extends LogInjectionSanitizer { + UrlEncoderSanitizer() { + exists(MethodCall mc | + mc.getMethod().getDeclaringType().hasQualifiedName("java.net", "URLEncoder") and + mc.getMethod().hasName("encode") and + this.asExpr() = mc + ) + } +} + private class LineBreaksLogInjectionSanitizer extends LogInjectionSanitizer { LineBreaksLogInjectionSanitizer() { logInjectionSanitizer(this.asExpr()) diff --git a/java/ql/src/Security/CWE/CWE-117/LogInjection.ql b/java/ql/src/Security/CWE/CWE-117/LogInjection.ql index f3efb578f76a..7974b1018943 100644 --- a/java/ql/src/Security/CWE/CWE-117/LogInjection.ql +++ b/java/ql/src/Security/CWE/CWE-117/LogInjection.ql @@ -13,9 +13,12 @@ import java import semmle.code.java.security.LogInjectionQuery +import semmle.code.java.dataflow.internal.ModelExclusions import LogInjectionFlow::PathGraph from LogInjectionFlow::PathNode source, LogInjectionFlow::PathNode sink -where LogInjectionFlow::flowPath(source, sink) +where + LogInjectionFlow::flowPath(source, sink) and + not isInTestFile(sink.getNode().getLocation().getFile()) select sink.getNode(), source, sink, "This log entry depends on a $@.", source.getNode(), "user-provided value" diff --git a/java/ql/src/change-notes/2025-06-14-log-injection-improvements.md b/java/ql/src/change-notes/2025-06-14-log-injection-improvements.md new file mode 100644 index 000000000000..223bd64cc2ee --- /dev/null +++ b/java/ql/src/change-notes/2025-06-14-log-injection-improvements.md @@ -0,0 +1,6 @@ +--- +category: majorAnalysis +--- +* The `java/log-injection` query now excludes calls to DEBUG and TRACE level logging methods (`debug`, `trace`, `fine`, `finer`, `finest`) from its results, since these log levels are typically disabled in production and do not present a realistic log injection attack surface. +* The `java/log-injection` query now recognizes `java.net.URLEncoder.encode()` as a sanitizer, since URL encoding replaces line breaks and other special characters with percent-encoded equivalents. +* The `java/log-injection` query now excludes results in test files to reduce noise from non-production code.