mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Support parameter->parameter flow
This commit is contained in:
@@ -12,6 +12,8 @@ import semmle.code.java.dataflow.internal.DataFlowImplCommon
|
||||
string captureFlow(Callable api) {
|
||||
result = captureQualifierFlow(api) or
|
||||
result = captureParameterFlowToReturnValue(api) or
|
||||
result = captureFieldFlowIn(api) or
|
||||
result = captureParameterToParameterFlow(api) or
|
||||
// TODO: merge next two?
|
||||
result = captureFieldFlowOut(api) or
|
||||
result = captureFieldFlowIntoParam(api)
|
||||
@@ -49,6 +51,36 @@ string captureFieldFlowIntoParam(Callable api) {
|
||||
)
|
||||
}
|
||||
|
||||
class FieldAssignment extends AssignExpr {
|
||||
FieldAssignment() { exists(Field f | f.getAnAccess() = this.getDest()) }
|
||||
}
|
||||
|
||||
class ParameterToFieldConfig extends TaintTracking::Configuration {
|
||||
ParameterToFieldConfig() { this = "ParameterToFieldConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
not source.asParameter().getType() instanceof PrimitiveType
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FieldAssignment a |
|
||||
a.getSource().getAChildExpr() = sink.asExpr() or a.getSource() = sink.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
string captureFieldFlowIn(Callable api) {
|
||||
exists(DataFlow::ParameterNode source, DataFlow::ExprNode sink, ParameterToFieldConfig config |
|
||||
sink.asExpr().getEnclosingCallable().getDeclaringType() =
|
||||
source.asParameter().getCallable().getDeclaringType() and
|
||||
config.hasFlow(source, sink) and
|
||||
source.asParameter().getCallable() = api
|
||||
|
|
||||
result =
|
||||
asTaintModel(api, "Argument[" + source.asParameter().getPosition() + "]", "Argument[-1]")
|
||||
)
|
||||
}
|
||||
|
||||
class ParameterToReturnValueTaintConfig extends TaintTracking::Configuration {
|
||||
ParameterToReturnValueTaintConfig() { this = "ParameterToReturnValueTaintConfig" }
|
||||
|
||||
@@ -87,6 +119,18 @@ string captureParameterFlowToReturnValue(Callable api) {
|
||||
)
|
||||
}
|
||||
|
||||
string captureParameterToParameterFlow(Callable api) {
|
||||
exists(DataFlow::ParameterNode source, DataFlow::PostUpdateNode sink |
|
||||
source.getEnclosingCallable() = api and
|
||||
sink.getPreUpdateNode().asExpr() = api.getAParameter().getAnAccess() and
|
||||
TaintTracking::localTaint(source, sink)
|
||||
|
|
||||
result =
|
||||
asTaintModel(api, parameterAccess(source.asParameter()),
|
||||
parameterAccess(sink.getPreUpdateNode().asExpr().(VarAccess).getVariable().(Parameter)))
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: handle cases like Ticker
|
||||
// TODO: "com.google.common.base;Converter;true;convertAll;(Iterable);;Element of Argument[0];Element of ReturnValue;taint",
|
||||
// TODO: infer interface from multiple implementations? e.g. UriComponentsContributor
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
| p;Factory;false;create;(String);;Argument[0];Argument[-1];taint; |
|
||||
| p;Factory;false;create;(String,int);;Argument[0];Argument[-1];taint; |
|
||||
| p;FinalClass;false;returnsInput;(String);;Argument[0];ReturnValue;taint; |
|
||||
| p;FluentAPI;false;returnsThis;(String);;Argument[-1];ReturnValue;value; |
|
||||
| p;ImmutablePojo;false;ImmutablePojo;(String,int);;Argument[0];Argument[-1];taint; |
|
||||
| p;ImmutablePojo;false;getValue;();;Argument[-1];ReturnValue;taint; |
|
||||
| p;ImmutablePojo;false;or;(String);;Argument[-1];ReturnValue;taint; |
|
||||
| p;ImmutablePojo;false;or;(String);;Argument[0];ReturnValue;taint; |
|
||||
| p;InnerClasses$CaptureMe;true;yesCm;(String);;Argument[0];ReturnValue;taint; |
|
||||
| p;InnerClasses;true;yes;(String);;Argument[0];ReturnValue;taint; |
|
||||
| p;Joiner;false;Joiner;(CharSequence);;Argument[0];Argument[-1];taint; |
|
||||
| p;Joiner;false;Joiner;(CharSequence,CharSequence,CharSequence);;Argument[0];Argument[-1];taint; |
|
||||
| p;Joiner;false;Joiner;(CharSequence,CharSequence,CharSequence);;Argument[1];Argument[-1];taint; |
|
||||
| p;Joiner;false;Joiner;(CharSequence,CharSequence,CharSequence);;Argument[2];Argument[-1];taint; |
|
||||
| p;Joiner;false;add;(CharSequence);;Argument[-1];ReturnValue;value; |
|
||||
| p;Joiner;false;merge;(Joiner);;Argument[-1];ReturnValue;value; |
|
||||
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[-1];ReturnValue;value; |
|
||||
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[0];Argument[-1];taint; |
|
||||
| p;Joiner;false;toString;();;Argument[-1];ReturnValue;taint; |
|
||||
| p;ParamFlow;true;addTo;(String,List);;Argument[0];Element of Argument[1];taint; |
|
||||
| p;ParamFlow;true;returnArrayElement;(String[]);;ArrayElement of Argument[0];ReturnValue;taint; |
|
||||
| p;ParamFlow;true;returnCollectionElement;(List);;Element of Argument[0];ReturnValue;taint; |
|
||||
@@ -10,4 +25,8 @@
|
||||
| p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[0];ReturnValue;taint; |
|
||||
| p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[1];ReturnValue;taint; |
|
||||
| p;ParamFlow;true;returnVarArgElement;(String[]);;ArrayElement of Argument[0];ReturnValue;taint; |
|
||||
| p;ParamFlow;true;returnsInput;(String);;Argument[0];ReturnValue;taint; |
|
||||
| p;ParamFlow;true;returnsInput;(String);;Argument[0];ReturnValue;taint; |
|
||||
| p;ParamFlow;true;writeChunked;(byte[],OutputStream);;ArrayElement of Argument[0];Argument[1];taint; |
|
||||
| p;Pojo;false;fillIn;(List);;Argument[-1];Element of Argument[0];taint; |
|
||||
| p;Pojo;false;getValue;();;Argument[-1];ReturnValue;taint; |
|
||||
| p;Pojo;false;setValue;(String);;Argument[0];Argument[-1];taint; |
|
||||
|
||||
22
java/ql/test/utils/model-generator/p/Factory.java
Normal file
22
java/ql/test/utils/model-generator/p/Factory.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package p;
|
||||
|
||||
public final class Factory {
|
||||
|
||||
private String value;
|
||||
|
||||
private int intValue;
|
||||
|
||||
public static Factory create(String value, int foo) {
|
||||
return new Factory(value, foo);
|
||||
}
|
||||
|
||||
public static Factory create(String value) {
|
||||
return new Factory(value, 0);
|
||||
}
|
||||
|
||||
private Factory(String value, int intValue) {
|
||||
this.value = value;
|
||||
this.intValue = intValue;
|
||||
}
|
||||
|
||||
}
|
||||
118
java/ql/test/utils/model-generator/p/Joiner.java
Normal file
118
java/ql/test/utils/model-generator/p/Joiner.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package p;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Joiner {
|
||||
private final String prefix;
|
||||
private final String delimiter;
|
||||
private final String suffix;
|
||||
private String[] elts;
|
||||
private int size;
|
||||
private int len;
|
||||
private String emptyValue;
|
||||
public Joiner(CharSequence delimiter) {
|
||||
this(delimiter, "", "");
|
||||
}
|
||||
|
||||
public Joiner(CharSequence delimiter,
|
||||
CharSequence prefix,
|
||||
CharSequence suffix) {
|
||||
Objects.requireNonNull(prefix, "The prefix must not be null");
|
||||
Objects.requireNonNull(delimiter, "The delimiter must not be null");
|
||||
Objects.requireNonNull(suffix, "The suffix must not be null");
|
||||
this.prefix = prefix.toString();
|
||||
this.delimiter = delimiter.toString();
|
||||
this.suffix = suffix.toString();
|
||||
checkAddLength(0, 0);
|
||||
}
|
||||
|
||||
public Joiner setEmptyValue(CharSequence emptyValue) {
|
||||
this.emptyValue = Objects.requireNonNull(emptyValue,
|
||||
"The empty value must not be null").toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
private static int getChars(String s, char[] chars, int start) {
|
||||
int len = s.length();
|
||||
s.getChars(0, len, chars, start);
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String[] elts = this.elts;
|
||||
if (elts == null && emptyValue != null) {
|
||||
return emptyValue;
|
||||
}
|
||||
final int size = this.size;
|
||||
final int addLen = prefix.length() + suffix.length();
|
||||
if (addLen == 0) {
|
||||
compactElts();
|
||||
return size == 0 ? "" : elts[0];
|
||||
}
|
||||
final String delimiter = this.delimiter;
|
||||
final char[] chars = new char[len + addLen];
|
||||
int k = getChars(prefix, chars, 0);
|
||||
if (size > 0) {
|
||||
k += getChars(elts[0], chars, k);
|
||||
for (int i = 1; i < size; i++) {
|
||||
k += getChars(delimiter, chars, k);
|
||||
k += getChars(elts[i], chars, k);
|
||||
}
|
||||
}
|
||||
k += getChars(suffix, chars, k);
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
public Joiner add(CharSequence newElement) {
|
||||
final String elt = String.valueOf(newElement);
|
||||
if (elts == null) {
|
||||
elts = new String[8];
|
||||
} else {
|
||||
if (size == elts.length)
|
||||
elts = Arrays.copyOf(elts, 2 * size);
|
||||
len = checkAddLength(len, delimiter.length());
|
||||
}
|
||||
len = checkAddLength(len, elt.length());
|
||||
elts[size++] = elt;
|
||||
return this;
|
||||
}
|
||||
|
||||
private int checkAddLength(int oldLen, int inc) {
|
||||
long newLen = (long)oldLen + (long)inc;
|
||||
long tmpLen = newLen + (long)prefix.length() + (long)suffix.length();
|
||||
if (tmpLen != (int)tmpLen) {
|
||||
throw new OutOfMemoryError("Requested array size exceeds VM limit");
|
||||
}
|
||||
return (int)newLen;
|
||||
}
|
||||
|
||||
public Joiner merge(Joiner other) {
|
||||
Objects.requireNonNull(other);
|
||||
if (other.elts == null) {
|
||||
return this;
|
||||
}
|
||||
other.compactElts();
|
||||
return add(other.elts[0]);
|
||||
}
|
||||
|
||||
private void compactElts() {
|
||||
if (size > 1) {
|
||||
final char[] chars = new char[len];
|
||||
int i = 1, k = getChars(elts[0], chars, 0);
|
||||
do {
|
||||
k += getChars(delimiter, chars, k);
|
||||
k += getChars(elts[i], chars, k);
|
||||
elts[i] = null;
|
||||
} while (++i < size);
|
||||
size = 1;
|
||||
elts[0] = new String(chars);
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return (size == 0 && emptyValue != null) ? emptyValue.length() :
|
||||
len + prefix.length() + suffix.length();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user