Add support for Apache Commons Lang StringUtils

This commit is contained in:
Chris Smowton
2021-02-09 14:21:39 +00:00
parent bf03c0f419
commit a2eeffa9c0
7 changed files with 1329 additions and 0 deletions

View File

@@ -63,3 +63,58 @@ private class ApacheLangArrayUtilsTaintPreservingMethod extends TaintPreservingC
src = [0, 2]
}
}
private Type getAnExcludedParameterType() {
result instanceof PrimitiveType or
result.(RefType).hasQualifiedName("java.nio.charset", "Charset") or
result.(RefType).hasQualifiedName("java.util", "Locale")
}
private class ApacheStringUtilsTaintPreservingMethod extends TaintPreservingCallable {
ApacheStringUtilsTaintPreservingMethod() {
this.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "StringUtils") and
this.hasName([
"abbreviate", "abbreviateMiddle", "appendIfMissing", "appendIfMissingIgnoreCase",
"capitalize", "center", "chomp", "chop", "defaultIfBlank", "defaultIfEmpty",
"defaultString", "deleteWhitespace", "difference", "firstNonBlank", "firstNonEmpty",
"getBytes", "getCommonPrefix", "getDigits", "getIfBlank", "getIfEmpty", "join", "joinWith",
"left", "leftPad", "lowerCase", "mid", "normalizeSpace", "overlay", "prependIfMissing",
"prependIfMissingIgnoreCase", "remove", "removeAll", "removeEnd", "removeEndIgnoreCase",
"removeFirst", "removeIgnoreCase", "removePattern", "removeStart", "removeStartIgnoreCase",
"repeat", "replace", "replaceAll", "replaceChars", "replaceEach", "replaceEachRepeatedly",
"replaceFirst", "replaceIgnoreCase", "replaceOnce", "replaceOnceIgnoreCase",
"replacePattern", "reverse", "reverseDelimited", "right", "rightPad", "rotate", "split",
"splitByCharacterType", "splitByCharacterTypeCamelCase", "splitByWholeSeparator",
"splitByWholeSeparatorPreserveAllTokens", "splitPreserveAllTokens", "strip", "stripAccents",
"stripAll", "stripEnd", "stripStart", "stripToEmpty", "stripToNull", "substring",
"substringAfter", "substringAfterLast", "substringBefore", "substringBeforeLast",
"substringBetween", "substringsBetween", "swapCase", "toCodePoints", "toEncodedString",
"toRootLowerCase", "toRootUpperCase", "toString", "trim", "trimToEmpty", "trimToNull",
"truncate", "uncapitalize", "unwrap", "upperCase", "valueOf", "wrap", "wrapIfMissing"
])
}
private predicate isExcludedParameter(int arg) {
this.getName().matches(["appendIfMissing%", "prependIfMissing%"]) and arg = [2, 3]
or
this.getName().matches(["remove%", "split%", "substring%", "strip%"]) and
arg = [1 .. getNumberOfParameters() - 1]
or
this.getName().matches(["chomp", "getBytes", "replace%", "toString", "unwrap"]) and arg = 1
or
this.getName() = "join" and
// Exclude joins of types that render numerically (char[] and non-primitive arrays
// are still considered taint sources)
exists(PrimitiveType pt |
this.getParameterType(arg).(Array).getComponentType() = pt and
not pt instanceof CharacterType
) and
arg = 0
}
override predicate returnsTaintFrom(int arg) {
arg = [0 .. getNumberOfParameters() - 1] and
not this.getParameterType(arg) = getAnExcludedParameterType() and
not isExcludedParameter(arg)
}
}

View File

