Skip to content

Commit 52d6626

Browse files
authored
Merge pull request #1242 from esben-semmle/js/whitelist-trailing-newline-removal
Approved by xiemaisi
2 parents 7d2c17f + f064ba0 commit 52d6626

File tree

4 files changed

+29
-3
lines changed

4 files changed

+29
-3
lines changed

change-notes/1.21/analysis-javascript.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
| Double escaping or unescaping | More results | This rule now considers the flow of regular expressions literals. |
3333
| Expression has no effect | Fewer false-positive results | This rule now treats uses of `Object.defineProperty` more conservatively. |
3434
| Incomplete regular expression for hostnames | More results | This rule now tracks regular expressions for host names further. |
35-
| Incomplete string escaping or encoding | More results | This rule now considers the flow of regular expressions literals. |
35+
| Incomplete string escaping or encoding | More results | This rule now considers the flow of regular expressions literals, and it no longer flags the removal of trailing newlines. |
3636
| Password in configuration file | Fewer false positive results | This query now excludes passwords that are inserted into the configuration file using a templating mechanism. |
3737
| Replacement of a substring with itself | More results | This rule now considers the flow of regular expressions literals. |
3838
| Server-side URL redirect | Fewer false-positive results | This rule now treats URLs as safe in more cases where the hostname cannot be tampered with. |

javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,20 @@ predicate isDelimiterUnwrapper(
129129
)
130130
}
131131

132+
/*
133+
* Holds if `repl` is a standalone use of `String.prototype.replace` to remove a single newline.
134+
*/
135+
136+
predicate removesTrailingNewLine(DataFlow::MethodCallNode repl) {
137+
repl.getMethodName() = "replace" and
138+
repl.getArgument(0).mayHaveStringValue("\n") and
139+
repl.getArgument(1).mayHaveStringValue("") and
140+
not exists(DataFlow::MethodCallNode other | other.getMethodName() = "replace" |
141+
repl.getAMethodCall() = other or
142+
other.getAMethodCall() = repl
143+
)
144+
}
145+
132146
from MethodCallExpr repl, Expr old, string msg
133147
where
134148
repl.getMethodName() = "replace" and
@@ -153,7 +167,9 @@ where
153167
not DataFlow::valueNode(repl.getReceiver()) = DataFlow::valueNode(repl).getASuccessor+() and
154168
// dont' flag unwrapper
155169
not isDelimiterUnwrapper(repl.flow(), _) and
156-
not isDelimiterUnwrapper(_, repl.flow())
170+
not isDelimiterUnwrapper(_, repl.flow()) and
171+
// dont' flag the removal of trailing newlines
172+
not removesTrailingNewLine(repl.flow())
157173
or
158174
exists(RegExpLiteral rel |
159175
isBackslashEscape(repl, rel) and

javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteSanitization.expected

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@
2525
| tst.js:140:2:140:27 | s.repla ... replace | This replaces only the first occurrence of /}/. |
2626
| tst.js:141:2:141:10 | s.replace | This replaces only the first occurrence of ']'. |
2727
| tst.js:141:2:141:27 | s.repla ... replace | This replaces only the first occurrence of '['. |
28-
| tst.js:185:9:185:17 | s.replace | This replaces only the first occurrence of /'/. |
28+
| tst.js:148:2:148:10 | x.replace | This replaces only the first occurrence of "\\n". |
29+
| tst.js:149:2:149:24 | x.repla ... replace | This replaces only the first occurrence of "\\n". |
30+
| tst.js:193:9:193:17 | s.replace | This replaces only the first occurrence of /'/. |

javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,14 @@ function good12(s) {
141141
s.replace(']', '').replace('[', ''); // probably OK, but still flagged
142142
}
143143

144+
function newlines(s) {
145+
// motivation for whitelist
146+
require("child_process").execSync("which emacs").toString().replace("\n", ""); // OK
147+
148+
x.replace("\n", "").replace(x, y); // NOT OK
149+
x.replace(x, y).replace("\n", ""); // NOT OK
150+
}
151+
144152
app.get('/some/path', function(req, res) {
145153
let untrusted = req.param("p");
146154

0 commit comments

Comments
 (0)