Skip to content

Commit 20b48a2

Browse files
author
Esben Sparre Andreasen
committed
JS: support relational indexof comparison sanitizers
1 parent 61bd003 commit 20b48a2

File tree

6 files changed

+122
-0
lines changed

6 files changed

+122
-0
lines changed

javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,60 @@ module TaintTracking {
643643

644644
}
645645

646+
/**
647+
* A less-than or greater-than expression
648+
*/
649+
private class ExclusiveRelationalComparison extends RelationalComparison {
650+
ExclusiveRelationalComparison() {
651+
this instanceof LTExpr or
652+
this instanceof GTExpr
653+
}
654+
}
655+
656+
/**
657+
* A check of the form `if(whitelist.indexOf(x) >= 0)`, which sanitizes `x` in its "then" branch.
658+
*
659+
* Similar relational checks are also supported.
660+
*/
661+
class RelationalIndexOfSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
662+
MethodCallExpr indexOf;
663+
override RelationalComparison astNode;
664+
boolean polarity;
665+
666+
RelationalIndexOfSanitizer() {
667+
exists (Expr lesser, Expr greater |
668+
astNode.getLesserOperand() = lesser and
669+
astNode.getGreaterOperand() = greater and
670+
indexOf.getMethodName() = "indexOf" |
671+
polarity = true and
672+
greater = indexOf and
673+
(
674+
lesser.getIntValue() = 0
675+
or
676+
lesser.getIntValue() = -1 and astNode instanceof ExclusiveRelationalComparison
677+
)
678+
or
679+
polarity = false and
680+
lesser = indexOf and
681+
(
682+
greater.getIntValue() = -1
683+
or
684+
greater.getIntValue() = 0 and astNode instanceof ExclusiveRelationalComparison
685+
)
686+
)
687+
}
688+
689+
override predicate sanitizes(boolean outcome, Expr e) {
690+
outcome = polarity and
691+
e = indexOf.getArgument(0)
692+
}
693+
694+
override predicate appliesTo(Configuration cfg) {
695+
any()
696+
}
697+
698+
}
699+
646700
/**
647701
* A check of the form `if(~whitelist.indexOf(x))`, which sanitizes `x` in its "then" branch.
648702
*

javascript/ql/test/library-tests/TaintBarriers/SanitizingGuard.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,8 @@
3131
| tst.js:166:9:166:16 | v == !!0 | ExampleConfiguration | true | tst.js:166:9:166:9 | v |
3232
| tst.js:184:9:184:21 | ~o.indexOf(v) | ExampleConfiguration | true | tst.js:184:20:184:20 | v |
3333
| tst.js:190:10:190:22 | ~o.indexOf(v) | ExampleConfiguration | true | tst.js:190:21:190:21 | v |
34+
| tst.js:202:9:202:26 | o.indexOf(v) <= -1 | ExampleConfiguration | false | tst.js:202:19:202:19 | v |
35+
| tst.js:208:9:208:25 | o.indexOf(v) >= 0 | ExampleConfiguration | true | tst.js:208:19:208:19 | v |
36+
| tst.js:214:9:214:24 | o.indexOf(v) < 0 | ExampleConfiguration | false | tst.js:214:19:214:19 | v |
37+
| tst.js:220:9:220:25 | o.indexOf(v) > -1 | ExampleConfiguration | true | tst.js:220:19:220:19 | v |
38+
| tst.js:226:9:226:26 | -1 >= o.indexOf(v) | ExampleConfiguration | false | tst.js:226:25:226:25 | v |

javascript/ql/test/library-tests/TaintBarriers/TaintedSink.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@
2828
| tst.js:182:10:182:10 | v | tst.js:181:13:181:20 | SOURCE() |
2929
| tst.js:187:14:187:14 | v | tst.js:181:13:181:20 | SOURCE() |
3030
| tst.js:191:14:191:14 | v | tst.js:181:13:181:20 | SOURCE() |
31+
| tst.js:200:10:200:10 | v | tst.js:199:13:199:20 | SOURCE() |
32+
| tst.js:203:14:203:14 | v | tst.js:199:13:199:20 | SOURCE() |
33+
| tst.js:211:14:211:14 | v | tst.js:199:13:199:20 | SOURCE() |
34+
| tst.js:215:14:215:14 | v | tst.js:199:13:199:20 | SOURCE() |
35+
| tst.js:223:14:223:14 | v | tst.js:199:13:199:20 | SOURCE() |
36+
| tst.js:227:14:227:14 | v | tst.js:199:13:199:20 | SOURCE() |

javascript/ql/test/library-tests/TaintBarriers/isBarrier.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,8 @@
2424
| tst.js:176:18:176:18 | v | ExampleConfiguration |
2525
| tst.js:185:14:185:14 | v | ExampleConfiguration |
2626
| tst.js:193:14:193:14 | v | ExampleConfiguration |
27+
| tst.js:205:14:205:14 | v | ExampleConfiguration |
28+
| tst.js:209:14:209:14 | v | ExampleConfiguration |
29+
| tst.js:217:14:217:14 | v | ExampleConfiguration |
30+
| tst.js:221:14:221:14 | v | ExampleConfiguration |
31+
| tst.js:229:14:229:14 | v | ExampleConfiguration |

javascript/ql/test/library-tests/TaintBarriers/tst.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,39 @@ function BitwiseIndexOfCheckSanitizer () {
194194
}
195195

196196
}
197+
198+
function RelationalIndexOfCheckSanitizer () {
199+
var v = SOURCE();
200+
SINK(v);
201+
202+
if (o.indexOf(v) <= -1) {
203+
SINK(v);
204+
} else {
205+
SINK(v);
206+
}
207+
208+
if (o.indexOf(v) >= 0) {
209+
SINK(v);
210+
} else {
211+
SINK(v);
212+
}
213+
214+
if (o.indexOf(v) < 0) {
215+
SINK(v);
216+
} else {
217+
SINK(v);
218+
}
219+
220+
if (o.indexOf(v) > -1) {
221+
SINK(v);
222+
} else {
223+
SINK(v);
224+
}
225+
226+
if (-1 >= o.indexOf(v)) {
227+
SINK(v);
228+
} else {
229+
SINK(v);
230+
}
231+
232+
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,19 @@ probalyAServer.on('request', (req, res) => {
5151
res.setHeader("Access-Control-Allow-Origin", null); // NOT OK (but not detected)
5252
res.setHeader("Access-Control-Allow-Credentials", true);
5353
});
54+
55+
server.on('request', (req, res) => {
56+
let origin = url.parse(req.url, true).query.origin;
57+
if (ALLOWED_ORIGINS.indexOf(origin) !== -1) {
58+
res.setHeader("Access-Control-Allow-Origin", origin); // OK, sanitized origin
59+
res.setHeader("Access-Control-Allow-Credentials", true);
60+
}
61+
});
62+
63+
server.on('request', (req, res) => {
64+
let origin = url.parse(req.url, true).query.origin;
65+
if (ALLOWED_ORIGINS.indexOf(origin) >= 0) {
66+
res.setHeader("Access-Control-Allow-Origin", origin); // OK, sanitized origin
67+
res.setHeader("Access-Control-Allow-Credentials", true);
68+
}
69+
});

0 commit comments

Comments
 (0)