Mergeback: rc/1.20 into Semmle/master

This commit is contained in:
Esben Sparre Andreasen
2019-04-16 08:46:15 +02:00
11 changed files with 351 additions and 24 deletions

View File

@@ -101,6 +101,34 @@ predicate allBackslashesEscaped(DataFlow::Node nd) {
allBackslashesEscaped(nd.getAPredecessor())
}
/**
* Holds if `repl` looks like a call to "String.prototype.replace" that deliberately removes the first occurrence of `str`.
*/
predicate removesFirstOccurence(DataFlow::MethodCallNode repl, string str) {
repl.getMethodName() = "replace" and
repl.getArgument(0).getStringValue() = str and
repl.getArgument(1).getStringValue() = ""
}
/**
* Holds if `leftUnwrap` and `rightUnwrap` unwraps a string from a pair of surrounding delimiters.
*/
predicate isDelimiterUnwrapper(
DataFlow::MethodCallNode leftUnwrap, DataFlow::MethodCallNode rightUnwrap
) {
exists(string left, string right |
left = "[" and right = "]"
or
left = "{" and right = "}"
or
left = "(" and right = ")"
|
removesFirstOccurence(leftUnwrap, left) and
removesFirstOccurence(rightUnwrap, right) and
leftUnwrap.getAMethodCall() = rightUnwrap
)
}
from MethodCallExpr repl, Expr old, string msg
where
repl.getMethodName() = "replace" and
@@ -122,7 +150,10 @@ where
)
) and
// don't flag replace operations in a loop
not DataFlow::valueNode(repl.getReceiver()) = DataFlow::valueNode(repl).getASuccessor+()
not DataFlow::valueNode(repl.getReceiver()) = DataFlow::valueNode(repl).getASuccessor+() and
// dont' flag unwrapper
not isDelimiterUnwrapper(repl.flow(), _) and
not isDelimiterUnwrapper(_, repl.flow())
or
exists(RegExpLiteral rel |
isBackslashEscape(repl, rel) and

View File

@@ -15,4 +15,14 @@
| tst.js:61:10:61:18 | s.replace | This replaces only the first occurrence of "'" + "". |
| tst.js:65:10:65:18 | s.replace | This replaces only the first occurrence of "'". |
| tst.js:69:10:69:18 | s.replace | This replaces only the first occurrence of "'" + "". |
| tst.js:169:9:169:17 | s.replace | This replaces only the first occurrence of /'/. |
| tst.js:133:2:133:10 | s.replace | This replaces only the first occurrence of '<'. |
| tst.js:133:2:133:27 | s.repla ... replace | This replaces only the first occurrence of '>'. |
| tst.js:135:2:135:10 | s.replace | This replaces only the first occurrence of '['. |
| tst.js:135:2:135:30 | s.repla ... replace | This replaces only the first occurrence of ']'. |
| tst.js:136:2:136:10 | s.replace | This replaces only the first occurrence of '{'. |
| tst.js:136:2:136:30 | s.repla ... replace | This replaces only the first occurrence of '}'. |
| tst.js:140:2:140:10 | s.replace | This replaces only the first occurrence of /{/. |
| tst.js:140:2:140:27 | s.repla ... replace | This replaces only the first occurrence of /}/. |
| tst.js:141:2:141:10 | s.replace | This replaces only the first occurrence of ']'. |
| tst.js:141:2:141:27 | s.repla ... replace | This replaces only the first occurrence of '['. |
| tst.js:185:9:185:17 | s.replace | This replaces only the first occurrence of /'/. |

View File

@@ -126,6 +126,21 @@ function good11(s) {
return s.replace("%d", "42");
}
function good12(s) {
s.replace('[', '').replace(']', ''); // OK
s.replace('(', '').replace(')', ''); // OK
s.replace('{', '').replace('}', ''); // OK
s.replace('<', '').replace('>', ''); // NOT OK: too common as a bad HTML sanitizer
s.replace('[', '\\[').replace(']', '\\]'); // NOT OK
s.replace('{', '\\{').replace('}', '\\}'); // NOT OK
s = s.replace('[', ''); // OK
s = s.replace(']', ''); // OK
s.replace(/{/, '').replace(/}/, ''); // NOT OK: should have used a string literal if a single replacement was intended
s.replace(']', '').replace('[', ''); // probably OK, but still flagged
}
app.get('/some/path', function(req, res) {
let untrusted = req.param("p");
@@ -162,6 +177,7 @@ app.get('/some/path', function(req, res) {
good10(untrusted);
flowifyComments(untrusted);
good11(untrusted);
good12(untrusted);
});
(function (s) {