@@ -0,0 +1,278 @@
import org.apache.commons.lang3.StringUtils;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
class Test {
String taint() { return "tainted"; }
void sink(Object o) {}
void test() throws Exception {
// All these calls should convey taint to `sink` except as noted.
sink(StringUtils.abbreviate(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.abbreviate(taint(), 0, 0)); // $hasTaintFlow=y
sink(StringUtils.abbreviate(taint(), "...", 0)); // $hasTaintFlow=y
sink(StringUtils.abbreviate("Untainted", taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.abbreviate(taint(), "...", 0, 0)); // $hasTaintFlow=y
sink(StringUtils.abbreviate("Untainted", taint(), 0, 0)); // $hasTaintFlow=y
sink(StringUtils.abbreviateMiddle(taint(), "...", 0)); // $hasTaintFlow=y
sink(StringUtils.abbreviateMiddle("Untainted", taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.appendIfMissing(taint(), "suffix", "candsuffix1", "candsuffix2")); // $hasTaintFlow=y
sink(StringUtils.appendIfMissing("prefix", taint(), "candsuffix1", "candsuffix2")); // $hasTaintFlow=y
// (next 2 calls) GOOD: candidate suffixes do not flow to the return value.
sink(StringUtils.appendIfMissing("prefix", "suffix", taint(), "candsuffix2"));
sink(StringUtils.appendIfMissing("prefix", "suffix", "candsuffix1", taint()));
sink(StringUtils.appendIfMissingIgnoreCase(taint(), "suffix", "candsuffix1", "candsuffix2")); // $hasTaintFlow=y
sink(StringUtils.appendIfMissingIgnoreCase("prefix", taint(), "candsuffix1", "candsuffix2")); // $hasTaintFlow=y
// (next 2 calls) GOOD: candidate suffixes do not flow to the return value.
sink(StringUtils.appendIfMissingIgnoreCase("prefix", "suffix", taint(), "candsuffix2"));
sink(StringUtils.appendIfMissingIgnoreCase("prefix", "suffix", "candsuffix1", taint()));
sink(StringUtils.capitalize(taint())); // $hasTaintFlow=y
sink(StringUtils.center(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.center(taint(), 0, 'x')); // $hasTaintFlow=y
sink(StringUtils.center(taint(), 0, "padding string")); // $hasTaintFlow=y
sink(StringUtils.center("Center me", 0, taint())); // $hasTaintFlow=y
sink(StringUtils.chomp(taint())); // $hasTaintFlow=y
sink(StringUtils.chomp(taint(), "separator")); // $hasTaintFlow=y
// GOOD: separator does not flow to the return value.
sink(StringUtils.chomp("Chomp me", taint()));
sink(StringUtils.chop(taint())); // $hasTaintFlow=y
sink(StringUtils.defaultIfBlank(taint(), "default")); // $hasTaintFlow=y
sink(StringUtils.defaultIfBlank("Perhaps blank", taint())); // $hasTaintFlow=y
sink(StringUtils.defaultIfEmpty(taint(), "default")); // $hasTaintFlow=y
sink(StringUtils.defaultIfEmpty("Perhaps empty", taint())); // $hasTaintFlow=y
sink(StringUtils.defaultString(taint())); // $hasTaintFlow=y
sink(StringUtils.defaultString(taint(), "default string")); // $hasTaintFlow=y
sink(StringUtils.defaultString("perhaps null", taint())); // $hasTaintFlow=y
sink(StringUtils.deleteWhitespace(taint())); // $hasTaintFlow=y
sink(StringUtils.difference(taint(), "rhs")); // $hasTaintFlow=y
sink(StringUtils.difference("lhs", taint())); // $hasTaintFlow=y
sink(StringUtils.firstNonBlank(taint(), "second string")); // $hasTaintFlow=y
sink(StringUtils.firstNonBlank("first string", taint())); // $hasTaintFlow=y
sink(StringUtils.firstNonEmpty(taint(), "second string")); // $hasTaintFlow=y
sink(StringUtils.firstNonEmpty("first string", taint())); // $hasTaintFlow=y
sink(StringUtils.getBytes(taint(), (Charset)null)); // $hasTaintFlow=y
sink(StringUtils.getBytes(taint(), "some charset")); // $hasTaintFlow=y
// GOOD: charset names are not a source of taint
sink(StringUtils.getBytes("some string", taint()));
sink(StringUtils.getCommonPrefix(taint(), "second string")); // $hasTaintFlow=y
sink(StringUtils.getCommonPrefix("first string", taint())); // $hasTaintFlow=y
sink(StringUtils.getDigits(taint())); // $hasTaintFlow=y
sink(StringUtils.getIfBlank(taint(), () -> "default")); // $hasTaintFlow=y
sink(StringUtils.getIfEmpty(taint(), () -> "default")); // $hasTaintFlow=y
// BAD (but not detected yet): latent taint in lambdas
sink(StringUtils.getIfBlank("maybe blank", () -> taint()));
sink(StringUtils.getIfEmpty("maybe blank", () -> taint()));
// GOOD: byte arrays render as numbers, so can't usefully convey most forms
// of tainted data.
sink(StringUtils.join(StringUtils.getBytes(taint(), "UTF-8"), ' '));
sink(StringUtils.join(StringUtils.getBytes(taint(), "UTF-8"), ' ', 0, 0));
sink(StringUtils.join(taint().toCharArray(), ' ')); // $hasTaintFlow=y
sink(StringUtils.join(taint().toCharArray(), ' ', 0, 0)); // $hasTaintFlow=y
// Testing the Iterable<?> overloads of `join`
List<String> taintedList = new ArrayList<>();
taintedList.add(taint());
sink(StringUtils.join(taintedList, ' ')); // $hasTaintFlow=y
sink(StringUtils.join(taintedList, "sep")); // $hasTaintFlow=y
List<String> untaintedList = new ArrayList<>();
sink(StringUtils.join(untaintedList, taint())); // $hasTaintFlow=y
// Testing the Iterator<?> overloads of `join`
sink(StringUtils.join(taintedList.iterator(), ' ')); // $hasTaintFlow=y
sink(StringUtils.join(taintedList.iterator(), "sep")); // $hasTaintFlow=y
sink(StringUtils.join(untaintedList.iterator(), taint())); // $hasTaintFlow=y
// Testing the List<?> overloads of `join`, which have start/end indices
sink(StringUtils.join(taintedList, ' ', 0, 0)); // $hasTaintFlow=y
sink(StringUtils.join(taintedList, "sep", 0, 0)); // $hasTaintFlow=y
sink(StringUtils.join(untaintedList, taint(), 0, 0)); // $hasTaintFlow=y
// Testing the Object[] overloads of `join`, which may have start/end indices
Object[] taintedArray = new Object[] { taint() };
sink(StringUtils.join(taintedArray, ' ')); // $hasTaintFlow=y
sink(StringUtils.join(taintedArray, "sep")); // $hasTaintFlow=y
sink(StringUtils.join(taintedArray, ' ', 0, 0)); // $hasTaintFlow=y
sink(StringUtils.join(taintedArray, "sep", 0, 0)); // $hasTaintFlow=y
Object[] untaintedArray = new Object[] { "safe" };
sink(StringUtils.join(untaintedArray, taint())); // $hasTaintFlow=y
sink(StringUtils.join(untaintedArray, taint(), 0, 0)); // $hasTaintFlow=y
// Testing the variadic overload of `join` and `joinWith`
sink(StringUtils.join(taint(), "other string")); // $hasTaintFlow=y
sink(StringUtils.join("other string before", taint())); // $hasTaintFlow=y
sink(StringUtils.joinWith("separator", taint(), "other string")); // $hasTaintFlow=y
sink(StringUtils.joinWith("separator", "other string before", taint())); // $hasTaintFlow=y
sink(StringUtils.joinWith(taint(), "other string before", "other string after")); // $hasTaintFlow=y
// End of `join` tests
sink(StringUtils.left(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.leftPad(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.leftPad(taint(), 0, ' ')); // $hasTaintFlow=y
sink(StringUtils.leftPad(taint(), 0, "padding")); // $hasTaintFlow=y
sink(StringUtils.leftPad("to pad", 0, taint())); // $hasTaintFlow=y
sink(StringUtils.lowerCase(taint())); // $hasTaintFlow=y
sink(StringUtils.lowerCase(taint(), Locale.UK)); // $hasTaintFlow=y
sink(StringUtils.mid(taint(), 0, 0)); // $hasTaintFlow=y
sink(StringUtils.normalizeSpace(taint())); // $hasTaintFlow=y
sink(StringUtils.overlay(taint(), "overlay", 0, 0)); // $hasTaintFlow=y
sink(StringUtils.overlay("underlay", taint(), 0, 0)); // $hasTaintFlow=y
sink(StringUtils.prependIfMissing(taint(), "append prefix", "check prefix 1", "check prefix 2")); // $hasTaintFlow=y
sink(StringUtils.prependIfMissing("original string", taint(), "check prefix 1", "check prefix 2")); // $hasTaintFlow=y
// (next 2 calls) GOOD: args 3+ are checked against but do not propagate to the return value
sink(StringUtils.prependIfMissing("original string", "append prefix", taint(), "check prefix 2"));
sink(StringUtils.prependIfMissing("original string", "append prefix", "check prefix 1", taint()));
sink(StringUtils.prependIfMissingIgnoreCase(taint(), "append prefix", "check prefix 1", "check prefix 2")); // $hasTaintFlow=y
sink(StringUtils.prependIfMissingIgnoreCase("original string", taint(), "check prefix 1", "check prefix 2")); // $hasTaintFlow=y
// (next 2 calls) GOOD: args 3+ are checked against but do not propagate to the return value
sink(StringUtils.prependIfMissingIgnoreCase("original string", "append prefix", taint(), "check prefix 2"));
sink(StringUtils.prependIfMissingIgnoreCase("original string", "append prefix", "check prefix 1", taint()));
sink(StringUtils.remove(taint(), ' ')); // $hasTaintFlow=y
sink(StringUtils.remove(taint(), "delete me")); // $hasTaintFlow=y
sink(StringUtils.removeAll(taint(), "delete me")); // $hasTaintFlow=y
sink(StringUtils.removeEnd(taint(), "delete me")); // $hasTaintFlow=y
sink(StringUtils.removeEndIgnoreCase(taint(), "delete me")); // $hasTaintFlow=y
sink(StringUtils.removeFirst(taint(), "delete me")); // $hasTaintFlow=y
sink(StringUtils.removeIgnoreCase(taint(), "delete me")); // $hasTaintFlow=y
sink(StringUtils.removePattern(taint(), "delete me")); // $hasTaintFlow=y
sink(StringUtils.removeStart(taint(), "delete me")); // $hasTaintFlow=y
sink(StringUtils.removeStartIgnoreCase(taint(), "delete me")); // $hasTaintFlow=y
// GOOD (next 9 calls): the removed string doesn't propagate to the return value
sink(StringUtils.remove("remove from", taint()));
sink(StringUtils.removeAll("remove from", taint()));
sink(StringUtils.removeEnd("remove from", taint()));
sink(StringUtils.removeEndIgnoreCase("remove from", taint()));
sink(StringUtils.removeFirst("remove from", taint()));
sink(StringUtils.removeIgnoreCase("remove from", taint()));
sink(StringUtils.removePattern("remove from", taint()));
sink(StringUtils.removeStart("remove from", taint()));
sink(StringUtils.removeStartIgnoreCase("remove from", taint()));
sink(StringUtils.repeat(taint(), 1)); // $hasTaintFlow=y
sink(StringUtils.repeat(taint(), "separator", 1)); // $hasTaintFlow=y
sink(StringUtils.repeat("repeat me", taint(), 1)); // $hasTaintFlow=y
sink(StringUtils.replace(taint(), "search", "replacement")); // $hasTaintFlow=y
sink(StringUtils.replace("haystack", "search", taint())); // $hasTaintFlow=y
sink(StringUtils.replace(taint(), "search", "replacement", 0)); // $hasTaintFlow=y
sink(StringUtils.replace("haystack", "search", taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.replaceAll(taint(), "search", "replacement")); // $hasTaintFlow=y
sink(StringUtils.replaceAll("haystack", "search", taint())); // $hasTaintFlow=y
sink(StringUtils.replaceChars(taint(), 'a', 'b')); // $hasTaintFlow=y
sink(StringUtils.replaceChars(taint(), "abc", "xyz")); // $hasTaintFlow=y
sink(StringUtils.replaceChars("haystack", "abc", taint())); // $hasTaintFlow=y
sink(StringUtils.replaceEach(taint(), new String[] { "search" }, new String[] { "replacement" })); // $hasTaintFlow=y
sink(StringUtils.replaceEach("haystack", new String[] { "search" }, new String[] { taint() })); // $hasTaintFlow=y
sink(StringUtils.replaceEachRepeatedly(taint(), new String[] { "search" }, new String[] { "replacement" })); // $hasTaintFlow=y
sink(StringUtils.replaceEachRepeatedly("haystack", new String[] { "search" }, new String[] { taint() })); // $hasTaintFlow=y
sink(StringUtils.replaceFirst(taint(), "search", "replacement")); // $hasTaintFlow=y
sink(StringUtils.replaceFirst("haystack", "search", taint())); // $hasTaintFlow=y
sink(StringUtils.replaceIgnoreCase(taint(), "search", "replacement")); // $hasTaintFlow=y
sink(StringUtils.replaceIgnoreCase("haystack", "search", taint())); // $hasTaintFlow=y
sink(StringUtils.replaceOnce(taint(), "search", "replacement")); // $hasTaintFlow=y
sink(StringUtils.replaceOnce("haystack", "search", taint())); // $hasTaintFlow=y
sink(StringUtils.replaceOnceIgnoreCase(taint(), "search", "replacement")); // $hasTaintFlow=y
sink(StringUtils.replaceOnceIgnoreCase("haystack", "search", taint())); // $hasTaintFlow=y
sink(StringUtils.replacePattern(taint(), "search", "replacement")); // $hasTaintFlow=y
sink(StringUtils.replacePattern("haystack", "search", taint())); // $hasTaintFlow=y
// GOOD (next 11 calls): searched string in replace methods does not flow to the return value.
sink(StringUtils.replace("haystack", taint(), "replacement"));
sink(StringUtils.replace("haystack", taint(), "replacement", 0));
sink(StringUtils.replaceAll("haystack", taint(), "replacement"));
sink(StringUtils.replaceChars("haystack", taint(), "xyz"));
sink(StringUtils.replaceEach("haystack", new String[] { taint() }, new String[] { "replacement" }));
sink(StringUtils.replaceEachRepeatedly("haystack", new String[] { taint() }, new String[] { "replacement" }));
sink(StringUtils.replaceFirst("haystack", taint(), "replacement"));
sink(StringUtils.replaceIgnoreCase("haystack", taint(), "replacement"));
sink(StringUtils.replaceOnce("haystack", taint(), "replacement"));
sink(StringUtils.replaceOnceIgnoreCase("haystack", taint(), "replacement"));
sink(StringUtils.replacePattern("haystack", taint(), "replacement"));
sink(StringUtils.reverse(taint())); // $hasTaintFlow=y
sink(StringUtils.reverseDelimited(taint(), ',')); // $hasTaintFlow=y
sink(StringUtils.right(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.rightPad(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.rightPad(taint(), 0, ' ')); // $hasTaintFlow=y
sink(StringUtils.rightPad(taint(), 0, "padding")); // $hasTaintFlow=y
sink(StringUtils.rightPad("to pad", 0, taint())); // $hasTaintFlow=y
sink(StringUtils.rotate(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.split(taint())); // $hasTaintFlow=y
sink(StringUtils.split(taint(), ' ')); // $hasTaintFlow=y
sink(StringUtils.split(taint(), " ,; // $hasTaintFlow=y")); // $hasTaintFlow=y
sink(StringUtils.split(taint(), " ,; // $hasTaintFlow=y", 0)); // $hasTaintFlow=y
sink(StringUtils.splitByCharacterType(taint())); // $hasTaintFlow=y
sink(StringUtils.splitByCharacterTypeCamelCase(taint())); // $hasTaintFlow=y
sink(StringUtils.splitByWholeSeparator(taint(), "separator")); // $hasTaintFlow=y
sink(StringUtils.splitByWholeSeparator(taint(), "separator", 0)); // $hasTaintFlow=y
sink(StringUtils.splitByWholeSeparatorPreserveAllTokens(taint(), "separator")); // $hasTaintFlow=y
sink(StringUtils.splitByWholeSeparatorPreserveAllTokens(taint(), "separator", 0)); // $hasTaintFlow=y
sink(StringUtils.splitPreserveAllTokens(taint())); // $hasTaintFlow=y
sink(StringUtils.splitPreserveAllTokens(taint(), ' ')); // $hasTaintFlow=y
sink(StringUtils.splitPreserveAllTokens(taint(), " ,;")); // $hasTaintFlow=y
sink(StringUtils.splitPreserveAllTokens(taint(), " ,;", 0)); // $hasTaintFlow=y
// GOOD (next 8 calls): separators don't propagate to the return value
sink(StringUtils.split("to split", taint()));
sink(StringUtils.split("to split", taint(), 0));
sink(StringUtils.splitPreserveAllTokens("to split", taint(), 0));
sink(StringUtils.splitByWholeSeparator("to split", taint()));
sink(StringUtils.splitByWholeSeparator("to split", taint(), 0));
sink(StringUtils.splitByWholeSeparatorPreserveAllTokens("to split", taint()));
sink(StringUtils.splitByWholeSeparatorPreserveAllTokens("to split", taint(), 0));
sink(StringUtils.splitPreserveAllTokens("to split", taint()));
sink(StringUtils.strip(taint())); // $hasTaintFlow=y
sink(StringUtils.strip(taint(), "charstoremove")); // $hasTaintFlow=y
sink(StringUtils.stripAccents(taint())); // $hasTaintFlow=y
sink(StringUtils.stripAll(new String[] { taint() }, "charstoremove")); // $hasTaintFlow=y
sink(StringUtils.stripEnd(taint(), "charstoremove")); // $hasTaintFlow=y
sink(StringUtils.stripStart(taint(), "charstoremove")); // $hasTaintFlow=y
// GOOD (next 4 calls): stripped chars do not flow to the return value.
sink(StringUtils.strip("original text", taint()));
sink(StringUtils.stripAll(new String[] { "original text" }, taint()));
sink(StringUtils.stripEnd("original text", taint()));
sink(StringUtils.stripStart("original text", taint()));
sink(StringUtils.stripToEmpty(taint())); // $hasTaintFlow=y
sink(StringUtils.stripToNull(taint())); // $hasTaintFlow=y
sink(StringUtils.substring(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.substring(taint(), 0, 0)); // $hasTaintFlow=y
sink(StringUtils.substringAfter(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.substringAfter(taint(), "separator")); // $hasTaintFlow=y
sink(StringUtils.substringAfterLast(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.substringAfterLast(taint(), "separator")); // $hasTaintFlow=y
sink(StringUtils.substringBefore(taint(), "separator")); // $hasTaintFlow=y
sink(StringUtils.substringBeforeLast(taint(), "separator")); // $hasTaintFlow=y
sink(StringUtils.substringBetween(taint(), "separator")); // $hasTaintFlow=y
sink(StringUtils.substringBetween(taint(), "start-tag", "end-tag")); // $hasTaintFlow=y
sink(StringUtils.substringsBetween(taint(), "start-tag", "end-tag")[0]); // $hasTaintFlow=y
// GOOD (next 9 calls): separators and bounding tags do not flow to the return value.
sink(StringUtils.substringAfter("original text", taint()));
sink(StringUtils.substringAfterLast("original text", taint()));
sink(StringUtils.substringBefore("original text", taint()));
sink(StringUtils.substringBeforeLast("original text", taint()));
sink(StringUtils.substringBetween("original text", taint()));
sink(StringUtils.substringBetween("original text", taint(), "end-tag"));
sink(StringUtils.substringBetween("original text", "start-tag", taint()));
sink(StringUtils.substringsBetween("original text", taint(), "end-tag")[0]);
sink(StringUtils.substringsBetween("original text", "start-tag", taint())[0]);
sink(StringUtils.swapCase(taint())); // $hasTaintFlow=y
sink(StringUtils.toCodePoints(taint())); // $hasTaintFlow=y
sink(StringUtils.toEncodedString(StringUtils.getBytes(taint(), "charset"), null)); // $hasTaintFlow=y
sink(StringUtils.toRootLowerCase(taint())); // $hasTaintFlow=y
sink(StringUtils.toRootUpperCase(taint())); // $hasTaintFlow=y
sink(StringUtils.toString(StringUtils.getBytes(taint(), "charset"), "charset")); // $hasTaintFlow=y
sink(StringUtils.trim(taint())); // $hasTaintFlow=y
sink(StringUtils.trimToEmpty(taint())); // $hasTaintFlow=y
sink(StringUtils.trimToNull(taint())); // $hasTaintFlow=y
sink(StringUtils.truncate(taint(), 0)); // $hasTaintFlow=y
sink(StringUtils.truncate(taint(), 0, 0)); // $hasTaintFlow=y
sink(StringUtils.uncapitalize(taint())); // $hasTaintFlow=y
sink(StringUtils.unwrap(taint(), '"')); // $hasTaintFlow=y
sink(StringUtils.unwrap(taint(), "separator")); // $hasTaintFlow=y
// GOOD: the wrapper string does not flow to the return value.
sink(StringUtils.unwrap("original string", taint()));
sink(StringUtils.upperCase(taint())); // $hasTaintFlow=y
sink(StringUtils.upperCase(taint(), null)); // $hasTaintFlow=y
sink(StringUtils.valueOf(taint().toCharArray())); // $hasTaintFlow=y
sink(StringUtils.wrap(taint(), '"')); // $hasTaintFlow=y
sink(StringUtils.wrap(taint(), "wrapper token")); // $hasTaintFlow=y
sink(StringUtils.wrap("wrap me", taint())); // $hasTaintFlow=y
sink(StringUtils.wrapIfMissing(taint(), '"')); // $hasTaintFlow=y
sink(StringUtils.wrapIfMissing(taint(), "wrapper token")); // $hasTaintFlow=y
sink(StringUtils.wrapIfMissing("wrap me", taint())); // $hasTaintFlow=y
}
}

View File

@@ -0,0 +1,30 @@
import java
import semmle.code.java.dataflow.TaintTracking
import TestUtilities.InlineExpectationsTest
class Conf extends TaintTracking::Configuration {
Conf() { this = "qltest:frameworks:apache-commons-lang3" }
override predicate isSource(DataFlow::Node n) {
n.asExpr().(MethodAccess).getMethod().hasName("taint")
}
override predicate isSink(DataFlow::Node n) {
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
}
}
class HasFlowTest extends InlineExpectationsTest {
HasFlowTest() { this = "HasFlowTest" }
override string getARelevantTag() { result = "hasTaintFlow" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasTaintFlow" and
exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) |
sink.getLocation() = location and
element = sink.toString() and
value = "y"
)
}
}

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/apache-commons-lang3-3.7

View File

@@ -0,0 +1,963 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.commons.lang3;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.Supplier;
import java.util.regex.Pattern;
public class StringUtils {
public static String abbreviate(final String str, final int maxWidth) {
return null;
}
public static String abbreviate(final String str, final int offset, final int maxWidth) {
return null;
}
public static String abbreviate(final String str, final String abbrevMarker, final int maxWidth) {
return null;
}
public static String abbreviate(final String str, final String abbrevMarker, int offset, final int maxWidth) {
return null;
}
public static String abbreviateMiddle(final String str, final String middle, final int length) {
return null;
}
public static String appendIfMissing(final String str, final CharSequence suffix, final CharSequence... suffixes) {
return null;
}
public static String appendIfMissingIgnoreCase(final String str, final CharSequence suffix, final CharSequence... suffixes) {
return null;
}
public static String capitalize(final String str) {
return null;
}
public static String center(final String str, final int size) {
return null;
}
public static String center(String str, final int size, final char padChar) {
return null;
}
public static String center(String str, final int size, String padStr) {
return null;
}
public static String chomp(final String str) {
return null;
}
public static String chomp(final String str, final String separator) {
return null;
}
public static String chop(final String str) {
return null;
}
public static int compare(final String str1, final String str2) {
return 0;
}
public static int compare(final String str1, final String str2, final boolean nullIsLess) {
return 0;
}
public static int compareIgnoreCase(final String str1, final String str2) {
return 0;
}
public static int compareIgnoreCase(final String str1, final String str2, final boolean nullIsLess) {
return 0;
}
public static boolean contains(final CharSequence seq, final CharSequence searchSeq) {
return false;
}
public static boolean contains(final CharSequence seq, final int searchChar) {
return false;
}
public static boolean containsAny(final CharSequence cs, final char... searchChars) {
return false;
}
public static boolean containsAny(final CharSequence cs, final CharSequence searchChars) {
return false;
}
public static boolean containsAny(final CharSequence cs, final CharSequence... searchCharSequences) {
return false;
}
public static boolean containsAnyIgnoreCase(final CharSequence cs, final CharSequence... searchCharSequences) {
return false;
}
public static boolean containsIgnoreCase(final CharSequence str, final CharSequence searchStr) {
return false;
}
public static boolean containsNone(final CharSequence cs, final char... searchChars) {
return false;
}
public static boolean containsNone(final CharSequence cs, final String invalidChars) {
return false;
}
public static boolean containsOnly(final CharSequence cs, final char... valid) {
return false;
}
public static boolean containsOnly(final CharSequence cs, final String validChars) {
return false;
}
public static boolean containsWhitespace(final CharSequence seq) {
return false;
}
public static int countMatches(final CharSequence str, final char ch) {
return 0;
}
public static int countMatches(final CharSequence str, final CharSequence sub) {
return 0;
}
public static <T extends CharSequence> T defaultIfBlank(final T str, final T defaultStr) {
return null;
}
public static <T extends CharSequence> T defaultIfEmpty(final T str, final T defaultStr) {
return null;
}
public static String defaultString(final String str) {
return null;
}
public static String defaultString(final String str, final String defaultStr) {
return null;
}
public static String deleteWhitespace(final String str) {
return null;
}
public static String difference(final String str1, final String str2) {
return null;
}
public static boolean endsWith(final CharSequence str, final CharSequence suffix) {
return false;
}
public static boolean endsWithAny(final CharSequence sequence, final CharSequence... searchStrings) {
return false;
}
public static boolean endsWithIgnoreCase(final CharSequence str, final CharSequence suffix) {
return false;
}
public static boolean equals(final CharSequence cs1, final CharSequence cs2) {
return false;
}
public static boolean equalsAny(final CharSequence string, final CharSequence... searchStrings) {
return false;
}
public static boolean equalsAnyIgnoreCase(final CharSequence string, final CharSequence...searchStrings) {
return false;
}
public static boolean equalsIgnoreCase(final CharSequence cs1, final CharSequence cs2) {
return false;
}
public static <T extends CharSequence> T firstNonBlank(final T... values) {
return null;
}
public static <T extends CharSequence> T firstNonEmpty(final T... values) {
return null;
}
public static byte[] getBytes(final String string, final Charset charset) {
return null;
}
public static byte[] getBytes(final String string, final String charset) throws UnsupportedEncodingException {
return null;
}
public static String getCommonPrefix(final String... strs) {
return null;
}
public static String getDigits(final String str) {
return null;
}
public static int getFuzzyDistance(final CharSequence term, final CharSequence query, final Locale locale) {
return 0;
}
public static <T extends CharSequence> T getIfBlank(final T str, final Supplier<T> defaultSupplier) {
return null;
}
public static <T extends CharSequence> T getIfEmpty(final T str, final Supplier<T> defaultSupplier) {
return null;
}
public static double getJaroWinklerDistance(final CharSequence first, final CharSequence second) {
return 0;
}
public static int getLevenshteinDistance(CharSequence s, CharSequence t) {
return 0;
}
public static int getLevenshteinDistance(CharSequence s, CharSequence t, final int threshold) {
return 0;
}
public static int indexOf(final CharSequence seq, final CharSequence searchSeq) {
return 0;
}
public static int indexOf(final CharSequence seq, final CharSequence searchSeq, final int startPos) {
return 0;
}
public static int indexOf(final CharSequence seq, final int searchChar) {
return 0;
}
public static int indexOf(final CharSequence seq, final int searchChar, final int startPos) {
return 0;
}
public static int indexOfAny(final CharSequence cs, final char... searchChars) {
return 0;
}
public static int indexOfAny(final CharSequence str, final CharSequence... searchStrs) {
return 0;
}
public static int indexOfAny(final CharSequence cs, final String searchChars) {
return 0;
}
public static int indexOfAnyBut(final CharSequence cs, final char... searchChars) {
return 0;
}
public static int indexOfAnyBut(final CharSequence seq, final CharSequence searchChars) {
return 0;
}
public static int indexOfDifference(final CharSequence... css) {
return 0;
}
public static int indexOfDifference(final CharSequence cs1, final CharSequence cs2) {
return 0;
}
public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) {
return 0;
}
public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int startPos) {
return 0;
}
public static boolean isAllBlank(final CharSequence... css) {
return false;
}
public static boolean isAllEmpty(final CharSequence... css) {
return false;
}
public static boolean isAllLowerCase(final CharSequence cs) {
return false;
}
public static boolean isAllUpperCase(final CharSequence cs) {
return false;
}
public static boolean isAlpha(final CharSequence cs) {
return false;
}
public static boolean isAlphanumeric(final CharSequence cs) {
return false;
}
public static boolean isAlphanumericSpace(final CharSequence cs) {
return false;
}
public static boolean isAlphaSpace(final CharSequence cs) {
return false;
}
public static boolean isAnyBlank(final CharSequence... css) {
return false;
}
public static boolean isAnyEmpty(final CharSequence... css) {
return false;
}
public static boolean isAsciiPrintable(final CharSequence cs) {
return false;
}
public static boolean isBlank(final CharSequence cs) {
return false;
}
public static boolean isEmpty(final CharSequence cs) {
return false;
}
public static boolean isMixedCase(final CharSequence cs) {
return false;
}
public static boolean isNoneBlank(final CharSequence... css) {
return false;
}
public static boolean isNoneEmpty(final CharSequence... css) {
return false;
}
public static boolean isNotBlank(final CharSequence cs) {
return false;
}
public static boolean isNotEmpty(final CharSequence cs) {
return false;
}
public static boolean isNumeric(final CharSequence cs) {
return false;
}
public static boolean isNumericSpace(final CharSequence cs) {
return false;
}
public static boolean isWhitespace(final CharSequence cs) {
return false;
}
public static String join(final boolean[] array, final char delimiter) {
return null;
}
public static String join(final boolean[] array, final char delimiter, final int startIndex, final int endIndex) {
return null;
}
public static String join(final byte[] array, final char delimiter) {
return null;
}
public static String join(final byte[] array, final char delimiter, final int startIndex, final int endIndex) {
return null;
}
public static String join(final char[] array, final char delimiter) {
return null;
}
public static String join(final char[] array, final char delimiter, final int startIndex, final int endIndex) {
return null;
}
public static String join(final double[] array, final char delimiter) {
return null;
}
public static String join(final double[] array, final char delimiter, final int startIndex, final int endIndex) {
return null;
}
public static String join(final float[] array, final char delimiter) {
return null;
}
public static String join(final float[] array, final char delimiter, final int startIndex, final int endIndex) {
return null;
}
public static String join(final int[] array, final char separator) {
return null;
}
public static String join(final int[] array, final char delimiter, final int startIndex, final int endIndex) {
return null;
}
public static String join(final Iterable<?> iterable, final char separator) {
return null;
}
public static String join(final Iterable<?> iterable, final String separator) {
return null;
}
public static String join(final Iterator<?> iterator, final char separator) {
return null;
}
public static String join(final Iterator<?> iterator, final String separator) {
return null;
}
public static String join(final List<?> list, final char separator, final int startIndex, final int endIndex) {
return null;
}
public static String join(final List<?> list, final String separator, final int startIndex, final int endIndex) {
return null;
}
public static String join(final long[] array, final char separator) {
return null;
}
public static String join(final long[] array, final char delimiter, final int startIndex, final int endIndex) {
return null;
}
public static String join(final Object[] array, final char delimiter) {
return null;
}
public static String join(final Object[] array, final char delimiter, final int startIndex, final int endIndex) {
return null;
}
public static String join(final Object[] array, final String delimiter) {
return null;
}
public static String join(final Object[] array, String delimiter, final int startIndex, final int endIndex) {
return null;
}
public static String join(final short[] array, final char delimiter) {
return null;
}
public static String join(final short[] array, final char delimiter, final int startIndex, final int endIndex) {
return null;
}
public static <T> String join(final T... elements) {
return null;
}
public static String joinWith(final String delimiter, final Object... array) {
return null;
}
public static int lastIndexOf(final CharSequence seq, final CharSequence searchSeq) {
return 0;
}
public static int lastIndexOf(final CharSequence seq, final CharSequence searchSeq, final int startPos) {
return 0;
}
public static int lastIndexOf(final CharSequence seq, final int searchChar) {
return 0;
}
public static int lastIndexOf(final CharSequence seq, final int searchChar, final int startPos) {
return 0;
}
public static int lastIndexOfAny(final CharSequence str, final CharSequence... searchStrs) {
return 0;
}
public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) {
return 0;
}
public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int startPos) {
return 0;
}
public static int lastOrdinalIndexOf(final CharSequence str, final CharSequence searchStr, final int ordinal) {
return 0;
}
public static String left(final String str, final int len) {
return null;
}
public static String leftPad(final String str, final int size) {
return null;
}
public static String leftPad(final String str, final int size, final char padChar) {
return null;
}
public static String leftPad(final String str, final int size, String padStr) {
return null;
}
public static int length(final CharSequence cs) {
return 0;
}
public static String lowerCase(final String str) {
return null;
}
public static String lowerCase(final String str, final Locale locale) {
return null;
}
public static String mid(final String str, int pos, final int len) {
return null;
}
public static String normalizeSpace(final String str) {
return null;
}
public static int ordinalIndexOf(final CharSequence str, final CharSequence searchStr, final int ordinal) {
return 0;
}
public static String overlay(final String str, String overlay, int start, int end) {
return null;
}
public static String prependIfMissing(final String str, final CharSequence prefix, final CharSequence... prefixes) {
return null;
}
public static String prependIfMissingIgnoreCase(final String str, final CharSequence prefix, final CharSequence... prefixes) {
return null;
}
public static String remove(final String str, final char remove) {
return null;
}
public static String remove(final String str, final String remove) {
return null;
}
public static String removeAll(final String text, final String regex) {
return null;
}
public static String removeEnd(final String str, final String remove) {
return null;
}
public static String removeEndIgnoreCase(final String str, final String remove) {
return null;
}
public static String removeFirst(final String text, final String regex) {
return null;
}
public static String removeIgnoreCase(final String str, final String remove) {
return null;
}
public static String removePattern(final String source, final String regex) {
return null;
}
public static String removeStart(final String str, final String remove) {
return null;
}
public static String removeStartIgnoreCase(final String str, final String remove) {
return null;
}
public static String repeat(final char ch, final int repeat) {
return null;
}
public static String repeat(final String str, final int repeat) {
return null;
}
public static String repeat(final String str, final String separator, final int repeat) {
return null;
}
public static String replace(final String text, final String searchString, final String replacement) {
return null;
}
public static String replace(final String text, final String searchString, final String replacement, final int max) {
return null;
}
public static String replaceAll(final String text, final String regex, final String replacement) {
return null;
}
public static String replaceChars(final String str, final char searchChar, final char replaceChar) {
return null;
}
public static String replaceChars(final String str, final String searchChars, String replaceChars) {
return null;
}
public static String replaceEach(final String text, final String[] searchList, final String[] replacementList) {
return null;
}
public static String replaceEachRepeatedly(final String text, final String[] searchList, final String[] replacementList) {
return null;
}
public static String replaceFirst(final String text, final String regex, final String replacement) {
return null;
}
public static String replaceIgnoreCase(final String text, final String searchString, final String replacement) {
return null;
}
public static String replaceIgnoreCase(final String text, final String searchString, final String replacement, final int max) {
return null;
}
public static String replaceOnce(final String text, final String searchString, final String replacement) {
return null;
}
public static String replaceOnceIgnoreCase(final String text, final String searchString, final String replacement) {
return null;
}
public static String replacePattern(final String source, final String regex, final String replacement) {
return null;
}
public static String reverse(final String str) {
return null;
}
public static String reverseDelimited(final String str, final char separatorChar) {
return null;
}
public static String right(final String str, final int len) {
return null;
}
public static String rightPad(final String str, final int size) {
return null;
}
public static String rightPad(final String str, final int size, final char padChar) {
return null;
}
public static String rightPad(final String str, final int size, String padStr) {
return null;
}
public static String rotate(final String str, final int shift) {
return null;
}
public static String[] split(final String str) {
return null;
}
public static String[] split(final String str, final char separatorChar) {
return null;
}
public static String[] split(final String str, final String separatorChars) {
return null;
}
public static String[] split(final String str, final String separatorChars, final int max) {
return null;
}
public static String[] splitByCharacterType(final String str) {
return null;
}
public static String[] splitByCharacterTypeCamelCase(final String str) {
return null;
}
public static String[] splitByWholeSeparator(final String str, final String separator) {
return null;
}
public static String[] splitByWholeSeparator( final String str, final String separator, final int max) {
return null;
}
public static String[] splitByWholeSeparatorPreserveAllTokens(final String str, final String separator) {
return null;
}
public static String[] splitByWholeSeparatorPreserveAllTokens(final String str, final String separator, final int max) {
return null;
}
public static String[] splitPreserveAllTokens(final String str) {
return null;
}
public static String[] splitPreserveAllTokens(final String str, final char separatorChar) {
return null;
}
public static String[] splitPreserveAllTokens(final String str, final String separatorChars) {
return null;
}
public static String[] splitPreserveAllTokens(final String str, final String separatorChars, final int max) {
return null;
}
public static boolean startsWith(final CharSequence str, final CharSequence prefix) {
return false;
}
public static boolean startsWithAny(final CharSequence sequence, final CharSequence... searchStrings) {
return false;
}
public static boolean startsWithIgnoreCase(final CharSequence str, final CharSequence prefix) {
return false;
}
public static String strip(final String str) {
return null;
}
public static String strip(String str, final String stripChars) {
return null;
}
public static String stripAccents(final String input) {
return null;
}
public static String[] stripAll(final String... strs) {
return null;
}
public static String[] stripAll(final String[] strs, final String stripChars) {
return null;
}
public static String stripEnd(final String str, final String stripChars) {
return null;
}
public static String stripStart(final String str, final String stripChars) {
return null;
}
public static String stripToEmpty(final String str) {
return null;
}
public static String stripToNull(String str) {
return null;
}
public static String substring(final String str, int start) {
return null;
}
public static String substring(final String str, int start, int end) {
return null;
}
public static String substringAfter(final String str, final int separator) {
return null;
}
public static String substringAfter(final String str, final String separator) {
return null;
}
public static String substringAfterLast(final String str, final int separator) {
return null;
}
public static String substringAfterLast(final String str, final String separator) {
return null;
}
public static String substringBefore(final String str, final String separator) {
return null;
}
public static String substringBeforeLast(final String str, final String separator) {
return null;
}
public static String substringBetween(final String str, final String tag) {
return null;
}
public static String substringBetween(final String str, final String open, final String close) {
return null;
}
public static String[] substringsBetween(final String str, final String open, final String close) {
return null;
}
public static String swapCase(final String str) {
return null;
}
public static int[] toCodePoints(final CharSequence cs) {
return null;
}
public static String toEncodedString(final byte[] bytes, final Charset charset) {
return null;
}
public static String toRootLowerCase(final String source) {
return null;
}
public static String toRootUpperCase(final String source) {
return null;
}
public static String toString(final byte[] bytes, final String charsetName) throws UnsupportedEncodingException {
return null;
}
public static String trim(final String str) {
return null;
}
public static String trimToEmpty(final String str) {
return null;
}
public static String trimToNull(final String str) {
return null;
}
public static String truncate(final String str, final int maxWidth) {
return null;
}
public static String truncate(final String str, final int offset, final int maxWidth) {
return null;
}
public static String uncapitalize(final String str) {
return null;
}
public static String unwrap(final String str, final char wrapChar) {
return null;
}
public static String unwrap(final String str, final String wrapToken) {
return null;
}
public static String upperCase(final String str) {
return null;
}
public static String upperCase(final String str, final Locale locale) {
return null;
}
public static String valueOf(final char[] value) {
return null;
}
public static String wrap(final String str, final char wrapWith) {
return null;
}
public static String wrap(final String str, final String wrapWith) {
return null;
}
public static String wrapIfMissing(final String str, final char wrapWith) {
return null;
}
public static String wrapIfMissing(final String str, final String wrapWith) {
return null;
}
public StringUtils() {
}
}