mirror of
https://github.com/github/codeql.git
synced 2026-04-24 08:15:14 +02:00
Distingush between whether or not a regex is matched against a full string
Also some fixes and additional tests
This commit is contained in:
@@ -13,16 +13,48 @@ private class RegexCompileFlowConf extends DataFlow2::Configuration {
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof StringLiteral }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) { sinkNode(node, "regex-compile") }
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
sinkNode(node, ["regex-compile", "regex-compile-match", "regex-compile-find"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `s` is used as a regex, with the mode `mode` (if known).
|
||||
* If regex mode is not known, `mode` will be `"None"`.
|
||||
*/
|
||||
predicate usedAsRegex(StringLiteral s, string mode) {
|
||||
any(RegexCompileFlowConf c).hasFlow(DataFlow2::exprNode(s), _) and
|
||||
mode = "None" // TODO: proper mode detection
|
||||
predicate usedAsRegex(StringLiteral s, string mode, boolean match_full_string) {
|
||||
exists(DataFlow::Node sink |
|
||||
any(RegexCompileFlowConf c).hasFlow(DataFlow2::exprNode(s), sink) and
|
||||
mode = "None" and // TODO: proper mode detection
|
||||
(if matchesFullString(sink) then match_full_string = true else match_full_string = false)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the regex that flows to `sink` is used to match against a full string,
|
||||
* as though it was implicitly surrounded by ^ and $.
|
||||
*/
|
||||
private predicate matchesFullString(DataFlow::Node sink) {
|
||||
sinkNode(sink, "regex-compile-match")
|
||||
or
|
||||
exists(DataFlow::Node matchSource, RegexCompileToMatchConf conf |
|
||||
matchSource.asExpr().(MethodAccess).getAnArgument() = sink.asExpr() and
|
||||
conf.hasFlow(matchSource, _)
|
||||
)
|
||||
}
|
||||
|
||||
private class RegexCompileToMatchConf extends DataFlow2::Configuration {
|
||||
RegexCompileToMatchConf() { this = "RegexCompileToMatchConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { sourceNode(node, "regex-compile") }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) { sinkNode(node, "regex-match") }
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma | node2.asExpr() = ma and node1.asExpr() = ma.getQualifier() |
|
||||
ma.getMethod().hasQualifiedName("java.util.regex", "Pattern", "matcher")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
private class RegexSourceCsv extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
//"namespace;type;subtypes;name;signature;ext;output;kind"
|
||||
"java.util.regex;Pattern;false;compile;(String);;ReturnValue;regex-compile",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private class RegexSinkCsv extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
@@ -10,13 +20,16 @@ private class RegexSinkCsv extends SinkModelCsv {
|
||||
//"namespace;type;subtypes;name;signature;ext;input;kind"
|
||||
"java.util.regex;Pattern;false;compile;(String);;Argument[0];regex-compile",
|
||||
"java.util.regex;Pattern;false;compile;(String,int);;Argument[0];regex-compile",
|
||||
"java.util.regex;Pattern;false;matches;(String,CharSequence);;Argument[0];regex-compile",
|
||||
"java.util;String;false;matches;(String);;Argument[0];regex-compile",
|
||||
"java.util;String;false;split;(String);;Argument[0];regex-compile",
|
||||
"java.util;String;false;split;(String,int);;Argument[0];regex-compile",
|
||||
"java.util;String;false;replaceAll;(String,String);;Argument[0];regex-compile",
|
||||
"java.util;String;false;replaceFirst;(String,String);;Argument[0];regex-compile",
|
||||
"com.google.common.base;Splitter;false;onPattern;(String);;Argument[0];regex-compile"
|
||||
"java.util.regex;Pattern;false;matches;(String,CharSequence);;Argument[0];regex-compile-match",
|
||||
"java.lang;String;false;matches;(String);;Argument[0];regex-compile-match",
|
||||
"java.lang;String;false;split;(String);;Argument[0];regex-compile-find",
|
||||
"java.lang;String;false;split;(String,int);;Argument[0];regex-compile-find",
|
||||
"java.lang;String;false;replaceAll;(String,String);;Argument[0];regex-compile-find",
|
||||
"java.lang;String;false;replaceFirst;(String,String);;Argument[0];regex-compile-find",
|
||||
"com.google.common.base;Splitter;false;onPattern;(String);;Argument[0];regex-compile-find",
|
||||
// regex-match sinks
|
||||
"java.util.regex;Pattern;false;asMatchPredicate;();;Argument[-1];regex-match",
|
||||
"java.util.regex;Matcher;false;matches;();;Argument[-1];regex-match",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -892,7 +892,9 @@ abstract class RegexString extends StringLiteral {
|
||||
|
||||
/** A string literal used as a regular expression */
|
||||
class Regex extends RegexString {
|
||||
Regex() { usedAsRegex(this, _) }
|
||||
boolean matches_full_string;
|
||||
|
||||
Regex() { usedAsRegex(this, _, matches_full_string) }
|
||||
|
||||
/**
|
||||
* Gets a mode (if any) of this regular expression. Can be any of:
|
||||
@@ -906,8 +908,14 @@ class Regex extends RegexString {
|
||||
*/
|
||||
string getAMode() {
|
||||
result != "None" and
|
||||
usedAsRegex(this, result)
|
||||
usedAsRegex(this, result, _)
|
||||
or
|
||||
result = this.getModeFromPrefix()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this regex is used to match against a full string,
|
||||
* as though it was implicitly surrounded by ^ and $.
|
||||
*/
|
||||
predicate matchesFullString() { matches_full_string = true }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user