Skip to content

Commit 317356e

Browse files
authored
Merge pull request github#2898 from asger-semmle/js/prototype-pollution-isobject-sanitizers
Approved by erik-krogh
2 parents 2d9df70 + f923b24 commit 317356e

File tree

3 files changed

+153
-1
lines changed

3 files changed

+153
-1
lines changed

javascript/ql/src/Security/CWE-400/PrototypePollutionUtility.ql

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ class PropNameTracking extends DataFlow::Configuration {
199199
node instanceof InstanceOfGuard or
200200
node instanceof TypeofGuard or
201201
node instanceof BlacklistInclusionGuard or
202-
node instanceof WhitelistInclusionGuard
202+
node instanceof WhitelistInclusionGuard or
203+
node instanceof IsPlainObjectGuard
203204
}
204205
}
205206

@@ -374,6 +375,25 @@ class WhitelistInclusionGuard extends DataFlow::LabeledBarrierGuardNode {
374375
}
375376
}
376377

378+
/**
379+
* A check of form `isPlainObject(e)` or similar, which sanitizes the `constructor`
380+
* payload in the true case, since it rejects objects with a non-standard `constructor`
381+
* property.
382+
*/
383+
class IsPlainObjectGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::CallNode {
384+
IsPlainObjectGuard() {
385+
exists(string name | name = "is-plain-object" or name = "is-extendable" |
386+
this = moduleImport(name).getACall()
387+
)
388+
}
389+
390+
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel lbl) {
391+
e = getArgument(0).asExpr() and
392+
outcome = true and
393+
lbl = "constructor"
394+
}
395+
}
396+
377397
/**
378398
* Gets a meaningful name for `node` if possible.
379399
*/

