From 6c55a67f9a3ac9427e093f022ee491a4f18660f5 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 13 Oct 2021 09:06:58 +0000 Subject: [PATCH] QL: Add query to find uses of .prefix or .suffix when comparing against literals. --- .../performance/PrefixSuffixEquality.ql | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 ql/src/queries/performance/PrefixSuffixEquality.ql diff --git a/ql/src/queries/performance/PrefixSuffixEquality.ql b/ql/src/queries/performance/PrefixSuffixEquality.ql new file mode 100644 index 00000000000..ef44e6ed070 --- /dev/null +++ b/ql/src/queries/performance/PrefixSuffixEquality.ql @@ -0,0 +1,49 @@ +/** + * @name Prefix or suffix predicate calls when comparing with literal + * @description Using 'myString.prefix(n) = "..."' instead of 'myString.matches("...%")' + * @kind problem + * @problem.severity error + * @id ql/prefix-or-suffix-equality-check + * @tags performance + * @precision high + */ + +import ql +import codeql_ql.ast.internal.Predicate + +class StringClass extends PrimitiveType { + StringClass() { this.getName() = "string" } +} + +class PrefixPredicate extends BuiltinPredicate { + PrefixPredicate() { this = any(StringClass sc).getClassPredicate("prefix", 1) } +} + +class SuffixPredicate extends BuiltinPredicate { + SuffixPredicate() { this = any(StringClass sc).getClassPredicate("suffix", 1) } +} + +class PrefixPredicateCall extends Call { + PrefixPredicateCall() { this.getTarget() instanceof PrefixPredicate } +} + +class SuffixPredicateCall extends Call { + SuffixPredicateCall() { this.getTarget() instanceof SuffixPredicate } +} + +class EqFormula extends ComparisonFormula { + EqFormula() { this.getSymbol() = "=" } +} + +pragma[inline] +string getMessage(Call call, String literal) { + call instanceof PrefixPredicateCall and result = ".matches(\"" + literal.getValue() + "%\")" + or + call instanceof SuffixPredicateCall and result = ".matches(\"%" + literal.getValue() + "\")" +} + +from EqFormula eq, PrefixPredicateCall call, String literal +where eq.getAnOperand() = call and eq.getAnOperand() = literal +select eq, + "Use " + getMessage(call, literal) + " instead (but be sure to escape " + literal.getValue() + + ")."