Skip to content

Commit 9ca1ebd

Browse files
author
Max Schaefer
committed
JavaScript: Add new query flagging identity replacements.
1 parent d2f11dc commit 9ca1ebd

10 files changed

Lines changed: 94 additions & 0 deletions

File tree

change-notes/1.19/analysis-javascript.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
|-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
1515
| Enabling Node.js integration for Electron web content renderers (`js/enabling-electron-renderer-node-integration`) | security, frameworks/electron, external/cwe/cwe-094 | Highlights Electron web content renderer preferences with Node.js integration enabled, indicating a violation of [CWE-94](https://cwe.mitre.org/data/definitions/94.html). Results are not shown on LGTM by default. |
1616
| Stored cross-site scripting (`js/stored-xss`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights uncontrolled stored values flowing into HTML content, indicating a violation of [CWE-079](https://cwe.mitre.org/data/definitions/79.html). Results shown on LGTM by default. |
17+
| Replacement of a substring with itself (`js/identity-replacement`) | correctness | Highlights string replacements that replace a string with itself, which usually indicates a mistake. Results shown on LGTM by default. |
1718

1819
## Changes to existing queries
1920

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
Replacing a substring with itself has no effect and usually indicates a mistake, such as
9+
misspelling a backslash escape.
10+
</p>
11+
</overview>
12+
13+
<recommendation>
14+
<p>
15+
Examine the string replacement to find and correct any typos.
16+
</p>
17+
</recommendation>
18+
19+
<example>
20+
<p>
21+
The following code snippet attempts to backslash-escape all double quotes in <code>raw</code>
22+
by replacing all instances of <code>"</code> with <code>\"</code>:
23+
</p>
24+
<sample src="examples/IdentityReplacement.js" />
25+
<p>
26+
However, the replacement string <code>'\"'</code> is actually the same as <code>'"'</code>,
27+
with <code>\"</code> interpreted as an identity escape, so the replacement does nothing.
28+
Instead, the replacement string should be <code>'\\"'</code>:
29+
</p>
30+
<sample src="examples/IdentityReplacementGood.js" />
31+
</example>
32+
33+
<references>
34+
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#Escape_notation">String escape notation</a>.</li>
35+
</references>
36+
</qhelp>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
* @tags correctness
8+
* @precision very-high
9+
*/
10+
11+
import javascript
12+
13+
/**
14+
* Holds if `e`, when used as the first argument of `String.prototype.replace`, matches
15+
* `s` and nothing else.
16+
*/
17+
predicate matchesString(Expr e, string s) {
18+
exists (RegExpConstant c |
19+
matchesConstant(e.(RegExpLiteral).getRoot(), c) and
20+
s = c.getValue()
21+
)
22+
or
23+
s = e.getStringValue()
24+
}
25+
26+
/**
27+
* Holds if `t` matches `c` and nothing else.
28+
*/
29+
predicate matchesConstant(RegExpTerm t, RegExpConstant c) {
30+
c = t
31+
or
32+
matchesConstant(t.(RegExpGroup).getAChild(), c)
33+
or
34+
exists (RegExpCharacterClass recc | recc = t and not recc.isInverted() |
35+
recc.getNumChild() = 1 and
36+
matchesConstant(recc.getChild(0), c)
37+
)
38+
}
39+
40+
from MethodCallExpr repl, string s
41+
where repl.getMethodName() = "replace" and
42+
matchesString(repl.getArgument(0), s) and
43+
repl.getArgument(1).getStringValue() = s
44+
select repl.getArgument(0), "This replaces '" + s + "' with itself."
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
var escaped = raw.replace(/"/g, '\"');
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
var escaped = raw.replace(/"/g, '\\"');
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| IdentityReplacement.js:1:27:1:30 | /"/g | This replaces '"' with itself. |
2+
| tst.js:1:13:1:16 | "\\\\" | This replaces '\\' with itself. |
3+
| tst.js:2:13:2:18 | /(\\\\)/ | This replaces '\\' with itself. |
4+
| tst.js:3:13:3:17 | /["]/ | This replaces '"' with itself. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
var escaped = raw.replace(/"/g, '\"');
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
RegExp/IdentityReplacement.ql
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
var escaped = raw.replace(/"/g, '\\"');
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
raw.replace("\\", "\\"); // NOT OK
2+
raw.replace(/(\\)/, "\\"); // NOT OK
3+
raw.replace(/["]/, "\""); // NOT OK
4+
raw.replace("\\", "\\\\"); // OK

0 commit comments

Comments
 (0)