mirror of
https://github.com/github/codeql.git
synced 2026-05-04 13:15:21 +02:00
C#: CSV-based flow summaries
This commit is contained in:
428
csharp/ql/src/semmle/code/csharp/dataflow/ExternalFlow.qll
Normal file
428
csharp/ql/src/semmle/code/csharp/dataflow/ExternalFlow.qll
Normal file
@@ -0,0 +1,428 @@
|
||||
/**
|
||||
* 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 namespace.
|
||||
* 2. The `type` column selects a type within that namespace.
|
||||
* 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 "Attribute". The empty string has no
|
||||
* effect. "Attribute" applies if `name` and `signature` were left blank and
|
||||
* acts by selecting an element that is attributed with the attribute type
|
||||
* selected by the first 4 columns. This can be another member such as a field,
|
||||
* property, method, or 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. For sinks, an `input` can be either "",
|
||||
* "Argument[n]", "Argument[n1..n2]", or "ReturnValue":
|
||||
* - "": Selects a write to the selected element in case this is a field or property.
|
||||
* - "Argument[n]": Selects an argument in a call to the selected element.
|
||||
* The arguments are zero-indexed, and `-1` specifies the qualifier.
|
||||
* - "Argument[n1..n2]": Similar to "Argument[n]" but select any argument in
|
||||
* the given range. The range is inclusive at both ends.
|
||||
* - "ReturnValue": Selects a value being returned by the selected element.
|
||||
* This requires that the selected element is a method with a body.
|
||||
*
|
||||
* For sources, an `output` can be either "", "Argument[n]", "Argument[n1..n2]",
|
||||
* "Parameter", "Parameter[n]", "Parameter[n1..n2]", or "ReturnValue":
|
||||
* - "": Selects a read of a selected field, property, or parameter.
|
||||
* - "Argument[n]": 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.
|
||||
* The arguments are zero-indexed, and `-1` specifies the qualifier.
|
||||
* - "Argument[n1..n2]": Similar to "Argument[n]" but select any argument in
|
||||
* the given range. The range is inclusive at both ends.
|
||||
* - "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`).
|
||||
* - "Parameter[n1..n2]": Similar to "Parameter[n]" but selects any parameter
|
||||
* in the given range. The range is inclusive at both ends.
|
||||
* - "ReturnValue": Selects the return value of a call to the selected element.
|
||||
*
|
||||
* For summaries, `input` and `output` may be prefixed by one of the following,
|
||||
* separated by the "of" keyword:
|
||||
* - "Element": Selects an element in a collection.
|
||||
* - "Field[f]": Selects the contents of field `f`.
|
||||
* - "Property[p]": Selects the contents of property `p`.
|
||||
*
|
||||
* 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 csharp
|
||||
private import internal.DataFlowDispatch
|
||||
private import internal.DataFlowPrivate
|
||||
private import internal.DataFlowPublic
|
||||
private import internal.FlowSummaryImpl::Public
|
||||
private import internal.FlowSummaryImpl::Private::External
|
||||
private import internal.FlowSummaryImplSpecific
|
||||
|
||||
/**
|
||||
* A module importing the frameworks that provide external flow data,
|
||||
* ensuring that they are visible to the taint tracking / data flow library.
|
||||
*/
|
||||
private module Frameworks {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) { any(SourceModelCsv s).row(row) }
|
||||
|
||||
private predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||
|
||||
private predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantNamespace(string namespace) {
|
||||
sourceModel(namespace, _, _, _, _, _, _, _) or
|
||||
sinkModel(namespace, _, _, _, _, _, _, _) or
|
||||
summaryModel(namespace, _, _, _, _, _, _, _, _)
|
||||
}
|
||||
|
||||
private predicate namespaceLink(string shortns, string longns) {
|
||||
relevantNamespace(shortns) and
|
||||
relevantNamespace(longns) and
|
||||
longns.prefix(longns.indexOf(".")) = shortns
|
||||
}
|
||||
|
||||
private predicate canonicalNamespace(string namespace) {
|
||||
relevantNamespace(namespace) and not namespaceLink(_, namespace)
|
||||
}
|
||||
|
||||
private predicate canonicalNamespaceLink(string namespace, string subns) {
|
||||
canonicalNamespace(namespace) and
|
||||
(subns = namespace or namespaceLink(namespace, subns))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if CSV framework coverage of `namespace` is `n` api endpoints of the
|
||||
* kind `(kind, part)`.
|
||||
*/
|
||||
predicate modelCoverage(string namespace, int namespaces, string kind, string part, int n) {
|
||||
namespaces = strictcount(string subns | canonicalNamespaceLink(namespace, subns)) and
|
||||
(
|
||||
part = "source" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string output |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sourceModel(subns, type, subtypes, name, signature, ext, output, kind)
|
||||
)
|
||||
or
|
||||
part = "sink" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sinkModel(subns, type, subtypes, name, signature, ext, input, kind)
|
||||
)
|
||||
or
|
||||
part = "summary" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string output |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
summaryModel(subns, type, subtypes, name, signature, ext, input, output, 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("|Attribute") 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"
|
||||
|
|
||||
(
|
||||
invalidSpecComponent(input, part) and
|
||||
not part = "" and
|
||||
not (part = "Argument" and pred = "sink") and
|
||||
not parseArg(part, _)
|
||||
or
|
||||
specSplit(input, part, _) and
|
||||
parseParam(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"
|
||||
|
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
not (part = ["Argument", "Parameter"] and pred = "source") 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, _, _, _)
|
||||
}
|
||||
|
||||
private class UnboundValueOrRefType extends ValueOrRefType {
|
||||
UnboundValueOrRefType() { this.isUnboundDeclaration() }
|
||||
|
||||
UnboundValueOrRefType getASubTypeUnbound() { result = this.getASubType().getUnboundDeclaration() }
|
||||
}
|
||||
|
||||
bindingset[namespace, type, subtypes]
|
||||
private UnboundValueOrRefType interpretType(string namespace, string type, boolean subtypes) {
|
||||
exists(UnboundValueOrRefType t |
|
||||
t.hasQualifiedName(namespace, type) and
|
||||
if subtypes = true then result = t.getASubTypeUnbound*() else result = t
|
||||
)
|
||||
}
|
||||
|
||||
private string paramsStringPartA(Callable c, int i) {
|
||||
i = -1 and result = "("
|
||||
or
|
||||
exists(int n |
|
||||
exists(c.getParameter(n)) and
|
||||
i = 2 * n - 1 and
|
||||
result = "," and
|
||||
n != 0
|
||||
)
|
||||
or
|
||||
i = 2 * c.getNumberOfParameters() and result = ")"
|
||||
}
|
||||
|
||||
private string paramsStringPartB(Callable c, int i, boolean fullyQualified) {
|
||||
exists(int n, string p, Type t |
|
||||
t = c.getParameter(n).getType() and
|
||||
i = 2 * n and
|
||||
result = p
|
||||
|
|
||||
fullyQualified = true and p = t.getQualifiedName()
|
||||
or
|
||||
fullyQualified = false and p = t.toStringWithTypes()
|
||||
)
|
||||
}
|
||||
|
||||
private string paramsString(Callable c, boolean fullyQualified) {
|
||||
fullyQualified in [false, true] and
|
||||
result =
|
||||
strictconcat(int i, string s |
|
||||
s in [paramsStringPartA(c, i), paramsStringPartB(c, i, fullyQualified)]
|
||||
|
|
||||
s order by i
|
||||
)
|
||||
}
|
||||
|
||||
private Element interpretElement0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
exists(UnboundValueOrRefType t | t = interpretType(namespace, type, subtypes) |
|
||||
exists(Member m |
|
||||
result = m and
|
||||
m.getDeclaringType() = t and
|
||||
m.hasName(name)
|
||||
|
|
||||
signature = ""
|
||||
or
|
||||
paramsString(m, _) = signature
|
||||
)
|
||||
or
|
||||
result = t and
|
||||
name = "" and
|
||||
signature = ""
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
|
||||
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 = "Attribute" and result.(Attributable).getAnAttribute().getType() = e
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sourceNode(Node node, string kind) {
|
||||
exists(InterpretNode n | isSourceNode(n, kind) and n.asNode() = node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sinkNode(Node node, string kind) {
|
||||
exists(InterpretNode n | isSinkNode(n, kind) and n.asNode() = node)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -10,6 +10,7 @@ private import DataFlowPublic
|
||||
private import FlowSummaryImpl::Private
|
||||
private import FlowSummaryImpl::Public
|
||||
private import semmle.code.csharp.Unification
|
||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
|
||||
/** Holds is `i` is a valid parameter position. */
|
||||
predicate parameterPosition(int i) { i in [-1 .. any(Parameter p).getPosition()] }
|
||||
@@ -82,7 +83,40 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
|
||||
* Holds if an external flow summary exists for `c` with input specification
|
||||
* `input`, output specification `output`, and kind `kind`.
|
||||
*/
|
||||
predicate summaryElement(DataFlowCallable c, string input, string output, string kind) { none() }
|
||||
predicate summaryElement(DataFlowCallable c, 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
|
||||
c = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external source specification exists for `e` with output specification
|
||||
* `output` and kind `kind`.
|
||||
*/
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external sink specification exists for `n` with input specification
|
||||
* `input` and kind `kind`.
|
||||
*/
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the summary component for specification component `c`, if any. */
|
||||
bindingset[c]
|
||||
@@ -102,18 +136,6 @@ SummaryComponent interpretComponentSpecific(string c) {
|
||||
|
||||
class SourceOrSinkElement = Element;
|
||||
|
||||
/**
|
||||
* Holds if an external source specification exists for `e` with output specification
|
||||
* `output` and kind `kind`.
|
||||
*/
|
||||
predicate sourceElement(Element e, string output, string kind) { none() }
|
||||
|
||||
/**
|
||||
* Holds if an external sink specification exists for `n` with input specification
|
||||
* `input` and kind `kind`.
|
||||
*/
|
||||
predicate sinkElement(Element e, string input, string kind) { none() }
|
||||
|
||||
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
|
||||
NormalReturnKind getReturnValueKind() { any() }
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
namespace 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;
|
||||
}
|
||||
|
||||
[SinkAttribute]
|
||||
object TaggedSinkMethod()
|
||||
{
|
||||
object resTag = new object();
|
||||
return resTag;
|
||||
}
|
||||
|
||||
void Sink1(object x) { }
|
||||
|
||||
[SinkAttribute]
|
||||
void TaggedSinkMethod(object x) { }
|
||||
|
||||
[SinkAttribute]
|
||||
object TaggedField;
|
||||
}
|
||||
|
||||
class SinkAttribute : System.Attribute { }
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
namespace 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);
|
||||
|
||||
x = TaggedSrcMethod();
|
||||
x = TaggedSrcField;
|
||||
|
||||
x = SrcTwoArg("", "");
|
||||
}
|
||||
|
||||
[SourceAttribute()]
|
||||
void Tagged1(object taggedMethodParam)
|
||||
{
|
||||
}
|
||||
|
||||
void Tagged2([SourceAttribute()] object taggedSrcParam)
|
||||
{
|
||||
}
|
||||
|
||||
object Src1() { return null; }
|
||||
|
||||
object Src1(string s) { return null; }
|
||||
|
||||
object Src2() { return null; }
|
||||
|
||||
public virtual object Src3() { return null; }
|
||||
|
||||
public virtual void SrcParam(object p) { }
|
||||
|
||||
class Sub : A
|
||||
{
|
||||
// inherit src2
|
||||
public override object Src3() { return null; }
|
||||
|
||||
public override void SrcParam(object p) { }
|
||||
}
|
||||
|
||||
void SrcArg(object src) { }
|
||||
|
||||
[SourceAttribute()]
|
||||
object TaggedSrcMethod() { return null; }
|
||||
|
||||
[SourceAttribute()]
|
||||
object TaggedSrcField;
|
||||
|
||||
object SrcTwoArg(string s1, string s2) { return null; }
|
||||
}
|
||||
|
||||
class SourceAttribute : System.Attribute { }
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
namespace 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);
|
||||
|
||||
this.StepFieldGetter();
|
||||
|
||||
this.StepFieldSetter(0);
|
||||
|
||||
this.StepPropertyGetter();
|
||||
|
||||
this.StepPropertySetter(0);
|
||||
|
||||
this.StepElementGetter();
|
||||
|
||||
this.StepElementSetter(0);
|
||||
|
||||
var gen = new Generic<int>();
|
||||
gen.StepGeneric(0);
|
||||
gen.StepGeneric2(false);
|
||||
}
|
||||
|
||||
object StepArgRes(object x) { return null; }
|
||||
|
||||
void StepArgArg(object @in, object @out) { }
|
||||
|
||||
void StepArgQual(object x) { }
|
||||
|
||||
object StepQualRes() { return null; }
|
||||
|
||||
void StepQualArg(object @out) { }
|
||||
|
||||
int Field;
|
||||
|
||||
int StepFieldGetter() => throw null;
|
||||
|
||||
void StepFieldSetter(int value) => throw null;
|
||||
|
||||
int Property { get; set; }
|
||||
|
||||
int StepPropertyGetter() => throw null;
|
||||
|
||||
void StepPropertySetter(int value) => throw null;
|
||||
|
||||
int StepElementGetter() => throw null;
|
||||
|
||||
void StepElementSetter(int value) => throw null;
|
||||
|
||||
class Generic<T>
|
||||
{
|
||||
public T StepGeneric(T t) => throw null;
|
||||
|
||||
public T StepGeneric2<S>(S s) => throw null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
invalidModelRow
|
||||
#select
|
||||
| Sinks.cs:8:19:8:22 | access to local variable arg1 | qltest |
|
||||
| Sinks.cs:11:13:11:41 | this access | qltest-arg |
|
||||
| Sinks.cs:11:30:11:40 | access to local variable argToTagged | qltest-arg |
|
||||
| Sinks.cs:14:27:14:36 | access to local variable fieldWrite | qltest-nospec |
|
||||
| Sinks.cs:20:20:20:22 | access to local variable res | qltest |
|
||||
| Sinks.cs:27:20:27:25 | access to local variable resTag | qltest-retval |
|
||||
@@ -0,0 +1,23 @@
|
||||
import csharp
|
||||
import DataFlow
|
||||
import semmle.code.csharp.dataflow.ExternalFlow
|
||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import CsvValidation
|
||||
|
||||
class SinkModelTest extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
//"namespace;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;SinkAttribute;false;;;Attribute;ReturnValue;qltest-retval",
|
||||
"My.Qltest;SinkAttribute;false;;;Attribute;Argument;qltest-arg",
|
||||
"My.Qltest;SinkAttribute;false;;;Attribute;;qltest-nospec"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::Node node, string kind
|
||||
where sinkNode(node, kind)
|
||||
select node, kind
|
||||
@@ -0,0 +1,24 @@
|
||||
invalidModelRow
|
||||
#select
|
||||
| Sources.cs:8:17:8:22 | call to method Src1 | qltest |
|
||||
| Sources.cs:8:17:8:22 | call to method Src1 | qltest-all-overloads |
|
||||
| Sources.cs:9:17:9:24 | call to method Src1 | qltest |
|
||||
| Sources.cs:9:17:9:24 | call to method Src1 | qltest-all-overloads |
|
||||
| Sources.cs:9:17:9:24 | call to method Src1 | qltest-alt |
|
||||
| Sources.cs:12:17:12:26 | call to method Src2 | qltest |
|
||||
| Sources.cs:12:17:12:26 | call to method Src2 | qltest-w-subtypes |
|
||||
| Sources.cs:13:17:13:26 | call to method Src3 | qltest-w-subtypes |
|
||||
| Sources.cs:15:13:15:21 | [post] this access | qltest-argany |
|
||||
| Sources.cs:15:20:15:20 | [post] access to local variable x | qltest-argany |
|
||||
| Sources.cs:15:20:15:20 | [post] access to local variable x | qltest-argnum |
|
||||
| Sources.cs:17:17:17:33 | call to method TaggedSrcMethod | qltest-retval |
|
||||
| Sources.cs:18:17:18:30 | access to field TaggedSrcField | qltest-nospec |
|
||||
| Sources.cs:20:17:20:33 | call to method SrcTwoArg | qltest-longsig |
|
||||
| Sources.cs:20:17:20:33 | call to method SrcTwoArg | qltest-shortsig |
|
||||
| Sources.cs:24:14:24:20 | this | qltest-param |
|
||||
| Sources.cs:24:29:24:45 | taggedMethodParam | qltest-param |
|
||||
| Sources.cs:28:49:28:62 | taggedSrcParam | qltest-nospec |
|
||||
| Sources.cs:28:49:28:62 | taggedSrcParam | qltest-param |
|
||||
| Sources.cs:40:45:40:45 | p | qltest-param-override |
|
||||
| Sources.cs:47:50:47:50 | p | qltest-param-override |
|
||||
| Sources.cs:53:16:53:30 | this | qltest-param |
|
||||
@@ -0,0 +1,34 @@
|
||||
import csharp
|
||||
import DataFlow
|
||||
import semmle.code.csharp.dataflow.ExternalFlow
|
||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import CsvValidation
|
||||
|
||||
class SourceModelTest extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
//"namespace;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;(System.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;true;SrcParam;(object);;Parameter[0];qltest-param-override",
|
||||
"My.Qltest;SourceAttribute;false;;;Attribute;ReturnValue;qltest-retval",
|
||||
"My.Qltest;SourceAttribute;false;;;Attribute;Parameter;qltest-param",
|
||||
"My.Qltest;SourceAttribute;false;;;Attribute;;qltest-nospec",
|
||||
"My.Qltest;A;false;SrcTwoArg;(string,string);;ReturnValue;qltest-shortsig",
|
||||
"My.Qltest;A;false;SrcTwoArg;(System.String,System.String);;ReturnValue;qltest-longsig"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::Node node, string kind
|
||||
where sourceNode(node, kind)
|
||||
select node, kind
|
||||
@@ -0,0 +1,20 @@
|
||||
invalidModelRow
|
||||
summaryThroughStep
|
||||
| Steps.cs:8:24:8:27 | access to local variable arg1 | Steps.cs:8:13:8:28 | call to method StepArgRes | false |
|
||||
| Steps.cs:12:24:12:29 | access to local variable argIn1 | Steps.cs:12:32:12:38 | [post] access to local variable argOut1 | false |
|
||||
| Steps.cs:15:24:15:29 | access to local variable argIn2 | Steps.cs:15:32:15:38 | [post] access to local variable argOut2 | false |
|
||||
| Steps.cs:18:25:18:28 | access to local variable arg2 | Steps.cs:18:13:18:29 | [post] this access | false |
|
||||
| Steps.cs:20:30:20:33 | access to local variable arg3 | Steps.cs:20:13:20:16 | [post] this access | false |
|
||||
| Steps.cs:22:13:22:16 | this access | Steps.cs:22:13:22:30 | call to method StepQualRes | false |
|
||||
| Steps.cs:23:13:23:25 | this access | Steps.cs:23:13:23:25 | call to method StepQualRes | false |
|
||||
| Steps.cs:26:13:26:31 | this access | Steps.cs:26:25:26:30 | [post] access to local variable argOut | false |
|
||||
| Steps.cs:41:29:41:29 | 0 | Steps.cs:41:13:41:30 | call to method StepGeneric | true |
|
||||
| Steps.cs:42:30:42:34 | false | Steps.cs:42:13:42:35 | call to method StepGeneric2 | true |
|
||||
summaryGetterStep
|
||||
| Steps.cs:28:13:28:16 | this access | Steps.cs:28:13:28:34 | call to method StepFieldGetter | Steps.cs:55:13:55:17 | field Field |
|
||||
| Steps.cs:32:13:32:16 | this access | Steps.cs:32:13:32:37 | call to method StepPropertyGetter | Steps.cs:61:13:61:20 | property Property |
|
||||
| Steps.cs:36:13:36:16 | this access | Steps.cs:36:13:36:36 | call to method StepElementGetter | file://:0:0:0:0 | element |
|
||||
summarySetterStep
|
||||
| Steps.cs:30:34:30:34 | 0 | Steps.cs:30:13:30:16 | [post] this access | Steps.cs:55:13:55:17 | field Field |
|
||||
| Steps.cs:34:37:34:37 | 0 | Steps.cs:34:13:34:16 | [post] this access | Steps.cs:61:13:61:20 | property Property |
|
||||
| Steps.cs:38:36:38:36 | 0 | Steps.cs:38:13:38:16 | [post] this access | file://:0:0:0:0 | element |
|
||||
@@ -0,0 +1,41 @@
|
||||
import csharp
|
||||
import DataFlow
|
||||
import semmle.code.csharp.dataflow.ExternalFlow
|
||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import CsvValidation
|
||||
|
||||
class SummaryModelTest extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
//"namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind",
|
||||
"My.Qltest;C;false;StepArgRes;(object);;Argument[0];ReturnValue;taint",
|
||||
"My.Qltest;C;false;StepArgArg;(object,object);;Argument[0];Argument[1];taint",
|
||||
"My.Qltest;C;false;StepArgQual;(object);;Argument[0];Argument[-1];taint",
|
||||
"My.Qltest;C;false;StepQualRes;();;Argument[-1];ReturnValue;taint",
|
||||
"My.Qltest;C;false;StepQualArg;(object);;Argument[-1];Argument[0];taint",
|
||||
"My.Qltest;C;false;StepFieldGetter;();;Field[My.Qltest.C.Field] of Argument[-1];ReturnValue;value",
|
||||
"My.Qltest;C;false;StepFieldSetter;(int);;Argument[0];Field[My.Qltest.C.Field] of Argument[-1];value",
|
||||
"My.Qltest;C;false;StepPropertyGetter;();;Property[My.Qltest.C.Property] of Argument[-1];ReturnValue;value",
|
||||
"My.Qltest;C;false;StepPropertySetter;(int);;Argument[0];Property[My.Qltest.C.Property] of Argument[-1];value",
|
||||
"My.Qltest;C;false;StepElementGetter;();;Element of Argument[-1];ReturnValue;value",
|
||||
"My.Qltest;C;false;StepElementSetter;(int);;Argument[0];Element of Argument[-1];value",
|
||||
"My.Qltest.C;Generic<>;false;StepGeneric;(T);;Argument[0];ReturnValue;value",
|
||||
"My.Qltest.C;Generic<>;false;StepGeneric2;(S);;Argument[0];ReturnValue;value"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
query predicate summaryThroughStep(
|
||||
DataFlow::Node node1, DataFlow::Node node2, boolean preservesValue
|
||||
) {
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStep(node1, node2, preservesValue)
|
||||
}
|
||||
|
||||
query predicate summaryGetterStep(DataFlow::Node arg, DataFlow::Node out, Content c) {
|
||||
FlowSummaryImpl::Private::Steps::summaryGetterStep(arg, c, out)
|
||||
}
|
||||
|
||||
query predicate summarySetterStep(DataFlow::Node arg, DataFlow::Node out, Content c) {
|
||||
FlowSummaryImpl::Private::Steps::summarySetterStep(arg, c, out)
|
||||
}
|
||||
Reference in New Issue
Block a user