|
| 1 | +/** |
| 2 | + * @name Replacement of a substring with itself |
| 3 | + * @description Replacing a substring with itself has no effect and may indicate a mistake. |
| 4 | + * @kind problem |
| 5 | + * @problem.severity warning |
| 6 | + * @id js/identity-replacement |
| 7 | + * @precision very-high |
| 8 | + * @tags correctness |
| 9 | + * security |
| 10 | + * external/cwe/cwe-116 |
| 11 | + */ |
| 12 | + |
| 13 | +import javascript |
| 14 | + |
| 15 | +/** |
| 16 | + * Holds if `e`, when used as the first argument of `String.prototype.replace`, matches |
| 17 | + * `s` and nothing else. |
| 18 | + */ |
| 19 | +predicate matchesString(Expr e, string s) { |
| 20 | + exists (RegExpLiteral rl | |
| 21 | + rl = e and |
| 22 | + not rl.isIgnoreCase() and |
| 23 | + regExpMatchesString(rl.getRoot(), s) |
| 24 | + ) |
| 25 | + or |
| 26 | + s = e.getStringValue() |
| 27 | +} |
| 28 | + |
| 29 | +/** |
| 30 | + * Holds if `t` matches `s` and nothing else. |
| 31 | + */ |
| 32 | +language[monotonicAggregates] |
| 33 | +predicate regExpMatchesString(RegExpTerm t, string s) { |
| 34 | + // constants match themselves |
| 35 | + s = t.(RegExpConstant).getValue() |
| 36 | + or |
| 37 | + // assertions match the empty string |
| 38 | + (t instanceof RegExpCaret or |
| 39 | + t instanceof RegExpDollar or |
| 40 | + t instanceof RegExpWordBoundary or |
| 41 | + t instanceof RegExpNonWordBoundary or |
| 42 | + t instanceof RegExpLookahead or |
| 43 | + t instanceof RegExpLookbehind) and |
| 44 | + s = "" |
| 45 | + or |
| 46 | + // groups match their content |
| 47 | + regExpMatchesString(t.(RegExpGroup).getAChild(), s) |
| 48 | + or |
| 49 | + // single-character classes match that character |
| 50 | + exists (RegExpCharacterClass recc | recc = t and not recc.isInverted() | |
| 51 | + recc.getNumChild() = 1 and |
| 52 | + regExpMatchesString(recc.getChild(0), s) |
| 53 | + ) |
| 54 | + or |
| 55 | + // sequences match the concatenation of their elements |
| 56 | + exists (RegExpSequence seq | seq = t | |
| 57 | + s = concat(int i, RegExpTerm child | child = seq.getChild(i) | |
| 58 | + any(string subs | regExpMatchesString(child, subs)) order by i |
| 59 | + ) |
| 60 | + ) |
| 61 | +} |
| 62 | + |
| 63 | +from MethodCallExpr repl, string s, string friendly |
| 64 | +where repl.getMethodName() = "replace" and |
| 65 | + matchesString(repl.getArgument(0), s) and |
| 66 | + repl.getArgument(1).getStringValue() = s and |
| 67 | + (if s = "" then friendly = "the empty string" else friendly = "'" + s + "'") |
| 68 | +select repl.getArgument(0), "This replaces " + friendly + " with itself." |
0 commit comments