mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge pull request #5136 from aschackmull/java/csv-models
Java: Add support for framework modelling through csv data.
This commit is contained in:
519
java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
Normal file
519
java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll
Normal file
@@ -0,0 +1,519 @@
|
||||
/**
|
||||
* INTERNAL use only. This is an experimental API subject to change without notice.
|
||||
*
|
||||
* Provides classes and predicates for dealing with flow models specified in CSV format.
|
||||
*
|
||||
* The CSV specification has the following columns:
|
||||
* - Sources:
|
||||
* `namespace; type; subtypes; name; signature; ext; output; kind`
|
||||
* - Sinks:
|
||||
* `namespace; type; subtypes; name; signature; ext; input; kind`
|
||||
* - Summaries:
|
||||
* `namespace; type; subtypes; name; signature; ext; input; output; kind`
|
||||
*
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
* 1. The `namespace` column selects a package.
|
||||
* 2. The `type` column selects a type within that package.
|
||||
* 3. The `subtypes` is a boolean that indicates whether to jump to an
|
||||
* arbitrary subtype of that type.
|
||||
* 4. The `name` column optionally selects a specific named member of the type.
|
||||
* 5. The `signature` column optionally restricts the named member. If
|
||||
* `signature` is blank then no such filtering is done. The format of the
|
||||
* signature is a comma-separated list of types enclosed in parentheses. The
|
||||
* types can be short names or fully qualified names (mixing these two options
|
||||
* is not allowed within a single signature).
|
||||
* 6. The `ext` column specifies additional API-graph-like edges. Currently
|
||||
* there are only two valid values: "" and "Annotated". The empty string has no
|
||||
* effect. "Annotated" applies if `name` and `signature` were left blank and
|
||||
* acts by selecting an element that is annotated by the annotation type
|
||||
* selected by the first 4 columns. This can be another member such as a field
|
||||
* or method, or a parameter.
|
||||
* 7. The `input` column specifies how data enters the element selected by the
|
||||
* first 6 columns, and the `output` column specifies how data leaves the
|
||||
* element selected by the first 6 columns. An `input` can be either "",
|
||||
* "Argument", "Argument[n]", "ReturnValue":
|
||||
* - "": Selects a write to the selected element in case this is a field.
|
||||
* - "Argument": Selects any argument in a call to the selected element.
|
||||
* - "Argument[n]": Similar to "Argument" but restricted to a specific numbered
|
||||
* argument (zero-indexed, and `-1` specifies the qualifier).
|
||||
* - "ReturnValue": Selects a value being returned by the selected element.
|
||||
* This requires that the selected element is a method with a body.
|
||||
*
|
||||
* An `output` can be either "", "Argument", "Argument[n]", "Parameter",
|
||||
* "Parameter[n]", or "ReturnValue":
|
||||
* - "": Selects a read of a selected field, or a selected parameter.
|
||||
* - "Argument": Selects the post-update value of an argument in a call to the
|
||||
* selected element. That is, the value of the argument after the call returns.
|
||||
* - "Argument[n]": Similar to "Argument" but restricted to a specific numbered
|
||||
* argument (zero-indexed, and `-1` specifies the qualifier).
|
||||
* - "Parameter": Selects the value of a parameter of the selected element.
|
||||
* "Parameter" is also allowed in case the selected element is already a
|
||||
* parameter itself.
|
||||
* - "Parameter[n]": Similar to "Parameter" but restricted to a specific
|
||||
* numbered parameter (zero-indexed, and `-1` specifies the value of `this`).
|
||||
* - "ReturnValue": Selects the return value of a call to the selected element.
|
||||
* 8. The `kind` column is a tag that can be referenced from QL to determine to
|
||||
* which classes the interpreted elements should be added. For example, for
|
||||
* sources "remote" indicates a default remote flow source, and for summaries
|
||||
* "taint" indicates a default additional taint step and "value" indicates a
|
||||
* globally applicable value-preserving step.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow::DataFlow
|
||||
private import internal.DataFlowPrivate
|
||||
|
||||
private predicate sourceModelCsv(string row) {
|
||||
row =
|
||||
[
|
||||
// ServletRequestGetParameterMethod
|
||||
"javax.servlet;ServletRequest;false;getParameter;(String);;ReturnValue;remote",
|
||||
"javax.servlet;ServletRequest;false;getParameterValues;(String);;ReturnValue;remote",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameter;(String);;ReturnValue;remote",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameterValues;(String);;ReturnValue;remote",
|
||||
// ServletRequestGetParameterMapMethod
|
||||
"javax.servlet;ServletRequest;false;getParameterMap;();;ReturnValue;remote",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameterMap;();;ReturnValue;remote",
|
||||
// ServletRequestGetParameterNamesMethod
|
||||
"javax.servlet;ServletRequest;false;getParameterNames;();;ReturnValue;remote",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameterNames;();;ReturnValue;remote",
|
||||
// HttpServletRequestGetQueryStringMethod
|
||||
"javax.servlet.http;HttpServletRequest;false;getQueryString;();;ReturnValue;remote",
|
||||
//
|
||||
// URLConnectionGetInputStreamMethod
|
||||
"java.net;URLConnection;false;getInputStream;();;ReturnValue;remote",
|
||||
// SocketGetInputStreamMethod
|
||||
"java.net;Socket;false;getInputStream;();;ReturnValue;remote",
|
||||
// BeanValidationSource
|
||||
"javax.validation;ConstraintValidator;true;isValid;;;Parameter[0];remote"
|
||||
]
|
||||
}
|
||||
|
||||
private predicate sinkModelCsv(string row) { none() }
|
||||
|
||||
private predicate summaryModelCsv(string row) { none() }
|
||||
|
||||
/**
|
||||
* A unit class for adding additional source model rows.
|
||||
*
|
||||
* Extend this class to add additional source definitions.
|
||||
*/
|
||||
class SourceModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a source definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional sink model rows.
|
||||
*
|
||||
* Extend this class to add additional sink definitions.
|
||||
*/
|
||||
class SinkModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a sink definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional summary model rows.
|
||||
*
|
||||
* Extend this class to add additional flow summary definitions.
|
||||
*/
|
||||
class SummaryModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a summary definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
private predicate sourceModel(string row) {
|
||||
sourceModelCsv(row) or
|
||||
any(SourceModelCsv s).row(row)
|
||||
}
|
||||
|
||||
private predicate sinkModel(string row) {
|
||||
sinkModelCsv(row) or
|
||||
any(SinkModelCsv s).row(row)
|
||||
}
|
||||
|
||||
private predicate summaryModel(string row) {
|
||||
summaryModelCsv(row) or
|
||||
any(SummaryModelCsv s).row(row)
|
||||
}
|
||||
|
||||
private predicate sourceModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind
|
||||
) {
|
||||
exists(string row |
|
||||
sourceModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = subtypes.toString() and
|
||||
subtypes = [true, false] and
|
||||
row.splitAt(";", 3) = name and
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = output and
|
||||
row.splitAt(";", 7) = kind
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sinkModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind
|
||||
) {
|
||||
exists(string row |
|
||||
sinkModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = subtypes.toString() and
|
||||
subtypes = [true, false] and
|
||||
row.splitAt(";", 3) = name and
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = kind
|
||||
)
|
||||
}
|
||||
|
||||
private predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind
|
||||
) {
|
||||
exists(string row |
|
||||
summaryModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = subtypes.toString() and
|
||||
subtypes = [true, false] and
|
||||
row.splitAt(";", 3) = name and
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = output and
|
||||
row.splitAt(";", 8) = kind
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides a query predicate to check the CSV data for validation errors. */
|
||||
module CsvValidation {
|
||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
||||
query predicate invalidModelRow(string msg) {
|
||||
exists(string pred, string namespace, string type, string name, string signature, string ext |
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _) and pred = "source"
|
||||
or
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _) and pred = "sink"
|
||||
or
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "summary"
|
||||
|
|
||||
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
|
||||
msg = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
or
|
||||
not type.regexpMatch("[a-zA-Z0-9_\\$]+") and
|
||||
msg = "Dubious type \"" + type + "\" in " + pred + " model."
|
||||
or
|
||||
not name.regexpMatch("[a-zA-Z0-9_]*") and
|
||||
msg = "Dubious name \"" + name + "\" in " + pred + " model."
|
||||
or
|
||||
not signature.regexpMatch("|\\([a-zA-Z0-9_\\.\\$<>,]*\\)") and
|
||||
msg = "Dubious signature \"" + signature + "\" in " + pred + " model."
|
||||
or
|
||||
not ext.regexpMatch("|Annotated") and
|
||||
msg = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
exists(string pred, string input, string part |
|
||||
sinkModel(_, _, _, _, _, _, input, _) and pred = "sink"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, input, _, _) and pred = "summary"
|
||||
|
|
||||
specSplit(input, part, _) and
|
||||
not part.regexpMatch("|Argument|ReturnValue") and
|
||||
not parseArg(part, _) and
|
||||
msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
exists(string pred, string output, string part |
|
||||
sourceModel(_, _, _, _, _, _, output, _) and pred = "source"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, _, output, _) and pred = "summary"
|
||||
|
|
||||
specSplit(output, part, _) and
|
||||
not part.regexpMatch("|Argument|Parameter|ReturnValue") and
|
||||
not parseArg(part, _) and
|
||||
not parseParam(part, _) and
|
||||
msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
exists(string pred, string row, int expect |
|
||||
sourceModel(row) and expect = 8 and pred = "source"
|
||||
or
|
||||
sinkModel(row) and expect = 8 and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and expect = 9 and pred = "summary"
|
||||
|
|
||||
exists(int cols |
|
||||
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
||||
cols != expect and
|
||||
msg =
|
||||
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
|
||||
"."
|
||||
)
|
||||
or
|
||||
exists(string b |
|
||||
b = row.splitAt(";", 2) and
|
||||
not b = ["true", "false"] and
|
||||
msg = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate elementSpec(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _) or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _) or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _)
|
||||
}
|
||||
|
||||
bindingset[namespace, type, subtypes]
|
||||
private RefType interpretType(string namespace, string type, boolean subtypes) {
|
||||
exists(RefType t |
|
||||
t.hasQualifiedName(namespace, type) and
|
||||
if subtypes = true then result.getASourceSupertype*() = t else result = t
|
||||
)
|
||||
}
|
||||
|
||||
private string paramsStringPart(Callable c, int i) {
|
||||
i = -1 and result = "("
|
||||
or
|
||||
exists(int n, string p | c.getParameterType(n).toString() = p |
|
||||
i = 2 * n and result = p
|
||||
or
|
||||
i = 2 * n - 1 and result = "," and n != 0
|
||||
)
|
||||
or
|
||||
i = 2 * c.getNumberOfParameters() and result = ")"
|
||||
}
|
||||
|
||||
private string paramsString(Callable c) {
|
||||
result = concat(int i | | paramsStringPart(c, i) order by i)
|
||||
}
|
||||
|
||||
private Element interpretElement0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
exists(RefType t | t = interpretType(namespace, type, subtypes) |
|
||||
exists(Member m |
|
||||
result = m and
|
||||
m.getDeclaringType() = t and
|
||||
m.hasName(name)
|
||||
|
|
||||
signature = "" or
|
||||
m.(Callable).getSignature() = any(string nameprefix) + signature or
|
||||
paramsString(m) = signature
|
||||
)
|
||||
or
|
||||
result = t and
|
||||
name = "" and
|
||||
signature = ""
|
||||
)
|
||||
}
|
||||
|
||||
private Element interpretElement(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, ext) and
|
||||
exists(Element e | e = interpretElement0(namespace, type, subtypes, name, signature) |
|
||||
ext = "" and result = e
|
||||
or
|
||||
ext = "Annotated" and result.(Annotatable).getAnAnnotation().getType() = e
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sourceElement(Element e, string output, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sinkElement(Element e, string input, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate summaryElement(Element e, string input, string output, string kind) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
private string inOutSpec() {
|
||||
sourceModel(_, _, _, _, _, _, result, _) or
|
||||
sinkModel(_, _, _, _, _, _, result, _) or
|
||||
summaryModel(_, _, _, _, _, _, result, _, _) or
|
||||
summaryModel(_, _, _, _, _, _, _, result, _)
|
||||
}
|
||||
|
||||
private predicate specSplit(string s, string c, int n) {
|
||||
inOutSpec() = s and s.splitAt(" of ", n) = c
|
||||
}
|
||||
|
||||
private predicate len(string s, int len) { len = 1 + max(int n | specSplit(s, _, n)) }
|
||||
|
||||
private string getLast(string s) {
|
||||
exists(int len |
|
||||
len(s, len) and
|
||||
specSplit(s, result, len - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate parseParam(string c, int n) {
|
||||
specSplit(_, c, _) and c.regexpCapture("Parameter\\[([-0-9]+)\\]", 1).toInt() = n
|
||||
}
|
||||
|
||||
private predicate parseArg(string c, int n) {
|
||||
specSplit(_, c, _) and c.regexpCapture("Argument\\[([-0-9]+)\\]", 1).toInt() = n
|
||||
}
|
||||
|
||||
private predicate inputNeedsReference(string c) {
|
||||
c = "Argument" or
|
||||
parseArg(c, _)
|
||||
}
|
||||
|
||||
private predicate outputNeedsReference(string c) {
|
||||
c = "Argument" or
|
||||
parseArg(c, _) or
|
||||
c = "ReturnValue"
|
||||
}
|
||||
|
||||
private predicate sourceElementRef(Top ref, string output, string kind) {
|
||||
exists(Element e |
|
||||
sourceElement(e, output, kind) and
|
||||
if outputNeedsReference(getLast(output)) then ref.(Call).getCallee() = e else ref = e
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sinkElementRef(Top ref, string input, string kind) {
|
||||
exists(Element e |
|
||||
sinkElement(e, input, kind) and
|
||||
if inputNeedsReference(getLast(input)) then ref.(Call).getCallee() = e else ref = e
|
||||
)
|
||||
}
|
||||
|
||||
private predicate summaryElementRef(Top ref, string input, string output, string kind) {
|
||||
exists(Element e |
|
||||
summaryElement(e, input, output, kind) and
|
||||
if inputNeedsReference(getLast(input)) then ref.(Call).getCallee() = e else ref = e
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TAstOrNode =
|
||||
TAst(Top t) or
|
||||
TNode(Node n)
|
||||
|
||||
private predicate interpretOutput(string output, int idx, Top ref, TAstOrNode node) {
|
||||
(
|
||||
sourceElementRef(ref, output, _) or
|
||||
summaryElementRef(ref, _, output, _)
|
||||
) and
|
||||
len(output, idx) and
|
||||
node = TAst(ref)
|
||||
or
|
||||
exists(Top mid, string c, Node n |
|
||||
interpretOutput(output, idx + 1, ref, TAst(mid)) and
|
||||
specSplit(output, c, idx) and
|
||||
node = TNode(n)
|
||||
|
|
||||
exists(int pos | n.(PostUpdateNode).getPreUpdateNode().(ArgumentNode).argumentOf(mid, pos) |
|
||||
c = "Argument" or parseArg(c, pos)
|
||||
)
|
||||
or
|
||||
exists(int pos | n.(ParameterNode).isParameterOf(mid, pos) |
|
||||
c = "Parameter" or parseParam(c, pos)
|
||||
)
|
||||
or
|
||||
(c = "Parameter" or c = "") and
|
||||
n.asParameter() = mid
|
||||
or
|
||||
c = "ReturnValue" and
|
||||
n.asExpr().(Call) = mid
|
||||
or
|
||||
c = "" and
|
||||
n.asExpr().(FieldRead).getField() = mid
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretInput(string input, int idx, Top ref, TAstOrNode node) {
|
||||
(
|
||||
sinkElementRef(ref, input, _) or
|
||||
summaryElementRef(ref, input, _, _)
|
||||
) and
|
||||
len(input, idx) and
|
||||
node = TAst(ref)
|
||||
or
|
||||
exists(Top mid, string c, Node n |
|
||||
interpretInput(input, idx + 1, ref, TAst(mid)) and
|
||||
specSplit(input, c, idx) and
|
||||
node = TNode(n)
|
||||
|
|
||||
exists(int pos | n.(ArgumentNode).argumentOf(mid, pos) | c = "Argument" or parseArg(c, pos))
|
||||
or
|
||||
exists(ReturnStmt ret |
|
||||
c = "ReturnValue" and
|
||||
n.asExpr() = ret.getResult() and
|
||||
mid = ret.getEnclosingCallable()
|
||||
)
|
||||
or
|
||||
exists(FieldWrite fw |
|
||||
c = "" and
|
||||
fw.getField() = mid and
|
||||
n.asExpr() = fw.getRHS()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
predicate sourceNode(Node node, string kind) {
|
||||
exists(Top ref, string output |
|
||||
sourceElementRef(ref, output, kind) and
|
||||
interpretOutput(output, 0, ref, TNode(node))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
predicate sinkNode(Node node, string kind) {
|
||||
exists(Top ref, string input |
|
||||
sinkElementRef(ref, input, kind) and
|
||||
interpretInput(input, 0, ref, TNode(node))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is specified as a flow step with the given kind
|
||||
* in a CSV flow model.
|
||||
*/
|
||||
predicate summaryStep(Node node1, Node node2, string kind) {
|
||||
exists(Top ref, string input, string output |
|
||||
summaryElementRef(ref, input, output, kind) and
|
||||
interpretInput(input, 0, ref, TNode(node1)) and
|
||||
interpretOutput(output, 0, ref, TNode(node2))
|
||||
)
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import semmle.code.java.frameworks.spring.SpringWebClient
|
||||
import semmle.code.java.frameworks.Guice
|
||||
import semmle.code.java.frameworks.struts.StrutsActions
|
||||
import semmle.code.java.frameworks.Thrift
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** A data flow source of remote user input. */
|
||||
abstract class RemoteFlowSource extends DataFlow::Node {
|
||||
@@ -31,6 +32,12 @@ abstract class RemoteFlowSource extends DataFlow::Node {
|
||||
abstract string getSourceType();
|
||||
}
|
||||
|
||||
private class ExternalRemoteFlowSource extends RemoteFlowSource {
|
||||
ExternalRemoteFlowSource() { sourceNode(this, "remote") }
|
||||
|
||||
override string getSourceType() { result = "external" }
|
||||
}
|
||||
|
||||
private class RemoteTaintedMethodAccessSource extends RemoteFlowSource {
|
||||
RemoteTaintedMethodAccessSource() {
|
||||
this.asExpr().(MethodAccess).getMethod() instanceof RemoteTaintedMethod
|
||||
|
||||
@@ -7,6 +7,7 @@ private import DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
import semmle.code.java.dataflow.InstanceAccess
|
||||
|
||||
cached
|
||||
@@ -405,6 +406,8 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
|
||||
or
|
||||
node2.asExpr().(AssignExpr).getSource() = node1.asExpr()
|
||||
or
|
||||
summaryStep(node1, node2, "value")
|
||||
or
|
||||
exists(MethodAccess ma, Method m |
|
||||
ma = node2.asExpr() and
|
||||
m = ma.getMethod() and
|
||||
|
||||
@@ -10,6 +10,7 @@ private import semmle.code.java.dataflow.internal.ContainerFlow
|
||||
private import semmle.code.java.frameworks.spring.SpringController
|
||||
private import semmle.code.java.frameworks.spring.SpringHttp
|
||||
private import semmle.code.java.frameworks.Networking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
import semmle.code.java.dataflow.FlowSteps
|
||||
|
||||
/**
|
||||
@@ -45,6 +46,8 @@ predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintUpdateStep(src.asExpr(),
|
||||
sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr())
|
||||
or
|
||||
summaryStep(src, sink, "taint")
|
||||
or
|
||||
exists(Argument arg |
|
||||
src.asExpr() = arg and
|
||||
arg.isVararg() and
|
||||
|
||||
62
java/ql/test/library-tests/dataflow/external-models/A.java
Normal file
62
java/ql/test/library-tests/dataflow/external-models/A.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package my.qltest;
|
||||
|
||||
public class A {
|
||||
void foo() {
|
||||
Object x;
|
||||
x = src1();
|
||||
x = src1("");
|
||||
|
||||
Sub sub = new Sub();
|
||||
x = sub.src2();
|
||||
x = sub.src3();
|
||||
|
||||
srcArg(x);
|
||||
|
||||
Handler h = srcparam1 -> { };
|
||||
|
||||
Handler h2 = new Handler() {
|
||||
@Override public void handle(Object srcparam2) { }
|
||||
};
|
||||
|
||||
x = taggedSrcMethod();
|
||||
x = taggedSrcField;
|
||||
|
||||
x = srcTwoArg("", "");
|
||||
}
|
||||
|
||||
@Tag
|
||||
void tagged1(Object taggedMethodParam) {
|
||||
}
|
||||
|
||||
void tagged2(@Tag Object taggedSrcParam) {
|
||||
}
|
||||
|
||||
Object src1() { return null; }
|
||||
|
||||
Object src1(String s) { return null; }
|
||||
|
||||
Object src2() { return null; }
|
||||
|
||||
Object src3() { return null; }
|
||||
|
||||
static class Sub extends A {
|
||||
// inherit src2
|
||||
@Override Object src3() { return null; }
|
||||
}
|
||||
|
||||
void srcArg(Object src) { }
|
||||
|
||||
interface Handler {
|
||||
void handle(Object src);
|
||||
}
|
||||
|
||||
@interface Tag { }
|
||||
|
||||
@Tag
|
||||
Object taggedSrcMethod() { return null; }
|
||||
|
||||
@Tag
|
||||
Object taggedSrcField;
|
||||
|
||||
Object srcTwoArg(String s1, String s2) { return null; }
|
||||
}
|
||||
35
java/ql/test/library-tests/dataflow/external-models/B.java
Normal file
35
java/ql/test/library-tests/dataflow/external-models/B.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package my.qltest;
|
||||
|
||||
public class B {
|
||||
void foo() {
|
||||
Object arg1 = new Object();
|
||||
sink1(arg1);
|
||||
|
||||
Object argToTagged = new Object();
|
||||
taggedSinkMethod(argToTagged);
|
||||
|
||||
Object fieldWrite = new Object();
|
||||
taggedField = fieldWrite;
|
||||
}
|
||||
|
||||
Object sinkMethod() {
|
||||
Object res = new Object();
|
||||
return res;
|
||||
}
|
||||
|
||||
@Tag
|
||||
Object taggedSinkMethod() {
|
||||
Object resTag = new Object();
|
||||
return resTag;
|
||||
}
|
||||
|
||||
void sink1(Object x) { }
|
||||
|
||||
@interface Tag { }
|
||||
|
||||
@Tag
|
||||
void taggedSinkMethod(Object x) { }
|
||||
|
||||
@Tag
|
||||
Object taggedField;
|
||||
}
|
||||
36
java/ql/test/library-tests/dataflow/external-models/C.java
Normal file
36
java/ql/test/library-tests/dataflow/external-models/C.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package my.qltest;
|
||||
|
||||
public class C {
|
||||
void foo() {
|
||||
Object arg1 = new Object();
|
||||
stepArgRes(arg1);
|
||||
|
||||
Object argIn1 = new Object();
|
||||
Object argOut1 = new Object();
|
||||
stepArgArg(argIn1, argOut1);
|
||||
Object argIn2 = new Object();
|
||||
Object argOut2 = new Object();
|
||||
stepArgArg(argIn2, argOut2);
|
||||
|
||||
Object arg2 = new Object();
|
||||
stepArgQual(arg2);
|
||||
Object arg3 = new Object();
|
||||
this.stepArgQual(arg3);
|
||||
|
||||
this.stepQualRes();
|
||||
stepQualRes();
|
||||
|
||||
Object argOut = new Object();
|
||||
stepQualArg(argOut);
|
||||
}
|
||||
|
||||
Object stepArgRes(Object x) { return null; }
|
||||
|
||||
void stepArgArg(Object in, Object out) { }
|
||||
|
||||
void stepArgQual(Object x) { }
|
||||
|
||||
Object stepQualRes() { return null; }
|
||||
|
||||
void stepQualArg(Object out) { }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
invalidModelRow
|
||||
#select
|
||||
| B.java:6:11:6:14 | arg1 | qltest |
|
||||
| B.java:9:5:9:33 | this <.method> | qltest-arg |
|
||||
| B.java:9:22:9:32 | argToTagged | qltest-arg |
|
||||
| B.java:12:19:12:28 | fieldWrite | qltest-nospec |
|
||||
| B.java:17:12:17:14 | res | qltest |
|
||||
| B.java:23:12:23:17 | resTag | qltest-retval |
|
||||
22
java/ql/test/library-tests/dataflow/external-models/sinks.ql
Normal file
22
java/ql/test/library-tests/dataflow/external-models/sinks.ql
Normal file
@@ -0,0 +1,22 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import CsvValidation
|
||||
|
||||
class SinkModelTest extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
//"package;type;overrides;name;signature;ext;spec;kind",
|
||||
"my.qltest;B;false;sink1;(Object);;Argument[0];qltest",
|
||||
"my.qltest;B;false;sinkMethod;();;ReturnValue;qltest",
|
||||
"my.qltest;B$Tag;false;;;Annotated;ReturnValue;qltest-retval",
|
||||
"my.qltest;B$Tag;false;;;Annotated;Argument;qltest-arg",
|
||||
"my.qltest;B$Tag;false;;;Annotated;;qltest-nospec"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::Node node, string kind
|
||||
where sinkNode(node, kind)
|
||||
select node, kind
|
||||
@@ -0,0 +1,24 @@
|
||||
invalidModelRow
|
||||
#select
|
||||
| A.java:6:9:6:14 | src1(...) | qltest |
|
||||
| A.java:6:9:6:14 | src1(...) | qltest-all-overloads |
|
||||
| A.java:7:9:7:16 | src1(...) | qltest |
|
||||
| A.java:7:9:7:16 | src1(...) | qltest-all-overloads |
|
||||
| A.java:7:9:7:16 | src1(...) | qltest-alt |
|
||||
| A.java:10:9:10:18 | src2(...) | qltest |
|
||||
| A.java:10:9:10:18 | src2(...) | qltest-w-subtypes |
|
||||
| A.java:11:9:11:18 | src3(...) | qltest-w-subtypes |
|
||||
| A.java:13:5:13:13 | this <.method> [post update] | qltest-argany |
|
||||
| A.java:13:12:13:12 | x [post update] | qltest-argany |
|
||||
| A.java:13:12:13:12 | x [post update] | qltest-argnum |
|
||||
| A.java:15:17:15:25 | srcparam1 | qltest-param-override |
|
||||
| A.java:18:36:18:51 | srcparam2 | qltest-param-override |
|
||||
| A.java:21:9:21:25 | taggedSrcMethod(...) | qltest-retval |
|
||||
| A.java:22:9:22:22 | taggedSrcField | qltest-nospec |
|
||||
| A.java:24:9:24:25 | srcTwoArg(...) | qltest-longsig |
|
||||
| A.java:24:9:24:25 | srcTwoArg(...) | qltest-shortsig |
|
||||
| A.java:28:8:28:14 | parameter this | qltest-param |
|
||||
| A.java:28:16:28:39 | taggedMethodParam | qltest-param |
|
||||
| A.java:31:16:31:41 | taggedSrcParam | qltest-nospec |
|
||||
| A.java:31:16:31:41 | taggedSrcParam | qltest-param |
|
||||
| A.java:56:10:56:24 | parameter this | qltest-param |
|
||||
33
java/ql/test/library-tests/dataflow/external-models/srcs.ql
Normal file
33
java/ql/test/library-tests/dataflow/external-models/srcs.ql
Normal file
@@ -0,0 +1,33 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import CsvValidation
|
||||
|
||||
class SourceModelTest extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
//"package;type;overrides;name;signature;ext;spec;kind",
|
||||
"my.qltest;A;false;src1;();;ReturnValue;qltest",
|
||||
"my.qltest;A;false;src1;(String);;ReturnValue;qltest",
|
||||
"my.qltest;A;false;src1;(java.lang.String);;ReturnValue;qltest-alt",
|
||||
"my.qltest;A;false;src1;;;ReturnValue;qltest-all-overloads",
|
||||
"my.qltest;A;false;src2;();;ReturnValue;qltest",
|
||||
"my.qltest;A;false;src3;();;ReturnValue;qltest",
|
||||
"my.qltest;A;true;src2;();;ReturnValue;qltest-w-subtypes",
|
||||
"my.qltest;A;true;src3;();;ReturnValue;qltest-w-subtypes",
|
||||
"my.qltest;A;false;srcArg;(Object);;Argument[0];qltest-argnum",
|
||||
"my.qltest;A;false;srcArg;(Object);;Argument;qltest-argany",
|
||||
"my.qltest;A$Handler;true;handle;(Object);;Parameter[0];qltest-param-override",
|
||||
"my.qltest;A$Tag;false;;;Annotated;ReturnValue;qltest-retval",
|
||||
"my.qltest;A$Tag;false;;;Annotated;Parameter;qltest-param",
|
||||
"my.qltest;A$Tag;false;;;Annotated;;qltest-nospec",
|
||||
"my.qltest;A;false;srcTwoArg;(String,String);;ReturnValue;qltest-shortsig",
|
||||
"my.qltest;A;false;srcTwoArg;(java.lang.String,java.lang.String);;ReturnValue;qltest-longsig"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::Node node, string kind
|
||||
where sourceNode(node, kind)
|
||||
select node, kind
|
||||
@@ -0,0 +1,10 @@
|
||||
invalidModelRow
|
||||
#select
|
||||
| C.java:6:16:6:19 | arg1 | C.java:6:5:6:20 | stepArgRes(...) | qltest |
|
||||
| C.java:10:16:10:21 | argIn1 | C.java:10:24:10:30 | argOut1 [post update] | qltest |
|
||||
| C.java:13:16:13:21 | argIn2 | C.java:13:24:13:30 | argOut2 [post update] | qltest |
|
||||
| C.java:16:17:16:20 | arg2 | C.java:16:5:16:21 | this <.method> [post update] | qltest |
|
||||
| C.java:18:22:18:25 | arg3 | C.java:18:5:18:8 | this [post update] | qltest |
|
||||
| C.java:20:5:20:8 | this | C.java:20:5:20:22 | stepQualRes(...) | qltest |
|
||||
| C.java:21:5:21:17 | this <.method> | C.java:21:5:21:17 | stepQualRes(...) | qltest |
|
||||
| C.java:24:5:24:23 | this <.method> | C.java:24:17:24:22 | argOut [post update] | qltest |
|
||||
22
java/ql/test/library-tests/dataflow/external-models/steps.ql
Normal file
22
java/ql/test/library-tests/dataflow/external-models/steps.ql
Normal file
@@ -0,0 +1,22 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import CsvValidation
|
||||
|
||||
class SummaryModelTest extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
//"package;type;overrides;name;signature;ext;inputspec;outputspec;kind",
|
||||
"my.qltest;C;false;stepArgRes;(Object);;Argument[0];ReturnValue;qltest",
|
||||
"my.qltest;C;false;stepArgArg;(Object,Object);;Argument[0];Argument[1];qltest",
|
||||
"my.qltest;C;false;stepArgQual;(Object);;Argument[0];Argument[-1];qltest",
|
||||
"my.qltest;C;false;stepQualRes;();;Argument[-1];ReturnValue;qltest",
|
||||
"my.qltest;C;false;stepQualArg;(Object);;Argument[-1];Argument[0];qltest"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::Node node1, DataFlow::Node node2, string kind
|
||||
where summaryStep(node1, node2, kind)
|
||||
select node1, node2, kind
|
||||
Reference in New Issue
Block a user