Add test cases for PolynomialRedos dataflow logic; make fixes

This commit is contained in:
Joe Farebrother
2022-02-15 17:50:50 +00:00
parent 5a4316d945
commit e23162d91b
6 changed files with 125 additions and 50 deletions

View File

@@ -37,7 +37,7 @@ abstract class RegexMatchMethodAccess extends MethodAccess {
Method m;
RegexMatchMethodAccess() {
this.getMethod().overrides*(m) and
this.getMethod().getSourceDeclaration().overrides*(m) and
m.hasQualifiedName(package, type, name) and
regexArg in [-1 .. m.getNumberOfParameters() - 1] and
stringArg in [-1 .. m.getNumberOfParameters() - 1]
@@ -79,9 +79,9 @@ private class JdkRegexMatchMethodAccess extends RegexMatchMethodAccess {
or
name = "matches" and regexArg = 0 and stringArg = 1
or
name = "split" and regexArg = 0 and stringArg = 1
name = "split" and regexArg = -1 and stringArg = 0
or
name = "splitAsStream" and regexArg = 0 and stringArg = 1
name = "splitAsStream" and regexArg = -1 and stringArg = 0
)
or
package = "java.lang" and
@@ -90,7 +90,7 @@ private class JdkRegexMatchMethodAccess extends RegexMatchMethodAccess {
regexArg = 0 and
stringArg = -1
or
package = "java.util" and
package = "java.util.function" and
type = "Predicate" and
name = "test" and
regexArg = -1 and
@@ -101,7 +101,7 @@ private class JdkRegexMatchMethodAccess extends RegexMatchMethodAccess {
private class JdkRegexFlowStep extends RegexAdditionalFlowStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma, Method m, string package, string type, string name, int arg |
ma.getMethod().overrides*(m) and
ma.getMethod().getSourceDeclaration().overrides*(m) and
m.hasQualifiedName(package, type, name) and
node1.asExpr() = argOf(ma, arg) and
node2.asExpr() = ma
@@ -116,7 +116,7 @@ private class JdkRegexFlowStep extends RegexAdditionalFlowStep {
arg = 0
)
or
package = "java.util" and
package = "java.util.function" and
type = "Predicate" and
name = ["and", "or", "not", "negate"] and
arg = [-1, 0]
@@ -126,7 +126,7 @@ private class JdkRegexFlowStep extends RegexAdditionalFlowStep {
private class GuavaRegexMatchMethodAccess extends RegexMatchMethodAccess {
GuavaRegexMatchMethodAccess() {
package = "com.google.common.collect" and
package = "com.google.common.base" and
regexArg = -1 and
stringArg = 0 and
type = ["Splitter", "Splitter$MapSplitter"] and
@@ -137,7 +137,7 @@ private class GuavaRegexMatchMethodAccess extends RegexMatchMethodAccess {
private class GuavaRegexFlowStep extends RegexAdditionalFlowStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma, Method m, string package, string type, string name, int arg |
ma.getMethod().overrides*(m) and
ma.getMethod().getSourceDeclaration().overrides*(m) and
m.hasQualifiedName(package, type, name) and
node1.asExpr() = argOf(ma, arg) and
node2.asExpr() = ma

View File

@@ -1,7 +1,6 @@
import java.util.regex.Pattern;
class Test {
class ExpRedosTest {
static String[] regs = {
// NOT GOOD; attack: "_" + "__".repeat(100)

View File

@@ -0,0 +1,35 @@
import java.util.regex.Pattern;
import java.util.function.Predicate;
import javax.servlet.http.HttpServletRequest;
import com.google.common.base.Splitter;
class PolyRedosTest {
void test(HttpServletRequest request) {
String tainted = request.getParameter("inp");
String reg = "a\\.\\d+E?\\d+b";
Predicate<String> dummyPred = (s -> s.length() % 7 == 0);
tainted.matches(reg); // $ hasTaintFlow
tainted.split(reg); // $ hasTaintFlow
tainted.split(reg, 7); // $ hasTaintFlow
Pattern.matches(reg, tainted); // $ hasTaintFlow
Pattern.compile(reg).matcher(tainted).matches(); // $ hasTaintFlow
Pattern.compile(reg).split(tainted); // $ hasTaintFlow
Pattern.compile(reg, Pattern.DOTALL).split(tainted); // $ hasTaintFlow
Pattern.compile(reg).split(tainted, 7); // $ hasTaintFlow
Pattern.compile(reg).splitAsStream(tainted); // $ hasTaintFlow
Pattern.compile(reg).asPredicate().test(tainted); // $ hasTaintFlow
Pattern.compile(reg).asMatchPredicate().negate().and(dummyPred).or(dummyPred).test(tainted); // $ hasTaintFlow
Predicate.not(dummyPred.and(dummyPred.or(Pattern.compile(reg).asPredicate()))).test(tainted); // $ hasTaintFlow
Splitter.on(Pattern.compile(reg)).split(tainted); // $ hasTaintFlow
Splitter.on(reg).split(tainted);
Splitter.onPattern(reg).split(tainted); // $ hasTaintFlow
Splitter.onPattern(reg).splitToList(tainted); // $ hasTaintFlow
Splitter.onPattern(reg).limit(7).omitEmptyStrings().trimResults().split(tainted); // $ hasTaintFlow
Splitter.onPattern(reg).withKeyValueSeparator(" => ").split(tainted); // $ hasTaintFlow
Splitter.on(";").withKeyValueSeparator(reg).split(tainted);
Splitter.on(";").withKeyValueSeparator(Splitter.onPattern(reg)).split(tainted); // $ hasTaintFlow
}
}

View File

@@ -0,0 +1 @@
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/guava-30.0

View File

@@ -0,0 +1,53 @@
// Generated automatically from com.google.common.base.CharMatcher for testing purposes
package com.google.common.base;
import com.google.common.base.Predicate;
abstract public class CharMatcher implements Predicate<Character>
{
protected CharMatcher(){}
public CharMatcher and(CharMatcher p0){ return null; }
public CharMatcher negate(){ return null; }
public CharMatcher or(CharMatcher p0){ return null; }
public CharMatcher precomputed(){ return null; }
public String collapseFrom(CharSequence p0, char p1){ return null; }
public String removeFrom(CharSequence p0){ return null; }
public String replaceFrom(CharSequence p0, CharSequence p1){ return null; }
public String replaceFrom(CharSequence p0, char p1){ return null; }
public String retainFrom(CharSequence p0){ return null; }
public String toString(){ return null; }
public String trimAndCollapseFrom(CharSequence p0, char p1){ return null; }
public String trimFrom(CharSequence p0){ return null; }
public String trimLeadingFrom(CharSequence p0){ return null; }
public String trimTrailingFrom(CharSequence p0){ return null; }
public abstract boolean matches(char p0);
public boolean apply(Character p0){ return false; }
public boolean matchesAllOf(CharSequence p0){ return false; }
public boolean matchesAnyOf(CharSequence p0){ return false; }
public boolean matchesNoneOf(CharSequence p0){ return false; }
public int countIn(CharSequence p0){ return 0; }
public int indexIn(CharSequence p0){ return 0; }
public int indexIn(CharSequence p0, int p1){ return 0; }
public int lastIndexIn(CharSequence p0){ return 0; }
public static CharMatcher any(){ return null; }
public static CharMatcher anyOf(CharSequence p0){ return null; }
public static CharMatcher ascii(){ return null; }
public static CharMatcher breakingWhitespace(){ return null; }
public static CharMatcher digit(){ return null; }
public static CharMatcher forPredicate(Predicate<? super Character> p0){ return null; }
public static CharMatcher inRange(char p0, char p1){ return null; }
public static CharMatcher invisible(){ return null; }
public static CharMatcher is(char p0){ return null; }
public static CharMatcher isNot(char p0){ return null; }
public static CharMatcher javaDigit(){ return null; }
public static CharMatcher javaIsoControl(){ return null; }
public static CharMatcher javaLetter(){ return null; }
public static CharMatcher javaLetterOrDigit(){ return null; }
public static CharMatcher javaLowerCase(){ return null; }
public static CharMatcher javaUpperCase(){ return null; }
public static CharMatcher none(){ return null; }
public static CharMatcher noneOf(CharSequence p0){ return null; }
public static CharMatcher singleWidth(){ return null; }
public static CharMatcher whitespace(){ return null; }
}

View File

@@ -1,48 +1,35 @@
/*
* Copyright (C) 2009 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
// Generated automatically from com.google.common.base.Splitter for testing purposes
package com.google.common.base;
import java.util.Iterator;
import com.google.common.base.CharMatcher;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Stream;
public final class Splitter {
public static Splitter on(final String separator) {
return null;
}
public Splitter omitEmptyStrings() {
return null;
}
public Iterable<String> split(final CharSequence sequence) {
return null;
}
public List<String> splitToList(CharSequence sequence) {
return null;
}
public MapSplitter withKeyValueSeparator(String separator) {
return null;
}
public static final class MapSplitter {
public Map<String, String> split(CharSequence sequence) {
return null;
public class Splitter
{
protected Splitter() {}
public Iterable<String> split(CharSequence p0){ return null; }
public List<String> splitToList(CharSequence p0){ return null; }
public Splitter limit(int p0){ return null; }
public Splitter omitEmptyStrings(){ return null; }
public Splitter trimResults(){ return null; }
public Splitter trimResults(CharMatcher p0){ return null; }
public Splitter.MapSplitter withKeyValueSeparator(Splitter p0){ return null; }
public Splitter.MapSplitter withKeyValueSeparator(String p0){ return null; }
public Splitter.MapSplitter withKeyValueSeparator(char p0){ return null; }
public Stream<String> splitToStream(CharSequence p0){ return null; }
public static Splitter fixedLength(int p0){ return null; }
public static Splitter on(CharMatcher p0){ return null; }
public static Splitter on(Pattern p0){ return null; }
public static Splitter on(String p0){ return null; }
public static Splitter on(char p0){ return null; }
public static Splitter onPattern(String p0){ return null; }
static public class MapSplitter
{
protected MapSplitter() {}
public Map<String, String> split(CharSequence p0){ return null; }
}
}
}