Initial support for source models

This commit is contained in:
Benjamin Muskalla
2021-09-28 15:04:07 +02:00
parent c844f5382f
commit a1d8dfb524
9 changed files with 93 additions and 8 deletions

View File

@@ -33,5 +33,5 @@ string captureSink(Callable api) {
from Callable api, string sink
where
sink = captureSink(api) and
not api.getCompilationUnit().getFile().getAbsolutePath().matches("%src/test/%")
not isInTestFile(api)
select sink order by sink

View File

@@ -0,0 +1,43 @@
import java
import Telemetry.ExternalAPI
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.ExternalFlow
import ModelGeneratorUtils
private import semmle.code.java.dataflow.internal.FlowSummaryImplSpecific
private import semmle.code.java.dataflow.internal.FlowSummaryImpl
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "Configuration" }
override predicate isSource(DataFlow::Node source) { sourceNode(source, _) }
override predicate isSink(DataFlow::Node sink) {
exists(Callable c |
sink.asExpr().getEnclosingCallable() = c and
c.isPublic() and
c.fromSource()
)
}
}
// TODO: internals
cached
predicate specificSourceNode(DataFlow::Node node, string output, string kind) {
exists(InterpretNode n | Private::External::isSourceNode(n, output, kind) and n.asNode() = node)
}
string captureSink(Callable api) {
exists(DataFlow::Node src, DataFlow::Node sink, Configuration config, string kind, string output |
config.hasFlow(src, sink) and
specificSourceNode(sink, output, kind) and
api = src.asExpr().getEnclosingCallable() and
result = asSourceModel(api, output, kind)
)
}
from Callable api, string sink
where
sink = captureSink(api) and
not isInTestFile(api)
select sink order by sink

View File

@@ -140,8 +140,7 @@ class TargetAPI extends Callable {
this.isPublic() and
this.fromSource() and
this.getDeclaringType().isPublic() and
not this.getCompilationUnit().getFile().getAbsolutePath().matches("%src/test/%") and
not this.getCompilationUnit().getFile().getAbsolutePath().matches("%src/guava-tests/%")
not isInTestFile(this)
}
}

View File

@@ -102,6 +102,8 @@ private class {0}{1}Csv extends {2} {{
}}
}}
"""
if rows.strip() == "":
return ""
return classTemplate.format(shortname.capitalize(), kind.capitalize(), superclass, rows)
@@ -111,6 +113,8 @@ summaryCsv = asCsvModel("SummaryModelCsv", "summary", summaryRows)
sinkRows = runQuery("sink models", "CaptureSinkModels.ql")
sinkCsv = asCsvModel("SinkModelCsv", "sinks", sinkRows)
sourceRows = runQuery("source models", "CaptureSourceModels.ql")
sourceCsv = asCsvModel("SourceModelCsv", "sources", sourceRows)
qllTemplate = """
/** Definitions of taint steps in the {0} framework */
@@ -120,12 +124,16 @@ private import semmle.code.java.dataflow.ExternalFlow
{1}
{2}
{3}
"""
qllContents = qllTemplate.format(shortname, summaryCsv, sinkCsv)
qllContents = qllTemplate.format(shortname, summaryCsv, sinkCsv, sourceCsv)
with open(frameworkTarget, "w") as frameworkQll:
frameworkQll.write(qllContents)
print("")
print("CSV model written to " + frameworkTarget)

View File

@@ -29,6 +29,13 @@ string asSinkModel(Callable api, string input, string kind) {
+ kind + ";" //
}
bindingset[output, kind]
string asSourceModel(Callable api, string output, string kind) {
result =
asPartialModel(api) + output + ";" //
+ kind + ";" //
}
/**
* Computes the first 6 columns for CSV rows.
*/
@@ -50,3 +57,8 @@ string parameterAccess(Parameter p) {
then result = "Element of Argument[" + p.getPosition() + "]"
else result = "Argument[" + p.getPosition() + "]"
}
predicate isInTestFile(Callable api) {
api.getCompilationUnit().getFile().getAbsolutePath().matches("%src/test/%") or
api.getCompilationUnit().getFile().getAbsolutePath().matches("%src/guava-tests/%")
}