Merge pull request #16722 from michaelnebel/csharp/modelgensourcesink

C#/Java: Respect manual neutrals, sources and sinks in model generation.
This commit is contained in:
Michael Nebel
2024-06-25 15:55:06 +02:00
committed by GitHub
24 changed files with 204 additions and 88 deletions

View File

@@ -9,6 +9,6 @@
import internal.CaptureModels
import internal.CaptureSummaryFlowQuery
from DataFlowTargetApi api, string noflow
from DataFlowSummaryTargetApi api, string noflow
where noflow = captureNoFlow(api)
select noflow order by noflow

View File

@@ -8,6 +8,6 @@
import internal.CaptureModels
from DataFlowTargetApi api, string sink
from DataFlowSinkTargetApi api, string sink
where sink = captureSink(api)
select sink order by sink

View File

@@ -8,6 +8,6 @@
import internal.CaptureModels
from DataFlowTargetApi api, string source
from DataFlowSourceTargetApi api, string source
where source = captureSource(api)
select source order by source

View File

@@ -9,6 +9,6 @@
import internal.CaptureModels
import internal.CaptureSummaryFlowQuery
from DataFlowTargetApi api, string flow
from DataFlowSummaryTargetApi api, string flow
where flow = captureFlow(api)
select flow order by flow

View File

