Skip to content

Commit c9786df

Browse files
authored
Merge pull request github#5344 from smowton/smowton/feature/commons-object-utils
Java: Add models for flow- and taint-preserving functions in Commons ObjectUtils
2 parents 195ed01 + 58d5c2c commit c9786df

File tree

5 files changed

+315
-4
lines changed

5 files changed

+315
-4
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Added models for `ObjectUtils` methods in the Apache Commons Lang library. This may lead to more results from any dataflow query where traversal of `ObjectUtils` methods means we can now complete a path from a source of tainted data to a corresponding sink.

java/ql/src/semmle/code/java/frameworks/apache/Lang.qll

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,3 +396,30 @@ private class ApacheRegExUtilsModel extends SummaryModelCsv {
396396
]
397397
}
398398
}
399+
400+
/**
401+
* Taint-propagating models for `ObjectUtils`.
402+
*/
403+
private class ApacheObjectUtilsModel extends SummaryModelCsv {
404+
override predicate row(string row) {
405+
row =
406+
[
407+
// Note all the functions annotated with `taint` flow really should have `value` flow,
408+
// but we don't support value-preserving varargs functions at the moment.
409+
"org.apache.commons.lang3;ObjectUtils;false;clone;;;Argument;ReturnValue;value",
410+
"org.apache.commons.lang3;ObjectUtils;false;cloneIfPossible;;;Argument;ReturnValue;value",
411+
"org.apache.commons.lang3;ObjectUtils;false;CONST;;;Argument;ReturnValue;value",
412+
"org.apache.commons.lang3;ObjectUtils;false;CONST_BYTE;;;Argument;ReturnValue;value",
413+
"org.apache.commons.lang3;ObjectUtils;false;CONST_SHORT;;;Argument;ReturnValue;value",
414+
"org.apache.commons.lang3;ObjectUtils;false;defaultIfNull;;;Argument;ReturnValue;value",
415+
"org.apache.commons.lang3;ObjectUtils;false;firstNonNull;;;Argument;ReturnValue;taint",
416+
"org.apache.commons.lang3;ObjectUtils;false;getIfNull;;;Argument[0];ReturnValue;value",
417+
"org.apache.commons.lang3;ObjectUtils;false;max;;;Argument;ReturnValue;taint",
418+
"org.apache.commons.lang3;ObjectUtils;false;median;;;Argument;ReturnValue;taint",
419+
"org.apache.commons.lang3;ObjectUtils;false;min;;;Argument;ReturnValue;taint",
420+
"org.apache.commons.lang3;ObjectUtils;false;mode;;;Argument;ReturnValue;taint",
421+
"org.apache.commons.lang3;ObjectUtils;false;requireNonEmpty;;;Argument[0];ReturnValue;value",
422+
"org.apache.commons.lang3;ObjectUtils;false;toString;(Object,String);;Argument[1];ReturnValue;value"
423+
]
424+
}
425+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import org.apache.commons.lang3.ObjectUtils;
2+
3+
public class ObjectUtilsTest {
4+
String taint() { return "tainted"; }
5+
6+
private static class IntSource {
7+
static int taint() { return 0; }
8+
}
9+
10+
void sink(Object o) {}
11+
12+
void test() throws Exception {
13+
sink(ObjectUtils.clone(taint())); // $hasValueFlow
14+
sink(ObjectUtils.cloneIfPossible(taint())); // $hasValueFlow
15+
sink(ObjectUtils.CONST(taint())); // $hasValueFlow
16+
sink(ObjectUtils.CONST_SHORT(IntSource.taint())); // $hasValueFlow
17+
sink(ObjectUtils.CONST_BYTE(IntSource.taint())); // $hasValueFlow
18+
sink(ObjectUtils.defaultIfNull(taint(), null)); // $hasValueFlow
19+
sink(ObjectUtils.defaultIfNull(null, taint())); // $hasValueFlow
20+
sink(ObjectUtils.firstNonNull(taint(), null, null)); // $hasTaintFlow $MISSING:hasValueFlow
21+
sink(ObjectUtils.firstNonNull(null, taint(), null)); // $hasTaintFlow $MISSING:hasValueFlow
22+
sink(ObjectUtils.firstNonNull(null, null, taint())); // $hasTaintFlow $MISSING:hasValueFlow
23+
sink(ObjectUtils.getIfNull(taint(), null)); // $hasValueFlow
24+
sink(ObjectUtils.max(taint(), null, null)); // $hasTaintFlow $MISSING:hasValueFlow
25+
sink(ObjectUtils.max(null, taint(), null)); // $hasTaintFlow $MISSING:hasValueFlow
26+
sink(ObjectUtils.max(null, null, taint())); // $hasTaintFlow $MISSING:hasValueFlow
27+
sink(ObjectUtils.median(taint(), null, null)); // $hasTaintFlow $MISSING:hasValueFlow
28+
sink(ObjectUtils.median((String)null, taint(), null)); // $hasTaintFlow $MISSING:hasValueFlow
29+
sink(ObjectUtils.median((String)null, null, taint())); // $hasTaintFlow $MISSING:hasValueFlow
30+
sink(ObjectUtils.min(taint(), null, null)); // $hasTaintFlow $MISSING:hasValueFlow
31+
sink(ObjectUtils.min(null, taint(), null)); // $hasTaintFlow $MISSING:hasValueFlow
32+
sink(ObjectUtils.min(null, null, taint())); // $hasTaintFlow $MISSING:hasValueFlow
33+
sink(ObjectUtils.mode(taint(), null, null)); // $hasTaintFlow $MISSING:hasValueFlow
34+
sink(ObjectUtils.mode(null, taint(), null)); // $hasTaintFlow $MISSING:hasValueFlow
35+
sink(ObjectUtils.mode(null, null, taint())); // $hasTaintFlow $MISSING:hasValueFlow
36+
sink(ObjectUtils.requireNonEmpty(taint(), "message")); // $hasValueFlow
37+
sink(ObjectUtils.requireNonEmpty("not null", taint())); // GOOD (message doesn't propagate to the return)
38+
sink(ObjectUtils.toString(taint(), "default string")); // GOOD (first argument is stringified)
39+
sink(ObjectUtils.toString(null, taint())); // $hasValueFlow
40+
}
41+
}

java/ql/test/library-tests/frameworks/apache-commons-lang3/flow.ql

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,20 @@ import java
22
import semmle.code.java.dataflow.TaintTracking
33
import TestUtilities.InlineExpectationsTest
44

5-
class Conf extends TaintTracking::Configuration {
6-
Conf() { this = "qltest:frameworks:apache-commons-lang3" }
5+
class TaintFlowConf extends TaintTracking::Configuration {
6+
TaintFlowConf() { this = "qltest:frameworks:apache-commons-lang3-taint-flow" }
7+
8+
override predicate isSource(DataFlow::Node n) {
9+
n.asExpr().(MethodAccess).getMethod().hasName("taint")
10+
}
11+
12+
override predicate isSink(DataFlow::Node n) {
13+
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
14+
}
15+
}
16+
17+
class ValueFlowConf extends DataFlow::Configuration {
18+
ValueFlowConf() { this = "qltest:frameworks:apache-commons-lang3-value-flow" }
719

820
override predicate isSource(DataFlow::Node n) {
921
n.asExpr().(MethodAccess).getMethod().hasName("taint")
@@ -17,11 +29,19 @@ class Conf extends TaintTracking::Configuration {
1729
class HasFlowTest extends InlineExpectationsTest {
1830
HasFlowTest() { this = "HasFlowTest" }
1931

20-
override string getARelevantTag() { result = "hasTaintFlow" }
32+
override string getARelevantTag() { result = ["hasTaintFlow", "hasValueFlow"] }
2133

2234
override predicate hasActualResult(Location location, string element, string tag, string value) {
2335
tag = "hasTaintFlow" and
24-
exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) |
36+
exists(DataFlow::Node src, DataFlow::Node sink, TaintFlowConf conf | conf.hasFlow(src, sink) |
37+
not any(ValueFlowConf vconf).hasFlow(src, sink) and
38+
sink.getLocation() = location and
39+
element = sink.toString() and
40+
value = ""
41+
)
42+
or
43+
tag = "hasValueFlow" and
44+
exists(DataFlow::Node src, DataFlow::Node sink, ValueFlowConf conf | conf.hasFlow(src, sink) |
2545
sink.getLocation() = location and
2646
element = sink.toString() and
2747
value = ""
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.lang3;
18+
19+
import java.io.IOException;
20+
import java.io.Serializable;
21+
import java.lang.reflect.Array;
22+
import java.lang.reflect.InvocationTargetException;
23+
import java.lang.reflect.Method;
24+
import java.time.Duration;
25+
import java.util.Collection;
26+
import java.util.Collections;
27+
import java.util.Comparator;
28+
import java.util.HashMap;
29+
import java.util.Map;
30+
import java.util.Objects;
31+
import java.util.TreeSet;
32+
import java.util.function.Supplier;
33+
34+
import org.apache.commons.lang3.text.StrBuilder;
35+
36+
@SuppressWarnings("deprecation") // deprecated class StrBuilder is imported
37+
// because it is part of the signature of deprecated methods
38+
public class ObjectUtils {
39+
public static class Null implements Serializable {
40+
}
41+
public static final Null NULL = new Null();
42+
43+
public static boolean allNotNull(final Object... values) {
44+
return false;
45+
}
46+
47+
public static boolean allNull(final Object... values) {
48+
return false;
49+
}
50+
51+
public static boolean anyNotNull(final Object... values) {
52+
return false;
53+
}
54+
55+
public static boolean anyNull(final Object... values) {
56+
return false;
57+
}
58+
59+
public static <T> T clone(final T obj) {
60+
return null;
61+
}
62+
63+
public static <T> T cloneIfPossible(final T obj) {
64+
return null;
65+
}
66+
67+
public static <T extends Comparable<? super T>> int compare(final T c1, final T c2) {
68+
return 0;
69+
}
70+
71+
public static <T extends Comparable<? super T>> int compare(final T c1, final T c2, final boolean nullGreater) {
72+
return 0;
73+
}
74+
75+
public static boolean CONST(final boolean v) {
76+
return false;
77+
}
78+
79+
public static byte CONST(final byte v) {
80+
return 0;
81+
}
82+
83+
public static char CONST(final char v) {
84+
return '\0';
85+
}
86+
87+
public static double CONST(final double v) {
88+
return 0;
89+
}
90+
91+
public static float CONST(final float v) {
92+
return 0;
93+
}
94+
95+
public static int CONST(final int v) {
96+
return 0;
97+
}
98+
99+
public static long CONST(final long v) {
100+
return 0;
101+
}
102+
103+
public static short CONST(final short v) {
104+
return 0;
105+
}
106+
107+
public static <T> T CONST(final T v) {
108+
return null;
109+
}
110+
111+
public static byte CONST_BYTE(final int v) {
112+
return 0;
113+
}
114+
115+
public static short CONST_SHORT(final int v) {
116+
return 0;
117+
}
118+
119+
public static <T> T defaultIfNull(final T object, final T defaultValue) {
120+
return null;
121+
}
122+
123+
public static boolean equals(final Object object1, final Object object2) {
124+
return false;
125+
}
126+
127+
public static <T> T firstNonNull(final T... values) {
128+
return null;
129+
}
130+
131+
public static <T> T getFirstNonNull(final Supplier<T>... suppliers) {
132+
return null;
133+
}
134+
135+
public static <T> T getIfNull(final T object, final Supplier<T> defaultSupplier) {
136+
return null;
137+
}
138+
139+
public static int hashCode(final Object obj) {
140+
return 0;
141+
}
142+
143+
public static int hashCodeMulti(final Object... objects) {
144+
return 0;
145+
}
146+
147+
public static void identityToString(final Appendable appendable, final Object object) throws IOException {
148+
}
149+
150+
public static String identityToString(final Object object) {
151+
return null;
152+
}
153+
154+
public static void identityToString(final StrBuilder builder, final Object object) {
155+
}
156+
157+
public static void identityToString(final StringBuffer buffer, final Object object) {
158+
}
159+
160+
public static void identityToString(final StringBuilder builder, final Object object) {
161+
}
162+
163+
public static boolean isEmpty(final Object object) {
164+
return false;
165+
}
166+
167+
public static boolean isNotEmpty(final Object object) {
168+
return false;
169+
}
170+
171+
public static <T extends Comparable<? super T>> T max(final T... values) {
172+
return null;
173+
}
174+
175+
public static <T> T median(final Comparator<T> comparator, final T... items) {
176+
return null;
177+
}
178+
179+
public static <T extends Comparable<? super T>> T median(final T... items) {
180+
return null;
181+
}
182+
183+
public static <T extends Comparable<? super T>> T min(final T... values) {
184+
return null;
185+
}
186+
187+
public static <T> T mode(final T... items) {
188+
return null;
189+
}
190+
191+
public static boolean notEqual(final Object object1, final Object object2) {
192+
return false;
193+
}
194+
195+
public static <T> T requireNonEmpty(final T obj) {
196+
return null;
197+
}
198+
199+
public static <T> T requireNonEmpty(final T obj, final String message) {
200+
return null;
201+
}
202+
203+
public static String toString(final Object obj) {
204+
return null;
205+
}
206+
207+
public static String toString(final Object obj, final String nullStr) {
208+
return null;
209+
}
210+
211+
public static String toString(final Object obj, final Supplier<String> supplier) {
212+
return null;
213+
}
214+
215+
public static void wait(final Object obj, final Duration duration) throws InterruptedException {
216+
}
217+
218+
public ObjectUtils() {
219+
}
220+
221+
}

0 commit comments

Comments
 (0)