javascript/ql/test/query-tests/Security/CWE-400/PrototypePollutionUtility.expected

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,54 @@ nodes
10421042
| PrototypePollutionUtility/tests.js:461:24:461:28 | value |
10431043
| PrototypePollutionUtility/tests.js:461:24:461:28 | value |
10441044
| PrototypePollutionUtility/tests.js:461:24:461:28 | value |
1045+
| PrototypePollutionUtility/tests.js:467:26:467:28 | dst |
1046+
| PrototypePollutionUtility/tests.js:467:31:467:33 | src |
1047+
| PrototypePollutionUtility/tests.js:467:31:467:33 | src |
1048+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key |
1049+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key |
1050+
| PrototypePollutionUtility/tests.js:471:29:471:31 | dst |
1051+
| PrototypePollutionUtility/tests.js:471:29:471:36 | dst[key] |
1052+
| PrototypePollutionUtility/tests.js:471:29:471:36 | dst[key] |
1053+
| PrototypePollutionUtility/tests.js:471:33:471:35 | key |
1054+
| PrototypePollutionUtility/tests.js:471:39:471:41 | src |
1055+
| PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] |
1056+
| PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] |
1057+
| PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] |
1058+
| PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] |
1059+
| PrototypePollutionUtility/tests.js:471:43:471:45 | key |
1060+
| PrototypePollutionUtility/tests.js:473:13:473:15 | dst |
1061+
| PrototypePollutionUtility/tests.js:473:13:473:15 | dst |
1062+
| PrototypePollutionUtility/tests.js:473:17:473:19 | key |
1063+
| PrototypePollutionUtility/tests.js:473:17:473:19 | key |
1064+
| PrototypePollutionUtility/tests.js:473:24:473:26 | src |
1065+
| PrototypePollutionUtility/tests.js:473:24:473:26 | src |
1066+
| PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
1067+
| PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
1068+
| PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
1069+
| PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
1070+
| PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
1071+
| PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
1072+
| PrototypePollutionUtility/tests.js:473:28:473:30 | key |
1073+
| PrototypePollutionUtility/tests.js:478:32:478:34 | src |
1074+
| PrototypePollutionUtility/tests.js:479:14:479:16 | key |
1075+
| PrototypePollutionUtility/tests.js:479:14:479:16 | key |
1076+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value |
1077+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value |
1078+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value |
1079+
| PrototypePollutionUtility/tests.js:482:21:482:23 | src |
1080+
| PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] |
1081+
| PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] |
1082+
| PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] |
1083+
| PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] |
1084+
| PrototypePollutionUtility/tests.js:482:25:482:27 | key |
1085+
| PrototypePollutionUtility/tests.js:484:38:484:42 | value |
1086+
| PrototypePollutionUtility/tests.js:484:38:484:42 | value |
1087+
| PrototypePollutionUtility/tests.js:486:17:486:19 | key |
1088+
| PrototypePollutionUtility/tests.js:486:17:486:19 | key |
1089+
| PrototypePollutionUtility/tests.js:486:24:486:28 | value |
1090+
| PrototypePollutionUtility/tests.js:486:24:486:28 | value |
1091+
| PrototypePollutionUtility/tests.js:486:24:486:28 | value |
1092+
| PrototypePollutionUtility/tests.js:486:24:486:28 | value |
10451093
| examples/PrototypePollutionUtility.js:1:16:1:18 | dst |
10461094
| examples/PrototypePollutionUtility.js:1:16:1:18 | dst |
10471095
| examples/PrototypePollutionUtility.js:1:21:1:23 | src |
@@ -2457,6 +2505,64 @@ edges
24572505
| PrototypePollutionUtility/tests.js:459:41:459:48 | dst[key] | PrototypePollutionUtility/tests.js:456:38:456:40 | dst |
24582506
| PrototypePollutionUtility/tests.js:459:45:459:47 | key | PrototypePollutionUtility/tests.js:459:41:459:48 | dst[key] |
24592507
| PrototypePollutionUtility/tests.js:459:45:459:47 | key | PrototypePollutionUtility/tests.js:459:41:459:48 | dst[key] |
2508+
| PrototypePollutionUtility/tests.js:467:26:467:28 | dst | PrototypePollutionUtility/tests.js:471:29:471:31 | dst |
2509+
| PrototypePollutionUtility/tests.js:467:26:467:28 | dst | PrototypePollutionUtility/tests.js:473:13:473:15 | dst |
2510+
| PrototypePollutionUtility/tests.js:467:26:467:28 | dst | PrototypePollutionUtility/tests.js:473:13:473:15 | dst |
2511+
| PrototypePollutionUtility/tests.js:467:31:467:33 | src | PrototypePollutionUtility/tests.js:471:39:471:41 | src |
2512+
| PrototypePollutionUtility/tests.js:467:31:467:33 | src | PrototypePollutionUtility/tests.js:473:24:473:26 | src |
2513+
| PrototypePollutionUtility/tests.js:467:31:467:33 | src | PrototypePollutionUtility/tests.js:473:24:473:26 | src |
2514+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:471:33:471:35 | key |
2515+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:471:33:471:35 | key |
2516+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:471:43:471:45 | key |
2517+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:471:43:471:45 | key |
2518+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:473:17:473:19 | key |
2519+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:473:17:473:19 | key |
2520+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:473:17:473:19 | key |
2521+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:473:17:473:19 | key |
2522+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:473:28:473:30 | key |
2523+
| PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:473:28:473:30 | key |
2524+
| PrototypePollutionUtility/tests.js:471:29:471:31 | dst | PrototypePollutionUtility/tests.js:471:29:471:36 | dst[key] |
2525+
| PrototypePollutionUtility/tests.js:471:29:471:36 | dst[key] | PrototypePollutionUtility/tests.js:467:26:467:28 | dst |
2526+
| PrototypePollutionUtility/tests.js:471:29:471:36 | dst[key] | PrototypePollutionUtility/tests.js:467:26:467:28 | dst |
2527+
| PrototypePollutionUtility/tests.js:471:33:471:35 | key | PrototypePollutionUtility/tests.js:471:29:471:36 | dst[key] |
2528+
| PrototypePollutionUtility/tests.js:471:39:471:41 | src | PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] |
2529+
| PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] | PrototypePollutionUtility/tests.js:467:31:467:33 | src |
2530+
| PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] | PrototypePollutionUtility/tests.js:467:31:467:33 | src |
2531+
| PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] | PrototypePollutionUtility/tests.js:467:31:467:33 | src |
2532+
| PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] | PrototypePollutionUtility/tests.js:467:31:467:33 | src |
2533+
| PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] | PrototypePollutionUtility/tests.js:467:31:467:33 | src |
2534+
| PrototypePollutionUtility/tests.js:471:43:471:45 | key | PrototypePollutionUtility/tests.js:471:39:471:46 | src[key] |
2535+
| PrototypePollutionUtility/tests.js:473:24:473:26 | src | PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
2536+
| PrototypePollutionUtility/tests.js:473:24:473:26 | src | PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
2537+
| PrototypePollutionUtility/tests.js:473:24:473:26 | src | PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
2538+
| PrototypePollutionUtility/tests.js:473:24:473:26 | src | PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
2539+
| PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] | PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
2540+
| PrototypePollutionUtility/tests.js:473:28:473:30 | key | PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
2541+
| PrototypePollutionUtility/tests.js:473:28:473:30 | key | PrototypePollutionUtility/tests.js:473:24:473:31 | src[key] |
2542+
| PrototypePollutionUtility/tests.js:478:32:478:34 | src | PrototypePollutionUtility/tests.js:482:21:482:23 | src |
2543+
| PrototypePollutionUtility/tests.js:479:14:479:16 | key | PrototypePollutionUtility/tests.js:482:25:482:27 | key |
2544+
| PrototypePollutionUtility/tests.js:479:14:479:16 | key | PrototypePollutionUtility/tests.js:482:25:482:27 | key |
2545+
| PrototypePollutionUtility/tests.js:479:14:479:16 | key | PrototypePollutionUtility/tests.js:486:17:486:19 | key |
2546+
| PrototypePollutionUtility/tests.js:479:14:479:16 | key | PrototypePollutionUtility/tests.js:486:17:486:19 | key |
2547+
| PrototypePollutionUtility/tests.js:479:14:479:16 | key | PrototypePollutionUtility/tests.js:486:17:486:19 | key |
2548+
| PrototypePollutionUtility/tests.js:479:14:479:16 | key | PrototypePollutionUtility/tests.js:486:17:486:19 | key |
2549+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value | PrototypePollutionUtility/tests.js:484:38:484:42 | value |
2550+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value | PrototypePollutionUtility/tests.js:484:38:484:42 | value |
2551+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value | PrototypePollutionUtility/tests.js:486:24:486:28 | value |
2552+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value | PrototypePollutionUtility/tests.js:486:24:486:28 | value |
2553+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value | PrototypePollutionUtility/tests.js:486:24:486:28 | value |
2554+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value | PrototypePollutionUtility/tests.js:486:24:486:28 | value |
2555+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value | PrototypePollutionUtility/tests.js:486:24:486:28 | value |
2556+
| PrototypePollutionUtility/tests.js:482:13:482:28 | value | PrototypePollutionUtility/tests.js:486:24:486:28 | value |
2557+
| PrototypePollutionUtility/tests.js:482:21:482:23 | src | PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] |
2558+
| PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] | PrototypePollutionUtility/tests.js:482:13:482:28 | value |
2559+
| PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] | PrototypePollutionUtility/tests.js:482:13:482:28 | value |
2560+
| PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] | PrototypePollutionUtility/tests.js:482:13:482:28 | value |
2561+
| PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] | PrototypePollutionUtility/tests.js:482:13:482:28 | value |
2562+
| PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] | PrototypePollutionUtility/tests.js:482:13:482:28 | value |
2563+
| PrototypePollutionUtility/tests.js:482:25:482:27 | key | PrototypePollutionUtility/tests.js:482:21:482:28 | src[key] |
2564+
| PrototypePollutionUtility/tests.js:484:38:484:42 | value | PrototypePollutionUtility/tests.js:478:32:478:34 | src |
2565+
| PrototypePollutionUtility/tests.js:484:38:484:42 | value | PrototypePollutionUtility/tests.js:478:32:478:34 | src |
24602566
| examples/PrototypePollutionUtility.js:1:16:1:18 | dst | examples/PrototypePollutionUtility.js:5:19:5:21 | dst |
24612567
| examples/PrototypePollutionUtility.js:1:16:1:18 | dst | examples/PrototypePollutionUtility.js:5:19:5:21 | dst |
24622568
| examples/PrototypePollutionUtility.js:1:16:1:18 | dst | examples/PrototypePollutionUtility.js:7:13:7:15 | dst |
@@ -2583,4 +2689,5 @@ edges
25832689
| PrototypePollutionUtility/tests.js:450:30:450:32 | dst | PrototypePollutionUtility/tests.js:444:25:444:27 | key | PrototypePollutionUtility/tests.js:450:30:450:32 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | PrototypePollutionUtility/tests.js:444:12:444:14 | src | src | PrototypePollutionUtility/tests.js:450:30:450:32 | dst | dst |
25842690
| PrototypePollutionUtility/tests.js:451:30:451:32 | dst | PrototypePollutionUtility/tests.js:444:25:444:27 | key | PrototypePollutionUtility/tests.js:451:30:451:32 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | PrototypePollutionUtility/tests.js:444:12:444:14 | src | src | PrototypePollutionUtility/tests.js:451:30:451:32 | dst | dst |
25852691
| PrototypePollutionUtility/tests.js:461:13:461:15 | dst | PrototypePollutionUtility/tests.js:457:25:457:27 | key | PrototypePollutionUtility/tests.js:461:13:461:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | PrototypePollutionUtility/tests.js:457:12:457:14 | src | src | PrototypePollutionUtility/tests.js:461:13:461:15 | dst | dst |
2692+
| PrototypePollutionUtility/tests.js:473:13:473:15 | dst | PrototypePollutionUtility/tests.js:468:14:468:16 | key | PrototypePollutionUtility/tests.js:473:13:473:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | PrototypePollutionUtility/tests.js:468:21:468:23 | src | src | PrototypePollutionUtility/tests.js:473:13:473:15 | dst | dst |
25862693
| examples/PrototypePollutionUtility.js:7:13:7:15 | dst | examples/PrototypePollutionUtility.js:2:14:2:16 | key | examples/PrototypePollutionUtility.js:7:13:7:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | examples/PrototypePollutionUtility.js:2:21:2:23 | src | src | examples/PrototypePollutionUtility.js:7:13:7:15 | dst | dst |

javascript/ql/test/query-tests/Security/CWE-400/PrototypePollutionUtility/tests.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,3 +462,28 @@ function copyUsingUnderscoreOrLodash(dst, src) {
462462
}
463463
});
464464
}
465+
466+
let isPlainObject = require('is-plain-object');
467+
function copyPlainObject(dst, src) {
468+
for (let key in src) {
469+
if (key === '__proto__') continue;
470+
if (dst[key] && isPlainObject(src)) {
471+
copyPlainObject(dst[key], src[key]);
472+
} else {
473+
dst[key] = src[key]; // OK - but flagged anyway
474+
}
475+
}
476+
}
477+
478+
function copyPlainObject2(dst, src) {
479+
for (let key in src) {
480+
if (key === '__proto__') continue;
481+
let target = dst[key];
482+
let value = src[key];
483+
if (isPlainObject(target) && isPlainObject(value)) {
484+
copyPlainObject2(target, value);
485+
} else {
486+
dst[key] = value; // OK
487+
}
488+
}
489+
}

0 commit comments

Comments
 (0)