Skip to content

Commit 7b0d9ea

Browse files
authored
Merge pull request #7054 from atorralba/atorralba/promote-log-injection
Java: Promote Log Injection from experimental
2 parents 4aacba8 + 1030ff7 commit 7b0d9ea

File tree

43 files changed

+5394
-89
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+5394
-89
lines changed

java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ private module Frameworks {
9595
private import semmle.code.java.frameworks.JaxWS
9696
private import semmle.code.java.frameworks.JoddJson
9797
private import semmle.code.java.frameworks.JsonJava
98+
private import semmle.code.java.frameworks.Logging
9899
private import semmle.code.java.frameworks.Objects
99100
private import semmle.code.java.frameworks.Optional
100101
private import semmle.code.java.frameworks.Stream

java/ql/lib/semmle/code/java/frameworks/Logging.qll

Lines changed: 329 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/** Provides classes and predicates related to Log Injection vulnerabilities. */
2+
3+
import java
4+
import semmle.code.java.dataflow.DataFlow
5+
import semmle.code.java.dataflow.ExternalFlow
6+
7+
/** A data flow sink for unvalidated user input that is used to log messages. */
8+
abstract class LogInjectionSink extends DataFlow::Node { }
9+
10+
/**
11+
* A node that sanitizes a message before logging to avoid log injection.
12+
*/
13+
abstract class LogInjectionSanitizer extends DataFlow::Node { }
14+
15+
/**
16+
* A unit class for adding additional taint steps.
17+
*
18+
* Extend this class to add additional taint steps that should apply to the `LogInjectionConfiguration`.
19+
*/
20+
class LogInjectionAdditionalTaintStep extends Unit {
21+
/**
22+
* Holds if the step from `node1` to `node2` should be considered a taint
23+
* step for the `LogInjectionConfiguration` configuration.
24+
*/
25+
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
26+
}
27+
28+
private class DefaultLogInjectionSink extends LogInjectionSink {
29+
DefaultLogInjectionSink() { sinkNode(this, "logging") }
30+
}
31+
32+
private class DefaultLogInjectionSanitizer extends LogInjectionSanitizer {
33+
DefaultLogInjectionSanitizer() {
34+
this.getType() instanceof BoxedType or this.getType() instanceof PrimitiveType
35+
}
36+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/** Provides taint tracking configurations to be used in queries related to the Log Injection vulnerability. */
2+
3+
import java
4+
import semmle.code.java.dataflow.FlowSources
5+
import semmle.code.java.security.LogInjection
6+
7+
/**
8+
* A taint-tracking configuration for tracking untrusted user input used in log entries.
9+
*/
10+
class LogInjectionConfiguration extends TaintTracking::Configuration {
11+
LogInjectionConfiguration() { this = "LogInjectionConfiguration" }
12+
13+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
14+
15+
override predicate isSink(DataFlow::Node sink) { sink instanceof LogInjectionSink }
16+
17+
override predicate isSanitizer(DataFlow::Node node) { node instanceof LogInjectionSanitizer }
18+
19+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
20+
any(LogInjectionAdditionalTaintStep c).step(node1, node2)
21+
}
22+
}

java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.qhelp renamed to java/ql/src/Security/CWE/CWE-117/LogInjection.qhelp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,15 @@ other forms of HTML injection.
2929
</recommendation>
3030

3131
<example>
32-
<p>In the example, a username, provided by the user, is logged using <code>logger.warn</code> (from <code>org.slf4j.Logger</code>).
32+
<p>In the first example, a username, provided by the user, is logged using <code>logger.warn</code> (from <code>org.slf4j.Logger</code>).
3333
In the first case (<code>/bad</code> endpoint), the username is logged without any sanitization.
3434
If a malicious user provides <code>Guest'%0AUser:'Admin</code> as a username parameter,
3535
the log entry will be split into two separate lines, where the first line will be <code>User:'Guest'</code> and the second one will be <code>User:'Admin'</code>.
3636
</p>
3737
<sample src="LogInjectionBad.java" />
3838

39-
<p> In the second case (<code>/good</code> endpoint), <code>matches()</code> is used to ensure the user input only has alphanumeric characters.
40-
If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter,
41-
the log entry will not be split into two separate lines, resulting in a single line <code>User:'Guest'User:'Admin'</code>.</p>
39+
<p> In the second example (<code>/good</code> endpoint), <code>matches()</code> is used to ensure the user input only has alphanumeric characters.
40+
If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter, the log entry will not be logged at all, preventing the injection.</p>
4241

4342
<sample src="LogInjectionGood.java" />
4443
</example>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @name Log Injection
3+
* @description Building log entries from user-controlled data may allow
4+
* insertion of forged log entries by malicious users.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @security-severity 7.8
8+
* @precision medium
9+
* @id java/log-injection
10+
* @tags security
11+
* external/cwe/cwe-117
12+
*/
13+
14+
import java
15+
import semmle.code.java.security.LogInjectionQuery
16+
import DataFlow::PathGraph
17+
18+
from LogInjectionConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
19+
where cfg.hasFlowPath(source, sink)
20+
select sink.getNode(), source, sink, "This $@ flows to a log entry.", source.getNode(),
21+
"user-provided value"

java/ql/src/experimental/Security/CWE/CWE-117/LogInjectionGood.java renamed to java/ql/src/Security/CWE/CWE-117/LogInjectionGood.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ public class LogInjection {
1616
public String good(@RequestParam(value = "username", defaultValue = "name") String username) {
1717
// The regex check here, allows only alphanumeric characters to pass.
1818
// Hence, does not result in log injection
19-
if (username.matches("\w*")) {
19+
if (username.matches("\\w*")) {
2020
log.warn("User:'{}'", username);
21-
21+
2222
return username;
2323
}
2424
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: newQuery
3+
---
4+
* The query "Log Injection" (`java/log-injection`) has been promoted from experimental to the main query pack. Its results will now appear by default. The query was originally [submitted as an experimental query by @porcupineyhairs and @dellalibera](https://github.com/github/codeql/pull/5099).

java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.ql

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)