mirror of
https://github.com/github/codeql.git
synced 2025-12-17 17:23:36 +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:
|
* The CSV specification has the following columns:
|
||||||
* - Sources:
|
* - Sources:
|
||||||
* `namespace; type; subtypes; name; signature; ext; output; kind`
|
* `namespace; type; subtypes; name; signature; ext; output; kind; provenance`
|
||||||
* - Sinks:
|
* - Sinks:
|
||||||
* `namespace; type; subtypes; name; signature; ext; input; kind`
|
* `namespace; type; subtypes; name; signature; ext; input; kind; provenance`
|
||||||
* - Summaries:
|
* - 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
|
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||||
* reading.
|
* reading.
|
||||||
* 1. The `namespace` column selects a namespace.
|
* 1. The `namespace` column selects a namespace.
|
||||||
@@ -163,11 +164,27 @@ class SummaryModelCsv extends Unit {
|
|||||||
abstract predicate row(string row);
|
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. */
|
/** Holds if a source model exists for the given parameters. */
|
||||||
predicate sourceModel(
|
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) {
|
private predicate relevantNamespace(string namespace) {
|
||||||
sourceModel(namespace, _, _, _, _, _, _, _, _) or
|
sourceModel(namespace, _, _, _, _, _, _, _, _) or
|
||||||
sinkModel(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. */
|
/** Provides a query predicate to check the CSV data for validation errors. */
|
||||||
module CsvValidation {
|
module CsvValidation {
|
||||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
private string getInvalidModelInput() {
|
||||||
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
|
|
||||||
exists(string pred, AccessPath input, string part |
|
exists(string pred, AccessPath input, string part |
|
||||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||||
or
|
or
|
||||||
@@ -332,9 +332,11 @@ module CsvValidation {
|
|||||||
part = input.getToken(_) and
|
part = input.getToken(_) and
|
||||||
parseParam(part, _)
|
parseParam(part, _)
|
||||||
) and
|
) 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 |
|
exists(string pred, string output, string part |
|
||||||
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
||||||
or
|
or
|
||||||
@@ -343,58 +345,123 @@ module CsvValidation {
|
|||||||
invalidSpecComponent(output, part) and
|
invalidSpecComponent(output, part) and
|
||||||
not part = "" and
|
not part = "" and
|
||||||
not (part = ["Argument", "Parameter"] and pred = "source") 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"
|
private string getInvalidModelKind() {
|
||||||
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
|
|
||||||
exists(string row, string kind | summaryModel(row) |
|
exists(string row, string kind | summaryModel(row) |
|
||||||
kind = row.splitAt(";", 8) and
|
kind = row.splitAt(";", 8) and
|
||||||
not kind = ["taint", "value"] and
|
not kind = ["taint", "value"] and
|
||||||
msg = "Invalid kind \"" + kind + "\" in summary model."
|
result = "Invalid kind \"" + kind + "\" in summary model."
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(string row, string kind | sinkModel(row) |
|
exists(string row, string kind | sinkModel(row) |
|
||||||
kind = row.splitAt(";", 7) and
|
kind = row.splitAt(";", 7) and
|
||||||
not kind = ["code", "sql", "xss", "remote", "html"] and
|
not kind = ["code", "sql", "xss", "remote", "html"] and
|
||||||
not kind.matches("encryption-%") and
|
not kind.matches("encryption-%") and
|
||||||
msg = "Invalid kind \"" + kind + "\" in sink model."
|
result = "Invalid kind \"" + kind + "\" in sink model."
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(string row, string kind | sourceModel(row) |
|
exists(string row, string kind | sourceModel(row) |
|
||||||
kind = row.splitAt(";", 7) and
|
kind = row.splitAt(";", 7) and
|
||||||
not kind = ["local", "file"] 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(
|
private predicate elementSpec(
|
||||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||||
) {
|
) {
|
||||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
|
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _)
|
||||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
|
or
|
||||||
|
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _)
|
||||||
|
or
|
||||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
||||||
|
or
|
||||||
|
negativeSummaryModel(namespace, type, name, signature, _) and ext = "" and subtypes = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate elementSpec(
|
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(
|
Element interpretElement(
|
||||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
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
|
* 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
|
* point it is still live, without crossing another SSA definition of the
|
||||||
* same source variable.
|
* same source variable.
|
||||||
|
|||||||
@@ -2129,18 +2129,37 @@ module Csv {
|
|||||||
if isBaseCallableOrPrototype(c) then result = "true" else result = "false"
|
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) {
|
string asPartialModel(DotNet::Callable c) {
|
||||||
exists(string namespace, string type, string name |
|
exists(string namespace, string type, string name, string parameters |
|
||||||
c.getDeclaringType().hasQualifiedName(namespace, type) and
|
partialModel(c, namespace, type, name, parameters) and
|
||||||
c.hasQualifiedName(_, name) and
|
|
||||||
result =
|
result =
|
||||||
namespace + ";" //
|
namespace + ";" //
|
||||||
+ type + ";" //
|
+ type + ";" //
|
||||||
+ getCallableOverride(c) + ";" //
|
+ getCallableOverride(c) + ";" //
|
||||||
+ name + ";" //
|
+ name + ";" //
|
||||||
+ "(" + parameterQualifiedTypeNamesToString(c) + ")" + ";" //
|
+ parameters + ";" //
|
||||||
+ /* ext + */ ";" //
|
+ /* 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() }
|
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. */
|
/** Provides a query predicate for outputting a set of relevant flow summaries. */
|
||||||
module TestOutput {
|
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 {
|
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
|
||||||
/** Gets the string representation of this callable used by `summary/1`. */
|
/** Gets the string representation of this callable used by `summary/1`. */
|
||||||
abstract string getCallableCsv();
|
abstract string getCallableCsv();
|
||||||
@@ -1109,6 +1119,14 @@ module Private {
|
|||||||
string toString() { result = super.toString() }
|
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. */
|
/** Render the kind in the format used in flow summaries. */
|
||||||
private string renderKind(boolean preservesValue) {
|
private string renderKind(boolean preservesValue) {
|
||||||
preservesValue = true and result = "value"
|
preservesValue = true and result = "value"
|
||||||
@@ -1116,8 +1134,12 @@ module Private {
|
|||||||
preservesValue = false and result = "taint"
|
preservesValue = false and result = "taint"
|
||||||
}
|
}
|
||||||
|
|
||||||
private string renderProvenance(RelevantSummarizedCallable c) {
|
private string renderProvenance(SummarizedCallable c) {
|
||||||
if c.(SummarizedCallable).isAutoGenerated() then result = "generated" else result = "manual"
|
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
|
c.relevantSummary(input, output, preservesValue) and
|
||||||
csv =
|
csv =
|
||||||
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
|
c.getCallableCsv() // Callable information
|
||||||
";" + renderKind(preservesValue) + ";" + renderProvenance(c)
|
+ 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
|
* 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
|
* `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 csharp
|
||||||
private import semmle.code.csharp.dispatch.Dispatch
|
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 import ExternalApi
|
||||||
|
|
||||||
private predicate getRelevantUsages(ExternalApi api, int usages) {
|
private predicate getRelevantUsages(ExternalApi api, int usages) {
|
||||||
not api.isUninteresting() and
|
not api.isUninteresting() and
|
||||||
not api.isSupported() and
|
not api.isSupported() and
|
||||||
|
not api instanceof FlowSummaryImpl::Public::NegativeSummarizedCallable and
|
||||||
usages = strictcount(DispatchCall c | c = api.getACall())
|
usages = strictcount(DispatchCall c | c = api.getACall())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||||
private import internal.CaptureModels
|
private import internal.CaptureModels
|
||||||
private import internal.CaptureFlow
|
private import internal.CaptureSummaryFlow
|
||||||
|
|
||||||
from TargetApi api, string flow
|
from TargetApi api, string flow
|
||||||
where flow = captureFlow(api) and hasSummary(api, false)
|
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 semmle.code.csharp.dataflow.ExternalFlow
|
||||||
private import internal.CaptureModels
|
private import internal.CaptureModels
|
||||||
private import internal.CaptureFlow
|
private import internal.CaptureSummaryFlow
|
||||||
|
|
||||||
from TargetApi api, string flow
|
from TargetApi api, string flow
|
||||||
where flow = captureFlow(api) and not hasSummary(api, false)
|
where flow = captureFlow(api) and not hasSummary(api, false)
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ private string asSummaryModel(TargetApi api, string input, string output, string
|
|||||||
+ "generated"
|
+ "generated"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string asNegativeSummaryModel(TargetApi api) { result = asPartialNegativeModel(api) + "generated" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value summary model for `api` with `input` and `output`.
|
* 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
|
api.getDeclaringType().getNamespace().getQualifiedName() != "" and
|
||||||
not api instanceof CS::ConversionOperator and
|
not api instanceof CS::ConversionOperator and
|
||||||
not api instanceof Util::MainMethod 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 asPartialModel = DataFlowPrivate::Csv::asPartialModel/1;
|
||||||
|
|
||||||
|
predicate asPartialNegativeModel = DataFlowPrivate::Csv::asPartialNegativeModel/1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds for type `t` for fields that are relevant as an intermediate
|
* Holds for type `t` for fields that are relevant as an intermediate
|
||||||
* read or write step in the data flow analysis.
|
* read or write step in the data flow analysis.
|
||||||
|
|||||||
@@ -79,3 +79,12 @@ string captureFlow(TargetApi api) {
|
|||||||
result = captureQualifierFlow(api) or
|
result = captureQualifierFlow(api) or
|
||||||
result = captureThroughFlow(api)
|
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 csharp
|
||||||
import semmle.code.csharp.dataflow.ExternalFlow
|
|
||||||
import DataFlow::PathGraph
|
import DataFlow::PathGraph
|
||||||
|
import semmle.code.csharp.dataflow.ExternalFlow
|
||||||
import CsvValidation
|
import CsvValidation
|
||||||
|
|
||||||
class SummaryModelTest extends SummaryModelCsv {
|
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 csharp
|
||||||
import DataFlow
|
import DataFlow
|
||||||
import semmle.code.csharp.dataflow.ExternalFlow
|
import semmle.code.csharp.dataflow.ExternalFlow
|
||||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
|
||||||
import CsvValidation
|
import CsvValidation
|
||||||
|
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
|
|
||||||
class SinkModelTest extends SinkModelCsv {
|
class SinkModelTest extends SinkModelCsv {
|
||||||
override predicate row(string row) {
|
override predicate row(string row) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import csharp
|
import csharp
|
||||||
import DataFlow
|
import DataFlow
|
||||||
import semmle.code.csharp.dataflow.ExternalFlow
|
import semmle.code.csharp.dataflow.ExternalFlow
|
||||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
|
||||||
import CsvValidation
|
import CsvValidation
|
||||||
|
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
|
|
||||||
class SourceModelTest extends SourceModelCsv {
|
class SourceModelTest extends SourceModelCsv {
|
||||||
override predicate row(string row) {
|
override predicate row(string row) {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import csharp
|
import csharp
|
||||||
import DataFlow
|
import DataFlow
|
||||||
import semmle.code.csharp.dataflow.ExternalFlow
|
import semmle.code.csharp.dataflow.ExternalFlow
|
||||||
|
import CsvValidation
|
||||||
import semmle.code.csharp.dataflow.FlowSummary
|
import semmle.code.csharp.dataflow.FlowSummary
|
||||||
import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
import CsvValidation
|
|
||||||
|
|
||||||
private class SummaryModelTest extends SummaryModelCsv {
|
private class SummaryModelTest extends SummaryModelCsv {
|
||||||
override predicate row(string row) {
|
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
|
import shared.FlowSummaries
|
||||||
|
|
||||||
private class IncludeAllSummarizedCallable extends IncludeSummarizedCallable {
|
private class IncludeAllSummarizedCallable extends IncludeSummarizedCallable {
|
||||||
IncludeAllSummarizedCallable() { exists(this) }
|
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[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;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 |
|
| 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;ToString;();;Argument[this];ReturnValue;taint;generated |
|
||||||
| System;ValueTuple<>;false;ValueTuple;(T1);;Argument[0];Argument[this].Field[System.ValueTuple<>.Item1];value;manual |
|
| 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 |
|
| 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;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;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 |
|
| System.Data.Entity;DbSet<>;false;UpdateRange;(System.Collections.Generic.IEnumerable<T>);;Argument[0].Element;Argument[this].Element;value;manual |
|
||||||
|
negativeSummary
|
||||||
sourceNode
|
sourceNode
|
||||||
sinkNode
|
sinkNode
|
||||||
| EntityFrameworkCore.cs:72:36:72:40 | "sql" | sql |
|
| 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.FlowSummary
|
||||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private::TestOutput
|
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 {
|
abstract class IncludeSummarizedCallable extends RelevantSummarizedCallable {
|
||||||
IncludeSummarizedCallable() {
|
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
|
||||||
@@ -99,3 +99,15 @@ public class HigherOrderParameters
|
|||||||
return map(o);
|
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
|
public class BasicFlow
|
||||||
{
|
{
|
||||||
|
// No flow summary and no negative summary either.
|
||||||
|
~BasicFlow() { }
|
||||||
|
|
||||||
private string tainted;
|
private string tainted;
|
||||||
|
|
||||||
public BasicFlow ReturnThis(object input)
|
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.
|
* A unit class for adding additional source model rows.
|
||||||
*
|
*
|
||||||
@@ -121,20 +115,14 @@ class SummaryModelCsv extends Unit {
|
|||||||
abstract predicate row(string row);
|
abstract predicate row(string row);
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate sourceModel(string row) {
|
/** Holds if `row` is a source model. */
|
||||||
sourceModelCsv(row) or
|
predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
|
||||||
any(SourceModelCsv s).row(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate sinkModel(string row) {
|
/** Holds if `row` is a sink model. */
|
||||||
sinkModelCsv(row) or
|
predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||||
any(SinkModelCsv s).row(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate summaryModel(string row) {
|
/** Holds if `row` is a summary model. */
|
||||||
summaryModelCsv(row) or
|
predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||||
any(SummaryModelCsv s).row(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if a source model exists for the given parameters. */
|
/** Holds if a source model exists for the given parameters. */
|
||||||
predicate sourceModel(
|
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. */
|
/** Provides a query predicate to check the CSV data for validation errors. */
|
||||||
module CsvValidation {
|
module CsvValidation {
|
||||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
private string getInvalidModelInput() {
|
||||||
query predicate invalidModelRow(string msg) {
|
|
||||||
exists(string pred, string namespace, string type, string name, string signature, string ext |
|
|
||||||
sourceModel(namespace, type, _, name, signature, ext, _, _) and pred = "source"
|
|
||||||
or
|
|
||||||
sinkModel(namespace, type, _, name, signature, ext, _, _) and pred = "sink"
|
|
||||||
or
|
|
||||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "summary"
|
|
||||||
|
|
|
||||||
not namespace.regexpMatch("[a-zA-Z0-9_\\./]*") and
|
|
||||||
msg = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
|
||||||
or
|
|
||||||
not type.regexpMatch("[a-zA-Z0-9_\\$<>]*") and
|
|
||||||
msg = "Dubious type \"" + type + "\" in " + pred + " model."
|
|
||||||
or
|
|
||||||
not name.regexpMatch("[a-zA-Z0-9_]*") and
|
|
||||||
msg = "Dubious name \"" + name + "\" in " + pred + " model."
|
|
||||||
or
|
|
||||||
not signature.regexpMatch("|\\([a-zA-Z0-9_\\.\\$<>,\\[\\]]*\\)") and
|
|
||||||
msg = "Dubious signature \"" + signature + "\" in " + pred + " model."
|
|
||||||
or
|
|
||||||
not ext.regexpMatch("|Annotated") and
|
|
||||||
msg = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string pred, AccessPath input, string part |
|
exists(string pred, AccessPath input, string part |
|
||||||
sinkModel(_, _, _, _, _, _, input, _) and pred = "sink"
|
sinkModel(_, _, _, _, _, _, input, _) and pred = "sink"
|
||||||
or
|
or
|
||||||
@@ -309,9 +273,11 @@ module CsvValidation {
|
|||||||
part = input.getToken(_) and
|
part = input.getToken(_) and
|
||||||
parseParam(part, _)
|
parseParam(part, _)
|
||||||
) and
|
) 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 |
|
exists(string pred, string output, string part |
|
||||||
sourceModel(_, _, _, _, _, _, output, _) and pred = "source"
|
sourceModel(_, _, _, _, _, _, output, _) and pred = "source"
|
||||||
or
|
or
|
||||||
@@ -320,9 +286,35 @@ module CsvValidation {
|
|||||||
invalidSpecComponent(output, part) and
|
invalidSpecComponent(output, part) and
|
||||||
not part = "" and
|
not part = "" and
|
||||||
not (part = "Parameter" and pred = "source") 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 |
|
exists(string pred, string row, int expect |
|
||||||
sourceModel(row) and expect = 8 and pred = "source"
|
sourceModel(row) and expect = 8 and pred = "source"
|
||||||
or
|
or
|
||||||
@@ -333,18 +325,46 @@ module CsvValidation {
|
|||||||
exists(int cols |
|
exists(int cols |
|
||||||
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
||||||
cols != expect and
|
cols != expect and
|
||||||
msg =
|
result =
|
||||||
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
|
"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]
|
pragma[nomagic]
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
import go
|
import go
|
||||||
import semmle.go.dataflow.ExternalFlow
|
import semmle.go.dataflow.ExternalFlow
|
||||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
|
||||||
import CsvValidation
|
import CsvValidation
|
||||||
|
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
import TestUtilities.InlineFlowTest
|
import TestUtilities.InlineFlowTest
|
||||||
|
|
||||||
class SummaryModelTest extends SummaryModelCsv {
|
class SummaryModelTest extends SummaryModelCsv {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import go
|
import go
|
||||||
import semmle.go.dataflow.ExternalFlow
|
import semmle.go.dataflow.ExternalFlow
|
||||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
|
||||||
import CsvValidation
|
import CsvValidation
|
||||||
|
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
|
|
||||||
class SummaryModelTest extends SummaryModelCsv {
|
class SummaryModelTest extends SummaryModelCsv {
|
||||||
override predicate row(string row) {
|
override predicate row(string row) {
|
||||||
|
|||||||
@@ -5,11 +5,13 @@
|
|||||||
*
|
*
|
||||||
* The CSV specification has the following columns:
|
* The CSV specification has the following columns:
|
||||||
* - Sources:
|
* - Sources:
|
||||||
* `namespace; type; subtypes; name; signature; ext; output; kind`
|
* `namespace; type; subtypes; name; signature; ext; output; kind; provenance`
|
||||||
* - Sinks:
|
* - Sinks:
|
||||||
* `namespace; type; subtypes; name; signature; ext; input; kind`
|
* `namespace; type; subtypes; name; signature; ext; input; kind; provenance`
|
||||||
* - Summaries:
|
* - 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
|
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||||
* reading.
|
* reading.
|
||||||
@@ -152,234 +154,6 @@ private module Frameworks {
|
|||||||
private import semmle.code.java.frameworks.KotlinStdLib
|
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.
|
* A unit class for adding additional source model rows.
|
||||||
*
|
*
|
||||||
@@ -410,21 +184,262 @@ class SummaryModelCsv extends Unit {
|
|||||||
abstract predicate row(string row);
|
abstract predicate row(string row);
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate sourceModel(string row) {
|
/**
|
||||||
sourceModelCsv(row) or
|
* A unit class for adding negative summary model rows.
|
||||||
any(SourceModelCsv s).row(row)
|
*
|
||||||
|
* 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) {
|
private class SourceModelCsvBase extends SourceModelCsv {
|
||||||
sinkModelCsv(row) or
|
override predicate row(string row) {
|
||||||
any(SinkModelCsv s).row(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) {
|
private class SinkModelCsvBase extends SinkModelCsv {
|
||||||
summaryModelCsv(row) or
|
override predicate row(string row) {
|
||||||
any(SummaryModelCsv s).row(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. */
|
/** Holds if a source model exists for the given parameters. */
|
||||||
predicate sourceModel(
|
predicate sourceModel(
|
||||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||||
@@ -492,6 +507,20 @@ predicate summaryModel(
|
|||||||
row.splitAt(";", 9) = provenance
|
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) {
|
private predicate relevantPackage(string package) {
|
||||||
sourceModel(package, _, _, _, _, _, _, _, _) or
|
sourceModel(package, _, _, _, _, _, _, _, _) or
|
||||||
sinkModel(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. */
|
/** Provides a query predicate to check the CSV data for validation errors. */
|
||||||
module CsvValidation {
|
module CsvValidation {
|
||||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
private string getInvalidModelInput() {
|
||||||
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
|
|
||||||
exists(string pred, string input, string part |
|
exists(string pred, string input, string part |
|
||||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||||
or
|
or
|
||||||
@@ -594,9 +592,11 @@ module CsvValidation {
|
|||||||
part = input.(AccessPath).getToken(0) and
|
part = input.(AccessPath).getToken(0) and
|
||||||
parseParam(part, _)
|
parseParam(part, _)
|
||||||
) and
|
) 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 |
|
exists(string pred, string output, string part |
|
||||||
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
||||||
or
|
or
|
||||||
@@ -605,35 +605,15 @@ module CsvValidation {
|
|||||||
invalidSpecComponent(output, part) and
|
invalidSpecComponent(output, part) and
|
||||||
not part = "" and
|
not part = "" and
|
||||||
not (part = ["Argument", "Parameter"] and pred = "source") 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"
|
private string getInvalidModelKind() {
|
||||||
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
|
|
||||||
exists(string row, string kind | summaryModel(row) |
|
exists(string row, string kind | summaryModel(row) |
|
||||||
kind = row.splitAt(";", 8) and
|
kind = row.splitAt(";", 8) and
|
||||||
not kind = ["taint", "value"] and
|
not kind = ["taint", "value"] and
|
||||||
msg = "Invalid kind \"" + kind + "\" in summary model."
|
result = "Invalid kind \"" + kind + "\" in summary model."
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(string row, string kind | sinkModel(row) |
|
exists(string row, string kind | sinkModel(row) |
|
||||||
@@ -647,25 +627,110 @@ module CsvValidation {
|
|||||||
] and
|
] and
|
||||||
not kind.matches("regex-use%") and
|
not kind.matches("regex-use%") and
|
||||||
not kind.matches("qltest%") and
|
not kind.matches("qltest%") and
|
||||||
msg = "Invalid kind \"" + kind + "\" in sink model."
|
result = "Invalid kind \"" + kind + "\" in sink model."
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(string row, string kind | sourceModel(row) |
|
exists(string row, string kind | sourceModel(row) |
|
||||||
kind = row.splitAt(";", 7) and
|
kind = row.splitAt(";", 7) and
|
||||||
not kind = ["remote", "contentprovider", "android-widget", "android-external-storage-dir"] and
|
not kind = ["remote", "contentprovider", "android-widget", "android-external-storage-dir"] and
|
||||||
not kind.matches("qltest%") 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]
|
pragma[nomagic]
|
||||||
private predicate elementSpec(
|
private predicate elementSpec(
|
||||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||||
) {
|
) {
|
||||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
|
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _)
|
||||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
|
or
|
||||||
|
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _)
|
||||||
|
or
|
||||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
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) {
|
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(
|
Element interpretElement(
|
||||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -240,6 +240,16 @@ module Public {
|
|||||||
*/
|
*/
|
||||||
predicate isAutoGenerated() { none() }
|
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. */
|
/** Provides a query predicate for outputting a set of relevant flow summaries. */
|
||||||
module TestOutput {
|
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 {
|
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
|
||||||
/** Gets the string representation of this callable used by `summary/1`. */
|
/** Gets the string representation of this callable used by `summary/1`. */
|
||||||
abstract string getCallableCsv();
|
abstract string getCallableCsv();
|
||||||
@@ -1109,6 +1119,14 @@ module Private {
|
|||||||
string toString() { result = super.toString() }
|
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. */
|
/** Render the kind in the format used in flow summaries. */
|
||||||
private string renderKind(boolean preservesValue) {
|
private string renderKind(boolean preservesValue) {
|
||||||
preservesValue = true and result = "value"
|
preservesValue = true and result = "value"
|
||||||
@@ -1116,8 +1134,12 @@ module Private {
|
|||||||
preservesValue = false and result = "taint"
|
preservesValue = false and result = "taint"
|
||||||
}
|
}
|
||||||
|
|
||||||
private string renderProvenance(RelevantSummarizedCallable c) {
|
private string renderProvenance(SummarizedCallable c) {
|
||||||
if c.(SummarizedCallable).isAutoGenerated() then result = "generated" else result = "manual"
|
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
|
c.relevantSummary(input, output, preservesValue) and
|
||||||
csv =
|
csv =
|
||||||
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
|
c.getCallableCsv() // Callable information
|
||||||
";" + renderKind(preservesValue) + ";" + renderProvenance(c)
|
+ 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. */
|
/** Gets the summary component for specification component `c`, if any. */
|
||||||
bindingset[c]
|
bindingset[c]
|
||||||
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
||||||
|
|||||||
@@ -7,11 +7,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java
|
import java
|
||||||
|
import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
import ExternalApi
|
import ExternalApi
|
||||||
|
|
||||||
private predicate getRelevantUsages(ExternalApi api, int usages) {
|
private predicate getRelevantUsages(ExternalApi api, int usages) {
|
||||||
not api.isUninteresting() and
|
not api.isUninteresting() and
|
||||||
not api.isSupported() and
|
not api.isSupported() and
|
||||||
|
not api instanceof FlowSummaryImpl::Public::NegativeSummarizedCallable and
|
||||||
usages =
|
usages =
|
||||||
strictcount(Call c |
|
strictcount(Call c |
|
||||||
c.getCallee().getSourceDeclaration() = api and
|
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
|
private import internal.CaptureModels
|
||||||
|
private import internal.CaptureSummaryFlow
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
from TargetApi api, string flow
|
from TargetApi api, string flow
|
||||||
where flow = captureFlow(api)
|
where flow = captureFlow(api)
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ private string asSummaryModel(TargetApi api, string input, string output, string
|
|||||||
+ "generated"
|
+ "generated"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string asNegativeSummaryModel(TargetApi api) { result = asPartialNegativeModel(api) + "generated" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value summary model for `api` with `input` and `output`.
|
* Gets the value summary model for `api` with `input` and `output`.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -98,16 +98,38 @@ private string typeAsSummaryModel(TargetApiSpecific api) {
|
|||||||
result = typeAsModel(bestTypeForModel(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.
|
* Computes the first 6 columns for CSV rows.
|
||||||
*/
|
*/
|
||||||
string asPartialModel(TargetApiSpecific api) {
|
string asPartialModel(TargetApiSpecific api) {
|
||||||
result =
|
exists(string type, string name, string parameters |
|
||||||
typeAsSummaryModel(api) + ";" //
|
partialModel(api, type, name, parameters) and
|
||||||
+ isExtensible(bestTypeForModel(api)) + ";" //
|
result =
|
||||||
+ api.getName() + ";" //
|
type + ";" //
|
||||||
+ ExternalFlow::paramsString(api) + ";" //
|
+ isExtensible(bestTypeForModel(api)) + ";" //
|
||||||
+ /* ext + */ ";" //
|
+ 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) {
|
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 java
|
||||||
import semmle.code.java.dataflow.DataFlow
|
import semmle.code.java.dataflow.DataFlow
|
||||||
import semmle.code.java.dataflow.ExternalFlow
|
import semmle.code.java.dataflow.ExternalFlow
|
||||||
import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
|
||||||
import CsvValidation
|
import CsvValidation
|
||||||
|
import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
|
|
||||||
class SummaryModelTest extends SummaryModelCsv {
|
class SummaryModelTest extends SummaryModelCsv {
|
||||||
override predicate row(string row) {
|
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.generateSinks = False
|
||||||
self.generateSources = False
|
self.generateSources = False
|
||||||
self.generateSummaries = False
|
self.generateSummaries = False
|
||||||
|
self.generateNegativeSummaries = False
|
||||||
self.dryRun = 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
|
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
|
outputQll is the name (and path) of the output QLL file. Usually, models are grouped by their
|
||||||
respective frameworks.
|
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:
|
Which models are generated is controlled by the flags:
|
||||||
--with-sinks
|
--with-sinks
|
||||||
--with-sources
|
--with-sources
|
||||||
--with-summaries
|
--with-summaries
|
||||||
|
--with-negative-summaries
|
||||||
If none of these flags are specified, all models are generated.
|
If none of these flags are specified, all models are generated.
|
||||||
|
|
||||||
--dry-run: Only run the queries, but don't write to file.
|
--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()
|
self.codeQlRoot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
|
||||||
if not target.endswith(".qll"):
|
if not target.endswith(".qll"):
|
||||||
target += ".qll"
|
target += ".qll"
|
||||||
self.filename = os.path.basename(target)
|
filename = os.path.basename(target)
|
||||||
self.shortname = self.filename[:-4]
|
dirname = os.path.dirname(target)
|
||||||
|
self.shortname = filename[:-4]
|
||||||
self.database = database
|
self.database = database
|
||||||
self.generatedFrameworks = os.path.join(
|
self.generatedFrameworks = os.path.join(
|
||||||
self.codeQlRoot, f"{self.language}/ql/lib/semmle/code/{self.language}/frameworks/")
|
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()
|
self.workDir = tempfile.mkdtemp()
|
||||||
os.makedirs(self.generatedFrameworks, exist_ok=True)
|
os.makedirs(self.generatedFrameworks, exist_ok=True)
|
||||||
@@ -76,12 +81,16 @@ Requirements: `codeql` should both appear on your path.
|
|||||||
sys.argv.remove("--with-summaries")
|
sys.argv.remove("--with-summaries")
|
||||||
generator.generateSummaries = True
|
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:
|
if "--dry-run" in sys.argv:
|
||||||
sys.argv.remove("--dry-run")
|
sys.argv.remove("--dry-run")
|
||||||
generator.dryRun = True
|
generator.dryRun = True
|
||||||
|
|
||||||
if not generator.generateSinks and not generator.generateSources and not generator.generateSummaries:
|
if not generator.generateSinks and not generator.generateSources and not generator.generateSummaries and not generator.generateNegativeSummaries:
|
||||||
generator.generateSinks = generator.generateSources = generator.generateSummaries = True
|
generator.generateSinks = generator.generateSources = generator.generateSummaries = generator.generateNegativeSummaries = True
|
||||||
|
|
||||||
if len(sys.argv) != 3:
|
if len(sys.argv) != 3:
|
||||||
generator.printHelp()
|
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):
|
return f"""
|
||||||
with open(self.frameworkTarget, "w") as frameworkQll:
|
/**
|
||||||
frameworkQll.write(content)
|
* 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)
|
ret = subprocess.call(cmd)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
print("Failed to format query. Failed command was: " + shlex.join(cmd))
|
print("Failed to format query. Failed command was: " + shlex.join(cmd))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
print("CSV model written to " + self.frameworkTarget)
|
print("CSV model written to " + target)
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
content = self.makeContent()
|
content = self.makeContent()
|
||||||
|
negativeContent = self.makeNegativeContent()
|
||||||
|
|
||||||
if self.dryRun:
|
if self.dryRun:
|
||||||
print("CSV Models generated, but not written to file.")
|
print("CSV Models generated, but not written to file.")
|
||||||
sys.exit(0)
|
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. */
|
/** An instance variable. */
|
||||||
class InstanceVariable extends Variable instanceof InstanceVariableImpl {
|
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 predicate isClassInstanceVariable() { super.isClassInstanceVariable() }
|
||||||
|
|
||||||
final override InstanceVariableAccess getAnAccess() { result.getVariable() = this }
|
final override InstanceVariableAccess getAnAccess() { result.getVariable() = this }
|
||||||
|
|||||||
@@ -240,6 +240,16 @@ module Public {
|
|||||||
*/
|
*/
|
||||||
predicate isAutoGenerated() { none() }
|
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. */
|
/** Provides a query predicate for outputting a set of relevant flow summaries. */
|
||||||
module TestOutput {
|
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 {
|
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
|
||||||
/** Gets the string representation of this callable used by `summary/1`. */
|
/** Gets the string representation of this callable used by `summary/1`. */
|
||||||
abstract string getCallableCsv();
|
abstract string getCallableCsv();
|
||||||
@@ -1109,6 +1119,14 @@ module Private {
|
|||||||
string toString() { result = super.toString() }
|
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. */
|
/** Render the kind in the format used in flow summaries. */
|
||||||
private string renderKind(boolean preservesValue) {
|
private string renderKind(boolean preservesValue) {
|
||||||
preservesValue = true and result = "value"
|
preservesValue = true and result = "value"
|
||||||
@@ -1116,8 +1134,12 @@ module Private {
|
|||||||
preservesValue = false and result = "taint"
|
preservesValue = false and result = "taint"
|
||||||
}
|
}
|
||||||
|
|
||||||
private string renderProvenance(RelevantSummarizedCallable c) {
|
private string renderProvenance(SummarizedCallable c) {
|
||||||
if c.(SummarizedCallable).isAutoGenerated() then result = "generated" else result = "manual"
|
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
|
c.relevantSummary(input, output, preservesValue) and
|
||||||
csv =
|
csv =
|
||||||
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
|
c.getCallableCsv() // Callable information
|
||||||
";" + renderKind(preservesValue) + ";" + renderProvenance(c)
|
+ 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]
|
bindingset[arg]
|
||||||
private SummaryComponent interpretElementArg(string arg) {
|
private SummaryComponent interpretElementArg(string arg) {
|
||||||
arg = "?" and
|
arg = "?" and
|
||||||
|
|||||||
@@ -111,18 +111,14 @@ class SummaryModelCsv extends Unit {
|
|||||||
abstract predicate row(string row);
|
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) }
|
/** Holds if `row` is a summary model. */
|
||||||
|
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 a source model exists for the given parameters. */
|
/** Holds if a source model exists for the given parameters. */
|
||||||
predicate sourceModel(
|
predicate sourceModel(
|
||||||
@@ -139,8 +135,9 @@ predicate sourceModel(
|
|||||||
row.splitAt(";", 4) = signature and
|
row.splitAt(";", 4) = signature and
|
||||||
row.splitAt(";", 5) = ext and
|
row.splitAt(";", 5) = ext and
|
||||||
row.splitAt(";", 6) = output 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. */
|
/** Holds if a sink model exists for the given parameters. */
|
||||||
@@ -158,8 +155,9 @@ predicate sinkModel(
|
|||||||
row.splitAt(";", 4) = signature and
|
row.splitAt(";", 4) = signature and
|
||||||
row.splitAt(";", 5) = ext and
|
row.splitAt(";", 5) = ext and
|
||||||
row.splitAt(";", 6) = input 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. */
|
/** Holds if a summary model exists for the given parameters. */
|
||||||
@@ -178,8 +176,9 @@ predicate summaryModel(
|
|||||||
row.splitAt(";", 5) = ext and
|
row.splitAt(";", 5) = ext and
|
||||||
row.splitAt(";", 6) = input and
|
row.splitAt(";", 6) = input and
|
||||||
row.splitAt(";", 7) = output 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) {
|
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. */
|
/** Provides a query predicate to check the CSV data for validation errors. */
|
||||||
module CsvValidation {
|
module CsvValidation {
|
||||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
private string getInvalidModelInput() {
|
||||||
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
|
|
||||||
exists(string pred, AccessPath input, string part |
|
exists(string pred, AccessPath input, string part |
|
||||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||||
or
|
or
|
||||||
@@ -277,9 +252,11 @@ module CsvValidation {
|
|||||||
part = input.getToken(_) and
|
part = input.getToken(_) and
|
||||||
parseParam(part, _)
|
parseParam(part, _)
|
||||||
) and
|
) 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 |
|
exists(string pred, string output, string part |
|
||||||
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
||||||
or
|
or
|
||||||
@@ -288,9 +265,35 @@ module CsvValidation {
|
|||||||
invalidSpecComponent(output, part) and
|
invalidSpecComponent(output, part) and
|
||||||
not part = "" and
|
not part = "" and
|
||||||
not (part = ["Argument", "Parameter"] and pred = "source") 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 |
|
exists(string pred, string row, int expect |
|
||||||
sourceModel(row) and expect = 8 and pred = "source"
|
sourceModel(row) and expect = 8 and pred = "source"
|
||||||
or
|
or
|
||||||
@@ -301,39 +304,46 @@ module CsvValidation {
|
|||||||
exists(int cols |
|
exists(int cols |
|
||||||
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
||||||
cols != expect and
|
cols != expect and
|
||||||
msg =
|
result =
|
||||||
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
|
"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
|
or
|
||||||
exists(string b |
|
sinkModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "sink"
|
||||||
b = row.splitAt(";", 2) and
|
or
|
||||||
not b = ["true", "false"] and
|
summaryModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "summary"
|
||||||
msg = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
|
|
||||||
)
|
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
|
||||||
)
|
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||||
or
|
or
|
||||||
exists(string row, string k, string kind | summaryModel(row) |
|
not type.regexpMatch("[a-zA-Z0-9_<>,\\+]+") and
|
||||||
k = row.splitAt(";", 8) and
|
result = "Dubious type \"" + type + "\" in " + pred + " model."
|
||||||
getKind(k, kind, _) and
|
or
|
||||||
not kind = ["taint", "value"] and
|
not name.regexpMatch("[a-zA-Z0-9_<>,]*") and
|
||||||
msg = "Invalid kind \"" + kind + "\" in summary model."
|
result = "Dubious member name \"" + name + "\" in " + pred + " model."
|
||||||
)
|
or
|
||||||
or
|
not signature.regexpMatch("|\\([a-zA-Z0-9_<>\\.\\+\\*,\\[\\]]*\\)") and
|
||||||
exists(string row, string k, string kind | sinkModel(row) |
|
result = "Dubious signature \"" + signature + "\" in " + pred + " model."
|
||||||
k = row.splitAt(";", 7) and
|
or
|
||||||
getKind(k, kind, _) and
|
not ext.regexpMatch("|Attribute") and
|
||||||
not kind = ["code", "sql", "xss", "remote", "html"] and
|
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
|
||||||
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."
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 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(
|
private predicate elementSpec(
|
||||||
|
|||||||
@@ -240,6 +240,16 @@ module Public {
|
|||||||
*/
|
*/
|
||||||
predicate isAutoGenerated() { none() }
|
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. */
|
/** Provides a query predicate for outputting a set of relevant flow summaries. */
|
||||||
module TestOutput {
|
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 {
|
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
|
||||||
/** Gets the string representation of this callable used by `summary/1`. */
|
/** Gets the string representation of this callable used by `summary/1`. */
|
||||||
abstract string getCallableCsv();
|
abstract string getCallableCsv();
|
||||||
@@ -1109,6 +1119,14 @@ module Private {
|
|||||||
string toString() { result = super.toString() }
|
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. */
|
/** Render the kind in the format used in flow summaries. */
|
||||||
private string renderKind(boolean preservesValue) {
|
private string renderKind(boolean preservesValue) {
|
||||||
preservesValue = true and result = "value"
|
preservesValue = true and result = "value"
|
||||||
@@ -1116,8 +1134,12 @@ module Private {
|
|||||||
preservesValue = false and result = "taint"
|
preservesValue = false and result = "taint"
|
||||||
}
|
}
|
||||||
|
|
||||||
private string renderProvenance(RelevantSummarizedCallable c) {
|
private string renderProvenance(SummarizedCallable c) {
|
||||||
if c.(SummarizedCallable).isAutoGenerated() then result = "generated" else result = "manual"
|
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
|
c.relevantSummary(input, output, preservesValue) and
|
||||||
csv =
|
csv =
|
||||||
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
|
c.getCallableCsv() // Callable information
|
||||||
";" + renderKind(preservesValue) + ";" + renderProvenance(c)
|
+ 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
|
* 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
|
* `output`, kind `kind`, and a flag `generated` stating whether the source specification is
|
||||||
|
|||||||
Reference in New Issue
Block a user