mirror of
https://github.com/github/codeql.git
synced 2026-05-01 11:45:14 +02:00
Merge pull request #447 from esben-semmle/js/indirect-sanitization
Approved by asger-semmle
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.dataflow.CallGraph
|
||||
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
|
||||
/**
|
||||
@@ -809,6 +810,74 @@ module TaintTracking {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that returns the result of a sanitizer check.
|
||||
*/
|
||||
private class SanitizingFunction extends Function {
|
||||
Parameter sanitizedParameter;
|
||||
|
||||
SanitizerGuardNode sanitizer;
|
||||
|
||||
boolean sanitizerOutcome;
|
||||
|
||||
SanitizingFunction() {
|
||||
exists(Expr e |
|
||||
exists(Expr returnExpr |
|
||||
returnExpr = sanitizer.asExpr()
|
||||
or
|
||||
// ad hoc support for conjunctions:
|
||||
returnExpr.(LogAndExpr).getAnOperand() = sanitizer.asExpr() and sanitizerOutcome = true
|
||||
or
|
||||
// ad hoc support for disjunctions:
|
||||
returnExpr.(LogOrExpr).getAnOperand() = sanitizer.asExpr() and sanitizerOutcome = false
|
||||
|
|
||||
exists(SsaExplicitDefinition ssa |
|
||||
ssa.getDef().getSource() = returnExpr and
|
||||
ssa.getVariable().getAUse() = getAReturnedExpr()
|
||||
)
|
||||
or
|
||||
returnExpr = getAReturnedExpr()
|
||||
) and
|
||||
DataFlow::parameterNode(sanitizedParameter).flowsToExpr(e) and
|
||||
sanitizer.sanitizes(sanitizerOutcome, e)
|
||||
) and
|
||||
getNumParameter() = 1 and
|
||||
sanitizedParameter = getParameter(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function sanitizes argument `e` of call `call`, provided the call evaluates to `outcome`.
|
||||
*/
|
||||
predicate isSanitizingCall(DataFlow::CallNode call, Expr e, boolean outcome) {
|
||||
exists(DataFlow::Node arg |
|
||||
arg.asExpr() = e and
|
||||
arg = call.getArgument(0) and
|
||||
call.getNumArgument() = 1 and
|
||||
FlowSteps::argumentPassing(call, arg, this, sanitizedParameter) and
|
||||
outcome = sanitizerOutcome
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function applies to the flow in `cfg`.
|
||||
*/
|
||||
predicate appliesTo(Configuration cfg) {
|
||||
cfg.isBarrierGuard(sanitizer)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that sanitizes an argument.
|
||||
*/
|
||||
private class AdditionalSanitizingCall extends AdditionalSanitizerGuardNode, DataFlow::CallNode {
|
||||
SanitizingFunction f;
|
||||
|
||||
AdditionalSanitizingCall() { f.isSanitizingCall(this, _, _) }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) { f.isSanitizingCall(this, e, outcome) }
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { f.appliesTo(cfg) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An equality test on `e.origin` or `e.source` where `e` is a `postMessage` event object,
|
||||
|
||||
@@ -38,3 +38,25 @@
|
||||
| tst.js:226:9:226:26 | -1 >= o.indexOf(v) | ExampleConfiguration | false | tst.js:226:25:226:25 | v |
|
||||
| tst.js:236:9:236:24 | isWhitelisted(v) | ExampleConfiguration | true | tst.js:236:23:236:23 | v |
|
||||
| tst.js:240:9:240:28 | config.allowValue(v) | ExampleConfiguration | true | tst.js:240:27:240:27 | v |
|
||||
| tst.js:252:16:252:36 | whiteli ... ains(x) | ExampleConfiguration | true | tst.js:252:35:252:35 | x |
|
||||
| tst.js:254:9:254:12 | f(v) | ExampleConfiguration | true | tst.js:254:11:254:11 | v |
|
||||
| tst.js:261:25:261:45 | whiteli ... ains(y) | ExampleConfiguration | true | tst.js:261:44:261:44 | y |
|
||||
| tst.js:264:9:264:12 | g(v) | ExampleConfiguration | true | tst.js:264:11:264:11 | v |
|
||||
| tst.js:271:25:271:45 | whiteli ... ains(z) | ExampleConfiguration | true | tst.js:271:44:271:44 | z |
|
||||
| tst.js:281:16:281:25 | x2 != null | ExampleConfiguration | false | tst.js:281:16:281:17 | x2 |
|
||||
| tst.js:281:30:281:51 | whiteli ... ins(x2) | ExampleConfiguration | true | tst.js:281:49:281:50 | x2 |
|
||||
| tst.js:283:9:283:13 | f2(v) | ExampleConfiguration | true | tst.js:283:12:283:12 | v |
|
||||
| tst.js:290:16:290:25 | x3 == null | ExampleConfiguration | true | tst.js:290:16:290:17 | x3 |
|
||||
| tst.js:290:30:290:51 | whiteli ... ins(x3) | ExampleConfiguration | true | tst.js:290:49:290:50 | x3 |
|
||||
| tst.js:299:17:299:38 | whiteli ... ins(x4) | ExampleConfiguration | true | tst.js:299:36:299:37 | x4 |
|
||||
| tst.js:308:18:308:39 | whiteli ... ins(x5) | ExampleConfiguration | true | tst.js:308:37:308:38 | x5 |
|
||||
| tst.js:317:26:317:47 | whiteli ... ins(x6) | ExampleConfiguration | true | tst.js:317:45:317:46 | x6 |
|
||||
| tst.js:327:25:327:34 | x7 != null | ExampleConfiguration | false | tst.js:327:25:327:26 | x7 |
|
||||
| tst.js:327:39:327:60 | whiteli ... ins(x7) | ExampleConfiguration | true | tst.js:327:58:327:59 | x7 |
|
||||
| tst.js:330:9:330:13 | f7(v) | ExampleConfiguration | true | tst.js:330:12:330:12 | v |
|
||||
| tst.js:337:25:337:46 | whiteli ... ins(x8) | ExampleConfiguration | true | tst.js:337:44:337:45 | x8 |
|
||||
| tst.js:338:16:338:25 | x8 != null | ExampleConfiguration | false | tst.js:338:16:338:17 | x8 |
|
||||
| tst.js:347:29:347:50 | whiteli ... ins(x9) | ExampleConfiguration | true | tst.js:347:48:347:49 | x9 |
|
||||
| tst.js:356:16:356:27 | x10 !== null | ExampleConfiguration | false | tst.js:356:16:356:18 | x10 |
|
||||
| tst.js:356:32:356:48 | x10 !== undefined | ExampleConfiguration | false | tst.js:356:32:356:34 | x10 |
|
||||
| tst.js:358:9:358:14 | f10(v) | ExampleConfiguration | false | tst.js:358:13:358:13 | v |
|
||||
|
||||
@@ -36,3 +36,23 @@
|
||||
| tst.js:227:14:227:14 | v | tst.js:199:13:199:20 | SOURCE() |
|
||||
| tst.js:239:14:239:14 | v | tst.js:235:13:235:20 | SOURCE() |
|
||||
| tst.js:243:14:243:14 | v | tst.js:235:13:235:20 | SOURCE() |
|
||||
| tst.js:249:10:249:10 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:257:14:257:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:267:14:267:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:275:14:275:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:277:14:277:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:286:14:286:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:293:14:293:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:295:14:295:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:302:14:302:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:304:14:304:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:311:14:311:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:313:14:313:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:321:14:321:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:323:14:323:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:333:14:333:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:341:14:341:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:343:14:343:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:350:14:350:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:352:14:352:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
| tst.js:359:14:359:14 | v | tst.js:248:13:248:20 | SOURCE() |
|
||||
|
||||
@@ -31,3 +31,9 @@
|
||||
| tst.js:229:14:229:14 | v | ExampleConfiguration |
|
||||
| tst.js:237:14:237:14 | v | ExampleConfiguration |
|
||||
| tst.js:241:14:241:14 | v | ExampleConfiguration |
|
||||
| tst.js:255:14:255:14 | v | ExampleConfiguration |
|
||||
| tst.js:265:14:265:14 | v | ExampleConfiguration |
|
||||
| tst.js:284:14:284:14 | v | ExampleConfiguration |
|
||||
| tst.js:331:14:331:14 | v | ExampleConfiguration |
|
||||
| tst.js:356:16:356:27 | x10 | ExampleConfiguration |
|
||||
| tst.js:361:14:361:14 | v | ExampleConfiguration |
|
||||
|
||||
@@ -243,3 +243,122 @@ function adhocWhitelisting() {
|
||||
SINK(v);
|
||||
|
||||
}
|
||||
|
||||
function IndirectSanitizer () {
|
||||
var v = SOURCE();
|
||||
SINK(v);
|
||||
|
||||
function f(x) {
|
||||
return whitelist.contains(x);
|
||||
}
|
||||
if (f(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function g(y) {
|
||||
var sanitized = whitelist.contains(y);
|
||||
return sanitized;
|
||||
}
|
||||
if (g(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function h(z) {
|
||||
var sanitized = whitelist.contains(z);
|
||||
return somethingElse();
|
||||
}
|
||||
if (h(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f2(x2) {
|
||||
return x2 != null && whitelist.contains(x2);
|
||||
}
|
||||
if (f2(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f3(x3) {
|
||||
return x3 == null || whitelist.contains(x3);
|
||||
}
|
||||
if (f3(v)) {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f4(x4) {
|
||||
return !whitelist.contains(x4);
|
||||
}
|
||||
if (f4(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
}
|
||||
|
||||
function f5(x5) {
|
||||
return !!whitelist.contains(x5);
|
||||
}
|
||||
if (f5(v)) {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f6(x6) {
|
||||
var sanitized = !whitelist.contains(x6);
|
||||
return !sanitized;
|
||||
}
|
||||
if (f6(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
}
|
||||
|
||||
function f7(x7) {
|
||||
var sanitized = x7 != null && whitelist.contains(x7);
|
||||
return sanitized;
|
||||
}
|
||||
if (f7(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f8(x8) {
|
||||
var sanitized = whitelist.contains(x8);
|
||||
return x8 != null && sanitized;
|
||||
}
|
||||
if (f8(v)) {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f9(x9) {
|
||||
return unknown() && whitelist.contains(x9) && unknown();
|
||||
}
|
||||
if (f9(v)) {
|
||||
SINK(v); // SANITIZATION OF THIS IS NOT YET SUPPORTED
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
function f10(x10) {
|
||||
return x10 !== null || x10 !== undefined;
|
||||
}
|
||||
if (f10(v)) {
|
||||
SINK(v);
|
||||
} else {
|
||||
SINK(v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
| express.js:12:26:12:44 | req.param("target") | Untrusted URL redirection due to $@. | express.js:12:26:12:44 | req.param("target") | user-provided value |
|
||||
| express.js:33:18:33:23 | target | Untrusted URL redirection due to $@. | express.js:27:16:27:34 | req.param("target") | user-provided value |
|
||||
| express.js:35:16:35:21 | target | Untrusted URL redirection due to $@. | express.js:27:16:27:34 | req.param("target") | user-provided value |
|
||||
| express.js:44:16:44:108 | (req.pa ... ntacts" | Untrusted URL redirection due to $@. | express.js:44:69:44:87 | req.param('action') | user-provided value |
|
||||
| express.js:53:26:53:28 | url | Untrusted URL redirection due to $@. | express.js:48:16:48:28 | req.params[0] | user-provided value |
|
||||
| express.js:78:16:78:43 | `${req. ... )}/foo` | Untrusted URL redirection due to $@. | express.js:78:19:78:37 | req.param("target") | user-provided value |
|
||||
| express.js:94:18:94:23 | target | Untrusted URL redirection due to $@. | express.js:87:16:87:34 | req.param("target") | user-provided value |
|
||||
| express.js:101:16:101:21 | target | Untrusted URL redirection due to $@. | express.js:87:16:87:34 | req.param("target") | user-provided value |
|
||||
| express.js:122:16:122:72 | [req.qu ... oin('') | Untrusted URL redirection due to $@. | express.js:122:17:122:30 | req.query.page | user-provided value |
|
||||
| express.js:40:16:40:108 | (req.pa ... ntacts" | Untrusted URL redirection due to $@. | express.js:40:69:40:87 | req.param('action') | user-provided value |
|
||||
| express.js:49:26:49:28 | url | Untrusted URL redirection due to $@. | express.js:44:16:44:28 | req.params[0] | user-provided value |
|
||||
| express.js:74:16:74:43 | `${req. ... )}/foo` | Untrusted URL redirection due to $@. | express.js:74:19:74:37 | req.param("target") | user-provided value |
|
||||
| express.js:90:18:90:23 | target | Untrusted URL redirection due to $@. | express.js:83:16:83:34 | req.param("target") | user-provided value |
|
||||
| express.js:97:16:97:21 | target | Untrusted URL redirection due to $@. | express.js:83:16:83:34 | req.param("target") | user-provided value |
|
||||
| express.js:118:16:118:72 | [req.qu ... oin('') | Untrusted URL redirection due to $@. | express.js:118:17:118:30 | req.query.page | user-provided value |
|
||||
| node.js:7:34:7:39 | target | Untrusted URL redirection due to $@. | node.js:6:26:6:32 | req.url | user-provided value |
|
||||
| node.js:15:34:15:45 | '/' + target | Untrusted URL redirection due to $@. | node.js:11:26:11:32 | req.url | user-provided value |
|
||||
| node.js:32:34:32:55 | target ... =" + me | Untrusted URL redirection due to $@. | node.js:29:26:29:32 | req.url | user-provided value |
|
||||
|
||||
@@ -35,10 +35,6 @@ app.get('/some/path', function(req, res) {
|
||||
res.redirect(target);
|
||||
});
|
||||
|
||||
function isLocalURL(target) {
|
||||
return new RegExp("^/(?![/\\])|^~/").exec(target);
|
||||
}
|
||||
|
||||
app.get('/foo', function(req, res) {
|
||||
// BAD: may be a global redirection
|
||||
res.redirect((req.param('action') && req.param('action') != "") ? req.param('action') : "/google_contacts")
|
||||
|
||||
Reference in New Issue
Block a user