mirror of
https://github.com/github/codeql.git
synced 2026-04-22 23:35:14 +02:00
Merge branch 'main' into identity-consistency-check
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
## 1.5.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.5.0
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.5.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.0
|
||||
lastReleaseVersion: 1.5.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.5.1-dev
|
||||
version: 1.5.2-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.5.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.5.0
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.5.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.0
|
||||
lastReleaseVersion: 1.5.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.5.1-dev
|
||||
version: 1.5.2-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.6.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
3
csharp/ql/lib/change-notes/released/0.6.1.md
Normal file
3
csharp/ql/lib/change-notes/released/0.6.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.0
|
||||
lastReleaseVersion: 0.6.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 0.6.1-dev
|
||||
version: 0.6.2-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -12,9 +12,9 @@ class Attribute extends Element, @cil_attribute {
|
||||
Method getConstructor() { cil_attribute(this, _, result) }
|
||||
|
||||
/** Gets the type of this attribute. */
|
||||
Type getType() { result = getConstructor().getDeclaringType() }
|
||||
Type getType() { result = this.getConstructor().getDeclaringType() }
|
||||
|
||||
override string toString() { result = "[" + getType().getName() + "(...)]" }
|
||||
override string toString() { result = "[" + this.getType().getName() + "(...)]" }
|
||||
|
||||
/** Gets the value of the `i`th argument of this attribute. */
|
||||
string getArgument(int i) { cil_attribute_positional_argument(this, i, result) }
|
||||
@@ -23,9 +23,9 @@ class Attribute extends Element, @cil_attribute {
|
||||
string getNamedArgument(string name) { cil_attribute_named_argument(this, name, result) }
|
||||
|
||||
/** Gets an argument of this attribute, if any. */
|
||||
string getAnArgument() { result = getArgument(_) or result = getNamedArgument(_) }
|
||||
string getAnArgument() { result = this.getArgument(_) or result = this.getNamedArgument(_) }
|
||||
|
||||
override CS::Location getLocation() { result = getDeclaration().getLocation() }
|
||||
override CS::Location getLocation() { result = this.getDeclaration().getLocation() }
|
||||
}
|
||||
|
||||
/** A generic attribute to a declaration. */
|
||||
|
||||
@@ -1,156 +1,183 @@
|
||||
/**
|
||||
* Provides classes for performing global (inter-procedural)
|
||||
* content-sensitive data flow analyses.
|
||||
*
|
||||
* Unlike `DataFlow::Global`, we allow for data to be stored (possibly nested) inside
|
||||
* contents of sources and sinks.
|
||||
* We track flow paths of the form
|
||||
*
|
||||
* ```
|
||||
* source --value-->* node
|
||||
* (--read--> node --value-->* node)*
|
||||
* --(non-value|value)-->* node
|
||||
* (--store--> node --value-->* node)*
|
||||
* --value-->* sink
|
||||
* ```
|
||||
*
|
||||
* where `--value-->` is a value-preserving flow step, `--read-->` is a read
|
||||
* step, `--store-->` is a store step, and `--(non-value)-->` is a
|
||||
* non-value-preserving flow step.
|
||||
*
|
||||
* That is, first a sequence of 0 or more reads, followed by 0 or more additional
|
||||
* steps, followed by 0 or more stores, with value-preserving steps allowed in
|
||||
* between all other steps.
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import codeql.util.Boolean
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Private as DataFlowPrivate
|
||||
|
||||
module ContentDataFlow {
|
||||
private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Private as DataFlowPrivate
|
||||
private import DataFlowImplForContentDataFlow as DF
|
||||
|
||||
class Node = DF::Node;
|
||||
|
||||
class FlowFeature = DF::FlowFeature;
|
||||
|
||||
class ContentSet = DF::ContentSet;
|
||||
|
||||
// predicate stageStats = DF::stageStats/8;
|
||||
/**
|
||||
* An input configuration for content data flow.
|
||||
*/
|
||||
signature module ConfigSig {
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends ContentDataFlowConfiguration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* // Optionally override `getAFeature`.
|
||||
* // Optionally override `accessPathLimit`.
|
||||
* // Optionally override `isRelevantContent`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Unlike `DataFlow::Configuration` (on which this class is based), we allow
|
||||
* for data to be stored (possibly nested) inside contents of sources and sinks.
|
||||
* We track flow paths of the form
|
||||
*
|
||||
* ```
|
||||
* source --value-->* node
|
||||
* (--read--> node --value-->* node)*
|
||||
* --(non-value|value)-->* node
|
||||
* (--store--> node --value-->* node)*
|
||||
* --value-->* sink
|
||||
* ```
|
||||
*
|
||||
* where `--value-->` is a value-preserving flow step, `--read-->` is a read
|
||||
* step, `--store-->` is a store step, and `--(non-value)-->` is a
|
||||
* non-value-preserving flow step.
|
||||
*
|
||||
* That is, first a sequence of 0 or more reads, followed by 0 or more additional
|
||||
* steps, followed by 0 or more stores, with value-preserving steps allowed in
|
||||
* between all other steps.
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
abstract predicate isSource(Node source);
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
abstract predicate isSink(Node sink);
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
default predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
default predicate isBarrier(DataFlow::Node node) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrier(Node node) { none() }
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
default DataFlow::FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
/** Gets a limit on the number of reads out of sources and number of stores into sinks. */
|
||||
default int accessPathLimit() { result = DataFlowPrivate::accessPathLimit() }
|
||||
|
||||
/** Gets a limit on the number of reads out of sources and number of stores into sinks. */
|
||||
int accessPathLimit() { result = DataFlowPrivate::accessPathLimit() }
|
||||
/** Holds if `c` is relevant for reads out of sources or stores into sinks. */
|
||||
default predicate isRelevantContent(DataFlow::ContentSet c) { any() }
|
||||
}
|
||||
|
||||
/** Holds if `c` is relevant for reads out of sources or stores into sinks. */
|
||||
predicate isRelevantContent(ContentSet c) { any() }
|
||||
/**
|
||||
* Constructs a global content data flow computation.
|
||||
*/
|
||||
module Global<ConfigSig ContentConfig> {
|
||||
private module FlowConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState = State;
|
||||
|
||||
/**
|
||||
* Holds if data stored inside `sourceAp` on `source` flows to `sinkAp` inside `sink`
|
||||
* for this configuration. `preservesValue` indicates whether any of the additional
|
||||
* flow steps defined by `isAdditionalFlowStep` are needed.
|
||||
*
|
||||
* For the source access path, `sourceAp`, the top of the stack represents the content
|
||||
* that was last read from. That is, if `sourceAp` is `Field1.Field2` (with `Field1`
|
||||
* being the top of the stack), then there is flow from `source.Field2.Field1`.
|
||||
*
|
||||
* For the sink access path, `sinkAp`, the top of the stack represents the content
|
||||
* that was last stored into. That is, if `sinkAp` is `Field1.Field2` (with `Field1`
|
||||
* being the top of the stack), then there is flow into `sink.Field1.Field2`.
|
||||
*/
|
||||
final predicate hasFlow(
|
||||
Node source, AccessPath sourceAp, Node sink, AccessPath sinkAp, boolean preservesValue
|
||||
) {
|
||||
exists(DF::PathNode pathSource, DF::PathNode pathSink |
|
||||
this.(ConfigurationAdapter).hasFlowPath(pathSource, pathSink) and
|
||||
nodeReaches(pathSource, TAccessPathNil(), TAccessPathNil(), pathSink, sourceAp, sinkAp) and
|
||||
source = pathSource.getNode() and
|
||||
sink = pathSink.getNode()
|
||||
|
|
||||
pathSink.getState().(InitState).decode(preservesValue)
|
||||
or
|
||||
pathSink.getState().(ReadState).decode(_, preservesValue)
|
||||
or
|
||||
pathSink.getState().(StoreState).decode(_, preservesValue)
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
ContentConfig::isSource(source) and
|
||||
state.(InitState).decode(true)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
ContentConfig::isSink(sink) and
|
||||
(
|
||||
state instanceof InitState or
|
||||
state instanceof StoreState or
|
||||
state instanceof ReadState
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
storeStep(node1, state1, _, node2, state2) or
|
||||
readStep(node1, state1, _, node2, state2) or
|
||||
additionalStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep = ContentConfig::isAdditionalFlowStep/2;
|
||||
|
||||
predicate isBarrier = ContentConfig::isBarrier/1;
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
|
||||
|
||||
DataFlow::FlowFeature getAFeature() { result = ContentConfig::getAFeature() }
|
||||
|
||||
// needed to record reads/stores inside summarized callables
|
||||
predicate includeHiddenNodes() { any() }
|
||||
}
|
||||
|
||||
private module Flow = DataFlow::GlobalWithState<FlowConfig>;
|
||||
|
||||
/**
|
||||
* Holds if data stored inside `sourceAp` on `source` flows to `sinkAp` inside `sink`
|
||||
* for this configuration. `preservesValue` indicates whether any of the additional
|
||||
* flow steps defined by `isAdditionalFlowStep` are needed.
|
||||
*
|
||||
* For the source access path, `sourceAp`, the top of the stack represents the content
|
||||
* that was last read from. That is, if `sourceAp` is `Field1.Field2` (with `Field1`
|
||||
* being the top of the stack), then there is flow from `source.Field2.Field1`.
|
||||
*
|
||||
* For the sink access path, `sinkAp`, the top of the stack represents the content
|
||||
* that was last stored into. That is, if `sinkAp` is `Field1.Field2` (with `Field1`
|
||||
* being the top of the stack), then there is flow into `sink.Field1.Field2`.
|
||||
*/
|
||||
predicate flow(
|
||||
DataFlow::Node source, AccessPath sourceAp, DataFlow::Node sink, AccessPath sinkAp,
|
||||
boolean preservesValue
|
||||
) {
|
||||
exists(Flow::PathNode pathSource, Flow::PathNode pathSink |
|
||||
Flow::flowPath(pathSource, pathSink) and
|
||||
nodeReaches(pathSource, TAccessPathNil(), TAccessPathNil(), pathSink, sourceAp, sinkAp) and
|
||||
source = pathSource.getNode() and
|
||||
sink = pathSink.getNode()
|
||||
|
|
||||
pathSink.getState().(InitState).decode(preservesValue)
|
||||
or
|
||||
pathSink.getState().(ReadState).decode(_, preservesValue)
|
||||
or
|
||||
pathSink.getState().(StoreState).decode(_, preservesValue)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TState =
|
||||
TInitState(Boolean preservesValue) or
|
||||
TStoreState(int size, Boolean preservesValue) {
|
||||
size in [1 .. ContentConfig::accessPathLimit()]
|
||||
} or
|
||||
TReadState(int size, Boolean preservesValue) { size in [1 .. ContentConfig::accessPathLimit()] }
|
||||
|
||||
abstract private class State extends TState {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/** A flow state representing no reads or stores. */
|
||||
private class InitState extends DF::FlowState {
|
||||
private class InitState extends State, TInitState {
|
||||
private boolean preservesValue_;
|
||||
|
||||
InitState() { this = "Init(" + preservesValue_ + ")" and preservesValue_ in [false, true] }
|
||||
InitState() { this = TInitState(preservesValue_) }
|
||||
|
||||
override string toString() { result = "Init(" + preservesValue_ + ")" }
|
||||
|
||||
predicate decode(boolean preservesValue) { preservesValue = preservesValue_ }
|
||||
}
|
||||
|
||||
/** A flow state representing that content has been stored into. */
|
||||
private class StoreState extends DF::FlowState {
|
||||
private class StoreState extends State, TStoreState {
|
||||
private boolean preservesValue_;
|
||||
private int size_;
|
||||
|
||||
StoreState() {
|
||||
preservesValue_ in [false, true] and
|
||||
size_ in [1 .. any(Configuration c).accessPathLimit()] and
|
||||
this = "StoreState(" + size_ + "," + preservesValue_ + ")"
|
||||
}
|
||||
StoreState() { this = TStoreState(size_, preservesValue_) }
|
||||
|
||||
override string toString() { result = "StoreState(" + size_ + "," + preservesValue_ + ")" }
|
||||
|
||||
predicate decode(int size, boolean preservesValue) {
|
||||
size = size_ and preservesValue = preservesValue_
|
||||
@@ -158,15 +185,13 @@ module ContentDataFlow {
|
||||
}
|
||||
|
||||
/** A flow state representing that content has been read from. */
|
||||
private class ReadState extends DF::FlowState {
|
||||
private class ReadState extends State, TReadState {
|
||||
private boolean preservesValue_;
|
||||
private int size_;
|
||||
|
||||
ReadState() {
|
||||
preservesValue_ in [false, true] and
|
||||
size_ in [1 .. any(Configuration c).accessPathLimit()] and
|
||||
this = "ReadState(" + size_ + "," + preservesValue_ + ")"
|
||||
}
|
||||
ReadState() { this = TReadState(size_, preservesValue_) }
|
||||
|
||||
override string toString() { result = "ReadState(" + size_ + "," + preservesValue_ + ")" }
|
||||
|
||||
predicate decode(int size, boolean preservesValue) {
|
||||
size = size_ and preservesValue = preservesValue_
|
||||
@@ -174,12 +199,12 @@ module ContentDataFlow {
|
||||
}
|
||||
|
||||
private predicate storeStep(
|
||||
Node node1, DF::FlowState state1, ContentSet c, Node node2, StoreState state2,
|
||||
Configuration config
|
||||
DataFlow::Node node1, State state1, DataFlow::ContentSet c, DataFlow::Node node2,
|
||||
StoreState state2
|
||||
) {
|
||||
exists(boolean preservesValue, int size |
|
||||
storeSet(node1, c, node2, _, _) and
|
||||
config.isRelevantContent(c) and
|
||||
ContentConfig::isRelevantContent(c) and
|
||||
state2.decode(size + 1, preservesValue)
|
||||
|
|
||||
state1.(InitState).decode(preservesValue) and size = 0
|
||||
@@ -191,12 +216,12 @@ module ContentDataFlow {
|
||||
}
|
||||
|
||||
private predicate readStep(
|
||||
Node node1, DF::FlowState state1, ContentSet c, Node node2, ReadState state2,
|
||||
Configuration config
|
||||
DataFlow::Node node1, State state1, DataFlow::ContentSet c, DataFlow::Node node2,
|
||||
ReadState state2
|
||||
) {
|
||||
exists(int size |
|
||||
readSet(node1, c, node2) and
|
||||
config.isRelevantContent(c) and
|
||||
ContentConfig::isRelevantContent(c) and
|
||||
state2.decode(size + 1, true)
|
||||
|
|
||||
state1.(InitState).decode(true) and
|
||||
@@ -207,9 +232,9 @@ module ContentDataFlow {
|
||||
}
|
||||
|
||||
private predicate additionalStep(
|
||||
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2, Configuration config
|
||||
DataFlow::Node node1, State state1, DataFlow::Node node2, State state2
|
||||
) {
|
||||
config.isAdditionalFlowStep(node1, node2) and
|
||||
ContentConfig::isAdditionalFlowStep(node1, node2) and
|
||||
(
|
||||
state1 instanceof InitState and
|
||||
state2.(InitState).decode(false)
|
||||
@@ -221,40 +246,9 @@ module ContentDataFlow {
|
||||
)
|
||||
}
|
||||
|
||||
private class ConfigurationAdapter extends DF::Configuration instanceof Configuration {
|
||||
final override predicate isSource(Node source, DF::FlowState state) {
|
||||
Configuration.super.isSource(source) and
|
||||
state.(InitState).decode(true)
|
||||
}
|
||||
|
||||
final override predicate isSink(Node sink, DF::FlowState state) {
|
||||
Configuration.super.isSink(sink) and
|
||||
(
|
||||
state instanceof InitState or
|
||||
state instanceof StoreState or
|
||||
state instanceof ReadState
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2
|
||||
) {
|
||||
storeStep(node1, state1, _, node2, state2, this) or
|
||||
readStep(node1, state1, _, node2, state2, this) or
|
||||
additionalStep(node1, state1, node2, state2, this)
|
||||
}
|
||||
|
||||
final override predicate isBarrier(Node node) { Configuration.super.isBarrier(node) }
|
||||
|
||||
final override FlowFeature getAFeature() { result = Configuration.super.getAFeature() }
|
||||
|
||||
// needed to record reads/stores inside summarized callables
|
||||
final override predicate includeHiddenNodes() { any() }
|
||||
}
|
||||
|
||||
private newtype TAccessPath =
|
||||
TAccessPathNil() or
|
||||
TAccessPathCons(ContentSet head, AccessPath tail) {
|
||||
TAccessPathCons(DataFlow::ContentSet head, AccessPath tail) {
|
||||
nodeReachesStore(_, _, _, _, head, _, tail)
|
||||
or
|
||||
nodeReachesRead(_, _, _, _, head, tail, _)
|
||||
@@ -263,7 +257,7 @@ module ContentDataFlow {
|
||||
/** An access path. */
|
||||
class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
ContentSet getHead() { this = TAccessPathCons(result, _) }
|
||||
DataFlow::ContentSet getHead() { this = TAccessPathCons(result, _) }
|
||||
|
||||
/** Gets the tail of this access path, if any. */
|
||||
AccessPath getTail() { this = TAccessPathCons(_, result) }
|
||||
@@ -278,7 +272,7 @@ module ContentDataFlow {
|
||||
this = TAccessPathNil() and
|
||||
result = ""
|
||||
or
|
||||
exists(ContentSet head, AccessPath tail |
|
||||
exists(DataFlow::ContentSet head, AccessPath tail |
|
||||
this = TAccessPathCons(head, tail) and
|
||||
result = head + "." + tail
|
||||
)
|
||||
@@ -287,7 +281,7 @@ module ContentDataFlow {
|
||||
|
||||
// important to use `edges` and not `PathNode::getASuccessor()`, as the latter
|
||||
// is not pruned for reachability
|
||||
private predicate pathSucc = DF::PathGraph::edges/2;
|
||||
private predicate pathSucc = Flow::PathGraph::edges/2;
|
||||
|
||||
/**
|
||||
* Provides a big-step flow relation, where flow stops at read/store steps that
|
||||
@@ -295,10 +289,10 @@ module ContentDataFlow {
|
||||
* summarized callables can be recorded as well.
|
||||
*/
|
||||
private module BigStepFlow {
|
||||
private predicate reachesSink(DF::PathNode node) {
|
||||
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
|
||||
private predicate reachesSink(Flow::PathNode node) {
|
||||
FlowConfig::isSink(node.getNode(), node.getState())
|
||||
or
|
||||
exists(DF::PathNode mid |
|
||||
exists(Flow::PathNode mid |
|
||||
pathSucc(node, mid) and
|
||||
reachesSink(mid)
|
||||
)
|
||||
@@ -309,76 +303,72 @@ module ContentDataFlow {
|
||||
* in the big-step relation.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate excludeStep(DF::PathNode pred, DF::PathNode succ) {
|
||||
private predicate excludeStep(Flow::PathNode pred, Flow::PathNode succ) {
|
||||
pathSucc(pred, succ) and
|
||||
(
|
||||
// we need to record reads/stores inside summarized callables
|
||||
DF::PathGraph::subpaths(pred, _, _, succ)
|
||||
Flow::PathGraph::subpaths(pred, _, _, succ)
|
||||
or
|
||||
// only allow flow into a summarized callable, as part of the big-step
|
||||
// relation, when flow can reach a sink without going back out
|
||||
DF::PathGraph::subpaths(pred, succ, _, _) and
|
||||
Flow::PathGraph::subpaths(pred, succ, _, _) and
|
||||
not reachesSink(succ)
|
||||
or
|
||||
// needed to record store steps
|
||||
storeStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
|
||||
pred.getConfiguration())
|
||||
storeStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState())
|
||||
or
|
||||
// needed to record read steps
|
||||
readStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
|
||||
pred.getConfiguration())
|
||||
readStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState())
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable getEnclosingCallableImpl(DF::PathNode node) {
|
||||
private DataFlowCallable getEnclosingCallableImpl(Flow::PathNode node) {
|
||||
result = getNodeEnclosingCallable(node.getNode())
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private DataFlowCallable getEnclosingCallable(DF::PathNode node) {
|
||||
private DataFlowCallable getEnclosingCallable(Flow::PathNode node) {
|
||||
pragma[only_bind_into](result) = getEnclosingCallableImpl(pragma[only_bind_out](node))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate bigStepEntry(DF::PathNode node) {
|
||||
node.getConfiguration() instanceof Configuration and
|
||||
private predicate bigStepEntry(Flow::PathNode node) {
|
||||
(
|
||||
any(ConfigurationAdapter config).isSource(node.getNode(), node.getState())
|
||||
FlowConfig::isSource(node.getNode(), node.getState())
|
||||
or
|
||||
excludeStep(_, node)
|
||||
or
|
||||
DF::PathGraph::subpaths(_, node, _, _)
|
||||
Flow::PathGraph::subpaths(_, node, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate bigStepExit(DF::PathNode node) {
|
||||
node.getConfiguration() instanceof Configuration and
|
||||
private predicate bigStepExit(Flow::PathNode node) {
|
||||
(
|
||||
bigStepEntry(node)
|
||||
or
|
||||
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
|
||||
FlowConfig::isSink(node.getNode(), node.getState())
|
||||
or
|
||||
excludeStep(node, _)
|
||||
or
|
||||
DF::PathGraph::subpaths(_, _, node, _)
|
||||
Flow::PathGraph::subpaths(_, _, node, _)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate step(DF::PathNode pred, DF::PathNode succ) {
|
||||
private predicate step(Flow::PathNode pred, Flow::PathNode succ) {
|
||||
pathSucc(pred, succ) and
|
||||
not excludeStep(pred, succ)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate stepRec(DF::PathNode pred, DF::PathNode succ) {
|
||||
private predicate stepRec(Flow::PathNode pred, Flow::PathNode succ) {
|
||||
step(pred, succ) and
|
||||
not bigStepEntry(pred)
|
||||
}
|
||||
|
||||
private predicate stepRecPlus(DF::PathNode n1, DF::PathNode n2) = fastTC(stepRec/2)(n1, n2)
|
||||
private predicate stepRecPlus(Flow::PathNode n1, Flow::PathNode n2) = fastTC(stepRec/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* Holds if there is flow `pathSucc+(pred) = succ`, and such a flow path does
|
||||
@@ -386,8 +376,8 @@ module ContentDataFlow {
|
||||
* steps.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate bigStep(DF::PathNode pred, DF::PathNode succ) {
|
||||
exists(DF::PathNode mid |
|
||||
private predicate bigStep(Flow::PathNode pred, Flow::PathNode succ) {
|
||||
exists(Flow::PathNode mid |
|
||||
bigStepEntry(pred) and
|
||||
step(pred, mid)
|
||||
|
|
||||
@@ -399,13 +389,13 @@ module ContentDataFlow {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate bigStepNotLocal(DF::PathNode pred, DF::PathNode succ) {
|
||||
predicate bigStepNotLocal(Flow::PathNode pred, Flow::PathNode succ) {
|
||||
bigStep(pred, succ) and
|
||||
not getEnclosingCallable(pred) = getEnclosingCallable(succ)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate bigStepMaybeLocal(DF::PathNode pred, DF::PathNode succ) {
|
||||
predicate bigStepMaybeLocal(Flow::PathNode pred, Flow::PathNode succ) {
|
||||
bigStep(pred, succ) and
|
||||
getEnclosingCallable(pred) = getEnclosingCallable(succ)
|
||||
}
|
||||
@@ -422,55 +412,54 @@ module ContentDataFlow {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeReaches(
|
||||
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node,
|
||||
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
|
||||
AccessPath reads, AccessPath stores
|
||||
) {
|
||||
exists(ConfigurationAdapter config |
|
||||
node = source and
|
||||
reads = scReads and
|
||||
stores = scStores
|
||||
|
|
||||
config.hasFlowPath(source, _) and
|
||||
node = source and
|
||||
reads = scReads and
|
||||
stores = scStores and
|
||||
(
|
||||
Flow::flowPath(source, _) and
|
||||
scReads = TAccessPathNil() and
|
||||
scStores = TAccessPathNil()
|
||||
or
|
||||
// the argument in a sub path can be reached, so we start flow from the sub path
|
||||
// parameter, while recording the read/store summary context
|
||||
exists(DF::PathNode arg |
|
||||
exists(Flow::PathNode arg |
|
||||
nodeReachesSubpathArg(_, _, _, arg, scReads, scStores) and
|
||||
DF::PathGraph::subpaths(arg, source, _, _)
|
||||
Flow::PathGraph::subpaths(arg, source, _, _)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(DF::PathNode mid |
|
||||
exists(Flow::PathNode mid |
|
||||
nodeReaches(source, scReads, scStores, mid, reads, stores) and
|
||||
BigStepFlow::bigStepMaybeLocal(mid, node)
|
||||
)
|
||||
or
|
||||
exists(DF::PathNode mid |
|
||||
exists(Flow::PathNode mid |
|
||||
nodeReaches(source, scReads, scStores, mid, reads, stores) and
|
||||
BigStepFlow::bigStepNotLocal(mid, node) and
|
||||
// when flow is not local, we cannot flow back out, so we may stop
|
||||
// flow early when computing summary flow
|
||||
any(ConfigurationAdapter config).hasFlowPath(source, _) and
|
||||
Flow::flowPath(source, _) and
|
||||
scReads = TAccessPathNil() and
|
||||
scStores = TAccessPathNil()
|
||||
)
|
||||
or
|
||||
// store step
|
||||
exists(AccessPath storesMid, ContentSet c |
|
||||
exists(AccessPath storesMid, DataFlow::ContentSet c |
|
||||
nodeReachesStore(source, scReads, scStores, node, c, reads, storesMid) and
|
||||
stores = TAccessPathCons(c, storesMid)
|
||||
)
|
||||
or
|
||||
// read step
|
||||
exists(AccessPath readsMid, ContentSet c |
|
||||
exists(AccessPath readsMid, DataFlow::ContentSet c |
|
||||
nodeReachesRead(source, scReads, scStores, node, c, readsMid, stores) and
|
||||
reads = TAccessPathCons(c, readsMid)
|
||||
)
|
||||
or
|
||||
// flow-through step; match outer stores/reads with inner store/read summary contexts
|
||||
exists(DF::PathNode mid, AccessPath innerScReads, AccessPath innerScStores |
|
||||
exists(Flow::PathNode mid, AccessPath innerScReads, AccessPath innerScStores |
|
||||
nodeReachesSubpathArg(source, scReads, scStores, mid, innerScReads, innerScStores) and
|
||||
subpathArgReachesOut(mid, innerScReads, innerScStores, node, reads, stores)
|
||||
)
|
||||
@@ -478,47 +467,45 @@ module ContentDataFlow {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeReachesStore(
|
||||
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
|
||||
AccessPath reads, AccessPath stores
|
||||
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
|
||||
DataFlow::ContentSet c, AccessPath reads, AccessPath stores
|
||||
) {
|
||||
exists(DF::PathNode mid |
|
||||
exists(Flow::PathNode mid |
|
||||
nodeReaches(source, scReads, scStores, mid, reads, stores) and
|
||||
storeStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
|
||||
node.getConfiguration()) and
|
||||
storeStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState()) and
|
||||
pathSucc(mid, node)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeReachesRead(
|
||||
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
|
||||
AccessPath reads, AccessPath stores
|
||||
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
|
||||
DataFlow::ContentSet c, AccessPath reads, AccessPath stores
|
||||
) {
|
||||
exists(DF::PathNode mid |
|
||||
exists(Flow::PathNode mid |
|
||||
nodeReaches(source, scReads, scStores, mid, reads, stores) and
|
||||
readStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
|
||||
node.getConfiguration()) and
|
||||
readStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState()) and
|
||||
pathSucc(mid, node)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeReachesSubpathArg(
|
||||
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode arg,
|
||||
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode arg,
|
||||
AccessPath reads, AccessPath stores
|
||||
) {
|
||||
nodeReaches(source, scReads, scStores, arg, reads, stores) and
|
||||
DF::PathGraph::subpaths(arg, _, _, _)
|
||||
Flow::PathGraph::subpaths(arg, _, _, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate subpathArgReachesOut(
|
||||
DF::PathNode arg, AccessPath scReads, AccessPath scStores, DF::PathNode out, AccessPath reads,
|
||||
AccessPath stores
|
||||
Flow::PathNode arg, AccessPath scReads, AccessPath scStores, Flow::PathNode out,
|
||||
AccessPath reads, AccessPath stores
|
||||
) {
|
||||
exists(DF::PathNode source, DF::PathNode ret |
|
||||
exists(Flow::PathNode source, Flow::PathNode ret |
|
||||
nodeReaches(source, scReads, scStores, ret, reads, stores) and
|
||||
DF::PathGraph::subpaths(arg, source, ret, out)
|
||||
Flow::PathGraph::subpaths(arg, source, ret, out)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -815,24 +815,20 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
exists(ContentSet cs |
|
||||
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a direct assignment to
|
||||
* `f`.
|
||||
* `c`.
|
||||
*
|
||||
* This includes reverse steps through reads when the result of the read has
|
||||
* been stored into, in order to handle cases like `x.f1.f2 = y`.
|
||||
*/
|
||||
cached
|
||||
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
|
||||
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
|
||||
predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
exists(ContentSet cs |
|
||||
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -932,36 +928,15 @@ private module Cached {
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
|
||||
|
||||
cached
|
||||
newtype TTypedContentApprox =
|
||||
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
|
||||
exists(Content cont |
|
||||
c = getContentApprox(cont) and
|
||||
store(_, cont, _, _, t)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
TypedContent getATypedContent(TypedContentApprox c) {
|
||||
exists(ContentApprox cls, DataFlowType t, Content cont |
|
||||
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
|
||||
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
|
||||
cls = getContentApprox(cont)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(TypedContent tc)
|
||||
TFrontNil() or
|
||||
TFrontHead(Content c)
|
||||
|
||||
cached
|
||||
newtype TApproxAccessPathFront =
|
||||
TApproxFrontNil(DataFlowType t) or
|
||||
TApproxFrontHead(TypedContentApprox tc)
|
||||
TApproxFrontNil() or
|
||||
TApproxFrontHead(ContentApprox c)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
/**
|
||||
* A `Node` at which a cast can occur such that the type should be checked.
|
||||
*/
|
||||
class CastingNode extends Node {
|
||||
class CastingNode instanceof Node {
|
||||
CastingNode() { castingNode(this) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate readStepWithTypes(
|
||||
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class ParamNode extends Node {
|
||||
class ParamNode instanceof Node {
|
||||
ParamNode() { parameterNode(this, _, _) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of callable `c` at the specified
|
||||
* position.
|
||||
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
|
||||
}
|
||||
|
||||
/** A data-flow node that represents a call argument. */
|
||||
class ArgNode extends Node {
|
||||
class ArgNode instanceof Node {
|
||||
ArgNode() { argumentNode(this, _, _) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Holds if this argument occurs at the given position in the given call. */
|
||||
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
argumentNode(this, call, pos)
|
||||
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
|
||||
* A node from which flow can return to the caller. This is either a regular
|
||||
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
|
||||
*/
|
||||
class ReturnNodeExt extends Node {
|
||||
class ReturnNodeExt instanceof Node {
|
||||
ReturnNodeExt() { returnNodeExt(this, _) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKindExt getKind() { returnNodeExt(this, result) }
|
||||
}
|
||||
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
|
||||
* A node to which data can flow from a call. Either an ordinary out node
|
||||
* or a post-update node associated with a call argument.
|
||||
*/
|
||||
class OutNodeExt extends Node {
|
||||
class OutNodeExt instanceof Node {
|
||||
OutNodeExt() { outNodeExt(this) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
|
||||
}
|
||||
}
|
||||
|
||||
/** An approximated `Content` tagged with the type of a containing object. */
|
||||
class TypedContentApprox extends MkTypedContentApprox {
|
||||
private ContentApprox c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
|
||||
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the content. */
|
||||
ContentApprox getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this approximated content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an approximated access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
|
||||
ContentApprox getHead() { this = TApproxFrontHead(result) }
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
Content getAHead() {
|
||||
exists(ContentApprox cont |
|
||||
this = TApproxFrontHead(cont) and
|
||||
result = cont.getATypedContent()
|
||||
cont = getContentApprox(result)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
|
||||
private DataFlowType t;
|
||||
|
||||
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
override string toString() { result = "nil" }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
|
||||
private TypedContentApprox tc;
|
||||
private ContentApprox c;
|
||||
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
|
||||
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
override string toString() { result = c.toString() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContent() { this = MkTypedContent(c, t) }
|
||||
|
||||
/** Gets the content. */
|
||||
Content getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this content. */
|
||||
string toString() { result = c.toString() }
|
||||
|
||||
/**
|
||||
* Holds if access paths with this `TypedContent` at their head always should
|
||||
* be tracked at high precision. This disables adaptive access path precision
|
||||
* for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision() { forceHighPrecision(c) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an access path. This is either a head or a nil.
|
||||
*/
|
||||
abstract class AccessPathFront extends TAccessPathFront {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
|
||||
abstract ApproxAccessPathFront toApprox();
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
Content getHead() { this = TFrontHead(result) }
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
private DataFlowType t;
|
||||
override string toString() { result = "nil" }
|
||||
|
||||
AccessPathFrontNil() { this = TFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
|
||||
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
private TypedContent tc;
|
||||
private Content c;
|
||||
|
||||
AccessPathFrontHead() { this = TFrontHead(tc) }
|
||||
AccessPathFrontHead() { this = TFrontHead(c) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
override string toString() { result = c.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
|
||||
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
|
||||
}
|
||||
|
||||
/** An optional access path front. */
|
||||
|
||||
@@ -1,398 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
|
||||
*
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
deprecated int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1)
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
@@ -23,11 +23,15 @@ abstract class Sink extends DataFlow::Node { }
|
||||
*/
|
||||
abstract private class InstanceMethodSink extends Sink {
|
||||
InstanceMethodSink() {
|
||||
not exists(
|
||||
SafeConstructorTrackingConfig safeConstructorTracking, DataFlow::Node safeTypeUsage,
|
||||
MethodCall mc
|
||||
|
|
||||
safeConstructorTracking.hasFlow(_, safeTypeUsage) and
|
||||
not exists(DataFlow::Node safeTypeUsage, MethodCall mc |
|
||||
(
|
||||
DataContractJsonSafeConstructorTracking::flowTo(safeTypeUsage) or
|
||||
JavaScriptSerializerSafeConstructorTracking::flowTo(safeTypeUsage) or
|
||||
XmlObjectSerializerDerivedConstructorTracking::flowTo(safeTypeUsage) or
|
||||
XmlSerializerSafeConstructorTracking::flowTo(safeTypeUsage) or
|
||||
DataContractSerializerSafeConstructorTracking::flowTo(safeTypeUsage) or
|
||||
XmlMessageFormatterSafeConstructorTracking::flowTo(safeTypeUsage)
|
||||
) and
|
||||
mc.getQualifier() = safeTypeUsage.asExpr() and
|
||||
mc.getAnArgument() = this.asExpr()
|
||||
)
|
||||
@@ -47,9 +51,11 @@ abstract class Sanitizer extends DataFlow::Node { }
|
||||
private class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `TaintToObjectMethodTracking` instead.
|
||||
*
|
||||
* User input to object method call deserialization flow tracking.
|
||||
*/
|
||||
class TaintToObjectMethodTrackingConfig extends TaintTracking::Configuration {
|
||||
deprecated class TaintToObjectMethodTrackingConfig extends TaintTracking::Configuration {
|
||||
TaintToObjectMethodTrackingConfig() { this = "TaintToObjectMethodTrackingConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -60,9 +66,27 @@ class TaintToObjectMethodTrackingConfig extends TaintTracking::Configuration {
|
||||
}
|
||||
|
||||
/**
|
||||
* User input to object method call deserialization flow tracking configuration.
|
||||
*/
|
||||
private module TaintToObjectMethodTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof InstanceMethodSink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* User input to object method call deserialization flow tracking module.
|
||||
*/
|
||||
module TaintToObjectMethodTracking = TaintTracking::Global<TaintToObjectMethodTrackingConfig>;
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `JsonConvertTracking` instead.
|
||||
*
|
||||
* User input to `JsonConvert` call deserialization flow tracking.
|
||||
*/
|
||||
class JsonConvertTrackingConfig extends TaintTracking::Configuration {
|
||||
deprecated class JsonConvertTrackingConfig extends TaintTracking::Configuration {
|
||||
JsonConvertTrackingConfig() { this = "JsonConvertTrackingConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -74,6 +98,24 @@ class JsonConvertTrackingConfig extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* User input to `JsonConvert` call deserialization flow tracking configuration.
|
||||
*/
|
||||
private module JsonConvertTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof NewtonsoftJsonConvertDeserializeObjectMethodSink
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* User input to `JsonConvert` call deserialization flow tracking module.
|
||||
*/
|
||||
module JsonConvertTracking = TaintTracking::Global<JsonConvertTrackingConfig>;
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `TypeNameTracking` instead.
|
||||
*
|
||||
@@ -186,9 +228,12 @@ private module TypeNameTrackingConfig implements DataFlow::ConfigSig {
|
||||
module TypeNameTracking = DataFlow::Global<TypeNameTrackingConfig>;
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `TaintToConstructorOrStaticMethodTracking` instead.
|
||||
*
|
||||
* User input to static method or constructor call deserialization flow tracking.
|
||||
*/
|
||||
class TaintToConstructorOrStaticMethodTrackingConfig extends TaintTracking::Configuration {
|
||||
deprecated class TaintToConstructorOrStaticMethodTrackingConfig extends TaintTracking::Configuration
|
||||
{
|
||||
TaintToConstructorOrStaticMethodTrackingConfig() {
|
||||
this = "TaintToConstructorOrStaticMethodTrackingConfig"
|
||||
}
|
||||
@@ -201,9 +246,28 @@ class TaintToConstructorOrStaticMethodTrackingConfig extends TaintTracking::Conf
|
||||
}
|
||||
|
||||
/**
|
||||
* User input to static method or constructor call deserialization flow tracking configuration.
|
||||
*/
|
||||
private module TaintToConstructorOrStaticMethodTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof ConstructorOrStaticMethodSink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* User input to static method or constructor call deserialization flow tracking module.
|
||||
*/
|
||||
module TaintToConstructorOrStaticMethodTracking =
|
||||
TaintTracking::Global<TaintToConstructorOrStaticMethodTrackingConfig>;
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `TaintToObjectTypeTracking` instead.
|
||||
*
|
||||
* User input to instance type flow tracking.
|
||||
*/
|
||||
class TaintToObjectTypeTrackingConfig extends TaintTracking2::Configuration {
|
||||
deprecated class TaintToObjectTypeTrackingConfig extends TaintTracking2::Configuration {
|
||||
TaintToObjectTypeTrackingConfig() { this = "TaintToObjectTypeTrackingConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -234,9 +298,47 @@ class TaintToObjectTypeTrackingConfig extends TaintTracking2::Configuration {
|
||||
}
|
||||
|
||||
/**
|
||||
* User input to instance type flow tracking config.
|
||||
*/
|
||||
private module TaintToObjectTypeTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget() instanceof UnsafeDeserializer and
|
||||
sink.asExpr() = mc.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(MethodCall mc, Method m |
|
||||
m = mc.getTarget() and
|
||||
m.getDeclaringType().hasQualifiedName("System", "Type") and
|
||||
m.hasName("GetType") and
|
||||
m.isStatic() and
|
||||
n1.asExpr() = mc.getArgument(0) and
|
||||
n2.asExpr() = mc
|
||||
)
|
||||
or
|
||||
exists(ObjectCreation oc |
|
||||
n1.asExpr() = oc.getAnArgument() and
|
||||
n2.asExpr() = oc and
|
||||
oc.getObjectType() instanceof StrongTypeDeserializer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User input to instance type flow tracking module.
|
||||
*/
|
||||
module TaintToObjectTypeTracking = TaintTracking::Global<TaintToObjectTypeTrackingConfig>;
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `WeakTypeCreationToUsageTracking` instead.
|
||||
*
|
||||
* Unsafe deserializer creation to usage tracking config.
|
||||
*/
|
||||
class WeakTypeCreationToUsageTrackingConfig extends TaintTracking2::Configuration {
|
||||
deprecated class WeakTypeCreationToUsageTrackingConfig extends TaintTracking2::Configuration {
|
||||
WeakTypeCreationToUsageTrackingConfig() { this = "DeserializerCreationToUsageTrackingConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
@@ -255,13 +357,30 @@ class WeakTypeCreationToUsageTrackingConfig extends TaintTracking2::Configuratio
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe deserializer creation to usage tracking config.
|
||||
* Unsafe deserializer creation to usage tracking config.
|
||||
*/
|
||||
abstract class SafeConstructorTrackingConfig extends TaintTracking2::Configuration {
|
||||
bindingset[this]
|
||||
SafeConstructorTrackingConfig() { any() }
|
||||
private module WeakTypeCreationToUsageTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getObjectType() instanceof WeakTypeDeserializer and
|
||||
source.asExpr() = oc
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget() instanceof UnsafeDeserializer and
|
||||
sink.asExpr() = mc.getQualifier()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsafe deserializer creation to usage tracking module.
|
||||
*/
|
||||
module WeakTypeCreationToUsageTracking =
|
||||
TaintTracking::Global<WeakTypeCreationToUsageTrackingConfig>;
|
||||
|
||||
/** BinaryFormatter */
|
||||
private predicate isBinaryFormatterCall(MethodCall mc, Method m) {
|
||||
m = mc.getTarget() and
|
||||
@@ -367,13 +486,8 @@ private class DataContractJsonSerializerDeserializeMethodSink extends DataContra
|
||||
}
|
||||
}
|
||||
|
||||
private class DataContractJsonSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
|
||||
{
|
||||
DataContractJsonSafeConstructorTrackingConfiguration() {
|
||||
this = "DataContractJsonSafeConstructorTrackingConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module DataContractJsonSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(ObjectCreation oc |
|
||||
oc = source.asExpr() and
|
||||
exists(Constructor c |
|
||||
@@ -385,7 +499,7 @@ private class DataContractJsonSafeConstructorTrackingConfiguration extends SafeC
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodCall mc |
|
||||
isDataContractJsonSerializerCall(mc, _) and
|
||||
mc.getQualifier() = sink.asExpr()
|
||||
@@ -393,6 +507,9 @@ private class DataContractJsonSafeConstructorTrackingConfiguration extends SafeC
|
||||
}
|
||||
}
|
||||
|
||||
private module DataContractJsonSafeConstructorTracking =
|
||||
TaintTracking::Global<DataContractJsonSafeConstructorTrackingConfig>;
|
||||
|
||||
/** JavaScriptSerializer */
|
||||
private predicate isJavaScriptSerializerCall(MethodCall mc, Method m) {
|
||||
m = mc.getTarget() and
|
||||
@@ -417,13 +534,8 @@ private class JavaScriptSerializerDeserializeMethodSink extends JavaScriptSerial
|
||||
}
|
||||
}
|
||||
|
||||
private class JavaScriptSerializerSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
|
||||
{
|
||||
JavaScriptSerializerSafeConstructorTrackingConfiguration() {
|
||||
this = "JavaScriptSerializerSafeConstructorTrackingConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module JavaScriptSerializerSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(ObjectCreation oc |
|
||||
oc = source.asExpr() and
|
||||
exists(Constructor c |
|
||||
@@ -434,7 +546,7 @@ private class JavaScriptSerializerSafeConstructorTrackingConfiguration extends S
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodCall mc |
|
||||
isJavaScriptSerializerCall(mc, _) and
|
||||
mc.getQualifier() = sink.asExpr()
|
||||
@@ -442,6 +554,9 @@ private class JavaScriptSerializerSafeConstructorTrackingConfiguration extends S
|
||||
}
|
||||
}
|
||||
|
||||
private module JavaScriptSerializerSafeConstructorTracking =
|
||||
TaintTracking::Global<JavaScriptSerializerSafeConstructorTrackingConfig>;
|
||||
|
||||
/** XmlObjectSerializer */
|
||||
private predicate isXmlObjectSerializerCall(MethodCall mc, Method m) {
|
||||
m = mc.getTarget() and
|
||||
@@ -461,13 +576,8 @@ private class XmlObjectSerializerDeserializeMethodSink extends XmlObjectSerializ
|
||||
}
|
||||
}
|
||||
|
||||
private class XmlObjectSerializerDerivedConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
|
||||
{
|
||||
XmlObjectSerializerDerivedConstructorTrackingConfiguration() {
|
||||
this = "XmlObjectSerializerDerivedConstructorTrackingConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module XmlObjectSerializerDerivedConstructorTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(ObjectCreation oc |
|
||||
oc = source.asExpr() and
|
||||
exists(ValueOrRefType declaringType |
|
||||
@@ -481,7 +591,7 @@ private class XmlObjectSerializerDerivedConstructorTrackingConfiguration extends
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodCall mc |
|
||||
isXmlObjectSerializerCall(mc, _) and
|
||||
mc.getQualifier() = sink.asExpr()
|
||||
@@ -489,6 +599,9 @@ private class XmlObjectSerializerDerivedConstructorTrackingConfiguration extends
|
||||
}
|
||||
}
|
||||
|
||||
private module XmlObjectSerializerDerivedConstructorTracking =
|
||||
TaintTracking::Global<XmlObjectSerializerDerivedConstructorTrackingConfig>;
|
||||
|
||||
/** XmlSerializer */
|
||||
private predicate isXmlSerializerCall(MethodCall mc, Method m) {
|
||||
m = mc.getTarget() and
|
||||
@@ -507,13 +620,8 @@ private class XmlSerializerDeserializeMethodSink extends XmlSerializerSink {
|
||||
}
|
||||
}
|
||||
|
||||
private class XmlSerializerSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
|
||||
{
|
||||
XmlSerializerSafeConstructorTrackingConfiguration() {
|
||||
this = "XmlSerializerSafeConstructorTrackingConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module XmlSerializerSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(ObjectCreation oc |
|
||||
oc = source.asExpr() and
|
||||
exists(Constructor c |
|
||||
@@ -525,7 +633,7 @@ private class XmlSerializerSafeConstructorTrackingConfiguration extends SafeCons
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodCall mc |
|
||||
isXmlSerializerCall(mc, _) and
|
||||
mc.getQualifier() = sink.asExpr()
|
||||
@@ -533,6 +641,9 @@ private class XmlSerializerSafeConstructorTrackingConfiguration extends SafeCons
|
||||
}
|
||||
}
|
||||
|
||||
private module XmlSerializerSafeConstructorTracking =
|
||||
TaintTracking::Global<XmlSerializerSafeConstructorTrackingConfig>;
|
||||
|
||||
/** DataContractSerializer */
|
||||
private predicate isDataContractSerializerCall(MethodCall mc, Method m) {
|
||||
m = mc.getTarget() and
|
||||
@@ -555,13 +666,8 @@ private class DataContractSerializerDeserializeMethodSink extends DataContractSe
|
||||
}
|
||||
}
|
||||
|
||||
private class DataContractSerializerSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
|
||||
{
|
||||
DataContractSerializerSafeConstructorTrackingConfiguration() {
|
||||
this = "DataContractSerializerSafeConstructorTrackingConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module DataContractSerializerSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(ObjectCreation oc |
|
||||
oc = source.asExpr() and
|
||||
exists(Constructor c |
|
||||
@@ -573,7 +679,7 @@ private class DataContractSerializerSafeConstructorTrackingConfiguration extends
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodCall mc |
|
||||
isDataContractSerializerCall(mc, _) and
|
||||
mc.getQualifier() = sink.asExpr()
|
||||
@@ -581,6 +687,9 @@ private class DataContractSerializerSafeConstructorTrackingConfiguration extends
|
||||
}
|
||||
}
|
||||
|
||||
private module DataContractSerializerSafeConstructorTracking =
|
||||
TaintTracking::Global<DataContractSerializerSafeConstructorTrackingConfig>;
|
||||
|
||||
/** XmlMessageFormatter */
|
||||
private predicate isXmlMessageFormatterCall(MethodCall mc, Method m) {
|
||||
m = mc.getTarget() and
|
||||
@@ -599,13 +708,8 @@ private class XmlMessageFormatterDeserializeMethodSink extends XmlMessageFormatt
|
||||
}
|
||||
}
|
||||
|
||||
private class XmlMessageFormatterSafeConstructorTrackingConfiguration extends SafeConstructorTrackingConfig
|
||||
{
|
||||
XmlMessageFormatterSafeConstructorTrackingConfiguration() {
|
||||
this = "XmlMessageFormatterSafeConstructorTrackingConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module XmlMessageFormatterSafeConstructorTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(ObjectCreation oc |
|
||||
oc = source.asExpr() and
|
||||
exists(Constructor c |
|
||||
@@ -617,7 +721,7 @@ private class XmlMessageFormatterSafeConstructorTrackingConfiguration extends Sa
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodCall mc |
|
||||
isXmlMessageFormatterCall(mc, _) and
|
||||
mc.getQualifier() = sink.asExpr()
|
||||
@@ -625,6 +729,9 @@ private class XmlMessageFormatterSafeConstructorTrackingConfiguration extends Sa
|
||||
}
|
||||
}
|
||||
|
||||
private module XmlMessageFormatterSafeConstructorTracking =
|
||||
TaintTracking::Global<XmlMessageFormatterSafeConstructorTrackingConfig>;
|
||||
|
||||
/** LosFormatter */
|
||||
private predicate isLosFormatterCall(MethodCall mc, Method m) {
|
||||
m = mc.getTarget() and
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.6.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Additional sinks modelling writes to unencrypted local files have been added to `ExternalLocationSink`, used by the `cs/cleartext-storage` and `cs/exposure-of-sensitive-information` queries.
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -13,43 +13,40 @@
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.UnsafeDeserializationQuery
|
||||
import DataFlow::PathGraph
|
||||
import Flow::PathGraph
|
||||
|
||||
from DataFlow::PathNode userInput, DataFlow::PathNode deserializeCallArg
|
||||
module Flow =
|
||||
DataFlow::MergePathGraph3<TaintToObjectMethodTracking::PathNode,
|
||||
TaintToConstructorOrStaticMethodTracking::PathNode, JsonConvertTracking::PathNode,
|
||||
TaintToObjectMethodTracking::PathGraph, TaintToConstructorOrStaticMethodTracking::PathGraph,
|
||||
JsonConvertTracking::PathGraph>;
|
||||
|
||||
from Flow::PathNode userInput, Flow::PathNode deserializeCallArg
|
||||
where
|
||||
exists(TaintToObjectMethodTrackingConfig taintTracking |
|
||||
// all flows from user input to deserialization with weak and strong type serializers
|
||||
taintTracking.hasFlowPath(userInput, deserializeCallArg)
|
||||
) and
|
||||
// all flows from user input to deserialization with weak and strong type serializers
|
||||
TaintToObjectMethodTracking::flowPath(userInput.asPathNode1(), deserializeCallArg.asPathNode1()) and
|
||||
// intersect with strong types, but user controlled or weak types deserialization usages
|
||||
(
|
||||
exists(
|
||||
DataFlow::Node weakTypeUsage,
|
||||
WeakTypeCreationToUsageTrackingConfig weakTypeDeserializerTracking, MethodCall mc
|
||||
|
|
||||
weakTypeDeserializerTracking.hasFlowTo(weakTypeUsage) and
|
||||
exists(DataFlow::Node weakTypeUsage, MethodCall mc |
|
||||
WeakTypeCreationToUsageTracking::flowTo(weakTypeUsage) and
|
||||
mc.getQualifier() = weakTypeUsage.asExpr() and
|
||||
mc.getAnArgument() = deserializeCallArg.getNode().asExpr()
|
||||
)
|
||||
or
|
||||
exists(
|
||||
TaintToObjectTypeTrackingConfig userControlledTypeTracking, DataFlow::Node taintedTypeUsage,
|
||||
MethodCall mc
|
||||
|
|
||||
userControlledTypeTracking.hasFlowTo(taintedTypeUsage) and
|
||||
exists(DataFlow::Node taintedTypeUsage, MethodCall mc |
|
||||
TaintToObjectTypeTracking::flowTo(taintedTypeUsage) and
|
||||
mc.getQualifier() = taintedTypeUsage.asExpr() and
|
||||
mc.getAnArgument() = deserializeCallArg.getNode().asExpr()
|
||||
)
|
||||
)
|
||||
or
|
||||
// no type check needed - straightforward taint -> sink
|
||||
exists(TaintToConstructorOrStaticMethodTrackingConfig taintTracking2 |
|
||||
taintTracking2.hasFlowPath(userInput, deserializeCallArg)
|
||||
)
|
||||
TaintToConstructorOrStaticMethodTracking::flowPath(userInput.asPathNode2(),
|
||||
deserializeCallArg.asPathNode2())
|
||||
or
|
||||
// JsonConvert static method call, but with additional unsafe typename tracking
|
||||
exists(JsonConvertTrackingConfig taintTrackingJsonConvert, DataFlow::Node settingsCallArg |
|
||||
taintTrackingJsonConvert.hasFlowPath(userInput, deserializeCallArg) and
|
||||
exists(DataFlow::Node settingsCallArg |
|
||||
JsonConvertTracking::flowPath(userInput.asPathNode3(), deserializeCallArg.asPathNode3()) and
|
||||
TypeNameTracking::flow(_, settingsCallArg) and
|
||||
deserializeCallArg.getNode().asExpr().getParent() = settingsCallArg.asExpr().getParent()
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Additional sinks modelling writes to unencrypted local files have been added to `ExternalLocationSink`, used by the `cs/cleartext-storage` and `cs/exposure-of-sensitive-information` queries.
|
||||
## 0.6.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Additional sinks modelling writes to unencrypted local files have been added to `ExternalLocationSink`, used by the `cs/cleartext-storage` and `cs/exposure-of-sensitive-information` queries.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.0
|
||||
lastReleaseVersion: 0.6.1
|
||||
|
||||
@@ -6,7 +6,7 @@ private import IRFunctionBaseInternal
|
||||
|
||||
private newtype TIRFunction =
|
||||
TFunctionIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } or
|
||||
TVarInitIRFunction(Language::GlobalVariable var) { IRConstruction::Raw::varHasIRFunc(var) }
|
||||
TVarInitIRFunction(Language::Variable var) { IRConstruction::Raw::varHasIRFunc(var) }
|
||||
|
||||
/**
|
||||
* The IR for a function. This base class contains only the predicates that are the same between all
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 0.6.1-dev
|
||||
version: 0.6.2-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -139,9 +139,9 @@ module ThroughFlowConfig implements DataFlow::StateConfigSig {
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
exists(DataFlowImplCommon::TypedContent tc |
|
||||
DataFlowImplCommon::store(node1, tc, node2, _) and
|
||||
isRelevantContent(tc.getContent()) and
|
||||
exists(DataFlow::Content c |
|
||||
DataFlowImplCommon::store(node1, c, node2, _, _) and
|
||||
isRelevantContent(c) and
|
||||
(
|
||||
state1 instanceof TaintRead and state2.(TaintStore).getStep() = 1
|
||||
or
|
||||
@@ -178,7 +178,7 @@ string captureThroughFlow(DataFlowTargetApi api) {
|
||||
string output
|
||||
|
|
||||
ThroughFlow::flow(p, returnNodeExt) and
|
||||
returnNodeExt.getEnclosingCallable() = api and
|
||||
returnNodeExt.(DataFlow::Node).getEnclosingCallable() = api and
|
||||
input = parameterNodeAsInput(p) and
|
||||
output = returnNodeAsOutput(returnNodeExt) and
|
||||
input != output and
|
||||
|
||||
@@ -121,7 +121,7 @@ class InstanceParameterNode = DataFlowPrivate::InstanceParameterNode;
|
||||
|
||||
pragma[nomagic]
|
||||
private CS::Parameter getParameter(DataFlowImplCommon::ReturnNodeExt node, ParameterPosition pos) {
|
||||
result = node.getEnclosingCallable().getParameter(pos.getPosition())
|
||||
result = node.(DataFlow::Node).getEnclosingCallable().getParameter(pos.getPosition())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.internal.ContentDataFlow
|
||||
import semmle.code.csharp.dataflow.internal.ContentDataFlow as ContentDataFlow
|
||||
|
||||
class Conf extends ContentDataFlow::Configuration {
|
||||
Conf() { this = "ContentFlowConf" }
|
||||
module ContentConfig implements ContentDataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ObjectCreation }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ObjectCreation }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget().hasUndecoratedName("Sink") and
|
||||
mc.getAnArgument() = sink.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override int accessPathLimit() { result = 2 }
|
||||
int accessPathLimit() { result = 2 }
|
||||
}
|
||||
|
||||
module ContentFlow = ContentDataFlow::Global<ContentConfig>;
|
||||
|
||||
from
|
||||
Conf conf, ContentDataFlow::Node source, ContentDataFlow::AccessPath sourceAp,
|
||||
ContentDataFlow::Node sink, ContentDataFlow::AccessPath sinkAp, boolean preservesValue
|
||||
where conf.hasFlow(source, sourceAp, sink, sinkAp, preservesValue)
|
||||
DataFlow::Node source, ContentFlow::AccessPath sourceAp, DataFlow::Node sink,
|
||||
ContentFlow::AccessPath sinkAp, boolean preservesValue
|
||||
where ContentFlow::flow(source, sourceAp, sink, sinkAp, preservesValue)
|
||||
select source, sourceAp, sink, sinkAp, preservesValue
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
edges
|
||||
| ../../../../resources/stubs/Newtonsoft.Json/13.0.1/Newtonsoft.Json.cs:930:20:930:20 | 4 : Int32 | Test.cs:19:32:19:52 | access to constant Auto : Int32 |
|
||||
| Test.cs:9:46:9:49 | access to parameter data : TextBox | Test.cs:9:46:9:54 | access to property Text |
|
||||
| Test.cs:17:46:17:49 | access to parameter data : TextBox | Test.cs:17:46:17:54 | access to property Text |
|
||||
| Test.cs:19:32:19:52 | access to constant Auto : Int32 | Test.cs:17:57:20:9 | object creation of type JsonSerializerSettings |
|
||||
| Test.cs:19:32:19:52 | access to constant Auto : TypeNameHandling | Test.cs:17:57:20:9 | object creation of type JsonSerializerSettings |
|
||||
| Test.cs:25:46:25:49 | access to parameter data : TextBox | Test.cs:25:46:25:54 | access to property Text |
|
||||
nodes
|
||||
| ../../../../resources/stubs/Newtonsoft.Json/13.0.1/Newtonsoft.Json.cs:930:20:930:20 | 4 : Int32 | semmle.label | 4 : Int32 |
|
||||
| Test.cs:9:46:9:49 | access to parameter data : TextBox | semmle.label | access to parameter data : TextBox |
|
||||
| Test.cs:9:46:9:54 | access to property Text | semmle.label | access to property Text |
|
||||
| Test.cs:17:46:17:49 | access to parameter data : TextBox | semmle.label | access to parameter data : TextBox |
|
||||
| Test.cs:17:46:17:54 | access to property Text | semmle.label | access to property Text |
|
||||
| Test.cs:17:57:20:9 | object creation of type JsonSerializerSettings | semmle.label | object creation of type JsonSerializerSettings |
|
||||
| Test.cs:19:32:19:52 | access to constant Auto : Int32 | semmle.label | access to constant Auto : Int32 |
|
||||
| Test.cs:19:32:19:52 | access to constant Auto : TypeNameHandling | semmle.label | access to constant Auto : TypeNameHandling |
|
||||
| Test.cs:25:46:25:49 | access to parameter data : TextBox | semmle.label | access to parameter data : TextBox |
|
||||
| Test.cs:25:46:25:54 | access to property Text | semmle.label | access to property Text |
|
||||
subpaths
|
||||
|
||||
Reference in New Issue
Block a user