Skip to content

Commit 3ed7f96

Browse files
authored
QL: Merge pull request #55 from github/mathiasvp/prefix-or-suffix-in-comparison
New query: Find uses of '.prefix' or '.suffix' when comparing against string literals
2 parents 87910e8 + df20f84 commit 3ed7f96

2 files changed

Lines changed: 60 additions & 0 deletions

File tree

ql/src/codeql_ql/ast/internal/Builtins.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
private import codeql_ql.ast.internal.Type
2+
13
predicate isBuiltinClassless(string sig) {
24
sig =
35
[
@@ -58,3 +60,8 @@ predicate isBuiltinMember(string qual, string ret, string name, string args) {
5860

5961
bindingset[args]
6062
string getArgType(string args, int i) { result = args.splitAt(",", i).trim() }
63+
64+
/** The primitive 'string' class. */
65+
class StringClass extends PrimitiveType {
66+
StringClass() { this.getName() = "string" }
67+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @name Prefix or suffix predicate calls when comparing with literal
3+
* @description Using 'myString.prefix(n) = "..."' instead of 'myString.matches("...%")'
4+
* @kind problem
5+
* @problem.severity error
6+
* @id ql/prefix-or-suffix-equality-check
7+
* @tags performance
8+
* @precision high
9+
*/
10+
11+
import ql
12+
import codeql_ql.ast.internal.Predicate
13+
import codeql_ql.ast.internal.Builtins
14+
15+
class PrefixPredicate extends BuiltinPredicate {
16+
PrefixPredicate() { this = any(StringClass sc).getClassPredicate("prefix", 1) }
17+
}
18+
19+
class SuffixPredicate extends BuiltinPredicate {
20+
SuffixPredicate() { this = any(StringClass sc).getClassPredicate("suffix", 1) }
21+
}
22+
23+
class PrefixPredicateCall extends Call {
24+
PrefixPredicateCall() { this.getTarget() instanceof PrefixPredicate }
25+
}
26+
27+
class SuffixPredicateCall extends Call {
28+
SuffixPredicateCall() { this.getTarget() instanceof SuffixPredicate }
29+
}
30+
31+
class EqFormula extends ComparisonFormula {
32+
EqFormula() { this.getSymbol() = "=" }
33+
}
34+
35+
bindingset[s]
36+
string escape(string s) { result = s.replaceAll("_", "\\\\_").replaceAll("%", "\\\\%") }
37+
38+
pragma[inline]
39+
string getMessage(FixPredicateCall call, String literal) {
40+
call instanceof PrefixPredicateCall and
41+
result = ".matches(\"" + escape(literal.getValue()) + "%\")"
42+
or
43+
call instanceof SuffixPredicateCall and
44+
result = ".matches(\"%" + escape(literal.getValue()) + "\")"
45+
}
46+
47+
class FixPredicateCall extends Call {
48+
FixPredicateCall() { this instanceof PrefixPredicateCall or this instanceof SuffixPredicateCall }
49+
}
50+
51+
from EqFormula eq, FixPredicateCall call, String literal
52+
where eq.getAnOperand() = call and eq.getAnOperand() = literal
53+
select eq, "Use " + getMessage(call, literal) + " instead."

0 commit comments

Comments
 (0)