mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
move files out of experimental
This commit is contained in:
38
java/ql/src/Security/CWE/CWE-730/RegexInjection.java
Normal file
38
java/ql/src/Security/CWE/CWE-730/RegexInjection.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package com.example.demo;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class DemoApplication {
|
||||
|
||||
@GetMapping("/string1")
|
||||
public String string1(@RequestParam(value = "input", defaultValue = "test") String input,
|
||||
@RequestParam(value = "pattern", defaultValue = ".*") String pattern) {
|
||||
// BAD: Unsanitized user input is used to construct a regular expression
|
||||
if (input.matches("^" + pattern + "=.*$"))
|
||||
return "match!";
|
||||
|
||||
return "doesn't match!";
|
||||
}
|
||||
|
||||
@GetMapping("/string2")
|
||||
public String string2(@RequestParam(value = "input", defaultValue = "test") String input,
|
||||
@RequestParam(value = "pattern", defaultValue = ".*") String pattern) {
|
||||
// GOOD: User input is sanitized before constructing the regex
|
||||
if (input.matches("^" + escapeSpecialRegexChars(pattern) + "=.*$"))
|
||||
return "match!";
|
||||
|
||||
return "doesn't match!";
|
||||
}
|
||||
|
||||
Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\]><-=!.+*?^$\\\\|]");
|
||||
|
||||
String escapeSpecialRegexChars(String str) {
|
||||
return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\$0");
|
||||
}
|
||||
}
|
||||
48
java/ql/src/Security/CWE/CWE-730/RegexInjection.qhelp
Normal file
48
java/ql/src/Security/CWE/CWE-730/RegexInjection.qhelp
Normal file
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Constructing a regular expression with unsanitized user input is dangerous as a malicious user may
|
||||
be able to modify the meaning of the expression. In particular, such a user may be able to provide
|
||||
a regular expression fragment that takes exponential time in the worst case, and use that to
|
||||
perform a Denial of Service attack.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Before embedding user input into a regular expression, use a sanitization function
|
||||
to escape meta-characters that have special meaning.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example shows a HTTP request parameter that is used to construct a regular expression:
|
||||
</p>
|
||||
<sample src="RegexInjection.java" />
|
||||
<p>
|
||||
In the first case the user-provided regex is not escaped.
|
||||
If a malicious user provides a regex that has exponential worst case performance,
|
||||
then this could lead to a Denial of Service.
|
||||
</p>
|
||||
<p>
|
||||
In the second case, the user input is escaped using <code>escapeSpecialRegexChars</code> before being included
|
||||
in the regular expression. This ensures that the user cannot insert characters which have a special
|
||||
meaning in regular expressions.
|
||||
</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS">Regular expression Denial of Service - ReDoS</a>.
|
||||
</li>
|
||||
<li>
|
||||
Wikipedia: <a href="https://en.wikipedia.org/wiki/ReDoS">ReDoS</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
105
java/ql/src/Security/CWE/CWE-730/RegexInjection.ql
Normal file
105
java/ql/src/Security/CWE/CWE-730/RegexInjection.ql
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @name Regular expression injection
|
||||
* @description User input should not be used in regular expressions without first being sanitized,
|
||||
* otherwise a malicious user may be able to provide a regex that could require
|
||||
* exponential time on certain inputs.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/regex-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-730
|
||||
* external/cwe/cwe-400
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A data flow sink for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
class RegexSink extends DataFlow::ExprNode {
|
||||
RegexSink() {
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
(
|
||||
m.getDeclaringType() instanceof TypeString and
|
||||
(
|
||||
ma.getArgument(0) = this.asExpr() and
|
||||
// TODO: confirm if more/less than the below need to be handled
|
||||
m.hasName(["matches", "split", "replaceFirst", "replaceAll"])
|
||||
)
|
||||
or
|
||||
// TODO: review Java Pattern API
|
||||
m.getDeclaringType().hasQualifiedName("java.util.regex", "Pattern") and
|
||||
(
|
||||
ma.getArgument(0) = this.asExpr() and
|
||||
// TODO: confirm if more/less than the below need to be handled
|
||||
m.hasName(["compile", "matches"])
|
||||
)
|
||||
or
|
||||
// TODO: read docs about regex APIs in Java
|
||||
m.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "RegExUtils") and
|
||||
(
|
||||
ma.getArgument(1) = this.asExpr() and
|
||||
m.getParameterType(1) instanceof TypeString and
|
||||
// TODO: confirm if more/less than the below need to be handled
|
||||
m.hasName([
|
||||
"removeAll", "removeFirst", "removePattern", "replaceAll", "replaceFirst",
|
||||
"replacePattern"
|
||||
])
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: is this abstract class needed? Are there pre-existing sanitizer classes that can be used instead?
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A call to a function whose name suggests that it escapes regular
|
||||
* expression meta-characters.
|
||||
*/
|
||||
class RegExpSanitizationCall extends Sanitizer {
|
||||
RegExpSanitizationCall() {
|
||||
exists(string calleeName, string sanitize, string regexp |
|
||||
calleeName = this.asExpr().(Call).getCallee().getName() and
|
||||
sanitize = "(?:escape|saniti[sz]e)" and // TODO: confirm this is sufficient
|
||||
regexp = "regexp?" // TODO: confirm this is sufficient
|
||||
|
|
||||
calleeName
|
||||
.regexpMatch("(?i)(" + sanitize + ".*" + regexp + ".*)" + "|(" + regexp + ".*" + sanitize +
|
||||
".*)") // TODO: confirm this is sufficient
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
class RegexInjectionConfiguration extends TaintTracking::Configuration {
|
||||
RegexInjectionConfiguration() { this = "RegexInjectionConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof RegexSink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, RegexInjectionConfiguration c
|
||||
where c.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This regular expression is constructed from a $@.",
|
||||
source.getNode(), "user-provided value"
|
||||
// from MethodAccess ma
|
||||
// where
|
||||
// // ma.getMethod().hasName("startsWith") and // graphhopper
|
||||
// // ma.getFile().getBaseName() = "NavigateResource.java" // graphhopper
|
||||
// // ma.getMethod().hasName("substring") and // jfinal
|
||||
// // ma.getFile().getBaseName() = "FileManager.java" // jfinal
|
||||
// ma.getMethod().hasName("startsWith") and // roller
|
||||
// ma.getFile().getBaseName() = "PageServlet.java" // roller (or RegexUtil.java)
|
||||
// ProteinArraySignificanceTestJSON.java or MockRKeys.java for cbioportal
|
||||
// select ma, "method access"
|
||||
Reference in New Issue
Block a user