Distingush between whether or not a regex is matched against a full string

Also some fixes and additional tests
This commit is contained in:
Joe Farebrother
2022-03-09 14:29:49 +00:00
parent 0a5268aeb4
commit 5555985ad6
6 changed files with 118 additions and 15 deletions

View File

@@ -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")
)
}
}
/**

View File

@@ -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",
]
}
}

View File

@@ -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 }
}