Skip to content

Commit fbcdb53

Browse files
committed
QL: Add option to follow 'cached' dependencies
1 parent bac573b commit fbcdb53

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

ql/ql/src/codeql_ql/dependency/DependencyPath.qll

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ private TypeExpr getABaseType(Class cls, boolean abstractExtension) {
7676
}
7777

7878
pragma[nomagic]
79-
predicate rawEdge(PathNode pred, PathNode succ) {
79+
private predicate basicEdge(PathNode pred, PathNode succ) {
8080
exists(Predicate predicat | pred.asAstNode() = predicat |
8181
succ.asAstNode().getEnclosingPredicate() = predicat
8282
or
@@ -138,11 +138,36 @@ predicate rawEdge(PathNode pred, PathNode succ) {
138138
pred.asAstNode() = top and
139139
succ.asAstNode().getFile() = top.getFile()
140140
)
141+
or
142+
// Add bidirectional edges between a class declaration and its type.
143+
exists(Class cls |
144+
pred.asAstNode() = cls and succ.asType() = cls.getType()
145+
or
146+
succ.asAstNode() = cls and pred.asType() = cls.getType()
147+
)
141148
}
142149

143-
private PathNode getAPredecessor(PathNode node) { rawEdge(result, node) }
144-
145-
private PathNode getASuccessor(PathNode node) { rawEdge(node, result) }
150+
private predicate cacheEdge(PathNode pred, PathNode succ) {
151+
// At a cached module, add bidirectional edges to every cached member
152+
exists(Module mod, Declaration decl |
153+
mod.hasAnnotation("cached") and
154+
decl = mod.getAChild() and
155+
decl.hasAnnotation("cached")
156+
|
157+
pred.asAstNode() = mod and succ.asAstNode() = decl
158+
or
159+
succ.asAstNode() = mod and pred.asAstNode() = decl
160+
)
161+
or
162+
// At a cached class, add edges from the class to its cached member predicates
163+
exists(Class cls, Predicate member |
164+
cls.hasAnnotation("cached") and
165+
member = cls.getAClassPredicate() and
166+
member.hasAnnotation("cached") and
167+
pred.asAstNode() = cls and
168+
succ.asAstNode() = member
169+
)
170+
}
146171

147172
signature module DependencyConfig {
148173
/**
@@ -154,9 +179,30 @@ signature module DependencyConfig {
154179
* Holds if a transitive dependency from a source to `sink` should be reported.
155180
*/
156181
predicate isSink(PathNode sink);
182+
183+
/**
184+
* Holds if the `cached` members of a `cached` module or class should be unified.
185+
*
186+
* Whether to set this depends on your use-case:
187+
* - If you wish to know why one predicate causes another predicate to be evaluated, this should be `any()`.
188+
* - If you wish to investigate recursion patterns or understand why the value of one predicate
189+
* is influenced by another predicate, it should be `none()`.
190+
*/
191+
predicate followCacheDependencies();
157192
}
158193

159194
module PathGraph<DependencyConfig C> {
195+
private predicate rawEdge(PathNode pred, PathNode succ) {
196+
basicEdge(pred, succ)
197+
or
198+
C::followCacheDependencies() and
199+
cacheEdge(pred, succ)
200+
}
201+
202+
private PathNode getAPredecessor(PathNode node) { rawEdge(result, node) }
203+
204+
private PathNode getASuccessor(PathNode node) { rawEdge(node, result) }
205+
160206
private PathNode reachableFromSource() {
161207
C::isSource(result)
162208
or

ql/ql/src/queries/explore/DependencyPath.ql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ module Config implements DependencyConfig {
2929
* Holds if a transitive dependency from a source to `sink` should be reported.
3030
*/
3131
predicate isSink(PathNode sink) { sink.asAstNode().(Predicate).getName() = "isLocalSourceNode" }
32+
33+
/**
34+
* Holds if the `cached` members of a `cached` module or class should be unified.
35+
*
36+
* Whether to set this depends on your use-case:
37+
* - If you wish to know why one predicate causes another predicate to be evaluated, this should be `any()`.
38+
* - If you wish to investigate recursion patterns or understand why the value of one predicate
39+
* is influenced by another predicate, it should be `none()`.
40+
*/
41+
predicate followCacheDependencies() { any() }
3242
}
3343

3444
import PathGraph<Config>

0 commit comments

Comments
 (0)