diff --git a/java/ql/lib/semmle/code/java/dataflow/DataFlow.qll b/java/ql/lib/semmle/code/java/dataflow/DataFlow.qll index 54eb809c7b9..c99a8a5a58e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/DataFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/DataFlow.qll @@ -10,6 +10,6 @@ import java module DataFlow { private import semmle.code.java.dataflow.internal.DataFlowImplSpecific private import codeql.dataflow.DataFlow - import DataFlowMake + import DataFlowMakeOverlay import Public } diff --git a/java/ql/lib/semmle/code/java/dataflow/TaintTracking.qll b/java/ql/lib/semmle/code/java/dataflow/TaintTracking.qll index 159604a95bd..34c7b717428 100644 --- a/java/ql/lib/semmle/code/java/dataflow/TaintTracking.qll +++ b/java/ql/lib/semmle/code/java/dataflow/TaintTracking.qll @@ -13,5 +13,5 @@ module TaintTracking { private import semmle.code.java.dataflow.internal.DataFlowImplSpecific private import semmle.code.java.dataflow.internal.TaintTrackingImplSpecific private import codeql.dataflow.TaintTracking - import TaintFlowMake + import TaintFlowMakeOverlay } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplSpecific.qll index 65034ee08b9..97f5020142e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplSpecific.qll @@ -6,6 +6,7 @@ module; private import semmle.code.Location private import codeql.dataflow.DataFlow +private import semmle.code.java.Overlay module Private { import DataFlowPrivate @@ -29,4 +30,6 @@ module JavaDataFlow implements InputSig { predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1; predicate viableImplInCallContext = Private::viableImplInCallContext/2; + + predicate isEvaluatingInOverlay = isOverlay/0; } diff --git a/shared/dataflow/codeql/dataflow/DataFlow.qll b/shared/dataflow/codeql/dataflow/DataFlow.qll index 3483287e3b3..49f84d45b2a 100644 --- a/shared/dataflow/codeql/dataflow/DataFlow.qll +++ b/shared/dataflow/codeql/dataflow/DataFlow.qll @@ -349,6 +349,18 @@ signature module InputSig { /** Holds if `fieldFlowBranchLimit` should be ignored for flow going into/out of `c`. */ 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 Lang> { @@ -645,10 +657,8 @@ private module PathGraphSigMod { } } -module DataFlowMake Lang> { +private module DataFlowMakeCore Lang> { private import Lang - private import internal.DataFlowImpl::MakeImpl - private import internal.DataFlowImplStage1::MakeImplStage1 import Configs /** @@ -691,59 +701,6 @@ module DataFlowMake Lang> { predicate flowToExpr(DataFlowExpr sink); } - /** - * Constructs a global data flow computation. - */ - module Global implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import DefaultState - import Config - - predicate accessPathLimit = Config::accessPathLimit/0; - - predicate isAdditionalFlowStep(Node node1, Node node2, string model) { - Config::isAdditionalFlowStep(node1, node2) and model = "Config" - } - } - - private module Stage1 = ImplStage1; - - import Stage1::PartialFlow - - private module Flow = Impl; - - import Flow - } - - /** - * Constructs a global data flow computation using flow state. - */ - module GlobalWithState implements GlobalFlowSig { - private module C implements FullStateConfigSig { - import Config - - predicate accessPathLimit = Config::accessPathLimit/0; - - predicate isAdditionalFlowStep(Node node1, Node node2, string model) { - Config::isAdditionalFlowStep(node1, node2) and model = "Config" - } - - predicate isAdditionalFlowStep( - Node node1, FlowState state1, Node node2, FlowState state2, string model - ) { - Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" - } - } - - private module Stage1 = ImplStage1; - - import Stage1::PartialFlow - - private module Flow = Impl; - - import Flow - } - signature class PathNodeSig { /** Gets a textual representation of this element. */ string toString(); @@ -1141,3 +1098,135 @@ module DataFlowMake Lang> { import PathGraph } } + +module DataFlowMake Lang> { + import DataFlowMakeCore + private import Lang + private import internal.DataFlowImpl::MakeImpl + private import internal.DataFlowImplStage1::MakeImplStage1 + + /** + * Constructs a global data flow computation. + */ + module Global implements GlobalFlowSig { + private module C implements FullStateConfigSig { + import DefaultState + import Config + + predicate accessPathLimit = Config::accessPathLimit/0; + + predicate isAdditionalFlowStep(Node node1, Node node2, string model) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate observeOverlayInformedIncrementalMode() { none() } + } + + private module Stage1 = ImplStage1; + + import Stage1::PartialFlow + + private module Flow = Impl; + + import Flow + } + + /** + * Constructs a global data flow computation using flow state. + */ + module GlobalWithState implements GlobalFlowSig { + private module C implements FullStateConfigSig { + import Config + + predicate accessPathLimit = Config::accessPathLimit/0; + + predicate isAdditionalFlowStep(Node node1, Node node2, string model) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { + Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" + } + + predicate observeOverlayInformedIncrementalMode() { none() } + } + + private module Stage1 = ImplStage1; + + import Stage1::PartialFlow + + private module Flow = Impl; + + import Flow + } +} + +module DataFlowMakeOverlay Lang> { + import DataFlowMake + private import Lang + private import internal.DataFlowImpl::MakeImpl + private import internal.DataFlowImplStage1::MakeImplStage1 + + /** + * Constructs a global data flow computation. + */ + module Global implements GlobalFlowSig { + private module C implements FullStateConfigSig { + import DefaultState + import Config + + predicate accessPathLimit = Config::accessPathLimit/0; + + predicate isAdditionalFlowStep(Node node1, Node node2, string model) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate observeOverlayInformedIncrementalMode() { + not Config::observeDiffInformedIncrementalMode() + } + } + + private module Stage1 = ImplStage1; + + import Stage1::PartialFlow + + private module Flow = OverlayImpl; + + import Flow + } + + /** + * Constructs a global data flow computation using flow state. + */ + module GlobalWithState implements GlobalFlowSig { + private module C implements FullStateConfigSig { + import Config + + predicate accessPathLimit = Config::accessPathLimit/0; + + predicate isAdditionalFlowStep(Node node1, Node node2, string model) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { + Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" + } + + predicate observeOverlayInformedIncrementalMode() { + not Config::observeDiffInformedIncrementalMode() + } + } + + private module Stage1 = ImplStage1; + + import Stage1::PartialFlow + + private module Flow = OverlayImpl; + + import Flow + } +} diff --git a/shared/dataflow/codeql/dataflow/TaintTracking.qll b/shared/dataflow/codeql/dataflow/TaintTracking.qll index bd4b4ecd6ca..7bb9535d096 100644 --- a/shared/dataflow/codeql/dataflow/TaintTracking.qll +++ b/shared/dataflow/codeql/dataflow/TaintTracking.qll @@ -47,16 +47,16 @@ signature module InputSig Lang> { /** * Construct the modules for taint-tracking analyses. */ -module TaintFlowMake< +private module TaintFlowMakeCore< LocationSig Location, DF::InputSig DataFlowLang, InputSig TaintTrackingLang> { - private import TaintTrackingLang - private import DF::DataFlowMake as DataFlow - private import MakeImpl as DataFlowInternal - private import MakeImplStage1 as DataFlowInternalStage1 + import TaintTrackingLang + import DF::DataFlowMake as DataFlow + import MakeImpl as DataFlowInternal + import MakeImplStage1 as DataFlowInternalStage1 - private module AddTaintDefaults implements + module AddTaintDefaults implements DataFlowInternal::FullStateConfigSig { import Config @@ -83,71 +83,9 @@ module TaintFlowMake< } } - /** - * Constructs a global taint tracking computation. - */ - module Global implements DataFlow::GlobalFlowSig { - private module Config0 implements DataFlowInternal::FullStateConfigSig { - import DataFlowInternal::DefaultState - import Config - - predicate isAdditionalFlowStep( - DataFlowLang::Node node1, DataFlowLang::Node node2, string model - ) { - Config::isAdditionalFlowStep(node1, node2) and model = "Config" - } - } - - private module C implements DataFlowInternal::FullStateConfigSig { - import AddTaintDefaults - } - - private module Stage1 = DataFlowInternalStage1::ImplStage1; - - import Stage1::PartialFlow - - private module Flow = DataFlowInternal::Impl; - - import Flow - } - - /** - * Constructs a global taint tracking computation using flow state. - */ - module GlobalWithState implements DataFlow::GlobalFlowSig { - private module Config0 implements DataFlowInternal::FullStateConfigSig { - import Config - - predicate isAdditionalFlowStep( - DataFlowLang::Node node1, DataFlowLang::Node node2, string model - ) { - Config::isAdditionalFlowStep(node1, node2) and model = "Config" - } - - predicate isAdditionalFlowStep( - DataFlowLang::Node node1, FlowState state1, DataFlowLang::Node node2, FlowState state2, - string model - ) { - Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" - } - } - - private module C implements DataFlowInternal::FullStateConfigSig { - import AddTaintDefaults - } - - private module Stage1 = DataFlowInternalStage1::ImplStage1; - - import Stage1::PartialFlow - - private module Flow = DataFlowInternal::Impl; - - import Flow - } - signature int speculationLimitSig(); - private module AddSpeculativeTaintSteps< + module AddSpeculativeTaintSteps< DataFlowInternal::FullStateConfigSig Config, speculationLimitSig/0 speculationLimit> implements DataFlowInternal::FullStateConfigSig { @@ -215,6 +153,79 @@ module TaintFlowMake< state1.getState() = state2.getState() } } +} + +module TaintFlowMake< + LocationSig Location, DF::InputSig DataFlowLang, + InputSig TaintTrackingLang> +{ + private import TaintFlowMakeCore + + /** + * Constructs a global taint tracking computation. + */ + module Global implements DataFlow::GlobalFlowSig { + private module Config0 implements DataFlowInternal::FullStateConfigSig { + import DataFlowInternal::DefaultState + import Config + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, DataFlowLang::Node node2, string model + ) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate observeOverlayInformedIncrementalMode() { none() } + } + + private module C implements DataFlowInternal::FullStateConfigSig { + import AddTaintDefaults + } + + private module Stage1 = DataFlowInternalStage1::ImplStage1; + + import Stage1::PartialFlow + + private module Flow = DataFlowInternal::Impl; + + import Flow + } + + /** + * Constructs a global taint tracking computation using flow state. + */ + module GlobalWithState implements DataFlow::GlobalFlowSig { + private module Config0 implements DataFlowInternal::FullStateConfigSig { + import Config + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, DataFlowLang::Node node2, string model + ) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, FlowState state1, DataFlowLang::Node node2, FlowState state2, + string model + ) { + Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" + } + + predicate observeOverlayInformedIncrementalMode() { none() } + } + + private module C implements DataFlowInternal::FullStateConfigSig { + import AddTaintDefaults + } + + private module Stage1 = DataFlowInternalStage1::ImplStage1; + + import Stage1::PartialFlow + + private module Flow = DataFlowInternal::Impl; + + import Flow + } /** * Constructs a global taint tracking computation that also allows a given @@ -232,6 +243,8 @@ module TaintFlowMake< ) { Config::isAdditionalFlowStep(node1, node2) and model = "Config" } + + predicate observeOverlayInformedIncrementalMode() { none() } } private module C implements DataFlowInternal::FullStateConfigSig { @@ -270,6 +283,8 @@ module TaintFlowMake< ) { Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" } + + predicate observeOverlayInformedIncrementalMode() { none() } } private module C implements DataFlowInternal::FullStateConfigSig { @@ -285,3 +300,157 @@ module TaintFlowMake< import Flow } } + +module TaintFlowMakeOverlay< + LocationSig Location, DF::InputSig DataFlowLang, + InputSig TaintTrackingLang> +{ + private import TaintFlowMakeCore + + /** + * Constructs a global taint tracking computation. + */ + module Global implements DataFlow::GlobalFlowSig { + private module Config0 implements DataFlowInternal::FullStateConfigSig { + import DataFlowInternal::DefaultState + import Config + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, DataFlowLang::Node node2, string model + ) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate observeOverlayInformedIncrementalMode() { + not Config::observeDiffInformedIncrementalMode() + } + } + + private module C implements DataFlowInternal::FullStateConfigSig { + import AddTaintDefaults + } + + private module Stage1 = DataFlowInternalStage1::ImplStage1; + + import Stage1::PartialFlow + + private module Flow = DataFlowInternal::OverlayImpl; + + import Flow + } + + /** + * Constructs a global taint tracking computation using flow state. + */ + module GlobalWithState implements DataFlow::GlobalFlowSig { + private module Config0 implements DataFlowInternal::FullStateConfigSig { + import Config + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, DataFlowLang::Node node2, string model + ) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, FlowState state1, DataFlowLang::Node node2, FlowState state2, + string model + ) { + Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" + } + + predicate observeOverlayInformedIncrementalMode() { + not Config::observeDiffInformedIncrementalMode() + } + } + + private module C implements DataFlowInternal::FullStateConfigSig { + import AddTaintDefaults + } + + private module Stage1 = DataFlowInternalStage1::ImplStage1; + + import Stage1::PartialFlow + + private module Flow = DataFlowInternal::OverlayImpl; + + import Flow + } + + /** + * Constructs a global taint tracking computation that also allows a given + * maximum number of speculative taint steps. + */ + module SpeculativeGlobal + implements DataFlow::GlobalFlowSig + { + private module Config0 implements DataFlowInternal::FullStateConfigSig { + import DataFlowInternal::DefaultState + import Config + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, DataFlowLang::Node node2, string model + ) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate observeOverlayInformedIncrementalMode() { + not Config::observeDiffInformedIncrementalMode() + } + } + + private module C implements DataFlowInternal::FullStateConfigSig { + import AddTaintDefaults> + } + + private module Stage1 = DataFlowInternalStage1::ImplStage1; + + import Stage1::PartialFlow + + private module Flow = DataFlowInternal::OverlayImpl; + + import Flow + } + + /** + * Constructs a global taint tracking computation using flow state that also + * allows a given maximum number of speculative taint steps. + */ + module SpeculativeGlobalWithState< + DataFlow::StateConfigSig Config, speculationLimitSig/0 speculationLimit> implements + DataFlow::GlobalFlowSig + { + private module Config0 implements DataFlowInternal::FullStateConfigSig { + import Config + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, DataFlowLang::Node node2, string model + ) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, FlowState state1, DataFlowLang::Node node2, FlowState state2, + string model + ) { + Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" + } + + predicate observeOverlayInformedIncrementalMode() { + not Config::observeDiffInformedIncrementalMode() + } + } + + private module C implements DataFlowInternal::FullStateConfigSig { + import AddTaintDefaults> + } + + private module Stage1 = DataFlowInternalStage1::ImplStage1; + + import Stage1::PartialFlow + + private module Flow = DataFlowInternal::OverlayImpl; + + import Flow + } +} diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index a7e0736432a..099866ab6bd 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -143,6 +143,15 @@ module MakeImpl Lang> { */ predicate observeDiffInformedIncrementalMode(); + /** + * Holds if sources and sinks should be filtered to only include those that + * may lead to a flow path with either a source or a sink in the overlay database. + * This only has an effect when running + * in overlay-informed incremental mode. This should be used in conjunction + * with the `OverlayImpl` implementation to merge the base results back in. + */ + predicate observeOverlayInformedIncrementalMode(); + Location getASelectedSourceLocation(Node source); Location getASelectedSinkLocation(Node sink); @@ -171,6 +180,56 @@ module MakeImpl Lang> { } } + /** + * Constructs a data flow computation given a full input configuration, and + * an initial stage 1 pruning with merging of overlay and base results. + */ + module OverlayImpl Stage1> { + private module Flow = Impl; + + import Flow + + /** + * Holds if data can flow from `source` to `sink`. + * + * This is a local predicate that only has results local to the overlay/base database. + */ + private predicate flowLocal(Node source, Node sink) = forceLocal(Flow::flow/2)(source, sink) + + /** + * Holds if data can flow from `source` to `sink`. + */ + predicate flow(Node source, Node sink) { + Flow::flow(source, sink) + or + // If we are overlay informed (i.e. we are not diff-informed), we + // merge in the local results which includes the base database results. + flowLocal(source, sink) and Config::observeOverlayInformedIncrementalMode() + } + + /** + * Holds if data can flow from some source to `sink`. + * This is a local predicate that only has results local to the overlay/base database. + */ + predicate flowToLocal(Node sink) = forceLocal(Flow::flowTo/1)(sink) + + /** + * Holds if data can flow from some source to `sink`. + */ + predicate flowTo(Node sink) { + Flow::flowTo(sink) + or + // If we are overlay informed (i.e. we are not diff-informed), we + // merge in the local results which includes the base database results. + flowToLocal(sink) and Config::observeOverlayInformedIncrementalMode() + } + + /** + * Holds if data can flow from some source to `sink`. + */ + predicate flowToExpr(Lang::DataFlowExpr sink) { flowTo(exprNode(sink)) } + } + /** * Constructs a data flow computation given a full input configuration, and * an initial stage 1 pruning. diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index c7883df0de1..bb79ff62f5b 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -4,7 +4,7 @@ * Provides an implementation of a fast initial pruning of global * (interprocedural) data flow reachability (Stage 1). */ -overlay[local?] +overlay[local?] // when this is removed, put `overlay[local?]` on `isOverlayNode`. module; private import codeql.util.Unit @@ -129,23 +129,76 @@ module MakeImplStage1 Lang> { private module AlertFiltering = AlertFilteringImpl; + /** + * 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) + } + + /** + * The filtering if we aren't meant to be diff-informed. + * + * Shared between sources and sinks. + */ + overlay[global] + pragma[inline] + private predicate nonDiffInformedFilter(Node node) { + // If we are in base-only global evaluation, do not filter out any sources/sinks. + not isEvaluatingInOverlay() + or + // If the configuration doesn't merge overlays, do not filter out any sources/sinks. + not Config::observeOverlayInformedIncrementalMode() + or + // If we are in global evaluation with an overlay present, restrict + // sources/sinks to those visible in the overlay. + isOverlayNode(node) + } + + overlay[global] pragma[nomagic] private predicate isFilteredSource(Node source) { Config::isSource(source, _) and + // Data flow is always incremental in one of two ways. + // 1. If the configuration is diff-informed, we filter to only include nodes in the diff, + // which gives the smallest set of nodes. + // If diff information is not available, we do not filter at all. + // 2. If not, in global evaluation with overlay, we filter to only + // include nodes from files in the overlay; flow from + // other nodes will be added back later. + // We start by seeing if we should be in case 1. if Config::observeDiffInformedIncrementalMode() - then AlertFiltering::filterByLocation(Config::getASelectedSourceLocation(source)) - else any() + then + // Case 1: We are meant to be diff-informed. + // We still only filter if we have diff information. + AlertFiltering::diffInformationAvailable() + implies + AlertFiltering::locationIsInDiff(Config::getASelectedSourceLocation(source)) + else nonDiffInformedFilter(source) } + overlay[global] pragma[nomagic] private predicate isFilteredSink(Node sink) { ( Config::isSink(sink, _) or Config::isSink(sink) ) and + // See the comments in `isFilteredSource` for the reasoning behind the following. if Config::observeDiffInformedIncrementalMode() - then AlertFiltering::filterByLocation(Config::getASelectedSinkLocation(sink)) - else any() + then + AlertFiltering::diffInformationAvailable() + implies + AlertFiltering::locationIsInDiff(Config::getASelectedSinkLocation(sink)) + else nonDiffInformedFilter(sink) } private predicate hasFilteredSource() { isFilteredSource(_) } diff --git a/shared/util/codeql/util/AlertFiltering.qll b/shared/util/codeql/util/AlertFiltering.qll index 1480421ae59..60597327c3b 100644 --- a/shared/util/codeql/util/AlertFiltering.qll +++ b/shared/util/codeql/util/AlertFiltering.qll @@ -82,6 +82,21 @@ module AlertFilteringImpl { ) } + /** 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 is a match for one of the active filtering * predicates in this module, or if all filtering predicates are inactive @@ -92,8 +107,17 @@ module AlertFilteringImpl { */ bindingset[location] predicate filterByLocation(Location location) { - not restrictAlertsTo(_, _, _) and not restrictAlertsToExactLocation(_, _, _, _, _) + not diffInformationAvailable() 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 | restrictAlertsToEntireFile(filePath) and location.hasLocationInfo(filePath, _, _, _, _)