mirror of
https://github.com/github/codeql.git
synced 2025-12-17 09:13:20 +01:00
Merge pull request #9867 from michaelnebel/csharp/nosummary
C#: Negative summaries (ie. no flow through)
This commit is contained in:
@@ -5,12 +5,13 @@
|
||||
*
|
||||
* The CSV specification has the following columns:
|
||||
* - Sources:
|
||||
* `namespace; type; subtypes; name; signature; ext; output; kind`
|
||||
* `namespace; type; subtypes; name; signature; ext; output; kind; provenance`
|
||||
* - Sinks:
|
||||
* `namespace; type; subtypes; name; signature; ext; input; kind`
|
||||
* `namespace; type; subtypes; name; signature; ext; input; kind; provenance`
|
||||
* - Summaries:
|
||||
* `namespace; type; subtypes; name; signature; ext; input; output; kind`
|
||||
*
|
||||
* `namespace; type; subtypes; name; signature; ext; input; output; kind; provenance`
|
||||
* - Negative Summaries:
|
||||
* `namespace; type; name; signature; provenance`
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
* 1. The `namespace` column selects a namespace.
|
||||
@@ -163,11 +164,27 @@ class SummaryModelCsv extends Unit {
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
private predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
|
||||
/**
|
||||
* A unit class for adding negative summary model rows.
|
||||
*
|
||||
* Extend this class to add additional flow summary definitions.
|
||||
*/
|
||||
class NegativeSummaryModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a negative summary definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
private predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||
/** Holds if `row` is a source model. */
|
||||
predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
|
||||
|
||||
private predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
/** Holds if `row` is a sink model. */
|
||||
predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||
|
||||
/** Holds if `row` is a summary model. */
|
||||
predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
|
||||
/** Holds if `row` is a negative summary model. */
|
||||
predicate negativeSummaryModel(string row) { any(NegativeSummaryModelCsv s).row(row) }
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
@@ -230,6 +247,20 @@ predicate summaryModel(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists indicating there is no flow for the given parameters. */
|
||||
predicate negativeSummaryModel(
|
||||
string namespace, string type, string name, string signature, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
negativeSummaryModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = name and
|
||||
row.splitAt(";", 3) = signature and
|
||||
row.splitAt(";", 4) = provenance
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantNamespace(string namespace) {
|
||||
sourceModel(namespace, _, _, _, _, _, _, _, _) or
|
||||
sinkModel(namespace, _, _, _, _, _, _, _, _) or
|
||||
@@ -286,38 +317,7 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
|
||||
|
||||
/** 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,
|
||||
string provenance
|
||||
|
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _, provenance) and pred = "source"
|
||||
or
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _, provenance) and pred = "sink"
|
||||
or
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _, provenance) 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 member 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
|
||||
not provenance = ["manual", "generated"] and
|
||||
msg = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
private string getInvalidModelInput() {
|
||||
exists(string pred, AccessPath input, string part |
|
||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||
or
|
||||
@@ -332,9 +332,11 @@ module CsvValidation {
|
||||
part = input.getToken(_) and
|
||||
parseParam(part, _)
|
||||
) and
|
||||
msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
result = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
}
|
||||
|
||||
private string getInvalidModelOutput() {
|
||||
exists(string pred, string output, string part |
|
||||
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
||||
or
|
||||
@@ -343,58 +345,123 @@ module CsvValidation {
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
not (part = ["Argument", "Parameter"] and pred = "source") and
|
||||
msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
result = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
exists(string pred, string row, int expect |
|
||||
sourceModel(row) and expect = 9 and pred = "source"
|
||||
or
|
||||
sinkModel(row) and expect = 9 and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and expect = 10 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 +
|
||||
" in " + row + "."
|
||||
)
|
||||
or
|
||||
exists(string b |
|
||||
b = row.splitAt(";", 2) and
|
||||
not b = ["true", "false"] and
|
||||
msg = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
||||
)
|
||||
)
|
||||
or
|
||||
}
|
||||
|
||||
private string getInvalidModelKind() {
|
||||
exists(string row, string kind | summaryModel(row) |
|
||||
kind = row.splitAt(";", 8) and
|
||||
not kind = ["taint", "value"] and
|
||||
msg = "Invalid kind \"" + kind + "\" in summary model."
|
||||
result = "Invalid kind \"" + kind + "\" in summary model."
|
||||
)
|
||||
or
|
||||
exists(string row, string kind | sinkModel(row) |
|
||||
kind = row.splitAt(";", 7) and
|
||||
not kind = ["code", "sql", "xss", "remote", "html"] and
|
||||
not kind.matches("encryption-%") and
|
||||
msg = "Invalid kind \"" + kind + "\" in sink model."
|
||||
result = "Invalid kind \"" + kind + "\" in sink model."
|
||||
)
|
||||
or
|
||||
exists(string row, string kind | sourceModel(row) |
|
||||
kind = row.splitAt(";", 7) and
|
||||
not kind = ["local", "file"] and
|
||||
msg = "Invalid kind \"" + kind + "\" in source model."
|
||||
result = "Invalid kind \"" + kind + "\" in source model."
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelSubtype() {
|
||||
exists(string pred, string row |
|
||||
sourceModel(row) and pred = "source"
|
||||
or
|
||||
sinkModel(row) and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and pred = "summary"
|
||||
|
|
||||
exists(string b |
|
||||
b = row.splitAt(";", 2) and
|
||||
not b = ["true", "false"] and
|
||||
result = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelColumnCount() {
|
||||
exists(string pred, string row, int expect |
|
||||
sourceModel(row) and expect = 9 and pred = "source"
|
||||
or
|
||||
sinkModel(row) and expect = 9 and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and expect = 10 and pred = "summary"
|
||||
or
|
||||
negativeSummaryModel(row) and expect = 5 and pred = "negative summary"
|
||||
|
|
||||
exists(int cols |
|
||||
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
||||
cols != expect and
|
||||
result =
|
||||
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
|
||||
" in " + row + "."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelSignature() {
|
||||
exists(
|
||||
string pred, string namespace, string type, string name, string signature, string ext,
|
||||
string provenance
|
||||
|
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _, provenance) and pred = "source"
|
||||
or
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _, provenance) and pred = "sink"
|
||||
or
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _, provenance) and
|
||||
pred = "summary"
|
||||
or
|
||||
negativeSummaryModel(namespace, type, name, signature, provenance) and
|
||||
ext = "" and
|
||||
pred = "negative summary"
|
||||
|
|
||||
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
|
||||
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
or
|
||||
not type.regexpMatch("[a-zA-Z0-9_<>,\\+]+") and
|
||||
result = "Dubious type \"" + type + "\" in " + pred + " model."
|
||||
or
|
||||
not name.regexpMatch("[a-zA-Z0-9_<>,]*") and
|
||||
result = "Dubious member name \"" + name + "\" in " + pred + " model."
|
||||
or
|
||||
not signature.regexpMatch("|\\([a-zA-Z0-9_<>\\.\\+\\*,\\[\\]]*\\)") and
|
||||
result = "Dubious signature \"" + signature + "\" in " + pred + " model."
|
||||
or
|
||||
not ext.regexpMatch("|Attribute") and
|
||||
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
|
||||
or
|
||||
not provenance = ["manual", "generated"] and
|
||||
result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
||||
query predicate invalidModelRow(string msg) {
|
||||
msg =
|
||||
[
|
||||
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
|
||||
getInvalidModelSubtype(), getInvalidModelColumnCount(), getInvalidModelKind()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _)
|
||||
or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _)
|
||||
or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
or
|
||||
negativeSummaryModel(namespace, type, name, signature, _) and ext = "" and subtypes = false
|
||||
}
|
||||
|
||||
private predicate elementSpec(
|
||||
@@ -508,7 +575,7 @@ private Element interpretElement0(
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
|
||||
/** Gets the source/sink/summary/negativesummary element corresponding to the supplied parameters. */
|
||||
Element interpretElement(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
|
||||
@@ -173,7 +173,7 @@ module Ssa {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds is this SSA definition is live at the end of basic block `bb`.
|
||||
* Holds if this SSA definition is live at the end of basic block `bb`.
|
||||
* That is, this definition reaches the end of basic block `bb`, at which
|
||||
* point it is still live, without crossing another SSA definition of the
|
||||
* same source variable.
|
||||
|
||||
@@ -2129,18 +2129,37 @@ module Csv {
|
||||
if isBaseCallableOrPrototype(c) then result = "true" else result = "false"
|
||||
}
|
||||
|
||||
/** Computes the first 6 columns for CSV rows of `c`. */
|
||||
private predicate partialModel(
|
||||
DotNet::Callable c, string namespace, string type, string name, string parameters
|
||||
) {
|
||||
c.getDeclaringType().hasQualifiedName(namespace, type) and
|
||||
c.hasQualifiedName(_, name) and
|
||||
parameters = "(" + parameterQualifiedTypeNamesToString(c) + ")"
|
||||
}
|
||||
|
||||
/** Computes the first 6 columns for positive CSV rows of `c`. */
|
||||
string asPartialModel(DotNet::Callable c) {
|
||||
exists(string namespace, string type, string name |
|
||||
c.getDeclaringType().hasQualifiedName(namespace, type) and
|
||||
c.hasQualifiedName(_, name) and
|
||||
exists(string namespace, string type, string name, string parameters |
|
||||
partialModel(c, namespace, type, name, parameters) and
|
||||
result =
|
||||
namespace + ";" //
|
||||
+ type + ";" //
|
||||
+ getCallableOverride(c) + ";" //
|
||||
+ name + ";" //
|
||||
+ "(" + parameterQualifiedTypeNamesToString(c) + ")" + ";" //
|
||||
+ parameters + ";" //
|
||||
+ /* ext + */ ";" //
|
||||
)
|
||||
}
|
||||
|
||||
/** Computes the first 4 columns for negative CSV rows of `c`. */
|
||||
string asPartialNegativeModel(DotNet::Callable c) {
|
||||
exists(string namespace, string type, string name, string parameters |
|
||||
partialModel(c, namespace, type, name, parameters) and
|
||||
result =
|
||||
namespace + ";" //
|
||||
+ type + ";" //
|
||||
+ name + ";" //
|
||||
+ parameters + ";" //
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +240,16 @@ module Public {
|
||||
*/
|
||||
predicate isAutoGenerated() { none() }
|
||||
}
|
||||
|
||||
/** A callable with a flow summary stating there is no flow via the callable. */
|
||||
class NegativeSummarizedCallable extends SummarizedCallableBase {
|
||||
NegativeSummarizedCallable() { negativeSummaryElement(this, _) }
|
||||
|
||||
/**
|
||||
* Holds if the negative summary is auto generated.
|
||||
*/
|
||||
predicate isAutoGenerated() { negativeSummaryElement(this, true) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1094,7 +1104,7 @@ module Private {
|
||||
|
||||
/** Provides a query predicate for outputting a set of relevant flow summaries. */
|
||||
module TestOutput {
|
||||
/** A flow summary to include in the `summary/3` query predicate. */
|
||||
/** A flow summary to include in the `summary/1` query predicate. */
|
||||
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/1`. */
|
||||
abstract string getCallableCsv();
|
||||
@@ -1109,6 +1119,14 @@ module Private {
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
/** A flow summary to include in the `negativeSummary/1` query predicate. */
|
||||
abstract class RelevantNegativeSummarizedCallable instanceof NegativeSummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/1`. */
|
||||
abstract string getCallableCsv();
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
/** Render the kind in the format used in flow summaries. */
|
||||
private string renderKind(boolean preservesValue) {
|
||||
preservesValue = true and result = "value"
|
||||
@@ -1116,8 +1134,12 @@ module Private {
|
||||
preservesValue = false and result = "taint"
|
||||
}
|
||||
|
||||
private string renderProvenance(RelevantSummarizedCallable c) {
|
||||
if c.(SummarizedCallable).isAutoGenerated() then result = "generated" else result = "manual"
|
||||
private string renderProvenance(SummarizedCallable c) {
|
||||
if c.isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
private string renderProvenanceNegative(NegativeSummarizedCallable c) {
|
||||
if c.isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1132,8 +1154,23 @@ module Private {
|
||||
|
|
||||
c.relevantSummary(input, output, preservesValue) and
|
||||
csv =
|
||||
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
|
||||
";" + renderKind(preservesValue) + ";" + renderProvenance(c)
|
||||
c.getCallableCsv() // Callable information
|
||||
+ getComponentStackCsv(input) + ";" // input
|
||||
+ getComponentStackCsv(output) + ";" // output
|
||||
+ renderKind(preservesValue) + ";" // kind
|
||||
+ renderProvenance(c) // provenance
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary `csv` exists (semi-colon separated format). Used for testing purposes.
|
||||
* The syntax is: "namespace;type;name;signature;provenance"",
|
||||
*/
|
||||
query predicate negativeSummary(string csv) {
|
||||
exists(RelevantNegativeSummarizedCallable c |
|
||||
csv =
|
||||
c.getCallableCsv() // Callable information
|
||||
+ renderProvenanceNegative(c) // provenance
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,18 @@ predicate summaryElement(Callable c, string input, string output, string kind, b
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary exists for `c`, which means that there is no
|
||||
* flow through `c`. The flag `generated` states whether the summary is autogenerated.
|
||||
*/
|
||||
predicate negativeSummaryElement(Callable c, boolean generated) {
|
||||
exists(string namespace, string type, string name, string signature, string provenance |
|
||||
negativeSummaryModel(namespace, type, name, signature, provenance) and
|
||||
generated = isGenerated(provenance) and
|
||||
c = interpretElement(namespace, type, false, name, signature, "")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external source specification exists for `e` with output specification
|
||||
* `output`, kind `kind`, and a flag `generated` stating whether the source specification is
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/** Provides modules for importing negative summaries. */
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
private import semmle.code.csharp.frameworks.GeneratedNegative
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* A module importing all generated negative Models as Data models.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
private module GeneratedFrameworks {
|
||||
private import generated.dotnet.NegativeRuntime
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,11 +8,14 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.dataflow.internal.NegativeSummary
|
||||
private import ExternalApi
|
||||
|
||||
private predicate getRelevantUsages(ExternalApi api, int usages) {
|
||||
not api.isUninteresting() and
|
||||
not api.isSupported() and
|
||||
not api instanceof FlowSummaryImpl::Public::NegativeSummarizedCallable and
|
||||
usages = strictcount(DispatchCall c | c = api.getACall())
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
private import internal.CaptureModels
|
||||
private import internal.CaptureFlow
|
||||
private import internal.CaptureSummaryFlow
|
||||
|
||||
from TargetApi api, string flow
|
||||
where flow = captureFlow(api) and hasSummary(api, false)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @name Capture negative summary models.
|
||||
* @description Finds negative summary models to be used by other queries.
|
||||
* @kind diagnostic
|
||||
* @id cs/utils/model-generator/negative-summary-models
|
||||
* @tags model-generator
|
||||
*/
|
||||
|
||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
private import internal.CaptureModels
|
||||
private import internal.CaptureSummaryFlow
|
||||
|
||||
from TargetApi api, string noflow
|
||||
where noflow = captureNoFlow(api) and not hasSummary(api, false)
|
||||
select noflow order by noflow
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
private import internal.CaptureModels
|
||||
private import internal.CaptureFlow
|
||||
private import internal.CaptureSummaryFlow
|
||||
|
||||
from TargetApi api, string flow
|
||||
where flow = captureFlow(api) and not hasSummary(api, false)
|
||||
|
||||
@@ -48,6 +48,8 @@ private string asSummaryModel(TargetApi api, string input, string output, string
|
||||
+ "generated"
|
||||
}
|
||||
|
||||
string asNegativeSummaryModel(TargetApi api) { result = asPartialNegativeModel(api) + "generated" }
|
||||
|
||||
/**
|
||||
* Gets the value summary model for `api` with `input` and `output`.
|
||||
*/
|
||||
|
||||
@@ -36,7 +36,8 @@ private predicate isRelevantForModels(CS::Callable api) {
|
||||
api.getDeclaringType().getNamespace().getQualifiedName() != "" and
|
||||
not api instanceof CS::ConversionOperator and
|
||||
not api instanceof Util::MainMethod and
|
||||
not isHigherOrder(api)
|
||||
not isHigherOrder(api) and
|
||||
not api instanceof CS::Destructor
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,6 +56,8 @@ class TargetApiSpecific extends DotNet::Callable {
|
||||
|
||||
predicate asPartialModel = DataFlowPrivate::Csv::asPartialModel/1;
|
||||
|
||||
predicate asPartialNegativeModel = DataFlowPrivate::Csv::asPartialNegativeModel/1;
|
||||
|
||||
/**
|
||||
* Holds for type `t` for fields that are relevant as an intermediate
|
||||
* read or write step in the data flow analysis.
|
||||
|
||||
@@ -79,3 +79,12 @@ string captureFlow(TargetApi api) {
|
||||
result = captureQualifierFlow(api) or
|
||||
result = captureThroughFlow(api)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the negative summary for `api`, if any.
|
||||
* A negative summary is generated, if there does not exist any positive flow.
|
||||
*/
|
||||
string captureNoFlow(TargetApi api) {
|
||||
not exists(captureFlow(api)) and
|
||||
result = asNegativeSummaryModel(api)
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.ExternalFlow
|
||||
import DataFlow::PathGraph
|
||||
import semmle.code.csharp.dataflow.ExternalFlow
|
||||
import CsvValidation
|
||||
|
||||
class SummaryModelTest extends SummaryModelCsv {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* CSV Validation of negative summaries.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.ExternalFlow
|
||||
import CsvValidation
|
||||
import semmle.code.csharp.dataflow.internal.NegativeSummary
|
||||
@@ -1,8 +1,8 @@
|
||||
import csharp
|
||||
import DataFlow
|
||||
import semmle.code.csharp.dataflow.ExternalFlow
|
||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import CsvValidation
|
||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
|
||||
class SinkModelTest extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import csharp
|
||||
import DataFlow
|
||||
import semmle.code.csharp.dataflow.ExternalFlow
|
||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import CsvValidation
|
||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
|
||||
class SourceModelTest extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import csharp
|
||||
import DataFlow
|
||||
import semmle.code.csharp.dataflow.ExternalFlow
|
||||
import CsvValidation
|
||||
import semmle.code.csharp.dataflow.FlowSummary
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import CsvValidation
|
||||
|
||||
private class SummaryModelTest extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,16 @@
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import shared.FlowSummaries
|
||||
|
||||
private class IncludeAllSummarizedCallable extends IncludeSummarizedCallable {
|
||||
IncludeAllSummarizedCallable() { exists(this) }
|
||||
}
|
||||
|
||||
private class IncludeNegativeSummarizedCallable extends RelevantNegativeSummarizedCallable {
|
||||
IncludeNegativeSummarizedCallable() {
|
||||
this instanceof FlowSummaryImpl::Public::NegativeSummarizedCallable
|
||||
}
|
||||
|
||||
/** Gets a string representing the callable in semi-colon separated format for use in flow summaries. */
|
||||
final override string getCallableCsv() { result = Csv::asPartialNegativeModel(this) }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
summary
|
||||
| Microsoft.CSharp.RuntimeBinder;Binder;false;BinaryOperation;(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,System.Linq.Expressions.ExpressionType,System.Type,System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>);;Argument[2];ReturnValue;taint;generated |
|
||||
| Microsoft.CSharp.RuntimeBinder;Binder;false;BinaryOperation;(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,System.Linq.Expressions.ExpressionType,System.Type,System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>);;Argument[3].Element;ReturnValue;taint;generated |
|
||||
| Microsoft.CSharp.RuntimeBinder;Binder;false;Convert;(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,System.Type,System.Type);;Argument[2];ReturnValue;taint;generated |
|
||||
@@ -10022,3 +10023,4 @@
|
||||
| System;ValueTuple<>;false;ToString;();;Argument[this];ReturnValue;taint;generated |
|
||||
| System;ValueTuple<>;false;ValueTuple;(T1);;Argument[0];Argument[this].Field[System.ValueTuple<>.Item1];value;manual |
|
||||
| System;ValueTuple<>;false;get_Item;(System.Int32);;Argument[this].Field[System.ValueTuple<>.Item1];ReturnValue;value;manual |
|
||||
negativeSummary
|
||||
|
||||
@@ -131,6 +131,7 @@ summary
|
||||
| System.Data.Entity;DbSet<>;false;AttachRange;(System.Collections.Generic.IEnumerable<T>);;Argument[0].Element;Argument[this].Element;value;manual |
|
||||
| System.Data.Entity;DbSet<>;false;Update;(T);;Argument[0];Argument[this].Element;value;manual |
|
||||
| System.Data.Entity;DbSet<>;false;UpdateRange;(System.Collections.Generic.IEnumerable<T>);;Argument[0].Element;Argument[this].Element;value;manual |
|
||||
negativeSummary
|
||||
sourceNode
|
||||
sinkNode
|
||||
| EntityFrameworkCore.cs:72:36:72:40 | "sql" | sql |
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
| System.Private.CoreLib.dll#System#DateTime.AddYears(System.Int32) | 2 |
|
||||
| System.Private.CoreLib.dll#System#DateTime.AddDays(System.Double) | 1 |
|
||||
| System.Private.CoreLib.dll#System#DateTime.DateTime(System.Int32,System.Int32,System.Int32) | 1 |
|
||||
| System.Private.CoreLib.dll#System#Guid.Parse(System.String) | 1 |
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
import semmle.code.csharp.dataflow.FlowSummary
|
||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private::TestOutput
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
import semmle.code.csharp.dataflow.internal.NegativeSummary
|
||||
|
||||
abstract class IncludeSummarizedCallable extends RelevantSummarizedCallable {
|
||||
IncludeSummarizedCallable() {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
| NoSummaries;BaseClass;M1;(System.String);generated |
|
||||
| NoSummaries;BaseClass;M2;(System.String);generated |
|
||||
| NoSummaries;EquatableBound;Equals;(System.Object);generated |
|
||||
| NoSummaries;EquatableUnBound<>;Equals;(T);generated |
|
||||
| NoSummaries;SimpleTypes;M1;(System.Boolean);generated |
|
||||
| NoSummaries;SimpleTypes;M2;(System.Boolean);generated |
|
||||
| NoSummaries;SimpleTypes;M3;(System.Int32);generated |
|
||||
| NoSummaries;SimpleTypes;M4;(System.Int32);generated |
|
||||
| Sinks;NewSinks;WrapFieldResponseWriteFile;();generated |
|
||||
| Sinks;NewSinks;WrapPrivateFieldResponseWriteFile;();generated |
|
||||
| Sinks;NewSinks;WrapPrivatePropResponseWriteFile;();generated |
|
||||
| Sinks;NewSinks;WrapPropPrivateSetResponseWriteFile;();generated |
|
||||
| Sinks;NewSinks;WrapPropResponseWriteFile;();generated |
|
||||
| Sinks;NewSinks;WrapResponseWrite;(System.Object);generated |
|
||||
| Sinks;NewSinks;WrapResponseWriteFile;(System.String);generated |
|
||||
| Sinks;NewSinks;get_PrivateSetTaintedProp;();generated |
|
||||
| Sinks;NewSinks;get_TaintedProp;();generated |
|
||||
| Sinks;NewSinks;set_PrivateSetTaintedProp;(System.String);generated |
|
||||
| Sinks;NewSinks;set_TaintedProp;(System.String);generated |
|
||||
| Sources;NewSources;WrapConsoleReadKey;();generated |
|
||||
| Sources;NewSources;WrapConsoleReadLine;();generated |
|
||||
| Sources;NewSources;WrapConsoleReadLineAndProcees;(System.String);generated |
|
||||
| Summaries;EqualsGetHashCodeNoFlow;Equals;(System.Object);generated |
|
||||
| Summaries;EqualsGetHashCodeNoFlow;GetHashCode;();generated |
|
||||
| Summaries;OperatorFlow;op_Increment;(Summaries.OperatorFlow);generated |
|
||||
@@ -0,0 +1 @@
|
||||
utils/model-generator/CaptureNegativeSummaryModels.ql
|
||||
@@ -98,4 +98,16 @@ public class HigherOrderParameters
|
||||
{
|
||||
return map(o);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BaseClass
|
||||
{
|
||||
// Negative summary.
|
||||
public virtual string M1(string s)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
// Negative summary.
|
||||
public abstract string M2(string s);
|
||||
}
|
||||
@@ -6,6 +6,9 @@ namespace Summaries;
|
||||
|
||||
public class BasicFlow
|
||||
{
|
||||
// No flow summary and no negative summary either.
|
||||
~BasicFlow() { }
|
||||
|
||||
private string tainted;
|
||||
|
||||
public BasicFlow ReturnThis(object input)
|
||||
|
||||
@@ -85,12 +85,6 @@ private class BuiltinModel extends SummaryModelCsv {
|
||||
}
|
||||
}
|
||||
|
||||
private predicate sourceModelCsv(string row) { none() }
|
||||
|
||||
private predicate sinkModelCsv(string row) { none() }
|
||||
|
||||
private predicate summaryModelCsv(string row) { none() }
|
||||
|
||||
/**
|
||||
* A unit class for adding additional source model rows.
|
||||
*
|
||||
@@ -121,20 +115,14 @@ class SummaryModelCsv extends Unit {
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
private predicate sourceModel(string row) {
|
||||
sourceModelCsv(row) or
|
||||
any(SourceModelCsv s).row(row)
|
||||
}
|
||||
/** Holds if `row` is a source model. */
|
||||
predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
|
||||
|
||||
private predicate sinkModel(string row) {
|
||||
sinkModelCsv(row) or
|
||||
any(SinkModelCsv s).row(row)
|
||||
}
|
||||
/** Holds if `row` is a sink model. */
|
||||
predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||
|
||||
private predicate summaryModel(string row) {
|
||||
summaryModelCsv(row) or
|
||||
any(SummaryModelCsv s).row(row)
|
||||
}
|
||||
/** Holds if `row` is a summary model. */
|
||||
predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
@@ -271,31 +259,7 @@ predicate modelCoverage(string package, int pkgs, string kind, string part, int
|
||||
|
||||
/** 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
|
||||
private string getInvalidModelInput() {
|
||||
exists(string pred, AccessPath input, string part |
|
||||
sinkModel(_, _, _, _, _, _, input, _) and pred = "sink"
|
||||
or
|
||||
@@ -309,9 +273,11 @@ module CsvValidation {
|
||||
part = input.getToken(_) and
|
||||
parseParam(part, _)
|
||||
) and
|
||||
msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
result = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
}
|
||||
|
||||
private string getInvalidModelOutput() {
|
||||
exists(string pred, string output, string part |
|
||||
sourceModel(_, _, _, _, _, _, output, _) and pred = "source"
|
||||
or
|
||||
@@ -320,9 +286,35 @@ module CsvValidation {
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
not (part = "Parameter" and pred = "source") and
|
||||
msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
result = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
}
|
||||
|
||||
private string getInvalidModelKind() {
|
||||
exists(string row, string kind | summaryModel(row) |
|
||||
kind = row.splitAt(";", 8) and
|
||||
not kind = ["taint", "value"] and
|
||||
result = "Invalid kind \"" + kind + "\" in summary model."
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelSubtype() {
|
||||
exists(string pred, string row |
|
||||
sourceModel(row) and pred = "source"
|
||||
or
|
||||
sinkModel(row) and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and pred = "summary"
|
||||
|
|
||||
exists(string b |
|
||||
b = row.splitAt(";", 2) and
|
||||
not b = ["true", "false"] and
|
||||
result = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelColumnCount() {
|
||||
exists(string pred, string row, int expect |
|
||||
sourceModel(row) and expect = 8 and pred = "source"
|
||||
or
|
||||
@@ -333,18 +325,46 @@ module CsvValidation {
|
||||
exists(int cols |
|
||||
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
||||
cols != expect and
|
||||
msg =
|
||||
result =
|
||||
"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 string getInvalidModelSignature() {
|
||||
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
|
||||
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
or
|
||||
not type.regexpMatch("[a-zA-Z0-9_\\$<>]*") and
|
||||
result = "Dubious type \"" + type + "\" in " + pred + " model."
|
||||
or
|
||||
not name.regexpMatch("[a-zA-Z0-9_]*") and
|
||||
result = "Dubious name \"" + name + "\" in " + pred + " model."
|
||||
or
|
||||
not signature.regexpMatch("|\\([a-zA-Z0-9_\\.\\$<>,\\[\\]]*\\)") and
|
||||
result = "Dubious signature \"" + signature + "\" in " + pred + " model."
|
||||
or
|
||||
not ext.regexpMatch("|Annotated") and
|
||||
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
||||
query predicate invalidModelRow(string msg) {
|
||||
msg =
|
||||
[
|
||||
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
|
||||
getInvalidModelSubtype(), getInvalidModelColumnCount(), getInvalidModelKind()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import CsvValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class SummaryModelTest extends SummaryModelCsv {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import CsvValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
|
||||
class SummaryModelTest extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
*
|
||||
* The CSV specification has the following columns:
|
||||
* - Sources:
|
||||
* `namespace; type; subtypes; name; signature; ext; output; kind`
|
||||
* `namespace; type; subtypes; name; signature; ext; output; kind; provenance`
|
||||
* - Sinks:
|
||||
* `namespace; type; subtypes; name; signature; ext; input; kind`
|
||||
* `namespace; type; subtypes; name; signature; ext; input; kind; provenance`
|
||||
* - Summaries:
|
||||
* `namespace; type; subtypes; name; signature; ext; input; output; kind`
|
||||
* `namespace; type; subtypes; name; signature; ext; input; output; kind; provenance`
|
||||
* - Negative Summaries:
|
||||
* `namespace; type; name; signature; provenance`
|
||||
*
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
@@ -152,234 +154,6 @@ private module Frameworks {
|
||||
private import semmle.code.java.frameworks.KotlinStdLib
|
||||
}
|
||||
|
||||
private predicate sourceModelCsv(string row) {
|
||||
row =
|
||||
[
|
||||
// org.springframework.security.web.savedrequest.SavedRequest
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getRedirectUrl;;;ReturnValue;remote;manual",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getCookies;;;ReturnValue;remote;manual",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getHeaderValues;;;ReturnValue;remote;manual",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getHeaderNames;;;ReturnValue;remote;manual",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getParameterValues;;;ReturnValue;remote;manual",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getParameterMap;;;ReturnValue;remote;manual",
|
||||
// ServletRequestGetParameterMethod
|
||||
"javax.servlet;ServletRequest;false;getParameter;(String);;ReturnValue;remote;manual",
|
||||
"javax.servlet;ServletRequest;false;getParameterValues;(String);;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameter;(String);;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameterValues;(String);;ReturnValue;remote;manual",
|
||||
// ServletRequestGetParameterMapMethod
|
||||
"javax.servlet;ServletRequest;false;getParameterMap;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameterMap;();;ReturnValue;remote;manual",
|
||||
// ServletRequestGetParameterNamesMethod
|
||||
"javax.servlet;ServletRequest;false;getParameterNames;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameterNames;();;ReturnValue;remote;manual",
|
||||
// HttpServletRequestGetQueryStringMethod
|
||||
"javax.servlet.http;HttpServletRequest;false;getQueryString;();;ReturnValue;remote;manual",
|
||||
//
|
||||
// URLConnectionGetInputStreamMethod
|
||||
"java.net;URLConnection;false;getInputStream;();;ReturnValue;remote;manual",
|
||||
// SocketGetInputStreamMethod
|
||||
"java.net;Socket;false;getInputStream;();;ReturnValue;remote;manual",
|
||||
// BeanValidationSource
|
||||
"javax.validation;ConstraintValidator;true;isValid;;;Parameter[0];remote;manual",
|
||||
// SpringMultipartRequestSource
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getFile;(String);;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getFileMap;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getFileNames;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getFiles;(String);;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getMultiFileMap;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getMultipartContentType;(String);;ReturnValue;remote;manual",
|
||||
// SpringMultipartFileSource
|
||||
"org.springframework.web.multipart;MultipartFile;true;getBytes;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartFile;true;getContentType;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartFile;true;getInputStream;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartFile;true;getName;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartFile;true;getOriginalFilename;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartFile;true;getResource;();;ReturnValue;remote;manual",
|
||||
// HttpServletRequest.get*
|
||||
"javax.servlet.http;HttpServletRequest;false;getHeader;(String);;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getHeaders;(String);;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getHeaderNames;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getPathInfo;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getRequestURI;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getRequestURL;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getRemoteUser;();;ReturnValue;remote;manual",
|
||||
// SpringWebRequestGetMethod
|
||||
"org.springframework.web.context.request;WebRequest;false;getDescription;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getHeader;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getHeaderNames;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getHeaderValues;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getParameter;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getParameterMap;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getParameterNames;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getParameterValues;;;ReturnValue;remote;manual",
|
||||
// TODO consider org.springframework.web.context.request.WebRequest.getRemoteUser
|
||||
// ServletRequestGetBodyMethod
|
||||
"javax.servlet;ServletRequest;false;getInputStream;();;ReturnValue;remote;manual",
|
||||
"javax.servlet;ServletRequest;false;getReader;();;ReturnValue;remote;manual",
|
||||
// CookieGet*
|
||||
"javax.servlet.http;Cookie;false;getValue;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;Cookie;false;getName;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;Cookie;false;getComment;();;ReturnValue;remote;manual",
|
||||
// ApacheHttp*
|
||||
"org.apache.http;HttpMessage;false;getParams;();;ReturnValue;remote;manual",
|
||||
"org.apache.http;HttpEntity;false;getContent;();;ReturnValue;remote;manual",
|
||||
// In the setting of Android we assume that XML has been transmitted over
|
||||
// the network, so may be tainted.
|
||||
// XmlPullGetMethod
|
||||
"org.xmlpull.v1;XmlPullParser;false;getName;();;ReturnValue;remote;manual",
|
||||
"org.xmlpull.v1;XmlPullParser;false;getNamespace;();;ReturnValue;remote;manual",
|
||||
"org.xmlpull.v1;XmlPullParser;false;getText;();;ReturnValue;remote;manual",
|
||||
// XmlAttrSetGetMethod
|
||||
"android.util;AttributeSet;false;getAttributeBooleanValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeCount;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeFloatValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeIntValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeListValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeName;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeNameResource;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeNamespace;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeResourceValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeUnsignedIntValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getClassAttribute;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getIdAttribute;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getIdAttributeResourceValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getPositionDescription;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getStyleAttribute;;;ReturnValue;remote;manual",
|
||||
// The current URL in a browser may be untrusted or uncontrolled.
|
||||
// WebViewGetUrlMethod
|
||||
"android.webkit;WebView;false;getUrl;();;ReturnValue;remote;manual",
|
||||
"android.webkit;WebView;false;getOriginalUrl;();;ReturnValue;remote;manual",
|
||||
// SpringRestTemplateResponseEntityMethod
|
||||
"org.springframework.web.client;RestTemplate;false;exchange;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.client;RestTemplate;false;getForEntity;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.client;RestTemplate;false;postForEntity;;;ReturnValue;remote;manual",
|
||||
// WebSocketMessageParameterSource
|
||||
"java.net.http;WebSocket$Listener;true;onText;(WebSocket,CharSequence,boolean);;Parameter[1];remote;manual",
|
||||
// PlayRequestGetMethod
|
||||
"play.mvc;Http$RequestHeader;false;queryString;;;ReturnValue;remote;manual",
|
||||
"play.mvc;Http$RequestHeader;false;getQueryString;;;ReturnValue;remote;manual",
|
||||
"play.mvc;Http$RequestHeader;false;header;;;ReturnValue;remote;manual",
|
||||
"play.mvc;Http$RequestHeader;false;getHeader;;;ReturnValue;remote;manual"
|
||||
]
|
||||
}
|
||||
|
||||
private predicate sinkModelCsv(string row) {
|
||||
row =
|
||||
[
|
||||
// Open URL
|
||||
"java.net;URL;false;openConnection;;;Argument[-1];open-url;manual",
|
||||
"java.net;URL;false;openStream;;;Argument[-1];open-url;manual",
|
||||
"java.net.http;HttpRequest;false;newBuilder;;;Argument[0];open-url;manual",
|
||||
"java.net.http;HttpRequest$Builder;false;uri;;;Argument[0];open-url;manual",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(URL[]);;Argument[0];open-url;manual",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(URL[],ClassLoader);;Argument[0];open-url;manual",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(URL[],ClassLoader,URLStreamHandlerFactory);;Argument[0];open-url;manual",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(String,URL[],ClassLoader);;Argument[1];open-url;manual",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(String,URL[],ClassLoader,URLStreamHandlerFactory);;Argument[1];open-url;manual",
|
||||
"java.net;URLClassLoader;false;newInstance;;;Argument[0];open-url;manual",
|
||||
// Bean validation
|
||||
"javax.validation;ConstraintValidatorContext;true;buildConstraintViolationWithTemplate;;;Argument[0];bean-validation;manual",
|
||||
// Set hostname
|
||||
"javax.net.ssl;HttpsURLConnection;true;setDefaultHostnameVerifier;;;Argument[0];set-hostname-verifier;manual",
|
||||
"javax.net.ssl;HttpsURLConnection;true;setHostnameVerifier;;;Argument[0];set-hostname-verifier;manual"
|
||||
]
|
||||
}
|
||||
|
||||
private predicate summaryModelCsv(string row) {
|
||||
row =
|
||||
[
|
||||
// qualifier to arg
|
||||
"java.io;InputStream;true;read;(byte[]);;Argument[-1];Argument[0];taint;manual",
|
||||
"java.io;InputStream;true;read;(byte[],int,int);;Argument[-1];Argument[0];taint;manual",
|
||||
"java.io;InputStream;true;readNBytes;(byte[],int,int);;Argument[-1];Argument[0];taint;manual",
|
||||
"java.io;InputStream;true;transferTo;(OutputStream);;Argument[-1];Argument[0];taint;manual",
|
||||
"java.io;ByteArrayOutputStream;false;writeTo;;;Argument[-1];Argument[0];taint;manual",
|
||||
"java.io;Reader;true;read;;;Argument[-1];Argument[0];taint;manual",
|
||||
// qualifier to return
|
||||
"java.io;ByteArrayOutputStream;false;toByteArray;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;ByteArrayOutputStream;false;toString;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;InputStream;true;readAllBytes;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;InputStream;true;readNBytes;(int);;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.util;StringTokenizer;false;nextElement;();;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.util;StringTokenizer;false;nextToken;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"javax.xml.transform.sax;SAXSource;false;getInputSource;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"javax.xml.transform.stream;StreamSource;false;getInputStream;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio;ByteBuffer;false;get;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.net;URI;false;toURL;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.net;URI;false;toString;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.net;URI;false;toAsciiString;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;toURI;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;toPath;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;getAbsoluteFile;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;getCanonicalFile;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;getAbsolutePath;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;getCanonicalPath;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio;ByteBuffer;false;array;();;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio.file;Path;true;normalize;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio.file;Path;true;resolve;;;Argument[-1..0];ReturnValue;taint;manual",
|
||||
"java.nio.file;Path;false;toFile;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio.file;Path;true;toString;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio.file;Path;true;toUri;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio.file;Paths;true;get;;;Argument[0..1];ReturnValue;taint;manual",
|
||||
"java.io;BufferedReader;true;readLine;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;Reader;true;read;();;Argument[-1];ReturnValue;taint;manual",
|
||||
// arg to return
|
||||
"java.nio;ByteBuffer;false;wrap;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Encoder;false;encode;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Encoder;false;encode;(ByteBuffer);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Encoder;false;encodeToString;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Encoder;false;wrap;(OutputStream);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Decoder;false;decode;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Decoder;false;decode;(ByteBuffer);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Decoder;false;decode;(String);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Decoder;false;wrap;(InputStream);;Argument[0];ReturnValue;taint;manual",
|
||||
"cn.hutool.core.codec;Base64;true;decode;;;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.shiro.codec;Base64;false;decode;(String);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;Encoder;true;encode;(Object);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;Decoder;true;decode;(Object);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;BinaryEncoder;true;encode;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;BinaryDecoder;true;decode;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;StringEncoder;true;encode;(String);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;StringDecoder;true;decode;(String);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.net;URLDecoder;false;decode;;;Argument[0];ReturnValue;taint;manual",
|
||||
"java.net;URI;false;create;;;Argument[0];ReturnValue;taint;manual",
|
||||
"javax.xml.transform.sax;SAXSource;false;sourceToInputSource;;;Argument[0];ReturnValue;taint;manual",
|
||||
// arg to arg
|
||||
"java.lang;System;false;arraycopy;;;Argument[0];Argument[2];taint;manual",
|
||||
// constructor flow
|
||||
"java.io;File;false;File;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;File;false;File;;;Argument[1];Argument[-1];taint;manual",
|
||||
"java.net;URI;false;URI;(String);;Argument[0];Argument[-1];taint;manual",
|
||||
"java.net;URL;false;URL;(String);;Argument[0];Argument[-1];taint;manual",
|
||||
"javax.xml.transform.stream;StreamSource;false;StreamSource;;;Argument[0];Argument[-1];taint;manual",
|
||||
"javax.xml.transform.sax;SAXSource;false;SAXSource;(InputSource);;Argument[0];Argument[-1];taint;manual",
|
||||
"javax.xml.transform.sax;SAXSource;false;SAXSource;(XMLReader,InputSource);;Argument[1];Argument[-1];taint;manual",
|
||||
"org.xml.sax;InputSource;false;InputSource;;;Argument[0];Argument[-1];taint;manual",
|
||||
"javax.servlet.http;Cookie;false;Cookie;;;Argument[0];Argument[-1];taint;manual",
|
||||
"javax.servlet.http;Cookie;false;Cookie;;;Argument[1];Argument[-1];taint;manual",
|
||||
"java.util.zip;ZipInputStream;false;ZipInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.util.zip;GZIPInputStream;false;GZIPInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.util;StringTokenizer;false;StringTokenizer;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.beans;XMLDecoder;false;XMLDecoder;;;Argument[0];Argument[-1];taint;manual",
|
||||
"com.esotericsoftware.kryo.io;Input;false;Input;;;Argument[0];Argument[-1];taint;manual",
|
||||
"com.esotericsoftware.kryo5.io;Input;false;Input;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;BufferedInputStream;false;BufferedInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;DataInputStream;false;DataInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;ByteArrayInputStream;false;ByteArrayInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;ObjectInputStream;false;ObjectInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;StringReader;false;StringReader;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;CharArrayReader;false;CharArrayReader;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;BufferedReader;false;BufferedReader;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;InputStreamReader;false;InputStreamReader;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;OutputStream;true;write;(byte[]);;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;OutputStream;true;write;(byte[],int,int);;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;OutputStream;true;write;(int);;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;FilterOutputStream;true;FilterOutputStream;(OutputStream);;Argument[0];Argument[-1];taint;manual"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional source model rows.
|
||||
*
|
||||
@@ -410,21 +184,262 @@ class SummaryModelCsv extends Unit {
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
private predicate sourceModel(string row) {
|
||||
sourceModelCsv(row) or
|
||||
any(SourceModelCsv s).row(row)
|
||||
/**
|
||||
* A unit class for adding negative summary model rows.
|
||||
*
|
||||
* Extend this class to add additional flow summary definitions.
|
||||
*/
|
||||
class NegativeSummaryModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a negative summary definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
private predicate sinkModel(string row) {
|
||||
sinkModelCsv(row) or
|
||||
any(SinkModelCsv s).row(row)
|
||||
private class SourceModelCsvBase extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
// org.springframework.security.web.savedrequest.SavedRequest
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getRedirectUrl;;;ReturnValue;remote;manual",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getCookies;;;ReturnValue;remote;manual",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getHeaderValues;;;ReturnValue;remote;manual",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getHeaderNames;;;ReturnValue;remote;manual",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getParameterValues;;;ReturnValue;remote;manual",
|
||||
"org.springframework.security.web.savedrequest;SavedRequest;true;getParameterMap;;;ReturnValue;remote;manual",
|
||||
// ServletRequestGetParameterMethod
|
||||
"javax.servlet;ServletRequest;false;getParameter;(String);;ReturnValue;remote;manual",
|
||||
"javax.servlet;ServletRequest;false;getParameterValues;(String);;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameter;(String);;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameterValues;(String);;ReturnValue;remote;manual",
|
||||
// ServletRequestGetParameterMapMethod
|
||||
"javax.servlet;ServletRequest;false;getParameterMap;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameterMap;();;ReturnValue;remote;manual",
|
||||
// ServletRequestGetParameterNamesMethod
|
||||
"javax.servlet;ServletRequest;false;getParameterNames;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getParameterNames;();;ReturnValue;remote;manual",
|
||||
// HttpServletRequestGetQueryStringMethod
|
||||
"javax.servlet.http;HttpServletRequest;false;getQueryString;();;ReturnValue;remote;manual",
|
||||
//
|
||||
// URLConnectionGetInputStreamMethod
|
||||
"java.net;URLConnection;false;getInputStream;();;ReturnValue;remote;manual",
|
||||
// SocketGetInputStreamMethod
|
||||
"java.net;Socket;false;getInputStream;();;ReturnValue;remote;manual",
|
||||
// BeanValidationSource
|
||||
"javax.validation;ConstraintValidator;true;isValid;;;Parameter[0];remote;manual",
|
||||
// SpringMultipartRequestSource
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getFile;(String);;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getFileMap;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getFileNames;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getFiles;(String);;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getMultiFileMap;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartRequest;true;getMultipartContentType;(String);;ReturnValue;remote;manual",
|
||||
// SpringMultipartFileSource
|
||||
"org.springframework.web.multipart;MultipartFile;true;getBytes;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartFile;true;getContentType;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartFile;true;getInputStream;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartFile;true;getName;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartFile;true;getOriginalFilename;();;ReturnValue;remote;manual",
|
||||
"org.springframework.web.multipart;MultipartFile;true;getResource;();;ReturnValue;remote;manual",
|
||||
// HttpServletRequest.get*
|
||||
"javax.servlet.http;HttpServletRequest;false;getHeader;(String);;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getHeaders;(String);;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getHeaderNames;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getPathInfo;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getRequestURI;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getRequestURL;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;HttpServletRequest;false;getRemoteUser;();;ReturnValue;remote;manual",
|
||||
// SpringWebRequestGetMethod
|
||||
"org.springframework.web.context.request;WebRequest;false;getDescription;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getHeader;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getHeaderNames;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getHeaderValues;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getParameter;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getParameterMap;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getParameterNames;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.context.request;WebRequest;false;getParameterValues;;;ReturnValue;remote;manual",
|
||||
// TODO consider org.springframework.web.context.request.WebRequest.getRemoteUser
|
||||
// ServletRequestGetBodyMethod
|
||||
"javax.servlet;ServletRequest;false;getInputStream;();;ReturnValue;remote;manual",
|
||||
"javax.servlet;ServletRequest;false;getReader;();;ReturnValue;remote;manual",
|
||||
// CookieGet*
|
||||
"javax.servlet.http;Cookie;false;getValue;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;Cookie;false;getName;();;ReturnValue;remote;manual",
|
||||
"javax.servlet.http;Cookie;false;getComment;();;ReturnValue;remote;manual",
|
||||
// ApacheHttp*
|
||||
"org.apache.http;HttpMessage;false;getParams;();;ReturnValue;remote;manual",
|
||||
"org.apache.http;HttpEntity;false;getContent;();;ReturnValue;remote;manual",
|
||||
// In the setting of Android we assume that XML has been transmitted over
|
||||
// the network, so may be tainted.
|
||||
// XmlPullGetMethod
|
||||
"org.xmlpull.v1;XmlPullParser;false;getName;();;ReturnValue;remote;manual",
|
||||
"org.xmlpull.v1;XmlPullParser;false;getNamespace;();;ReturnValue;remote;manual",
|
||||
"org.xmlpull.v1;XmlPullParser;false;getText;();;ReturnValue;remote;manual",
|
||||
// XmlAttrSetGetMethod
|
||||
"android.util;AttributeSet;false;getAttributeBooleanValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeCount;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeFloatValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeIntValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeListValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeName;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeNameResource;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeNamespace;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeResourceValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeUnsignedIntValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getAttributeValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getClassAttribute;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getIdAttribute;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getIdAttributeResourceValue;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getPositionDescription;;;ReturnValue;remote;manual",
|
||||
"android.util;AttributeSet;false;getStyleAttribute;;;ReturnValue;remote;manual",
|
||||
// The current URL in a browser may be untrusted or uncontrolled.
|
||||
// WebViewGetUrlMethod
|
||||
"android.webkit;WebView;false;getUrl;();;ReturnValue;remote;manual",
|
||||
"android.webkit;WebView;false;getOriginalUrl;();;ReturnValue;remote;manual",
|
||||
// SpringRestTemplateResponseEntityMethod
|
||||
"org.springframework.web.client;RestTemplate;false;exchange;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.client;RestTemplate;false;getForEntity;;;ReturnValue;remote;manual",
|
||||
"org.springframework.web.client;RestTemplate;false;postForEntity;;;ReturnValue;remote;manual",
|
||||
// WebSocketMessageParameterSource
|
||||
"java.net.http;WebSocket$Listener;true;onText;(WebSocket,CharSequence,boolean);;Parameter[1];remote;manual",
|
||||
// PlayRequestGetMethod
|
||||
"play.mvc;Http$RequestHeader;false;queryString;;;ReturnValue;remote;manual",
|
||||
"play.mvc;Http$RequestHeader;false;getQueryString;;;ReturnValue;remote;manual",
|
||||
"play.mvc;Http$RequestHeader;false;header;;;ReturnValue;remote;manual",
|
||||
"play.mvc;Http$RequestHeader;false;getHeader;;;ReturnValue;remote;manual"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private predicate summaryModel(string row) {
|
||||
summaryModelCsv(row) or
|
||||
any(SummaryModelCsv s).row(row)
|
||||
private class SinkModelCsvBase extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
// Open URL
|
||||
"java.net;URL;false;openConnection;;;Argument[-1];open-url;manual",
|
||||
"java.net;URL;false;openStream;;;Argument[-1];open-url;manual",
|
||||
"java.net.http;HttpRequest;false;newBuilder;;;Argument[0];open-url;manual",
|
||||
"java.net.http;HttpRequest$Builder;false;uri;;;Argument[0];open-url;manual",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(URL[]);;Argument[0];open-url;manual",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(URL[],ClassLoader);;Argument[0];open-url;manual",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(URL[],ClassLoader,URLStreamHandlerFactory);;Argument[0];open-url;manual",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(String,URL[],ClassLoader);;Argument[1];open-url;manual",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(String,URL[],ClassLoader,URLStreamHandlerFactory);;Argument[1];open-url;manual",
|
||||
"java.net;URLClassLoader;false;newInstance;;;Argument[0];open-url;manual",
|
||||
// Bean validation
|
||||
"javax.validation;ConstraintValidatorContext;true;buildConstraintViolationWithTemplate;;;Argument[0];bean-validation;manual",
|
||||
// Set hostname
|
||||
"javax.net.ssl;HttpsURLConnection;true;setDefaultHostnameVerifier;;;Argument[0];set-hostname-verifier;manual",
|
||||
"javax.net.ssl;HttpsURLConnection;true;setHostnameVerifier;;;Argument[0];set-hostname-verifier;manual"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private class SummaryModelCsvBase extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
// qualifier to arg
|
||||
"java.io;InputStream;true;read;(byte[]);;Argument[-1];Argument[0];taint;manual",
|
||||
"java.io;InputStream;true;read;(byte[],int,int);;Argument[-1];Argument[0];taint;manual",
|
||||
"java.io;InputStream;true;readNBytes;(byte[],int,int);;Argument[-1];Argument[0];taint;manual",
|
||||
"java.io;InputStream;true;transferTo;(OutputStream);;Argument[-1];Argument[0];taint;manual",
|
||||
"java.io;ByteArrayOutputStream;false;writeTo;;;Argument[-1];Argument[0];taint;manual",
|
||||
"java.io;Reader;true;read;;;Argument[-1];Argument[0];taint;manual",
|
||||
// qualifier to return
|
||||
"java.io;ByteArrayOutputStream;false;toByteArray;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;ByteArrayOutputStream;false;toString;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;InputStream;true;readAllBytes;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;InputStream;true;readNBytes;(int);;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.util;StringTokenizer;false;nextElement;();;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.util;StringTokenizer;false;nextToken;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"javax.xml.transform.sax;SAXSource;false;getInputSource;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"javax.xml.transform.stream;StreamSource;false;getInputStream;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio;ByteBuffer;false;get;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.net;URI;false;toURL;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.net;URI;false;toString;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.net;URI;false;toAsciiString;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;toURI;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;toPath;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;getAbsoluteFile;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;getCanonicalFile;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;getAbsolutePath;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;File;true;getCanonicalPath;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio;ByteBuffer;false;array;();;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio.file;Path;true;normalize;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio.file;Path;true;resolve;;;Argument[-1..0];ReturnValue;taint;manual",
|
||||
"java.nio.file;Path;false;toFile;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio.file;Path;true;toString;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio.file;Path;true;toUri;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.nio.file;Paths;true;get;;;Argument[0..1];ReturnValue;taint;manual",
|
||||
"java.io;BufferedReader;true;readLine;;;Argument[-1];ReturnValue;taint;manual",
|
||||
"java.io;Reader;true;read;();;Argument[-1];ReturnValue;taint;manual",
|
||||
// arg to return
|
||||
"java.nio;ByteBuffer;false;wrap;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Encoder;false;encode;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Encoder;false;encode;(ByteBuffer);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Encoder;false;encodeToString;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Encoder;false;wrap;(OutputStream);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Decoder;false;decode;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Decoder;false;decode;(ByteBuffer);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Decoder;false;decode;(String);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.util;Base64$Decoder;false;wrap;(InputStream);;Argument[0];ReturnValue;taint;manual",
|
||||
"cn.hutool.core.codec;Base64;true;decode;;;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.shiro.codec;Base64;false;decode;(String);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;Encoder;true;encode;(Object);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;Decoder;true;decode;(Object);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;BinaryEncoder;true;encode;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;BinaryDecoder;true;decode;(byte[]);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;StringEncoder;true;encode;(String);;Argument[0];ReturnValue;taint;manual",
|
||||
"org.apache.commons.codec;StringDecoder;true;decode;(String);;Argument[0];ReturnValue;taint;manual",
|
||||
"java.net;URLDecoder;false;decode;;;Argument[0];ReturnValue;taint;manual",
|
||||
"java.net;URI;false;create;;;Argument[0];ReturnValue;taint;manual",
|
||||
"javax.xml.transform.sax;SAXSource;false;sourceToInputSource;;;Argument[0];ReturnValue;taint;manual",
|
||||
// arg to arg
|
||||
"java.lang;System;false;arraycopy;;;Argument[0];Argument[2];taint;manual",
|
||||
// constructor flow
|
||||
"java.io;File;false;File;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;File;false;File;;;Argument[1];Argument[-1];taint;manual",
|
||||
"java.net;URI;false;URI;(String);;Argument[0];Argument[-1];taint;manual",
|
||||
"java.net;URL;false;URL;(String);;Argument[0];Argument[-1];taint;manual",
|
||||
"javax.xml.transform.stream;StreamSource;false;StreamSource;;;Argument[0];Argument[-1];taint;manual",
|
||||
"javax.xml.transform.sax;SAXSource;false;SAXSource;(InputSource);;Argument[0];Argument[-1];taint;manual",
|
||||
"javax.xml.transform.sax;SAXSource;false;SAXSource;(XMLReader,InputSource);;Argument[1];Argument[-1];taint;manual",
|
||||
"org.xml.sax;InputSource;false;InputSource;;;Argument[0];Argument[-1];taint;manual",
|
||||
"javax.servlet.http;Cookie;false;Cookie;;;Argument[0];Argument[-1];taint;manual",
|
||||
"javax.servlet.http;Cookie;false;Cookie;;;Argument[1];Argument[-1];taint;manual",
|
||||
"java.util.zip;ZipInputStream;false;ZipInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.util.zip;GZIPInputStream;false;GZIPInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.util;StringTokenizer;false;StringTokenizer;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.beans;XMLDecoder;false;XMLDecoder;;;Argument[0];Argument[-1];taint;manual",
|
||||
"com.esotericsoftware.kryo.io;Input;false;Input;;;Argument[0];Argument[-1];taint;manual",
|
||||
"com.esotericsoftware.kryo5.io;Input;false;Input;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;BufferedInputStream;false;BufferedInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;DataInputStream;false;DataInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;ByteArrayInputStream;false;ByteArrayInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;ObjectInputStream;false;ObjectInputStream;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;StringReader;false;StringReader;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;CharArrayReader;false;CharArrayReader;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;BufferedReader;false;BufferedReader;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;InputStreamReader;false;InputStreamReader;;;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;OutputStream;true;write;(byte[]);;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;OutputStream;true;write;(byte[],int,int);;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;OutputStream;true;write;(int);;Argument[0];Argument[-1];taint;manual",
|
||||
"java.io;FilterOutputStream;true;FilterOutputStream;(OutputStream);;Argument[0];Argument[-1];taint;manual"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `row` is a source model. */
|
||||
predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
|
||||
|
||||
/** Holds if `row` is a sink model. */
|
||||
predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||
|
||||
/** Holds if `row` is a summary model. */
|
||||
predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
|
||||
/** Holds if `row` is negative summary model. */
|
||||
predicate negativeSummaryModel(string row) { any(NegativeSummaryModelCsv 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,
|
||||
@@ -492,6 +507,20 @@ predicate summaryModel(
|
||||
row.splitAt(";", 9) = provenance
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists indicating there is no flow for the given parameters. */
|
||||
predicate negativeSummaryModel(
|
||||
string namespace, string type, string name, string signature, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
negativeSummaryModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = name and
|
||||
row.splitAt(";", 3) = signature and
|
||||
row.splitAt(";", 4) = provenance
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantPackage(string package) {
|
||||
sourceModel(package, _, _, _, _, _, _, _, _) or
|
||||
sinkModel(package, _, _, _, _, _, _, _, _) or
|
||||
@@ -548,38 +577,7 @@ predicate modelCoverage(string package, int pkgs, string kind, string part, int
|
||||
|
||||
/** 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,
|
||||
string provenance
|
||||
|
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _, provenance) and pred = "source"
|
||||
or
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _, provenance) and pred = "sink"
|
||||
or
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _, provenance) 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
|
||||
not provenance = ["manual", "generated"] and
|
||||
msg = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
private string getInvalidModelInput() {
|
||||
exists(string pred, string input, string part |
|
||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||
or
|
||||
@@ -594,9 +592,11 @@ module CsvValidation {
|
||||
part = input.(AccessPath).getToken(0) and
|
||||
parseParam(part, _)
|
||||
) and
|
||||
msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
result = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
}
|
||||
|
||||
private string getInvalidModelOutput() {
|
||||
exists(string pred, string output, string part |
|
||||
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
||||
or
|
||||
@@ -605,35 +605,15 @@ module CsvValidation {
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
not (part = ["Argument", "Parameter"] and pred = "source") and
|
||||
msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
result = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
exists(string pred, string row, int expect |
|
||||
sourceModel(row) and expect = 9 and pred = "source"
|
||||
or
|
||||
sinkModel(row) and expect = 9 and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and expect = 10 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 +
|
||||
" in " + row + "."
|
||||
)
|
||||
or
|
||||
exists(string b |
|
||||
b = row.splitAt(";", 2) and
|
||||
not b = ["true", "false"] and
|
||||
msg = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
||||
)
|
||||
)
|
||||
or
|
||||
}
|
||||
|
||||
private string getInvalidModelKind() {
|
||||
exists(string row, string kind | summaryModel(row) |
|
||||
kind = row.splitAt(";", 8) and
|
||||
not kind = ["taint", "value"] and
|
||||
msg = "Invalid kind \"" + kind + "\" in summary model."
|
||||
result = "Invalid kind \"" + kind + "\" in summary model."
|
||||
)
|
||||
or
|
||||
exists(string row, string kind | sinkModel(row) |
|
||||
@@ -647,25 +627,110 @@ module CsvValidation {
|
||||
] and
|
||||
not kind.matches("regex-use%") and
|
||||
not kind.matches("qltest%") and
|
||||
msg = "Invalid kind \"" + kind + "\" in sink model."
|
||||
result = "Invalid kind \"" + kind + "\" in sink model."
|
||||
)
|
||||
or
|
||||
exists(string row, string kind | sourceModel(row) |
|
||||
kind = row.splitAt(";", 7) and
|
||||
not kind = ["remote", "contentprovider", "android-widget", "android-external-storage-dir"] and
|
||||
not kind.matches("qltest%") and
|
||||
msg = "Invalid kind \"" + kind + "\" in source model."
|
||||
result = "Invalid kind \"" + kind + "\" in source model."
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelSubtype() {
|
||||
exists(string pred, string row |
|
||||
sourceModel(row) and pred = "source"
|
||||
or
|
||||
sinkModel(row) and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and pred = "summary"
|
||||
|
|
||||
exists(string b |
|
||||
b = row.splitAt(";", 2) and
|
||||
not b = ["true", "false"] and
|
||||
result = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelColumnCount() {
|
||||
exists(string pred, string row, int expect |
|
||||
sourceModel(row) and expect = 9 and pred = "source"
|
||||
or
|
||||
sinkModel(row) and expect = 9 and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and expect = 10 and pred = "summary"
|
||||
or
|
||||
negativeSummaryModel(row) and expect = 5 and pred = "negative summary"
|
||||
|
|
||||
exists(int cols |
|
||||
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
||||
cols != expect and
|
||||
result =
|
||||
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
|
||||
" in " + row + "."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelSignature() {
|
||||
exists(
|
||||
string pred, string namespace, string type, string name, string signature, string ext,
|
||||
string provenance
|
||||
|
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _, provenance) and pred = "source"
|
||||
or
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _, provenance) and pred = "sink"
|
||||
or
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _, provenance) and
|
||||
pred = "summary"
|
||||
or
|
||||
negativeSummaryModel(namespace, type, name, signature, provenance) and
|
||||
ext = "" and
|
||||
pred = "negative summary"
|
||||
|
|
||||
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
|
||||
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
or
|
||||
not type.regexpMatch("[a-zA-Z0-9_\\$<>]+") and
|
||||
result = "Dubious type \"" + type + "\" in " + pred + " model."
|
||||
or
|
||||
not name.regexpMatch("[a-zA-Z0-9_]*") and
|
||||
result = "Dubious name \"" + name + "\" in " + pred + " model."
|
||||
or
|
||||
not signature.regexpMatch("|\\([a-zA-Z0-9_\\.\\$<>,\\[\\]]*\\)") and
|
||||
result = "Dubious signature \"" + signature + "\" in " + pred + " model."
|
||||
or
|
||||
not ext.regexpMatch("|Annotated") and
|
||||
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
|
||||
or
|
||||
not provenance = ["manual", "generated"] and
|
||||
result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
||||
query predicate invalidModelRow(string msg) {
|
||||
msg =
|
||||
[
|
||||
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
|
||||
getInvalidModelSubtype(), getInvalidModelColumnCount(), getInvalidModelKind()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
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
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _)
|
||||
or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _)
|
||||
or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
or
|
||||
negativeSummaryModel(namespace, type, name, signature, _) and ext = "" and subtypes = false
|
||||
}
|
||||
|
||||
private string paramsStringPart(Callable c, int i) {
|
||||
@@ -714,7 +779,7 @@ private Element interpretElement0(
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
|
||||
/** Gets the source/sink/summary/negativesummary element corresponding to the supplied parameters. */
|
||||
Element interpretElement(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
|
||||
@@ -240,6 +240,16 @@ module Public {
|
||||
*/
|
||||
predicate isAutoGenerated() { none() }
|
||||
}
|
||||
|
||||
/** A callable with a flow summary stating there is no flow via the callable. */
|
||||
class NegativeSummarizedCallable extends SummarizedCallableBase {
|
||||
NegativeSummarizedCallable() { negativeSummaryElement(this, _) }
|
||||
|
||||
/**
|
||||
* Holds if the negative summary is auto generated.
|
||||
*/
|
||||
predicate isAutoGenerated() { negativeSummaryElement(this, true) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1094,7 +1104,7 @@ module Private {
|
||||
|
||||
/** Provides a query predicate for outputting a set of relevant flow summaries. */
|
||||
module TestOutput {
|
||||
/** A flow summary to include in the `summary/3` query predicate. */
|
||||
/** A flow summary to include in the `summary/1` query predicate. */
|
||||
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/1`. */
|
||||
abstract string getCallableCsv();
|
||||
@@ -1109,6 +1119,14 @@ module Private {
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
/** A flow summary to include in the `negativeSummary/1` query predicate. */
|
||||
abstract class RelevantNegativeSummarizedCallable instanceof NegativeSummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/1`. */
|
||||
abstract string getCallableCsv();
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
/** Render the kind in the format used in flow summaries. */
|
||||
private string renderKind(boolean preservesValue) {
|
||||
preservesValue = true and result = "value"
|
||||
@@ -1116,8 +1134,12 @@ module Private {
|
||||
preservesValue = false and result = "taint"
|
||||
}
|
||||
|
||||
private string renderProvenance(RelevantSummarizedCallable c) {
|
||||
if c.(SummarizedCallable).isAutoGenerated() then result = "generated" else result = "manual"
|
||||
private string renderProvenance(SummarizedCallable c) {
|
||||
if c.isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
private string renderProvenanceNegative(NegativeSummarizedCallable c) {
|
||||
if c.isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1132,8 +1154,23 @@ module Private {
|
||||
|
|
||||
c.relevantSummary(input, output, preservesValue) and
|
||||
csv =
|
||||
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
|
||||
";" + renderKind(preservesValue) + ";" + renderProvenance(c)
|
||||
c.getCallableCsv() // Callable information
|
||||
+ getComponentStackCsv(input) + ";" // input
|
||||
+ getComponentStackCsv(output) + ";" // output
|
||||
+ renderKind(preservesValue) + ";" // kind
|
||||
+ renderProvenance(c) // provenance
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary `csv` exists (semi-colon separated format). Used for testing purposes.
|
||||
* The syntax is: "namespace;type;name;signature;provenance"",
|
||||
*/
|
||||
query predicate negativeSummary(string csv) {
|
||||
exists(RelevantNegativeSummarizedCallable c |
|
||||
csv =
|
||||
c.getCallableCsv() // Callable information
|
||||
+ renderProvenanceNegative(c) // provenance
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,18 @@ predicate summaryElement(Callable c, string input, string output, string kind, b
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary exists for `c`, which means that there is no
|
||||
* flow through `c`. The flag `generated` states whether the summary is autogenerated.
|
||||
*/
|
||||
predicate negativeSummaryElement(Callable c, boolean generated) {
|
||||
exists(string namespace, string type, string name, string signature, string provenance |
|
||||
negativeSummaryModel(namespace, type, name, signature, provenance) and
|
||||
generated = isGenerated(provenance) and
|
||||
c = interpretElement(namespace, type, false, name, signature, "")
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the summary component for specification component `c`, if any. */
|
||||
bindingset[c]
|
||||
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import ExternalApi
|
||||
|
||||
private predicate getRelevantUsages(ExternalApi api, int usages) {
|
||||
not api.isUninteresting() and
|
||||
not api.isSupported() and
|
||||
not api instanceof FlowSummaryImpl::Public::NegativeSummarizedCallable and
|
||||
usages =
|
||||
strictcount(Call c |
|
||||
c.getCallee().getSourceDeclaration() = api and
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name Capture negative summary models.
|
||||
* @description Finds negative summary models to be used by other queries.
|
||||
* @kind diagnostic
|
||||
* @id java/utils/model-generator/negative-summary-models
|
||||
* @tags model-generator
|
||||
*/
|
||||
|
||||
private import internal.CaptureModels
|
||||
private import internal.CaptureSummaryFlow
|
||||
|
||||
from TargetApi api, string noflow
|
||||
where noflow = captureNoFlow(api)
|
||||
select noflow order by noflow
|
||||
@@ -7,78 +7,7 @@
|
||||
*/
|
||||
|
||||
private import internal.CaptureModels
|
||||
|
||||
/**
|
||||
* Capture fluent APIs that return `this`.
|
||||
* Example of a fluent API:
|
||||
* ```java
|
||||
* public class Foo {
|
||||
* public Foo someAPI() {
|
||||
* // some side-effect
|
||||
* return this;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Capture APIs that transfer taint from an input parameter to an output return
|
||||
* value or parameter.
|
||||
* Allows a sequence of read steps followed by a sequence of store steps.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* ```java
|
||||
* public class Foo {
|
||||
* private String tainted;
|
||||
*
|
||||
* public String returnsTainted() {
|
||||
* return tainted;
|
||||
* }
|
||||
*
|
||||
* public void putsTaintIntoParameter(List<String> foo) {
|
||||
* foo.add(tainted);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* Captured Models:
|
||||
* ```
|
||||
* p;Foo;true;returnsTainted;;Argument[-1];ReturnValue;taint
|
||||
* p;Foo;true;putsTaintIntoParameter;(List);Argument[-1];Argument[0];taint
|
||||
* ```
|
||||
*
|
||||
* ```java
|
||||
* public class Foo {
|
||||
* private String tainted;
|
||||
* public void doSomething(String input) {
|
||||
* tainted = input;
|
||||
* }
|
||||
* ```
|
||||
* Captured Model:
|
||||
* ```p;Foo;true;doSomething;(String);Argument[0];Argument[-1];taint```
|
||||
*
|
||||
* ```java
|
||||
* public class Foo {
|
||||
* public String returnData(String tainted) {
|
||||
* return tainted.substring(0,10)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* Captured Model:
|
||||
* ```p;Foo;true;returnData;;Argument[0];ReturnValue;taint```
|
||||
*
|
||||
* ```java
|
||||
* public class Foo {
|
||||
* public void addToList(String tainted, List<String> foo) {
|
||||
* foo.add(tainted);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* Captured Model:
|
||||
* ```p;Foo;true;addToList;;Argument[0];Argument[1];taint```
|
||||
*/
|
||||
string captureFlow(TargetApi api) {
|
||||
result = captureQualifierFlow(api) or
|
||||
result = captureThroughFlow(api)
|
||||
}
|
||||
private import internal.CaptureSummaryFlow
|
||||
|
||||
from TargetApi api, string flow
|
||||
where flow = captureFlow(api)
|
||||
|
||||
@@ -48,6 +48,8 @@ private string asSummaryModel(TargetApi api, string input, string output, string
|
||||
+ "generated"
|
||||
}
|
||||
|
||||
string asNegativeSummaryModel(TargetApi api) { result = asPartialNegativeModel(api) + "generated" }
|
||||
|
||||
/**
|
||||
* Gets the value summary model for `api` with `input` and `output`.
|
||||
*/
|
||||
|
||||
@@ -98,16 +98,38 @@ private string typeAsSummaryModel(TargetApiSpecific api) {
|
||||
result = typeAsModel(bestTypeForModel(api))
|
||||
}
|
||||
|
||||
private predicate partialModel(TargetApiSpecific api, string type, string name, string parameters) {
|
||||
type = typeAsSummaryModel(api) and
|
||||
name = api.getName() and
|
||||
parameters = ExternalFlow::paramsString(api)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the first 6 columns for CSV rows.
|
||||
*/
|
||||
string asPartialModel(TargetApiSpecific api) {
|
||||
result =
|
||||
typeAsSummaryModel(api) + ";" //
|
||||
+ isExtensible(bestTypeForModel(api)) + ";" //
|
||||
+ api.getName() + ";" //
|
||||
+ ExternalFlow::paramsString(api) + ";" //
|
||||
+ /* ext + */ ";" //
|
||||
exists(string type, string name, string parameters |
|
||||
partialModel(api, type, name, parameters) and
|
||||
result =
|
||||
type + ";" //
|
||||
+ isExtensible(bestTypeForModel(api)) + ";" //
|
||||
+ name + ";" //
|
||||
+ parameters + ";" //
|
||||
+ /* ext + */ ";" //
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the first 4 columns for negative CSV rows.
|
||||
*/
|
||||
string asPartialNegativeModel(TargetApiSpecific api) {
|
||||
exists(string type, string name, string parameters |
|
||||
partialModel(api, type, name, parameters) and
|
||||
result =
|
||||
type + ";" //
|
||||
+ name + ";" //
|
||||
+ parameters + ";" //
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isPrimitiveTypeUsedForBulkData(J::Type t) {
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
private import CaptureModels
|
||||
|
||||
/**
|
||||
* Capture fluent APIs that return `this`.
|
||||
* Example of a fluent API:
|
||||
* ```java
|
||||
* public class Foo {
|
||||
* public Foo someAPI() {
|
||||
* // some side-effect
|
||||
* return this;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Capture APIs that transfer taint from an input parameter to an output return
|
||||
* value or parameter.
|
||||
* Allows a sequence of read steps followed by a sequence of store steps.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* ```java
|
||||
* public class Foo {
|
||||
* private String tainted;
|
||||
*
|
||||
* public String returnsTainted() {
|
||||
* return tainted;
|
||||
* }
|
||||
*
|
||||
* public void putsTaintIntoParameter(List<String> foo) {
|
||||
* foo.add(tainted);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* Captured Models:
|
||||
* ```
|
||||
* p;Foo;true;returnsTainted;;Argument[-1];ReturnValue;taint
|
||||
* p;Foo;true;putsTaintIntoParameter;(List);Argument[-1];Argument[0];taint
|
||||
* ```
|
||||
*
|
||||
* ```java
|
||||
* public class Foo {
|
||||
* private String tainted;
|
||||
* public void doSomething(String input) {
|
||||
* tainted = input;
|
||||
* }
|
||||
* ```
|
||||
* Captured Model:
|
||||
* ```p;Foo;true;doSomething;(String);Argument[0];Argument[-1];taint```
|
||||
*
|
||||
* ```java
|
||||
* public class Foo {
|
||||
* public String returnData(String tainted) {
|
||||
* return tainted.substring(0,10)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* Captured Model:
|
||||
* ```p;Foo;true;returnData;;Argument[0];ReturnValue;taint```
|
||||
*
|
||||
* ```java
|
||||
* public class Foo {
|
||||
* public void addToList(String tainted, List<String> foo) {
|
||||
* foo.add(tainted);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* Captured Model:
|
||||
* ```p;Foo;true;addToList;;Argument[0];Argument[1];taint```
|
||||
*/
|
||||
string captureFlow(TargetApi api) {
|
||||
result = captureQualifierFlow(api) or
|
||||
result = captureThroughFlow(api)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the negative summary for `api`, if any.
|
||||
* A negative summary is generated, if there does not exist any positive flow.
|
||||
*/
|
||||
string captureNoFlow(TargetApi api) {
|
||||
not exists(captureFlow(api)) and
|
||||
result = asNegativeSummaryModel(api)
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import CsvValidation
|
||||
import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
|
||||
class SummaryModelTest extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
| p;AbstractImplOfExternalSPI;AbstractImplOfExternalSPI;();generated |
|
||||
| p;Factory;getIntValue;();generated |
|
||||
| p;FinalClass;FinalClass;();generated |
|
||||
| p;FinalClass;returnsConstant;();generated |
|
||||
| p;FluentAPI$Inner;Inner;();generated |
|
||||
| p;FluentAPI$Inner;notThis;(String);generated |
|
||||
| p;FluentAPI;FluentAPI;();generated |
|
||||
| p;ImmutablePojo;getX;();generated |
|
||||
| p;ImplOfExternalSPI;ImplOfExternalSPI;();generated |
|
||||
| p;InnerClasses$CaptureMe;CaptureMe;();generated |
|
||||
| p;InnerClasses;InnerClasses;();generated |
|
||||
| p;InnerHolder;InnerHolder;();generated |
|
||||
| p;Joiner;length;();generated |
|
||||
| p;MultipleImpls$Strat1;Strat1;();generated |
|
||||
| p;MultipleImpls$Strat2;Strat2;();generated |
|
||||
| p;MultipleImpls$Strat3;Strat3;();generated |
|
||||
| p;MultipleImpls$Strategy;doSomething;(String);generated |
|
||||
| p;MultipleImpls;MultipleImpls;();generated |
|
||||
| p;ParamFlow;ParamFlow;();generated |
|
||||
| p;ParamFlow;ignorePrimitiveReturnValue;(String);generated |
|
||||
| p;ParamFlow;mapType;(Class);generated |
|
||||
| p;Pojo;Pojo;();generated |
|
||||
| p;Pojo;doNotSetValue;(String);generated |
|
||||
| p;Pojo;getBigDecimal;();generated |
|
||||
| p;Pojo;getBigInt;();generated |
|
||||
| p;Pojo;getBoxedArray;();generated |
|
||||
| p;Pojo;getBoxedCollection;();generated |
|
||||
| p;Pojo;getBoxedValue;();generated |
|
||||
| p;Pojo;getFloatArray;();generated |
|
||||
| p;Pojo;getIntValue;();generated |
|
||||
| p;Pojo;getPrimitiveArray;();generated |
|
||||
| p;PrivateFlowViaPublicInterface$SPI;openStream;();generated |
|
||||
| p;PrivateFlowViaPublicInterface$SPI;openStreamNone;();generated |
|
||||
| p;PrivateFlowViaPublicInterface;PrivateFlowViaPublicInterface;();generated |
|
||||
| p;PrivateFlowViaPublicInterface;createAnSPIWithoutTrackingFile;(File);generated |
|
||||
| p;Sinks;Sinks;();generated |
|
||||
| p;Sinks;copyFileToDirectory;(Path,Path,CopyOption[]);generated |
|
||||
| p;Sinks;propagate;(String);generated |
|
||||
| p;Sinks;readUrl;(URL,Charset);generated |
|
||||
| p;Sources;Sources;();generated |
|
||||
| p;Sources;readUrl;(URL);generated |
|
||||
| p;Sources;socketStream;();generated |
|
||||
| p;Sources;sourceToParameter;(InputStream[],List);generated |
|
||||
| p;Sources;wrappedSocketStream;();generated |
|
||||
@@ -0,0 +1 @@
|
||||
utils/model-generator/CaptureNegativeSummaryModels.ql
|
||||
@@ -14,6 +14,7 @@ class Generator:
|
||||
self.generateSinks = False
|
||||
self.generateSources = False
|
||||
self.generateSummaries = False
|
||||
self.generateNegativeSummaries = False
|
||||
self.dryRun = False
|
||||
|
||||
|
||||
@@ -25,11 +26,13 @@ This generates summary, source and sink models for the code in the database.
|
||||
The files will be placed in `{self.language}/ql/lib/semmle/code/{self.language}/frameworks/<outputQll>` where
|
||||
outputQll is the name (and path) of the output QLL file. Usually, models are grouped by their
|
||||
respective frameworks.
|
||||
If negative summaries are produced a file prefixed with `Negative` will be generated and stored in the same folder.
|
||||
|
||||
Which models are generated is controlled by the flags:
|
||||
--with-sinks
|
||||
--with-sources
|
||||
--with-summaries
|
||||
--with-negative-summaries
|
||||
If none of these flags are specified, all models are generated.
|
||||
|
||||
--dry-run: Only run the queries, but don't write to file.
|
||||
@@ -46,12 +49,14 @@ Requirements: `codeql` should both appear on your path.
|
||||
self.codeQlRoot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
|
||||
if not target.endswith(".qll"):
|
||||
target += ".qll"
|
||||
self.filename = os.path.basename(target)
|
||||
self.shortname = self.filename[:-4]
|
||||
filename = os.path.basename(target)
|
||||
dirname = os.path.dirname(target)
|
||||
self.shortname = filename[:-4]
|
||||
self.database = database
|
||||
self.generatedFrameworks = os.path.join(
|
||||
self.codeQlRoot, f"{self.language}/ql/lib/semmle/code/{self.language}/frameworks/")
|
||||
self.frameworkTarget = os.path.join(self.generatedFrameworks, target)
|
||||
self.frameworkTarget = os.path.join(self.generatedFrameworks, dirname, filename)
|
||||
self.negativeFrameworkTarget = os.path.join(self.generatedFrameworks, dirname, "Negative" + filename)
|
||||
|
||||
self.workDir = tempfile.mkdtemp()
|
||||
os.makedirs(self.generatedFrameworks, exist_ok=True)
|
||||
@@ -76,12 +81,16 @@ Requirements: `codeql` should both appear on your path.
|
||||
sys.argv.remove("--with-summaries")
|
||||
generator.generateSummaries = True
|
||||
|
||||
if "--with-negative-summaries" in sys.argv:
|
||||
sys.argv.remove("--with-negative-summaries")
|
||||
generator.generateNegativeSummaries = True
|
||||
|
||||
if "--dry-run" in sys.argv:
|
||||
sys.argv.remove("--dry-run")
|
||||
generator.dryRun = True
|
||||
|
||||
if not generator.generateSinks and not generator.generateSources and not generator.generateSummaries:
|
||||
generator.generateSinks = generator.generateSources = generator.generateSummaries = True
|
||||
if not generator.generateSinks and not generator.generateSources and not generator.generateSummaries and not generator.generateNegativeSummaries:
|
||||
generator.generateSinks = generator.generateSources = generator.generateSummaries = generator.generateNegativeSummaries = True
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
generator.printHelp()
|
||||
@@ -181,26 +190,51 @@ private import semmle.code.{self.language}.dataflow.ExternalFlow
|
||||
|
||||
"""
|
||||
|
||||
def makeNegativeContent(self):
|
||||
if self.generateNegativeSummaries:
|
||||
negativeSummaryRows = self.runQuery("negative summary models", "CaptureNegativeSummaryModels.ql")
|
||||
negativeSummaryCsv = self.asCsvModel("NegativeSummaryModelCsv", "NegativeSummary", negativeSummaryRows)
|
||||
else:
|
||||
negativeSummaryCsv = ""
|
||||
|
||||
def save(self, content):
|
||||
with open(self.frameworkTarget, "w") as frameworkQll:
|
||||
frameworkQll.write(content)
|
||||
return f"""
|
||||
/**
|
||||
* THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
|
||||
* Definitions of negative summaries in the {self.shortname} framework.
|
||||
*/
|
||||
|
||||
cmd = ['codeql', 'query', 'format', '--in-place', self.frameworkTarget]
|
||||
import {self.language}
|
||||
private import semmle.code.{self.language}.dataflow.ExternalFlow
|
||||
|
||||
{negativeSummaryCsv}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def save(self, content, target):
|
||||
with open(target, "w") as targetQll:
|
||||
targetQll.write(content)
|
||||
|
||||
cmd = ['codeql', 'query', 'format', '--in-place', target]
|
||||
ret = subprocess.call(cmd)
|
||||
if ret != 0:
|
||||
print("Failed to format query. Failed command was: " + shlex.join(cmd))
|
||||
sys.exit(1)
|
||||
|
||||
print("")
|
||||
print("CSV model written to " + self.frameworkTarget)
|
||||
print("CSV model written to " + target)
|
||||
|
||||
|
||||
def run(self):
|
||||
content = self.makeContent()
|
||||
negativeContent = self.makeNegativeContent()
|
||||
|
||||
if self.dryRun:
|
||||
print("CSV Models generated, but not written to file.")
|
||||
sys.exit(0)
|
||||
|
||||
self.save(content)
|
||||
if self.generateSinks or self.generateSinks or self.generateSummaries:
|
||||
self.save(content, self.frameworkTarget)
|
||||
|
||||
if self.generateNegativeSummaries:
|
||||
self.save(negativeContent, self.negativeFrameworkTarget)
|
||||
|
||||
@@ -63,7 +63,7 @@ class GlobalVariable extends Variable instanceof GlobalVariableImpl {
|
||||
|
||||
/** An instance variable. */
|
||||
class InstanceVariable extends Variable instanceof InstanceVariableImpl {
|
||||
/** Holds is this variable is a class instance variable. */
|
||||
/** Holds if this variable is a class instance variable. */
|
||||
final predicate isClassInstanceVariable() { super.isClassInstanceVariable() }
|
||||
|
||||
final override InstanceVariableAccess getAnAccess() { result.getVariable() = this }
|
||||
|
||||
@@ -240,6 +240,16 @@ module Public {
|
||||
*/
|
||||
predicate isAutoGenerated() { none() }
|
||||
}
|
||||
|
||||
/** A callable with a flow summary stating there is no flow via the callable. */
|
||||
class NegativeSummarizedCallable extends SummarizedCallableBase {
|
||||
NegativeSummarizedCallable() { negativeSummaryElement(this, _) }
|
||||
|
||||
/**
|
||||
* Holds if the negative summary is auto generated.
|
||||
*/
|
||||
predicate isAutoGenerated() { negativeSummaryElement(this, true) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1094,7 +1104,7 @@ module Private {
|
||||
|
||||
/** Provides a query predicate for outputting a set of relevant flow summaries. */
|
||||
module TestOutput {
|
||||
/** A flow summary to include in the `summary/3` query predicate. */
|
||||
/** A flow summary to include in the `summary/1` query predicate. */
|
||||
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/1`. */
|
||||
abstract string getCallableCsv();
|
||||
@@ -1109,6 +1119,14 @@ module Private {
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
/** A flow summary to include in the `negativeSummary/1` query predicate. */
|
||||
abstract class RelevantNegativeSummarizedCallable instanceof NegativeSummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/1`. */
|
||||
abstract string getCallableCsv();
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
/** Render the kind in the format used in flow summaries. */
|
||||
private string renderKind(boolean preservesValue) {
|
||||
preservesValue = true and result = "value"
|
||||
@@ -1116,8 +1134,12 @@ module Private {
|
||||
preservesValue = false and result = "taint"
|
||||
}
|
||||
|
||||
private string renderProvenance(RelevantSummarizedCallable c) {
|
||||
if c.(SummarizedCallable).isAutoGenerated() then result = "generated" else result = "manual"
|
||||
private string renderProvenance(SummarizedCallable c) {
|
||||
if c.isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
private string renderProvenanceNegative(NegativeSummarizedCallable c) {
|
||||
if c.isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1132,8 +1154,23 @@ module Private {
|
||||
|
|
||||
c.relevantSummary(input, output, preservesValue) and
|
||||
csv =
|
||||
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
|
||||
";" + renderKind(preservesValue) + ";" + renderProvenance(c)
|
||||
c.getCallableCsv() // Callable information
|
||||
+ getComponentStackCsv(input) + ";" // input
|
||||
+ getComponentStackCsv(output) + ";" // output
|
||||
+ renderKind(preservesValue) + ";" // kind
|
||||
+ renderProvenance(c) // provenance
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary `csv` exists (semi-colon separated format). Used for testing purposes.
|
||||
* The syntax is: "namespace;type;name;signature;provenance"",
|
||||
*/
|
||||
query predicate negativeSummary(string csv) {
|
||||
exists(RelevantNegativeSummarizedCallable c |
|
||||
csv =
|
||||
c.getCallableCsv() // Callable information
|
||||
+ renderProvenanceNegative(c) // provenance
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,13 @@ predicate summaryElement(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary exists for `c`, which means that there is no
|
||||
* flow through `c`. The flag `generated` states whether the summary is autogenerated.
|
||||
* Note. Negative flow summaries has not been implemented for ruby.
|
||||
*/
|
||||
predicate negativeSummaryElement(FlowSummary::SummarizedCallable c, boolean generated) { none() }
|
||||
|
||||
bindingset[arg]
|
||||
private SummaryComponent interpretElementArg(string arg) {
|
||||
arg = "?" and
|
||||
|
||||
@@ -111,18 +111,14 @@ class SummaryModelCsv extends Unit {
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
private predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
|
||||
/** Holds if `row` is a source model. */
|
||||
predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
|
||||
|
||||
private predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||
/** Holds if `row` is a sink model. */
|
||||
predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||
|
||||
private predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
|
||||
bindingset[input]
|
||||
private predicate getKind(string input, string kind, boolean generated) {
|
||||
input.splitAt(":", 0) = "generated" and kind = input.splitAt(":", 1) and generated = true
|
||||
or
|
||||
not input.matches("%:%") and kind = input and generated = false
|
||||
}
|
||||
/** Holds if `row` is a summary model. */
|
||||
predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
@@ -139,8 +135,9 @@ predicate sourceModel(
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = output and
|
||||
exists(string k | row.splitAt(";", 7) = k and getKind(k, kind, generated))
|
||||
)
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
generated = false
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
@@ -158,8 +155,9 @@ predicate sinkModel(
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = input and
|
||||
exists(string k | row.splitAt(";", 7) = k and getKind(k, kind, generated))
|
||||
)
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
generated = false
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
@@ -178,8 +176,9 @@ predicate summaryModel(
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = output and
|
||||
exists(string k | row.splitAt(";", 8) = k and getKind(k, kind, generated))
|
||||
)
|
||||
row.splitAt(";", 8) = kind
|
||||
) and
|
||||
generated = false
|
||||
}
|
||||
|
||||
private predicate relevantNamespace(string namespace) {
|
||||
@@ -238,31 +237,7 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
|
||||
|
||||
/** 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 member 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
|
||||
private string getInvalidModelInput() {
|
||||
exists(string pred, AccessPath input, string part |
|
||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||
or
|
||||
@@ -277,9 +252,11 @@ module CsvValidation {
|
||||
part = input.getToken(_) and
|
||||
parseParam(part, _)
|
||||
) and
|
||||
msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
result = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
}
|
||||
|
||||
private string getInvalidModelOutput() {
|
||||
exists(string pred, string output, string part |
|
||||
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
||||
or
|
||||
@@ -288,9 +265,35 @@ module CsvValidation {
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
not (part = ["Argument", "Parameter"] and pred = "source") and
|
||||
msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
result = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
}
|
||||
|
||||
private string getInvalidModelKind() {
|
||||
exists(string row, string kind | summaryModel(row) |
|
||||
kind = row.splitAt(";", 8) and
|
||||
not kind = ["taint", "value"] and
|
||||
result = "Invalid kind \"" + kind + "\" in summary model."
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelSubtype() {
|
||||
exists(string pred, string row |
|
||||
sourceModel(row) and pred = "source"
|
||||
or
|
||||
sinkModel(row) and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and pred = "summary"
|
||||
|
|
||||
exists(string b |
|
||||
b = row.splitAt(";", 2) and
|
||||
not b = ["true", "false"] and
|
||||
result = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelColumnCount() {
|
||||
exists(string pred, string row, int expect |
|
||||
sourceModel(row) and expect = 8 and pred = "source"
|
||||
or
|
||||
@@ -301,39 +304,46 @@ module CsvValidation {
|
||||
exists(int cols |
|
||||
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
||||
cols != expect and
|
||||
msg =
|
||||
result =
|
||||
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
|
||||
"."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelSignature() {
|
||||
exists(string pred, string namespace, string type, string name, string signature, string ext |
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "source"
|
||||
or
|
||||
exists(string b |
|
||||
b = row.splitAt(";", 2) and
|
||||
not b = ["true", "false"] and
|
||||
msg = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(string row, string k, string kind | summaryModel(row) |
|
||||
k = row.splitAt(";", 8) and
|
||||
getKind(k, kind, _) and
|
||||
not kind = ["taint", "value"] and
|
||||
msg = "Invalid kind \"" + kind + "\" in summary model."
|
||||
)
|
||||
or
|
||||
exists(string row, string k, string kind | sinkModel(row) |
|
||||
k = row.splitAt(";", 7) and
|
||||
getKind(k, kind, _) and
|
||||
not kind = ["code", "sql", "xss", "remote", "html"] and
|
||||
msg = "Invalid kind \"" + kind + "\" in sink model."
|
||||
)
|
||||
or
|
||||
exists(string row, string k, string kind | sourceModel(row) |
|
||||
k = row.splitAt(";", 7) and
|
||||
getKind(k, kind, _) and
|
||||
not kind = "local" and
|
||||
msg = "Invalid kind \"" + kind + "\" in source model."
|
||||
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
|
||||
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
or
|
||||
not type.regexpMatch("[a-zA-Z0-9_<>,\\+]+") and
|
||||
result = "Dubious type \"" + type + "\" in " + pred + " model."
|
||||
or
|
||||
not name.regexpMatch("[a-zA-Z0-9_<>,]*") and
|
||||
result = "Dubious member name \"" + name + "\" in " + pred + " model."
|
||||
or
|
||||
not signature.regexpMatch("|\\([a-zA-Z0-9_<>\\.\\+\\*,\\[\\]]*\\)") and
|
||||
result = "Dubious signature \"" + signature + "\" in " + pred + " model."
|
||||
or
|
||||
not ext.regexpMatch("|Attribute") and
|
||||
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
||||
query predicate invalidModelRow(string msg) {
|
||||
msg =
|
||||
[
|
||||
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
|
||||
getInvalidModelSubtype(), getInvalidModelColumnCount(), getInvalidModelKind()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private predicate elementSpec(
|
||||
|
||||
@@ -240,6 +240,16 @@ module Public {
|
||||
*/
|
||||
predicate isAutoGenerated() { none() }
|
||||
}
|
||||
|
||||
/** A callable with a flow summary stating there is no flow via the callable. */
|
||||
class NegativeSummarizedCallable extends SummarizedCallableBase {
|
||||
NegativeSummarizedCallable() { negativeSummaryElement(this, _) }
|
||||
|
||||
/**
|
||||
* Holds if the negative summary is auto generated.
|
||||
*/
|
||||
predicate isAutoGenerated() { negativeSummaryElement(this, true) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1094,7 +1104,7 @@ module Private {
|
||||
|
||||
/** Provides a query predicate for outputting a set of relevant flow summaries. */
|
||||
module TestOutput {
|
||||
/** A flow summary to include in the `summary/3` query predicate. */
|
||||
/** A flow summary to include in the `summary/1` query predicate. */
|
||||
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/1`. */
|
||||
abstract string getCallableCsv();
|
||||
@@ -1109,6 +1119,14 @@ module Private {
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
/** A flow summary to include in the `negativeSummary/1` query predicate. */
|
||||
abstract class RelevantNegativeSummarizedCallable instanceof NegativeSummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/1`. */
|
||||
abstract string getCallableCsv();
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
/** Render the kind in the format used in flow summaries. */
|
||||
private string renderKind(boolean preservesValue) {
|
||||
preservesValue = true and result = "value"
|
||||
@@ -1116,8 +1134,12 @@ module Private {
|
||||
preservesValue = false and result = "taint"
|
||||
}
|
||||
|
||||
private string renderProvenance(RelevantSummarizedCallable c) {
|
||||
if c.(SummarizedCallable).isAutoGenerated() then result = "generated" else result = "manual"
|
||||
private string renderProvenance(SummarizedCallable c) {
|
||||
if c.isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
private string renderProvenanceNegative(NegativeSummarizedCallable c) {
|
||||
if c.isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1132,8 +1154,23 @@ module Private {
|
||||
|
|
||||
c.relevantSummary(input, output, preservesValue) and
|
||||
csv =
|
||||
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
|
||||
";" + renderKind(preservesValue) + ";" + renderProvenance(c)
|
||||
c.getCallableCsv() // Callable information
|
||||
+ getComponentStackCsv(input) + ";" // input
|
||||
+ getComponentStackCsv(output) + ";" // output
|
||||
+ renderKind(preservesValue) + ";" // kind
|
||||
+ renderProvenance(c) // provenance
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary `csv` exists (semi-colon separated format). Used for testing purposes.
|
||||
* The syntax is: "namespace;type;name;signature;provenance"",
|
||||
*/
|
||||
query predicate negativeSummary(string csv) {
|
||||
exists(RelevantNegativeSummarizedCallable c |
|
||||
csv =
|
||||
c.getCallableCsv() // Callable information
|
||||
+ renderProvenanceNegative(c) // provenance
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,13 @@ predicate summaryElement(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary exists for `c`, which means that there is no
|
||||
* flow through `c`. The flag `generated` states whether the summary is autogenerated.
|
||||
* Note. Negative flow summaries has not been implemented for swift.
|
||||
*/
|
||||
predicate negativeSummaryElement(AbstractFunctionDecl c, boolean generated) { none() }
|
||||
|
||||
/**
|
||||
* Holds if an external source specification exists for `e` with output specification
|
||||
* `output`, kind `kind`, and a flag `generated` stating whether the source specification is
|
||||
|
||||
Reference in New Issue
Block a user