diff --git a/javascript/ql/src/Security/CWE-400/PrototypePollutionUtility.ql b/javascript/ql/src/Security/CWE-400/PrototypePollutionUtility.ql index d8f7f8b1760..09c2a7bd0b5 100644 --- a/javascript/ql/src/Security/CWE-400/PrototypePollutionUtility.ql +++ b/javascript/ql/src/Security/CWE-400/PrototypePollutionUtility.ql @@ -144,6 +144,26 @@ class DynamicPropRead extends DataFlow::SourceNode, DataFlow::ValueNode { /** Gets the base of the dynamic read. */ DataFlow::Node getBase() { result = astNode.getBase().flow() } + + /** + * Holds if the value of this read was assigned to earlier in the same basic block. + * + * For example, this is true for `dst[x]` on line 2 below: + * ```js + * dst[x] = {}; + * dst[x][y] = src[y]; + * ``` + */ + predicate hasDominatingAssignment() { + exists(DataFlow::PropWrite write, BasicBlock bb, int i, int j, SsaVariable ssaVar | + write = getBase().getALocalSource().getAPropertyWrite() and + bb.getNode(i) = write.getWriteNode() and + bb.getNode(j) = astNode and + i < j and + write.getPropertyNameExpr() = ssaVar.getAUse() and + astNode.getIndex() = ssaVar.getAUse() + ) + } } /** @@ -238,11 +258,13 @@ class PropNameTracking extends DataFlow::Configuration { // Step through `p -> x[p]` exists(PropRead read | pred = read.getPropertyNameExpr().flow() and + not read.(DynamicPropRead).hasDominatingAssignment() and succ = read ) or // Step through `x -> x[p]` exists(DynamicPropRead read | + not read.hasDominatingAssignment() and pred = read.getBase() and succ = read ) diff --git a/javascript/ql/test/query-tests/Security/CWE-400/PrototypePollutionUtility.expected b/javascript/ql/test/query-tests/Security/CWE-400/PrototypePollutionUtility.expected index 0b12cd1b86e..3b5f3a06e30 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/PrototypePollutionUtility.expected +++ b/javascript/ql/test/query-tests/Security/CWE-400/PrototypePollutionUtility.expected @@ -707,6 +707,42 @@ nodes | PrototypePollutionUtility/tests.js:280:24:280:31 | src[key] | | PrototypePollutionUtility/tests.js:280:28:280:30 | key | | PrototypePollutionUtility/tests.js:280:28:280:30 | key | +| PrototypePollutionUtility/tests.js:285:28:285:30 | src | +| PrototypePollutionUtility/tests.js:285:28:285:30 | src | +| PrototypePollutionUtility/tests.js:285:33:285:36 | path | +| PrototypePollutionUtility/tests.js:285:33:285:36 | path | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | +| PrototypePollutionUtility/tests.js:289:40:289:42 | src | +| PrototypePollutionUtility/tests.js:289:40:289:42 | src | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | +| PrototypePollutionUtility/tests.js:289:44:289:46 | key | +| PrototypePollutionUtility/tests.js:289:44:289:46 | key | +| PrototypePollutionUtility/tests.js:289:50:289:78 | path ? ... y : key | +| PrototypePollutionUtility/tests.js:289:50:289:78 | path ? ... y : key | +| PrototypePollutionUtility/tests.js:289:76:289:78 | key | +| PrototypePollutionUtility/tests.js:289:76:289:78 | key | +| PrototypePollutionUtility/tests.js:292:24:292:27 | path | +| PrototypePollutionUtility/tests.js:292:24:292:27 | path | +| PrototypePollutionUtility/tests.js:292:24:292:27 | path | +| PrototypePollutionUtility/tests.js:293:30:293:32 | key | +| PrototypePollutionUtility/tests.js:293:30:293:32 | key | +| PrototypePollutionUtility/tests.js:293:30:293:32 | key | +| PrototypePollutionUtility/tests.js:293:37:293:39 | src | +| PrototypePollutionUtility/tests.js:293:37:293:39 | src | +| PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:41:293:43 | key | +| PrototypePollutionUtility/tests.js:293:41:293:43 | key | | examples/PrototypePollutionUtility.js:1:16:1:18 | dst | | examples/PrototypePollutionUtility.js:1:16:1:18 | dst | | examples/PrototypePollutionUtility.js:1:21:1:23 | src | @@ -1696,6 +1732,56 @@ edges | PrototypePollutionUtility/tests.js:280:28:280:30 | key | PrototypePollutionUtility/tests.js:280:24:280:31 | src[key] | | PrototypePollutionUtility/tests.js:280:28:280:30 | key | PrototypePollutionUtility/tests.js:280:24:280:31 | src[key] | | PrototypePollutionUtility/tests.js:280:28:280:30 | key | PrototypePollutionUtility/tests.js:280:24:280:31 | src[key] | +| PrototypePollutionUtility/tests.js:285:28:285:30 | src | PrototypePollutionUtility/tests.js:289:40:289:42 | src | +| PrototypePollutionUtility/tests.js:285:28:285:30 | src | PrototypePollutionUtility/tests.js:289:40:289:42 | src | +| PrototypePollutionUtility/tests.js:285:28:285:30 | src | PrototypePollutionUtility/tests.js:293:37:293:39 | src | +| PrototypePollutionUtility/tests.js:285:28:285:30 | src | PrototypePollutionUtility/tests.js:293:37:293:39 | src | +| PrototypePollutionUtility/tests.js:285:33:285:36 | path | PrototypePollutionUtility/tests.js:292:24:292:27 | path | +| PrototypePollutionUtility/tests.js:285:33:285:36 | path | PrototypePollutionUtility/tests.js:292:24:292:27 | path | +| PrototypePollutionUtility/tests.js:285:33:285:36 | path | PrototypePollutionUtility/tests.js:292:24:292:27 | path | +| PrototypePollutionUtility/tests.js:285:33:285:36 | path | PrototypePollutionUtility/tests.js:292:24:292:27 | path | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:289:44:289:46 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:289:44:289:46 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:289:44:289:46 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:289:44:289:46 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:289:76:289:78 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:289:76:289:78 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:289:76:289:78 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:289:76:289:78 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:30:293:32 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:30:293:32 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:30:293:32 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:30:293:32 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:30:293:32 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:30:293:32 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:30:293:32 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:41:293:43 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:41:293:43 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:41:293:43 | key | +| PrototypePollutionUtility/tests.js:286:14:286:16 | key | PrototypePollutionUtility/tests.js:293:41:293:43 | key | +| PrototypePollutionUtility/tests.js:289:40:289:42 | src | PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | +| PrototypePollutionUtility/tests.js:289:40:289:42 | src | PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | PrototypePollutionUtility/tests.js:285:28:285:30 | src | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | PrototypePollutionUtility/tests.js:285:28:285:30 | src | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | PrototypePollutionUtility/tests.js:285:28:285:30 | src | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | PrototypePollutionUtility/tests.js:285:28:285:30 | src | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | PrototypePollutionUtility/tests.js:285:28:285:30 | src | +| PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | PrototypePollutionUtility/tests.js:285:28:285:30 | src | +| PrototypePollutionUtility/tests.js:289:44:289:46 | key | PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | +| PrototypePollutionUtility/tests.js:289:44:289:46 | key | PrototypePollutionUtility/tests.js:289:40:289:47 | src[key] | +| PrototypePollutionUtility/tests.js:289:50:289:78 | path ? ... y : key | PrototypePollutionUtility/tests.js:285:33:285:36 | path | +| PrototypePollutionUtility/tests.js:289:50:289:78 | path ? ... y : key | PrototypePollutionUtility/tests.js:285:33:285:36 | path | +| PrototypePollutionUtility/tests.js:289:76:289:78 | key | PrototypePollutionUtility/tests.js:289:50:289:78 | path ? ... y : key | +| PrototypePollutionUtility/tests.js:289:76:289:78 | key | PrototypePollutionUtility/tests.js:289:50:289:78 | path ? ... y : key | +| PrototypePollutionUtility/tests.js:293:37:293:39 | src | PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:37:293:39 | src | PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:37:293:39 | src | PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:37:293:39 | src | PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:41:293:43 | key | PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:41:293:43 | key | PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:41:293:43 | key | PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | +| PrototypePollutionUtility/tests.js:293:41:293:43 | key | PrototypePollutionUtility/tests.js:293:37:293:44 | src[key] | | examples/PrototypePollutionUtility.js:1:16:1:18 | dst | examples/PrototypePollutionUtility.js:5:19:5:21 | dst | | examples/PrototypePollutionUtility.js:1:16:1:18 | dst | examples/PrototypePollutionUtility.js:5:19:5:21 | dst | | examples/PrototypePollutionUtility.js:1:16:1:18 | dst | examples/PrototypePollutionUtility.js:7:13:7:15 | dst | diff --git a/javascript/ql/test/query-tests/Security/CWE-400/PrototypePollutionUtility/tests.js b/javascript/ql/test/query-tests/Security/CWE-400/PrototypePollutionUtility/tests.js index de765fde1b1..58a65cf6cc9 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/PrototypePollutionUtility/tests.js +++ b/javascript/ql/test/query-tests/Security/CWE-400/PrototypePollutionUtility/tests.js @@ -280,4 +280,20 @@ function copyUsingReflect(dst, src) { dst[key] = src[key]; // NOT OK } }); -} \ No newline at end of file +} + +function copyWithPath(dst, src, path) { + for (let key in src) { + if (src.hasOwnProperty(key)) { + if (dst[key]) { + copyWithPath(dst[key], src[key], path ? path + '.' + key : key); + } else { + let target = {}; + target[path] = {}; + target[path][key] = src[key]; // OK + doSomething(target); + } + } + } + return dst; +}