mirror of
https://github.com/github/codeql.git
synced 2026-06-09 06:58:50 +02:00
Compare commits
7 Commits
codeql-cli
...
alexet/glo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
652c048af5 | ||
|
|
eb31b1c0d3 | ||
|
|
979df35109 | ||
|
|
93e3702555 | ||
|
|
1f265efac8 | ||
|
|
8616943794 | ||
|
|
ee2de0170c |
@@ -10,7 +10,7 @@
|
|||||||
* of the field in case the field is not amenable to a non-trivial SSA
|
* of the field in case the field is not amenable to a non-trivial SSA
|
||||||
* representation.
|
* representation.
|
||||||
*/
|
*/
|
||||||
overlay[local?]
|
overlay[local]
|
||||||
module;
|
module;
|
||||||
|
|
||||||
import java
|
import java
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ module;
|
|||||||
|
|
||||||
private import semmle.code.Location
|
private import semmle.code.Location
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
|
private import semmle.code.java.Overlay
|
||||||
|
|
||||||
module Private {
|
module Private {
|
||||||
import DataFlowPrivate
|
import DataFlowPrivate
|
||||||
@@ -29,4 +30,6 @@ module JavaDataFlow implements InputSig<Location> {
|
|||||||
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
|
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
|
||||||
|
|
||||||
predicate viableImplInCallContext = Private::viableImplInCallContext/2;
|
predicate viableImplInCallContext = Private::viableImplInCallContext/2;
|
||||||
|
|
||||||
|
predicate isEvaluatingInOverlay = isOverlay/0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ overlay[caller?]
|
|||||||
pragma[inline]
|
pragma[inline]
|
||||||
predicate localFlow(Node node1, Node node2) { node1 = node2 or localFlowStepPlus(node1, node2) }
|
predicate localFlow(Node node1, Node node2) { node1 = node2 or localFlowStepPlus(node1, node2) }
|
||||||
|
|
||||||
|
overlay[caller?]
|
||||||
private predicate localFlowStepPlus(Node node1, Node node2) = fastTC(localFlowStep/2)(node1, node2)
|
private predicate localFlowStepPlus(Node node1, Node node2) = fastTC(localFlowStep/2)(node1, node2)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
overlay[local?]
|
overlay[local]
|
||||||
module;
|
module;
|
||||||
|
|
||||||
import java
|
import java
|
||||||
@@ -157,15 +157,20 @@ private predicate hasEntryDef(TrackedVar v, BasicBlock b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if `n` might update the locally tracked variable `v`. */
|
/** Holds if `n` might update the locally tracked variable `v`. */
|
||||||
|
overlay[global]
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
private predicate uncertainVariableUpdateImpl(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||||
exists(Call c | c = n.asCall() | updatesNamedField(c, v, _)) and
|
exists(Call c | c = n.asCall() | updatesNamedField(c, v, _)) and
|
||||||
b.getNode(i) = n and
|
b.getNode(i) = n and
|
||||||
hasDominanceInformation(b)
|
hasDominanceInformation(b)
|
||||||
or
|
or
|
||||||
uncertainVariableUpdate(v.getQualifier(), n, b, i)
|
uncertainVariableUpdateImpl(v.getQualifier(), n, b, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Holds if `n` might update the locally tracked variable `v`. */
|
||||||
|
predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) =
|
||||||
|
forceLocal(uncertainVariableUpdateImpl/4)(v, n, b, i)
|
||||||
|
|
||||||
private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||||
private import java as J
|
private import java as J
|
||||||
|
|
||||||
@@ -345,6 +350,7 @@ private module Cached {
|
|||||||
* Constructor --(intraInstanceCallEdge)-->+ Method(setter of this.f)
|
* Constructor --(intraInstanceCallEdge)-->+ Method(setter of this.f)
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
|
overlay[global]
|
||||||
private predicate intraInstanceCallEdge(Callable c1, Method m2) {
|
private predicate intraInstanceCallEdge(Callable c1, Method m2) {
|
||||||
exists(MethodCall ma, RefType t1 |
|
exists(MethodCall ma, RefType t1 |
|
||||||
ma.getCaller() = c1 and
|
ma.getCaller() = c1 and
|
||||||
@@ -365,6 +371,7 @@ private module Cached {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
private Callable tgt(Call c) {
|
private Callable tgt(Call c) {
|
||||||
result = viableImpl_v2(c)
|
result = viableImpl_v2(c)
|
||||||
or
|
or
|
||||||
@@ -374,11 +381,13 @@ private module Cached {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if `(c1,c2)` is an edge in the call graph. */
|
/** Holds if `(c1,c2)` is an edge in the call graph. */
|
||||||
|
overlay[global]
|
||||||
private predicate callEdge(Callable c1, Callable c2) {
|
private predicate callEdge(Callable c1, Callable c2) {
|
||||||
exists(Call c | c.getCaller() = c1 and c2 = tgt(c))
|
exists(Call c | c.getCaller() = c1 and c2 = tgt(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if `(c1,c2)` is an edge in the call graph excluding `intraInstanceCallEdge`. */
|
/** Holds if `(c1,c2)` is an edge in the call graph excluding `intraInstanceCallEdge`. */
|
||||||
|
overlay[global]
|
||||||
private predicate crossInstanceCallEdge(Callable c1, Callable c2) {
|
private predicate crossInstanceCallEdge(Callable c1, Callable c2) {
|
||||||
callEdge(c1, c2) and not intraInstanceCallEdge(c1, c2)
|
callEdge(c1, c2) and not intraInstanceCallEdge(c1, c2)
|
||||||
}
|
}
|
||||||
@@ -392,6 +401,7 @@ private module Cached {
|
|||||||
relevantFieldUpdate(_, f.getField(), _)
|
relevantFieldUpdate(_, f.getField(), _)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
private predicate source(Call call, TrackedField f, Field field, Callable c, boolean fresh) {
|
private predicate source(Call call, TrackedField f, Field field, Callable c, boolean fresh) {
|
||||||
relevantCall(call, f) and
|
relevantCall(call, f) and
|
||||||
field = f.getField() and
|
field = f.getField() and
|
||||||
@@ -405,9 +415,11 @@ private module Cached {
|
|||||||
* `fresh` indicates whether the instance `this` in `c` has been freshly
|
* `fresh` indicates whether the instance `this` in `c` has been freshly
|
||||||
* allocated along the call-chain.
|
* allocated along the call-chain.
|
||||||
*/
|
*/
|
||||||
|
overlay[global]
|
||||||
private newtype TCallableNode =
|
private newtype TCallableNode =
|
||||||
MkCallableNode(Callable c, boolean fresh) { source(_, _, _, c, fresh) or edge(_, c, fresh) }
|
MkCallableNode(Callable c, boolean fresh) { source(_, _, _, c, fresh) or edge(_, c, fresh) }
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
private predicate edge(TCallableNode n, Callable c2, boolean f2) {
|
private predicate edge(TCallableNode n, Callable c2, boolean f2) {
|
||||||
exists(Callable c1, boolean f1 | n = MkCallableNode(c1, f1) |
|
exists(Callable c1, boolean f1 | n = MkCallableNode(c1, f1) |
|
||||||
intraInstanceCallEdge(c1, c2) and f2 = f1
|
intraInstanceCallEdge(c1, c2) and f2 = f1
|
||||||
@@ -417,6 +429,7 @@ private module Cached {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
private predicate edge(TCallableNode n1, TCallableNode n2) {
|
private predicate edge(TCallableNode n1, TCallableNode n2) {
|
||||||
exists(Callable c2, boolean f2 |
|
exists(Callable c2, boolean f2 |
|
||||||
edge(n1, c2, f2) and
|
edge(n1, c2, f2) and
|
||||||
@@ -424,6 +437,7 @@ private module Cached {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate source(Call call, TrackedField f, Field field, TCallableNode n) {
|
private predicate source(Call call, TrackedField f, Field field, TCallableNode n) {
|
||||||
exists(Callable c, boolean fresh |
|
exists(Callable c, boolean fresh |
|
||||||
@@ -432,24 +446,28 @@ private module Cached {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
private predicate sink(Callable c, Field f, TCallableNode n) {
|
private predicate sink(Callable c, Field f, TCallableNode n) {
|
||||||
setsOwnField(c, f) and n = MkCallableNode(c, false)
|
setsOwnField(c, f) and n = MkCallableNode(c, false)
|
||||||
or
|
or
|
||||||
setsOtherField(c, f) and n = MkCallableNode(c, _)
|
setsOtherField(c, f) and n = MkCallableNode(c, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
private predicate prunedNode(TCallableNode n) {
|
private predicate prunedNode(TCallableNode n) {
|
||||||
sink(_, _, n)
|
sink(_, _, n)
|
||||||
or
|
or
|
||||||
exists(TCallableNode mid | edge(n, mid) and prunedNode(mid))
|
exists(TCallableNode mid | edge(n, mid) and prunedNode(mid))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
private predicate prunedEdge(TCallableNode n1, TCallableNode n2) {
|
private predicate prunedEdge(TCallableNode n1, TCallableNode n2) {
|
||||||
prunedNode(n1) and
|
prunedNode(n1) and
|
||||||
prunedNode(n2) and
|
prunedNode(n2) and
|
||||||
edge(n1, n2)
|
edge(n1, n2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
private predicate edgePlus(TCallableNode c1, TCallableNode c2) = fastTC(prunedEdge/2)(c1, c2)
|
private predicate edgePlus(TCallableNode c1, TCallableNode c2) = fastTC(prunedEdge/2)(c1, c2)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -457,6 +475,7 @@ private module Cached {
|
|||||||
* where `f` and `call` share the same enclosing callable in which a
|
* where `f` and `call` share the same enclosing callable in which a
|
||||||
* `FieldRead` of `f` is reachable from `call`.
|
* `FieldRead` of `f` is reachable from `call`.
|
||||||
*/
|
*/
|
||||||
|
overlay[global]
|
||||||
pragma[noopt]
|
pragma[noopt]
|
||||||
private predicate updatesNamedFieldImpl(Call call, TrackedField f, Callable setter) {
|
private predicate updatesNamedFieldImpl(Call call, TrackedField f, Callable setter) {
|
||||||
exists(TCallableNode src, TCallableNode sink, Field field |
|
exists(TCallableNode src, TCallableNode sink, Field field |
|
||||||
@@ -467,17 +486,23 @@ private module Cached {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bindingset[call, f]
|
bindingset[call, f]
|
||||||
|
overlay[global]
|
||||||
pragma[inline_late]
|
pragma[inline_late]
|
||||||
private predicate updatesNamedField0(Call call, TrackedField f, Callable setter) {
|
private predicate updatesNamedField0(Call call, TrackedField f, Callable setter) {
|
||||||
updatesNamedField(call, f, setter)
|
updatesNamedField(call, f, setter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
cached
|
cached
|
||||||
predicate defUpdatesNamedField(SsaImplicitUpdate def, TrackedField f, Callable setter) {
|
predicate defUpdatesNamedFieldImpl(SsaImplicitUpdate def, TrackedField f, Callable setter) {
|
||||||
f = def.getSourceVariable() and
|
f = def.getSourceVariable() and
|
||||||
updatesNamedField0(def.getCfgNode().asCall(), f, setter)
|
updatesNamedField0(def.getCfgNode().asCall(), f, setter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
predicate defUpdatesNamedField(SsaImplicitUpdate def, TrackedField f, Callable setter) =
|
||||||
|
forceLocal(defUpdatesNamedFieldImpl/3)(def, f, setter)
|
||||||
|
|
||||||
cached
|
cached
|
||||||
predicate ssaUncertainImplicitUpdate(SsaImplicitUpdate def) {
|
predicate ssaUncertainImplicitUpdate(SsaImplicitUpdate def) {
|
||||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||||
@@ -545,6 +570,7 @@ private module Cached {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
|
overlay[global]
|
||||||
module DataFlowIntegration {
|
module DataFlowIntegration {
|
||||||
import DataFlowIntegrationImpl
|
import DataFlowIntegrationImpl
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
* Provides predicates for reasoning about runtime call targets through virtual
|
* Provides predicates for reasoning about runtime call targets through virtual
|
||||||
* dispatch.
|
* dispatch.
|
||||||
*/
|
*/
|
||||||
overlay[local?]
|
|
||||||
module;
|
module;
|
||||||
|
|
||||||
import java
|
import java
|
||||||
|
|||||||
@@ -170,12 +170,15 @@ private module RegexFlow = DataFlow::Global<RegexFlowConfig>;
|
|||||||
* As an optimisation, only regexes containing an infinite repitition quatifier (`+`, `*`, or `{x,}`)
|
* As an optimisation, only regexes containing an infinite repitition quatifier (`+`, `*`, or `{x,}`)
|
||||||
* and therefore may be relevant for ReDoS queries are considered.
|
* and therefore may be relevant for ReDoS queries are considered.
|
||||||
*/
|
*/
|
||||||
predicate usedAsRegex(StringLiteral regex, string mode, boolean match_full_string) {
|
predicate usedAsRegexG(StringLiteral regex, string mode, boolean match_full_string) {
|
||||||
RegexFlow::flow(DataFlow::exprNode(regex), _) and
|
RegexFlow::flow(DataFlow::exprNode(regex), _) and
|
||||||
mode = "None" and // TODO: proper mode detection
|
mode = "None" and // TODO: proper mode detection
|
||||||
(if matchesFullString(regex) then match_full_string = true else match_full_string = false)
|
(if matchesFullString(regex) then match_full_string = true else match_full_string = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[local]
|
||||||
|
predicate usedAsRegex(StringLiteral regex, string mode, boolean match_full_string) = forceLocal(usedAsRegexG/3)(regex, mode, match_full_string)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `regex` is used as a regular expression that is matched against a full string,
|
* Holds if `regex` is used as a regular expression that is matched against a full string,
|
||||||
* as though it was implicitly surrounded by ^ and $.
|
* as though it was implicitly surrounded by ^ and $.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/** Provides a class hierarchy corresponding to a parse tree of regular expressions. */
|
/** Provides a class hierarchy corresponding to a parse tree of regular expressions. */
|
||||||
overlay[local?]
|
overlay[local]
|
||||||
module;
|
module;
|
||||||
|
|
||||||
private import semmle.code.java.regex.regex as RE // importing under a namescape to avoid naming conflict for `Top`.
|
private import semmle.code.java.regex.regex as RE // importing under a namescape to avoid naming conflict for `Top`.
|
||||||
|
|||||||
@@ -936,6 +936,7 @@ abstract class RegexString extends StringLiteral {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A string literal used as a regular expression */
|
/** A string literal used as a regular expression */
|
||||||
|
overlay[local]
|
||||||
class Regex extends RegexString {
|
class Regex extends RegexString {
|
||||||
boolean matches_full_string;
|
boolean matches_full_string;
|
||||||
|
|
||||||
|
|||||||
@@ -349,6 +349,18 @@ signature module InputSig<LocationSig Location> {
|
|||||||
|
|
||||||
/** Holds if `fieldFlowBranchLimit` should be ignored for flow going into/out of `c`. */
|
/** Holds if `fieldFlowBranchLimit` should be ignored for flow going into/out of `c`. */
|
||||||
default predicate ignoreFieldFlowBranchLimit(DataFlowCallable c) { none() }
|
default predicate ignoreFieldFlowBranchLimit(DataFlowCallable c) { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the evaluator is currently evaluating with an overlay. The
|
||||||
|
* implementation of this predicate needs to be `overlay[local]`. For a
|
||||||
|
* language with no overlay support, `none()` is a valid implementation.
|
||||||
|
*
|
||||||
|
* When called from a local predicate, this predicate holds if we are in the
|
||||||
|
* overlay-only local evaluation. When called from a global predicate, this
|
||||||
|
* predicate holds if we are evaluating globally with overlay and base both
|
||||||
|
* visible.
|
||||||
|
*/
|
||||||
|
default predicate isEvaluatingInOverlay() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
module Configs<LocationSig Location, InputSig<Location> Lang> {
|
module Configs<LocationSig Location, InputSig<Location> Lang> {
|
||||||
|
|||||||
@@ -3376,24 +3376,81 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
|
|||||||
import S6
|
import S6
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data can flow from `source` to `sink`.
|
* Holds if data can flow from `source` to `sink`. When running in overlay
|
||||||
|
* incremental mode on a supported language, this predicate only has
|
||||||
|
* results where either the source or sink is in the overlay database.
|
||||||
*/
|
*/
|
||||||
predicate flow(Node source, Node sink) {
|
private predicate flowIncremental(Node source, Node sink) {
|
||||||
exists(PathNode source0, PathNode sink0 |
|
exists(PathNode source0, PathNode sink0 |
|
||||||
flowPath(source0, sink0) and source0.getNode() = source and sink0.getNode() = sink
|
flowPath(source0, sink0) and source0.getNode() = source and sink0.getNode() = sink
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[local]
|
||||||
|
private predicate flowLocal(Node source, Node sink) =
|
||||||
|
forceLocal(flowIncremental/2)(source, sink)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if data can flow from `source` to `sink`.
|
||||||
|
*/
|
||||||
|
predicate flow(Node source, Node sink) {
|
||||||
|
flowIncremental(source, sink)
|
||||||
|
or
|
||||||
|
flowLocal(source, sink)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if data can flow from some source to `sink`. When running in
|
||||||
|
* overlay incremental mode on a supported language, this predicate only
|
||||||
|
* has results where either the source or sink is in the overlay database.
|
||||||
|
*/
|
||||||
|
private predicate flowToIncremental(Node sink) {
|
||||||
|
exists(PathNode n | n.isSink() and n.getNode() = sink)
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay[local]
|
||||||
|
private predicate flowToLocal(Node sink) = forceLocal(flowToIncremental/1)(sink)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data can flow from some source to `sink`.
|
* Holds if data can flow from some source to `sink`.
|
||||||
*/
|
*/
|
||||||
predicate flowTo(Node sink) { exists(PathNode n | n.isSink() and n.getNode() = sink) }
|
predicate flowTo(Node sink) {
|
||||||
|
flowToIncremental(sink)
|
||||||
|
or
|
||||||
|
flowToLocal(sink)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data can flow from some source to `sink`.
|
* Holds if data can flow from some source to `sink`.
|
||||||
*/
|
*/
|
||||||
predicate flowToExpr(Expr sink) { flowTo(exprNode(sink)) }
|
predicate flowToExpr(Expr sink) { flowTo(exprNode(sink)) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if data can flow from `source` to some sink. When running in
|
||||||
|
* overlay incremental mode on a supported language, this predicate only
|
||||||
|
* has results where either the source or sink is in the overlay database.
|
||||||
|
*/
|
||||||
|
private predicate flowFromIncremental(Node source) {
|
||||||
|
exists(PathNode n | n.isSource() and n.getNode() = source)
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay[local]
|
||||||
|
private predicate flowFromLocal(Node source) = forceLocal(flowFromIncremental/1)(source)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if data can flow from `source` to some sink.
|
||||||
|
*/
|
||||||
|
predicate flowFrom(Node source) {
|
||||||
|
flowFromIncremental(source)
|
||||||
|
or
|
||||||
|
flowFromLocal(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if data can flow from `source` to some sink.
|
||||||
|
*/
|
||||||
|
predicate flowFromExpr(Expr source) { flowFrom(exprNode(source)) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL: Only for debugging.
|
* INTERNAL: Only for debugging.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Provides an implementation of a fast initial pruning of global
|
* Provides an implementation of a fast initial pruning of global
|
||||||
* (interprocedural) data flow reachability (Stage 1).
|
* (interprocedural) data flow reachability (Stage 1).
|
||||||
*/
|
*/
|
||||||
overlay[local?]
|
overlay[local?] // when this is removed, put `overlay[local?]` on `isOverlayNode`.
|
||||||
module;
|
module;
|
||||||
|
|
||||||
private import codeql.util.Unit
|
private import codeql.util.Unit
|
||||||
@@ -129,23 +129,86 @@ module MakeImplStage1<LocationSig Location, InputSig<Location> Lang> {
|
|||||||
|
|
||||||
private module AlertFiltering = AlertFilteringImpl<Location>;
|
private module AlertFiltering = AlertFilteringImpl<Location>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the given node is visible in overlay-only local evaluation.
|
||||||
|
*
|
||||||
|
* This predicate needs to be `overlay[local?]`, either directly or
|
||||||
|
* through annotations from an outer scope. If `Node` is global for the
|
||||||
|
* language under analysis, then every node is considered an overlay
|
||||||
|
* node, which means there will effectively be no overlay-based
|
||||||
|
* filtering of sources and sinks.
|
||||||
|
*/
|
||||||
|
private predicate isOverlayNode(Node node) {
|
||||||
|
isEvaluatingInOverlay() and
|
||||||
|
// Any local node is an overlay node if we are evaluating in overlay mode
|
||||||
|
exists(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `node` comes from a file that was in the diff for which we
|
||||||
|
* are producing results.
|
||||||
|
*/
|
||||||
|
overlay[global]
|
||||||
|
private predicate isDiffFileNode(Node node) {
|
||||||
|
exists(string filePath |
|
||||||
|
node.getLocation().hasLocationInfo(filePath, _, _, _, _) and
|
||||||
|
AlertFiltering::fileIsInDiff(filePath)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate isFilteredSource(Node source) {
|
private predicate isFilteredSource(Node source) {
|
||||||
Config::isSource(source, _) and
|
Config::isSource(source, _) and
|
||||||
if Config::observeDiffInformedIncrementalMode()
|
// Data flow is always incremental in one of two ways.
|
||||||
then AlertFiltering::filterByLocation(Config::getASelectedSourceLocation(source))
|
// 1. If the configuration is diff-informed and diff information is
|
||||||
else any()
|
// available, we filter to only include nodes in the diff, which
|
||||||
|
// gives the smallest set of nodes.
|
||||||
|
// 2. If not, in global evaluation with overlay, we filter to only
|
||||||
|
// include nodes from files in the overlay or the diff; flow from
|
||||||
|
// other nodes will be added back later. There can be two reasons
|
||||||
|
// why we are in this case:
|
||||||
|
// 1. This could be the primary configuration for a query that
|
||||||
|
// hasn't yet become diff-informed. In that case, the
|
||||||
|
// `getASelectedSourceLocation` information is probably just the
|
||||||
|
// default, and it's a fairly safe overapproximation to
|
||||||
|
// effectively expand to all nodes in the file (via
|
||||||
|
// `isDiffFileNode`).
|
||||||
|
// 2. This could be a secondary configuration, like a helper
|
||||||
|
// configuration for finding sources or sinks of a primary
|
||||||
|
// configuration. In that case, the results of this configuration
|
||||||
|
// are typically in the same file as the final alert.
|
||||||
|
if
|
||||||
|
Config::observeDiffInformedIncrementalMode() and
|
||||||
|
AlertFiltering::diffInformationAvailable()
|
||||||
|
then AlertFiltering::locationIsInDiff(Config::getASelectedSourceLocation(source))
|
||||||
|
else (
|
||||||
|
// If we are in base-only global evaluation, do not filter out any sources.
|
||||||
|
not isEvaluatingInOverlay()
|
||||||
|
or
|
||||||
|
// If we are in global evaluation with an overlay present, restrict
|
||||||
|
// sources to those visible in the overlay or
|
||||||
|
isOverlayNode(source)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlay[global]
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate isFilteredSink(Node sink) {
|
private predicate isFilteredSink(Node sink) {
|
||||||
(
|
(
|
||||||
Config::isSink(sink, _) or
|
Config::isSink(sink, _) or
|
||||||
Config::isSink(sink)
|
Config::isSink(sink)
|
||||||
) and
|
) and
|
||||||
if Config::observeDiffInformedIncrementalMode()
|
// See the comments in `isFilteredSource` for the reasoning behind the following.
|
||||||
then AlertFiltering::filterByLocation(Config::getASelectedSinkLocation(sink))
|
if
|
||||||
else any()
|
Config::observeDiffInformedIncrementalMode() and
|
||||||
|
AlertFiltering::diffInformationAvailable()
|
||||||
|
then AlertFiltering::locationIsInDiff(Config::getASelectedSinkLocation(sink))
|
||||||
|
else (
|
||||||
|
not isEvaluatingInOverlay()
|
||||||
|
or
|
||||||
|
isOverlayNode(sink)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate hasFilteredSource() { isFilteredSource(_) }
|
private predicate hasFilteredSource() { isFilteredSource(_) }
|
||||||
|
|||||||
@@ -82,6 +82,21 @@ module AlertFilteringImpl<LocationSig Location> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Holds if diff information is available in this evaluation. */
|
||||||
|
predicate diffInformationAvailable() {
|
||||||
|
restrictAlertsTo(_, _, _) or restrictAlertsToExactLocation(_, _, _, _, _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if diff information is available, and `filePath` is in the diff
|
||||||
|
* range.
|
||||||
|
*/
|
||||||
|
predicate fileIsInDiff(string filePath) {
|
||||||
|
restrictAlertsTo(filePath, _, _)
|
||||||
|
or
|
||||||
|
restrictAlertsToExactLocation(filePath, _, _, _, _)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if the given location intersects the diff range. When that is the
|
* Holds if the given location intersects the diff range. When that is the
|
||||||
* case, ensuring that alerts mentioning this location are included in the
|
* case, ensuring that alerts mentioning this location are included in the
|
||||||
@@ -103,8 +118,17 @@ module AlertFilteringImpl<LocationSig Location> {
|
|||||||
*/
|
*/
|
||||||
bindingset[location]
|
bindingset[location]
|
||||||
predicate filterByLocation(Location location) {
|
predicate filterByLocation(Location location) {
|
||||||
not restrictAlertsTo(_, _, _) and not restrictAlertsToExactLocation(_, _, _, _, _)
|
not diffInformationAvailable()
|
||||||
or
|
or
|
||||||
|
locationIsInDiff(location)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like `filterByLocation`, except that if there is no diff range, this
|
||||||
|
* predicate never holds.
|
||||||
|
*/
|
||||||
|
bindingset[location]
|
||||||
|
predicate locationIsInDiff(Location location) {
|
||||||
exists(string filePath |
|
exists(string filePath |
|
||||||
restrictAlertsToEntireFile(filePath) and
|
restrictAlertsToEntireFile(filePath) and
|
||||||
location.hasLocationInfo(filePath, _, _, _, _)
|
location.hasLocationInfo(filePath, _, _, _, _)
|
||||||
|
|||||||
Reference in New Issue
Block a user