Skip to content

Commit 5b3ef70

Browse files
committed
QL: ql/unqueryable-code query
1 parent b45f56a commit 5b3ef70

File tree

2 files changed

+63
-8
lines changed

2 files changed

+63
-8
lines changed

ql/ql/src/codeql_ql/style/DeadCodeQuery.qll

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ private AstNode publicApi() {
3030
*/
3131
private AstNode queryPredicate() {
3232
// result = query relation that is "transitively" imported by a .ql file.
33-
PathProblemQuery::importsQueryRelation(result).asFile().getExtension() = "ql"
33+
// PathProblemQuery::importsQueryRelation(result).asFile().getExtension() = "ql"
34+
// any query predicate. Query predicates are usually meant to be used.
35+
result.(Predicate).hasAnnotation("query")
3436
or
3537
// the from-where-select
3638
result instanceof Select
@@ -200,8 +202,9 @@ private AstNode benign() {
200202
result instanceof BlockComment or
201203
not exists(result.toString()) or // <- invalid code
202204
// cached-stages pattern
203-
result.(Module).getAMember().(ClasslessPredicate).getName() = "forceStage" or
204-
result.(ClasslessPredicate).getName() = "forceStage" or
205+
result.(Module).getAMember().(ClasslessPredicate).getName() =
206+
["forceStage", "forceCachingInSameStageforceCachingInSameStage"] or
207+
result.(ClasslessPredicate).getName() = ["forceStage", "forceCachingInSameStage"] or
205208
result.getLocation().getFile().getBaseName() = "Caching.qll" or
206209
// sometimes contains dead code - ignore
207210
result.getLocation().getFile().getRelativePath().matches("%/tutorials/%") or
@@ -239,16 +242,46 @@ private AstNode queryable() {
239242
result = aliveStep(queryable())
240243
}
241244

245+
// The benign cases are mostly
246+
private AstNode benignUnqueryable() {
247+
result = benign() or
248+
// cached-stages pattern
249+
// sometimes contains dead code - ignore
250+
result.(Module).getName() = "Debugging" or
251+
result.getLocation().getFile() = benignUnqueryableFile()
252+
}
253+
254+
pragma[noinline]
255+
private File benignUnqueryableFile() {
256+
result.getAbsolutePath().matches("%/explore/%") or
257+
result.getRelativePath().matches("%/tutorials/%") or
258+
result.getBaseName() =
259+
[
260+
"Expr.qll", "TypeScript.qll", "YAML.qll", "Tokens.qll", "Instruction.qll", "Persistence.qll",
261+
"ES2015Modules.qll"
262+
] or // lots of classes that exist for completeness
263+
result.getBaseName() = ["CachedStages.qll", "Caching.qll", "tutorial.qll"] or
264+
result.getBaseName() = "PrettyPrintAst.qll" or // it's dead code, but seems intentional
265+
result.getBaseName() = "SensitiveDataHeuristics.qll" or // not all langs use all the things
266+
result.getAbsolutePath().matches("%/ql/ql/test%") // QL-for-QL tests contain plenty of unqueryable code on purpose
267+
}
268+
242269
/**
243270
* Gets an AstNode that does not affect any query result.
244271
* Is interresting as an quick-eval target to investigate dead code.
245272
* (It is intentional that this predicate is a result of this predicate).
246273
*/
247-
AstNode unQueryable(string msg) {
274+
AstNode unQueryable() {
248275
not result = queryable() and
249276
not result = deprecated() and
250-
not result = benign() and
251-
not result.getParent() = any(AstNode node | not node = queryable()) and
252-
msg = result.getLocation().getFile().getBaseName() and
253-
result.getLocation().getFile().getAbsolutePath().matches("%/javascript/%")
277+
not result = benignUnqueryable() and
278+
not result.getParent() = any(AstNode node | not node = queryable())
279+
}
280+
281+
AstNode unqueryableLang(string lang) {
282+
lang = "/" + ["cpp", "csharp", "java", "javascript", "python", "ruby"] + "/" and
283+
result = unQueryable() and
284+
result.getLocation().getFile().getAbsolutePath().matches("%" + lang + "%")
254285
}
286+
287+
int countLang(string lang) { result = strictcount(unqueryableLang(lang)) }
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @name Unqueryable code
3+
* @description Code that cannot affect the outcome of any query is suspicous.
4+
* @kind problem
5+
* @problem.severity recommendation
6+
* @id ql/unqueryable-code
7+
* @precision high
8+
*/
9+
10+
import ql
11+
import codeql_ql.style.DeadCodeQuery
12+
13+
from AstNode node
14+
where
15+
node = unQueryable() and
16+
// The below prevents the query from being too loud. The files below contain a lot of unqueryable code.
17+
// I think some of it is from some languages not using all features of a shared library, but I'm not sure (haven't look much into it).
18+
not node.getLocation()
19+
.getFile()
20+
.getBaseName()
21+
.matches(["DataFlowImpl", "SsaImplCommon", "FlowSummary"] + "%")
22+
select node, "Code cannot affect the outcome of any query."

0 commit comments

Comments
 (0)