mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Add models for flow- and taint-preserving functions in Commons ObjectUtils.
These should all be value-preserving, but we don't support value-preserving varargs methods yet.
This commit is contained in:
2
java/change-notes/2021-03-05-commons-object-utils.md
Normal file
2
java/change-notes/2021-03-05-commons-object-utils.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Add models for ObjectUtils functions in the Apache Commons-Lang library. This may lead to more results from any dataflow query where traversal of ObjectUtils functions means we can now complete a path from a source of tainted data to a corresponding sink.
|
||||
@@ -396,3 +396,28 @@ private class ApacheRegExUtilsModel extends SummaryModelCsv {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-propagating models for `ObjectUtils`.
|
||||
*/
|
||||
private class ApacheObjectUtilsModel extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
// Note all the functions annotated with `taint` flow really should have `value` flow,
|
||||
// but we don't support value-preserving varargs functions at the moment.
|
||||
"org.apache.commons.lang3;ObjectUtils;false;clone;;;Argument;ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;cloneIfPossible;;;Argument;ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;CONST;;;Argument;ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;defaultIfNull;;;Argument;ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;firstNonNull;;;Argument;ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;getIfNull;;;Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;max;;;Argument;ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;median;;;Argument;ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;min;;;Argument;ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;mode;;;Argument;ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;requireNonEmpty;;;Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;toString;(Object,String);;Argument[1];ReturnValue;value"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
public class ObjectUtilsTest {
|
||||
String taint() { return "tainted"; }
|
||||
|
||||
void sink(Object o) {}
|
||||
|
||||
void test() throws Exception {
|
||||
sink(ObjectUtils.clone(taint())); // $hasTaintFlow=y $hasValueFlow=y
|
||||
sink(ObjectUtils.cloneIfPossible(taint())); // $hasTaintFlow=y $hasValueFlow=y
|
||||
sink(ObjectUtils.CONST(taint())); // $hasTaintFlow=y $hasValueFlow=y
|
||||
sink(ObjectUtils.defaultIfNull(taint(), null)); // $hasTaintFlow=y $hasValueFlow=y
|
||||
sink(ObjectUtils.defaultIfNull(null, taint())); // $hasTaintFlow=y $hasValueFlow=y
|
||||
sink(ObjectUtils.firstNonNull(taint(), null, null)); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.firstNonNull(null, taint(), null)); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.firstNonNull(null, null, taint())); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.getIfNull(taint(), null)); // $hasTaintFlow=y $hasValueFlow=y
|
||||
sink(ObjectUtils.max(taint(), null, null)); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.max(null, taint(), null)); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.max(null, null, taint())); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.median(taint(), null, null)); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.median((String)null, taint(), null)); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.median((String)null, null, taint())); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.min(taint(), null, null)); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.min(null, taint(), null)); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.min(null, null, taint())); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.mode(taint(), null, null)); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.mode(null, taint(), null)); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.mode(null, null, taint())); // $hasTaintFlow=y $MISSING:hasValueFlow=y
|
||||
sink(ObjectUtils.requireNonEmpty(taint(), "message")); // $hasTaintFlow=y $hasValueFlow=y
|
||||
sink(ObjectUtils.requireNonEmpty("not null", taint())); // GOOD (message doesn't propagate to the return)
|
||||
sink(ObjectUtils.toString(taint(), "default string")); // GOOD (first argument is stringified)
|
||||
sink(ObjectUtils.toString(null, taint())); // $hasTaintFlow=y $hasValueFlow=y
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,20 @@ import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:frameworks:apache-commons-lang3" }
|
||||
class TaintFlowConf extends TaintTracking::Configuration {
|
||||
TaintFlowConf() { this = "qltest:frameworks:apache-commons-lang3-taint-flow" }
|
||||
|
||||
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 ValueFlowConf extends DataFlow::Configuration {
|
||||
ValueFlowConf() { this = "qltest:frameworks:apache-commons-lang3-value-flow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) {
|
||||
n.asExpr().(MethodAccess).getMethod().hasName("taint")
|
||||
@@ -17,11 +29,18 @@ class Conf extends TaintTracking::Configuration {
|
||||
class HasFlowTest extends InlineExpectationsTest {
|
||||
HasFlowTest() { this = "HasFlowTest" }
|
||||
|
||||
override string getARelevantTag() { result = "hasTaintFlow" }
|
||||
override string getARelevantTag() { result = ["hasTaintFlow", "hasValueFlow"] }
|
||||
|
||||
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) |
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, TaintFlowConf conf | conf.hasFlow(src, sink) |
|
||||
sink.getLocation() = location and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
or
|
||||
tag = "hasValueFlow" and
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, ValueFlowConf conf | conf.hasFlow(src, sink) |
|
||||
sink.getLocation() = location and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.lang3.text.StrBuilder;
|
||||
|
||||
@SuppressWarnings("deprecation") // deprecated class StrBuilder is imported
|
||||
// because it is part of the signature of deprecated methods
|
||||
public class ObjectUtils {
|
||||
public static class Null implements Serializable {
|
||||
}
|
||||
public static final Null NULL = new Null();
|
||||
|
||||
public static boolean allNotNull(final Object... values) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean allNull(final Object... values) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean anyNotNull(final Object... values) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean anyNull(final Object... values) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T> T clone(final T obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T cloneIfPossible(final T obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T extends Comparable<? super T>> int compare(final T c1, final T c2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static <T extends Comparable<? super T>> int compare(final T c1, final T c2, final boolean nullGreater) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static boolean CONST(final boolean v) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static byte CONST(final byte v) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static char CONST(final char v) {
|
||||
return '\0';
|
||||
}
|
||||
|
||||
public static double CONST(final double v) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static float CONST(final float v) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int CONST(final int v) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long CONST(final long v) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static short CONST(final short v) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static <T> T CONST(final T v) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte CONST_BYTE(final int v) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static short CONST_SHORT(final int v) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static <T> T defaultIfNull(final T object, final T defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean equals(final Object object1, final Object object2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T> T firstNonNull(final T... values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T getFirstNonNull(final Supplier<T>... suppliers) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T getIfNull(final T object, final Supplier<T> defaultSupplier) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int hashCode(final Object obj) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int hashCodeMulti(final Object... objects) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void identityToString(final Appendable appendable, final Object object) throws IOException {
|
||||
}
|
||||
|
||||
public static String identityToString(final Object object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void identityToString(final StrBuilder builder, final Object object) {
|
||||
}
|
||||
|
||||
public static void identityToString(final StringBuffer buffer, final Object object) {
|
||||
}
|
||||
|
||||
public static void identityToString(final StringBuilder builder, final Object object) {
|
||||
}
|
||||
|
||||
public static boolean isEmpty(final Object object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isNotEmpty(final Object object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T extends Comparable<? super T>> T max(final T... values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T median(final Comparator<T> comparator, final T... items) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T extends Comparable<? super T>> T median(final T... items) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T extends Comparable<? super T>> T min(final T... values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T mode(final T... items) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean notEqual(final Object object1, final Object object2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T> T requireNonEmpty(final T obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T requireNonEmpty(final T obj, final String message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String toString(final Object obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String toString(final Object obj, final String nullStr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String toString(final Object obj, final Supplier<String> supplier) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void wait(final Object obj, final Duration duration) throws InterruptedException {
|
||||
}
|
||||
|
||||
public ObjectUtils() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user