Skip to content

Commit 55ceb9b

Browse files
authored
Merge pull request #91 from esben-semmle/js/additional-indexof-sanitizers
Approved by xiemaisi
2 parents 1f844e2 + a1d79ef commit 55ceb9b

File tree

7 files changed

+121
-1
lines changed

7 files changed

+121
-1
lines changed

javascript/ql/src/semmle/javascript/Expr.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,6 +1545,14 @@ class RelationalComparison extends Comparison {
15451545
Expr getGreaterOperand() {
15461546
result = getAnOperand() and result != getLesserOperand()
15471547
}
1548+
1549+
/**
1550+
* Holds if this is a comparison with `<=` or `>=`.
1551+
*/
1552+
predicate isInclusive() {
1553+
this instanceof LEExpr or
1554+
this instanceof GEExpr
1555+
}
15481556
}
15491557

15501558
/** A (pre or post) increment expression. */

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,12 +643,56 @@ module TaintTracking {
643643

644644
}
645645

646+
/**
647+
* A check of the form `if(whitelist.indexOf(x) >= 0)`, which sanitizes `x` in its "then" branch.
648+
*
649+
* Similar relational checks are also supported.
650+
*/
651+
private class RelationalIndexOfSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
652+
MethodCallExpr indexOf;
653+
override RelationalComparison astNode;
654+
boolean polarity;
655+
656+
RelationalIndexOfSanitizer() {
657+
exists (Expr lesser, Expr greater |
658+
astNode.getLesserOperand() = lesser and
659+
astNode.getGreaterOperand() = greater and
660+
indexOf.getMethodName() = "indexOf" |
661+
polarity = true and
662+
greater = indexOf and
663+
(
664+
lesser.getIntValue() >= 0
665+
or
666+
lesser.getIntValue() = -1 and not astNode.isInclusive()
667+
)
668+
or
669+
polarity = false and
670+
lesser = indexOf and
671+
(
672+
greater.getIntValue() = -1
673+
or
674+
greater.getIntValue() = 0 and not astNode.isInclusive()
675+
)
676+
)
677+
}
678+
679+
override predicate sanitizes(boolean outcome, Expr e) {
680+
outcome = polarity and
681+
e = indexOf.getArgument(0)
682+
}
683+
684+
override predicate appliesTo(Configuration cfg) {
685+
any()
686+
}
687+
688+
}
689+
646690
/**
647691
* A check of the form `if(~whitelist.indexOf(x))`, which sanitizes `x` in its "then" branch.
648692
*
649693
* This sanitizer is equivalent to `if(whitelist.indexOf(x) != -1)`, since `~n = 0` iff `n = -1`.
650694
*/
651-
class BitwiseIndexOfSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
695+
private class BitwiseIndexOfSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
652696
MethodCallExpr indexOf;
653697
override BitNotExpr astNode;
654698

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)