@@ -29,12 +29,16 @@ private class ReturnNodeExt extends DataFlow::Node {
}
}
class DataFlowTargetApi extends TargetApiSpecific {
DataFlowTargetApi() { not isUninterestingForDataFlowModels(this) }
class DataFlowSummaryTargetApi extends SummaryTargetApi {
DataFlowSummaryTargetApi() { not isUninterestingForDataFlowModels(this) }
}
class DataFlowSourceTargetApi = SourceTargetApi;
class DataFlowSinkTargetApi = SinkTargetApi;
private module ModelPrintingInput implements ModelPrintingSig {
class Api = DataFlowTargetApi;
class Api = TargetApiBase;
string getProvenance() { result = "df-generated" }
}
@@ -89,7 +93,7 @@ string asInputArgument(DataFlow::Node source) { result = asInputArgumentSpecific
/**
* Gets the summary model of `api`, if it follows the `fluent` programming pattern (returns `this`).
*/
string captureQualifierFlow(TargetApiSpecific api) {
string captureQualifierFlow(DataFlowSummaryTargetApi api) {
exists(ReturnNodeExt ret |
api = returnNodeEnclosingCallable(ret) and
isOwnInstanceAccessNode(ret)
@@ -150,7 +154,7 @@ module PropagateFlowConfig implements DataFlow::StateConfigSig {
predicate isSource(DataFlow::Node source, FlowState state) {
source instanceof DataFlow::ParameterNode and
source.getEnclosingCallable() instanceof DataFlowTargetApi and
source.getEnclosingCallable() instanceof DataFlowSummaryTargetApi and
state.(TaintRead).getStep() = 0
}
@@ -195,7 +199,7 @@ private module PropagateFlow = TaintTracking::GlobalWithState<PropagateFlowConfi
/**
* Gets the summary model(s) of `api`, if there is flow from parameters to return value or parameter.
*/
string captureThroughFlow(DataFlowTargetApi api) {
string captureThroughFlow(DataFlowSummaryTargetApi api) {
exists(DataFlow::ParameterNode p, ReturnNodeExt returnNodeExt, string input, string output |
PropagateFlow::flow(p, returnNodeExt) and
returnNodeExt.(DataFlow::Node).getEnclosingCallable() = api and
@@ -222,10 +226,8 @@ module PropagateFromSourceConfig implements DataFlow::ConfigSig {
}
predicate isSink(DataFlow::Node sink) {
exists(DataFlowTargetApi c |
sink instanceof ReturnNodeExt and
sink.getEnclosingCallable() = c
)
sink instanceof ReturnNodeExt and
sink.getEnclosingCallable() instanceof DataFlowSourceTargetApi
}
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSinkCallContext }
@@ -244,7 +246,7 @@ private module PropagateFromSource = TaintTracking::Global<PropagateFromSourceCo
/**
* Gets the source model(s) of `api`, if there is flow from an existing known source to the return of `api`.
*/
string captureSource(DataFlowTargetApi api) {
string captureSource(DataFlowSourceTargetApi api) {
exists(DataFlow::Node source, ReturnNodeExt sink, string kind |
PropagateFromSource::flow(source, sink) and
ExternalFlow::sourceNode(source, kind) and
@@ -262,7 +264,9 @@ string captureSource(DataFlowTargetApi api) {
* into an existing known sink (then the API itself becomes a sink).
*/
module PropagateToSinkConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { apiSource(source) }
predicate isSource(DataFlow::Node source) {
apiSource(source) and source.getEnclosingCallable() instanceof DataFlowSinkTargetApi
}
predicate isSink(DataFlow::Node sink) {
exists(string kind | isRelevantSinkKind(kind) and ExternalFlow::sinkNode(sink, kind))
@@ -286,7 +290,7 @@ private module PropagateToSink = TaintTracking::Global<PropagateToSinkConfig>;
/**
* Gets the sink model(s) of `api`, if there is flow from a parameter to an existing known sink.
*/
string captureSink(DataFlowTargetApi api) {
string captureSink(DataFlowSinkTargetApi api) {
exists(DataFlow::Node src, DataFlow::Node sink, string kind |
PropagateToSink::flow(src, sink) and
ExternalFlow::sinkNode(sink, kind) and

View File

@@ -58,11 +58,21 @@ private J::Callable liftedImpl(J::Callable m) {
not exists(getARelevantOverride(result))
}
private predicate hasManualModel(Callable api) {
private predicate hasManualSummaryModel(Callable api) {
api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()).asCallable() or
api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel()).asCallable()
}
private predicate hasManualSourceModel(Callable api) {
api = any(ExternalFlow::SourceCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel()).asCallable()
}
private predicate hasManualSinkModel(Callable api) {
api = any(ExternalFlow::SinkCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel()).asCallable()
}
/**
* Holds if it is irrelevant to generate models for `api` based on data flow analysis.
*
@@ -72,6 +82,28 @@ predicate isUninterestingForDataFlowModels(Callable api) {
api.getDeclaringType() instanceof J::Interface and not exists(api.getBody())
}
/**
* A class of callables that are potentially relevant for generating summary and
* neutral models.
*/
class SummaryTargetApi extends TargetApiBase {
SummaryTargetApi() { not hasManualSummaryModel(this.lift()) }
}
/**
* A class of callables that are potentially relevant for generating sink models.
*/
class SinkTargetApi extends TargetApiBase {
SinkTargetApi() { not hasManualSinkModel(this.lift()) }
}
/**
* A class of callables that are potentially relevant for generating source models.
*/
class SourceTargetApi extends TargetApiBase {
SourceTargetApi() { not hasManualSourceModel(this.lift()) }
}
/**
* Holds if it is irrelevant to generate models for `api` based on type-based analysis.
*
@@ -86,13 +118,10 @@ predicate isUninterestingForTypeBasedFlowModels(Callable api) { none() }
* In the Standard library and 3rd party libraries it is the callables (or callables that have a
* super implementation) that can be called from outside the library itself.
*/
class TargetApiSpecific extends Callable {
class TargetApiBase extends Callable {
private Callable lift;
TargetApiSpecific() {
lift = liftedImpl(this) and
not hasManualModel(lift)
}
TargetApiBase() { lift = liftedImpl(this) }
/**
* Gets the callable that a model will be lifted to.
@@ -222,15 +251,6 @@ predicate sinkModelSanitizer(DataFlow::Node node) {
)
}
private class ManualNeutralSinkCallable extends Callable {
ManualNeutralSinkCallable() {
this =
any(FlowSummaryImpl::Public::NeutralCallable nc |
nc.hasManualModel() and nc.getKind() = "sink"
).asCallable()
}
}
/**
* Holds if `source` is an api entrypoint relevant for creating sink models.
*/
@@ -239,14 +259,10 @@ predicate apiSource(DataFlow::Node source) {
source.asExpr().(J::FieldAccess).isOwnFieldAccess() or
source instanceof DataFlow::ParameterNode
) and
exists(Callable enclosing | enclosing = source.getEnclosingCallable() |
exists(liftedImpl(enclosing)) and
not enclosing instanceof ManualNeutralSinkCallable and
exists(J::RefType t |
t = enclosing.getDeclaringType().getAnAncestor() and
not t instanceof J::TypeObject and
t.isPublic()
)
exists(J::RefType t |
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
not t instanceof J::TypeObject and
t.isPublic()
)
}
@@ -254,7 +270,7 @@ predicate apiSource(DataFlow::Node source) {
* Holds if it is not relevant to generate a source model for `api`, even
* if flow is detected from a node within `source` to a sink within `api`.
*/
predicate irrelevantSourceSinkApi(Callable source, TargetApiSpecific api) { none() }
predicate irrelevantSourceSinkApi(Callable source, SourceTargetApi api) { none() }
/**
* Gets the MaD input string representation of `source`.

View File

@@ -67,7 +67,7 @@ private import CaptureModels
* Captured Model:
* ```p;Foo;true;addToList;;Argument[0];Argument[1];taint;df-generated```
*/
string captureFlow(DataFlowTargetApi api) {
string captureFlow(DataFlowSummaryTargetApi api) {
result = captureQualifierFlow(api) or
result = captureThroughFlow(api)
}
@@ -77,8 +77,8 @@ string captureFlow(DataFlowTargetApi api) {
* A neutral summary model is generated, if we are not generating
* a summary model that applies to `api`.
*/
string captureNoFlow(DataFlowTargetApi api) {
not exists(DataFlowTargetApi api0 | exists(captureFlow(api0)) and api0.lift() = api.lift()) and
string captureNoFlow(DataFlowSummaryTargetApi api) {
not exists(DataFlowSummaryTargetApi api0 | exists(captureFlow(api0)) and api0.lift() = api.lift()) and
api.isRelevant() and
result = Printing::asNeutralSummaryModel(api)
}

View File

@@ -295,7 +295,7 @@ private module Printing = ModelPrinting<ModelPrintingInput>;
* A class of callables that are relevant generating summaries for based
* on the Theorems for Free approach.
*/
class TypeBasedFlowTargetApi extends Specific::TargetApiSpecific {
class TypeBasedFlowTargetApi extends Specific::SummaryTargetApi {
TypeBasedFlowTargetApi() { not Specific::isUninterestingForTypeBasedFlowModels(this) }
/**

View File

@@ -5,6 +5,7 @@ extensions:
extensible: sinkModel
data:
- [ "p", "Sinks", False, "sink", "(Object)", "", "Argument[0]", "test-sink", "manual" ]
- [ "p", "Sinks", False, "manualSinkAlreadyDefined", "(Object)", "", "Argument[0]", "test-sink", "manual" ]
- addsTo:
pack: codeql/java-all

View File

@@ -5,3 +5,10 @@ extensions:
extensible: sourceModel
data:
- [ "p", "Sources", False, "source", "()", "", "ReturnValue", "test-source", "manual" ]
- [ "p", "Sources", False, "manualSourceAlreadyDefined", "()", "", "ReturnValue", "test-source", "manual" ]
- addsTo:
pack: codeql/java-all
extensible: neutralModel
data:
- ["p", "Sources", "manualNeutralSource", "()", "source", "manual"]

View File

@@ -71,4 +71,10 @@ public class Sinks {
Boolean b = s == "hello";
sink(b);
}
// Not a new sink as this callable already has a manual sink.
// neutral=p;Sinks;manualSinkAlreadyDefined;(Object);summary;df-generated
public void manualSinkAlreadyDefined(Object o) {
sink(o);
}
}

View File

@@ -66,4 +66,17 @@ public class Sources {
return value.toString();
}
}
// Not a new source as this callable has been manually modelled
// as source neutral.
// neutral=p;Sources;manualNeutralSource;();summary;df-generated
public String manualNeutralSource() {
return source();
}
// Not a new source as this callable already has a manual source.
// neutral=p;Sources;manualSourceAlreadyDefined;();summary;df-generated
public String manualSourceAlreadyDefined() {
return source();
}
}