mirror of
https://github.com/github/codeql.git
synced 2026-04-22 15:25:18 +02:00
Merge pull request #16722 from michaelnebel/csharp/modelgensourcesink
C#/Java: Respect manual neutrals, sources and sinks in model generation.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user