Skip to content

Commit 8af2333

Browse files
asger-semmleasgerf
authored andcommitted
JS: Support enumeration through Object.entries
1 parent ac2f0a8 commit 8af2333

File tree

3 files changed

+123
-18
lines changed

3 files changed

+123
-18
lines changed

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

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,29 +39,13 @@ SourceNode getAnEnumeratedArrayElement(SourceNode array) {
3939
* A data flow node that refers to the name of a property obtained by enumerating
4040
* the properties of some object.
4141
*/
42-
class EnumeratedPropName extends DataFlow::Node {
43-
DataFlow::Node object;
44-
45-
EnumeratedPropName() {
46-
exists(ForInStmt stmt |
47-
this = DataFlow::lvalueNode(stmt.getLValue()) and
48-
object = stmt.getIterationDomain().flow()
49-
)
50-
or
51-
exists(CallNode call, string name |
52-
call = globalVarRef("Object").getAMemberCall(name) and
53-
(name = "keys" or name = "getOwnPropertyNames") and
54-
object = call.getArgument(0) and
55-
this = getAnEnumeratedArrayElement(call)
56-
)
57-
}
58-
42+
abstract class EnumeratedPropName extends DataFlow::Node {
5943
/**
6044
* Gets the object whose properties are being enumerated.
6145
*
6246
* For example, gets `src` in `for (var key in src)`.
6347
*/
64-
Node getSourceObject() { result = object }
48+
abstract DataFlow::Node getSourceObject();
6549

6650
/**
6751
* Gets a local reference of the source object.
@@ -86,6 +70,53 @@ class EnumeratedPropName extends DataFlow::Node {
8670
}
8771
}
8872

73+
/**
74+
* Property enumeration through `for-in` for `Object.keys` or `Object.getOwnPropertyName`.
75+
*/
76+
class ForInEnumeratedPropName extends EnumeratedPropName {
77+
DataFlow::Node object;
78+
79+
ForInEnumeratedPropName() {
80+
exists(ForInStmt stmt |
81+
this = DataFlow::lvalueNode(stmt.getLValue()) and
82+
object = stmt.getIterationDomain().flow()
83+
)
84+
or
85+
exists(CallNode call, string name |
86+
call = globalVarRef("Object").getAMemberCall(name) and
87+
(name = "keys" or name = "getOwnPropertyNames") and
88+
object = call.getArgument(0) and
89+
this = getAnEnumeratedArrayElement(call)
90+
)
91+
}
92+
93+
override Node getSourceObject() { result = object }
94+
}
95+
96+
/**
97+
* Property enumeration through `Object.entries`.
98+
*/
99+
class EntriesEnumeratedPropName extends EnumeratedPropName {
100+
CallNode entries;
101+
SourceNode entry;
102+
103+
EntriesEnumeratedPropName() {
104+
entries = globalVarRef("Object").getAMemberCall("entries") and
105+
entry = getAnEnumeratedArrayElement(entries) and
106+
this = entry.getAPropertyRead("0")
107+
}
108+
109+
override DataFlow::Node getSourceObject() {
110+
result = entries.getArgument(0)
111+
}
112+
113+
override PropRead getASourceProp() {
114+
result = super.getASourceProp()
115+
or
116+
result = entry.getAPropertyRead("1")
117+
}
118+
}
119+
89120
/**
90121
* Holds if the properties of `node` are enumerated locally.
91122
*/

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

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,35 @@ nodes
638638
| PrototypePollutionUtility/tests.js:257:51:257:58 | src[key] |
639639
| PrototypePollutionUtility/tests.js:257:55:257:57 | key |
640640
| PrototypePollutionUtility/tests.js:257:55:257:57 | key |
641+
| PrototypePollutionUtility/tests.js:263:27:263:29 | dst |
642+
| PrototypePollutionUtility/tests.js:263:27:263:29 | dst |
643+
| PrototypePollutionUtility/tests.js:265:13:265:26 | key |
644+
| PrototypePollutionUtility/tests.js:265:13:265:26 | key |
645+
| PrototypePollutionUtility/tests.js:265:19:265:26 | entry[0] |
646+
| PrototypePollutionUtility/tests.js:265:19:265:26 | entry[0] |
647+
| PrototypePollutionUtility/tests.js:265:19:265:26 | entry[0] |
648+
| PrototypePollutionUtility/tests.js:266:13:266:28 | value |
649+
| PrototypePollutionUtility/tests.js:266:13:266:28 | value |
650+
| PrototypePollutionUtility/tests.js:266:21:266:28 | entry[1] |
651+
| PrototypePollutionUtility/tests.js:266:21:266:28 | entry[1] |
652+
| PrototypePollutionUtility/tests.js:266:21:266:28 | entry[1] |
653+
| PrototypePollutionUtility/tests.js:268:30:268:32 | dst |
654+
| PrototypePollutionUtility/tests.js:268:30:268:32 | dst |
655+
| PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] |
656+
| PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] |
657+
| PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] |
658+
| PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] |
659+
| PrototypePollutionUtility/tests.js:268:34:268:36 | key |
660+
| PrototypePollutionUtility/tests.js:268:34:268:36 | key |
661+
| PrototypePollutionUtility/tests.js:270:13:270:15 | dst |
662+
| PrototypePollutionUtility/tests.js:270:13:270:15 | dst |
663+
| PrototypePollutionUtility/tests.js:270:13:270:15 | dst |
664+
| PrototypePollutionUtility/tests.js:270:17:270:19 | key |
665+
| PrototypePollutionUtility/tests.js:270:17:270:19 | key |
666+
| PrototypePollutionUtility/tests.js:270:17:270:19 | key |
667+
| PrototypePollutionUtility/tests.js:270:24:270:28 | value |
668+
| PrototypePollutionUtility/tests.js:270:24:270:28 | value |
669+
| PrototypePollutionUtility/tests.js:270:24:270:28 | value |
641670
| examples/PrototypePollutionUtility.js:1:16:1:18 | dst |
642671
| examples/PrototypePollutionUtility.js:1:16:1:18 | dst |
643672
| examples/PrototypePollutionUtility.js:1:21:1:23 | src |
@@ -1539,6 +1568,38 @@ edges
15391568
| PrototypePollutionUtility/tests.js:257:51:257:58 | src[key] | PrototypePollutionUtility/tests.js:257:27:257:59 | mergeWi ... c[key]) |
15401569
| PrototypePollutionUtility/tests.js:257:55:257:57 | key | PrototypePollutionUtility/tests.js:257:51:257:58 | src[key] |
15411570
| PrototypePollutionUtility/tests.js:257:55:257:57 | key | PrototypePollutionUtility/tests.js:257:51:257:58 | src[key] |
1571+
| PrototypePollutionUtility/tests.js:263:27:263:29 | dst | PrototypePollutionUtility/tests.js:268:30:268:32 | dst |
1572+
| PrototypePollutionUtility/tests.js:263:27:263:29 | dst | PrototypePollutionUtility/tests.js:268:30:268:32 | dst |
1573+
| PrototypePollutionUtility/tests.js:263:27:263:29 | dst | PrototypePollutionUtility/tests.js:270:13:270:15 | dst |
1574+
| PrototypePollutionUtility/tests.js:263:27:263:29 | dst | PrototypePollutionUtility/tests.js:270:13:270:15 | dst |
1575+
| PrototypePollutionUtility/tests.js:263:27:263:29 | dst | PrototypePollutionUtility/tests.js:270:13:270:15 | dst |
1576+
| PrototypePollutionUtility/tests.js:263:27:263:29 | dst | PrototypePollutionUtility/tests.js:270:13:270:15 | dst |
1577+
| PrototypePollutionUtility/tests.js:265:13:265:26 | key | PrototypePollutionUtility/tests.js:268:34:268:36 | key |
1578+
| PrototypePollutionUtility/tests.js:265:13:265:26 | key | PrototypePollutionUtility/tests.js:268:34:268:36 | key |
1579+
| PrototypePollutionUtility/tests.js:265:13:265:26 | key | PrototypePollutionUtility/tests.js:270:17:270:19 | key |
1580+
| PrototypePollutionUtility/tests.js:265:13:265:26 | key | PrototypePollutionUtility/tests.js:270:17:270:19 | key |
1581+
| PrototypePollutionUtility/tests.js:265:13:265:26 | key | PrototypePollutionUtility/tests.js:270:17:270:19 | key |
1582+
| PrototypePollutionUtility/tests.js:265:13:265:26 | key | PrototypePollutionUtility/tests.js:270:17:270:19 | key |
1583+
| PrototypePollutionUtility/tests.js:265:19:265:26 | entry[0] | PrototypePollutionUtility/tests.js:265:13:265:26 | key |
1584+
| PrototypePollutionUtility/tests.js:265:19:265:26 | entry[0] | PrototypePollutionUtility/tests.js:265:13:265:26 | key |
1585+
| PrototypePollutionUtility/tests.js:265:19:265:26 | entry[0] | PrototypePollutionUtility/tests.js:265:13:265:26 | key |
1586+
| PrototypePollutionUtility/tests.js:265:19:265:26 | entry[0] | PrototypePollutionUtility/tests.js:265:13:265:26 | key |
1587+
| PrototypePollutionUtility/tests.js:266:13:266:28 | value | PrototypePollutionUtility/tests.js:270:24:270:28 | value |
1588+
| PrototypePollutionUtility/tests.js:266:13:266:28 | value | PrototypePollutionUtility/tests.js:270:24:270:28 | value |
1589+
| PrototypePollutionUtility/tests.js:266:13:266:28 | value | PrototypePollutionUtility/tests.js:270:24:270:28 | value |
1590+
| PrototypePollutionUtility/tests.js:266:13:266:28 | value | PrototypePollutionUtility/tests.js:270:24:270:28 | value |
1591+
| PrototypePollutionUtility/tests.js:266:21:266:28 | entry[1] | PrototypePollutionUtility/tests.js:266:13:266:28 | value |
1592+
| PrototypePollutionUtility/tests.js:266:21:266:28 | entry[1] | PrototypePollutionUtility/tests.js:266:13:266:28 | value |
1593+
| PrototypePollutionUtility/tests.js:266:21:266:28 | entry[1] | PrototypePollutionUtility/tests.js:266:13:266:28 | value |
1594+
| PrototypePollutionUtility/tests.js:266:21:266:28 | entry[1] | PrototypePollutionUtility/tests.js:266:13:266:28 | value |
1595+
| PrototypePollutionUtility/tests.js:268:30:268:32 | dst | PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] |
1596+
| PrototypePollutionUtility/tests.js:268:30:268:32 | dst | PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] |
1597+
| PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] | PrototypePollutionUtility/tests.js:263:27:263:29 | dst |
1598+
| PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] | PrototypePollutionUtility/tests.js:263:27:263:29 | dst |
1599+
| PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] | PrototypePollutionUtility/tests.js:263:27:263:29 | dst |
1600+
| PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] | PrototypePollutionUtility/tests.js:263:27:263:29 | dst |
1601+
| PrototypePollutionUtility/tests.js:268:34:268:36 | key | PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] |
1602+
| PrototypePollutionUtility/tests.js:268:34:268:36 | key | PrototypePollutionUtility/tests.js:268:30:268:37 | dst[key] |
15421603
| examples/PrototypePollutionUtility.js:1:16:1:18 | dst | examples/PrototypePollutionUtility.js:5:19:5:21 | dst |
15431604
| examples/PrototypePollutionUtility.js:1:16:1:18 | dst | examples/PrototypePollutionUtility.js:5:19:5:21 | dst |
15441605
| examples/PrototypePollutionUtility.js:1:16:1:18 | dst | examples/PrototypePollutionUtility.js:7:13:7:15 | dst |
@@ -1654,4 +1715,5 @@ edges
16541715
| PrototypePollutionUtility/tests.js:154:13:154:15 | dst | PrototypePollutionUtility/tests.js:150:14:150:16 | key | PrototypePollutionUtility/tests.js:154:13:154:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | PrototypePollutionUtility/tests.js:150:21:150:23 | src | src | PrototypePollutionUtility/tests.js:154:13:154:15 | dst | dst |
16551716
| PrototypePollutionUtility/tests.js:196:13:196:15 | dst | PrototypePollutionUtility/tests.js:192:19:192:25 | keys[i] | PrototypePollutionUtility/tests.js:196:13:196:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | PrototypePollutionUtility/tests.js:190:28:190:30 | src | src | PrototypePollutionUtility/tests.js:196:13:196:15 | dst | dst |
16561717
| PrototypePollutionUtility/tests.js:233:5:233:13 | map[key1] | PrototypePollutionUtility/tests.js:238:14:238:16 | key | PrototypePollutionUtility/tests.js:233:5:233:13 | map[key1] | Properties are copied from $@ to $@ without guarding against prototype pollution. | PrototypePollutionUtility/tests.js:238:21:238:24 | data | data | PrototypePollutionUtility/tests.js:233:5:233:13 | map[key1] | this object |
1718+
| PrototypePollutionUtility/tests.js:270:13:270:15 | dst | PrototypePollutionUtility/tests.js:265:19:265:26 | entry[0] | PrototypePollutionUtility/tests.js:270:13:270:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | PrototypePollutionUtility/tests.js:264:20:264:22 | src | src | PrototypePollutionUtility/tests.js:270:13:270:15 | dst | dst |
16571719
| 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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,15 @@ function mergeWithCopy(dst, src) {
259259
}
260260
return result;
261261
}
262+
263+
function copyUsingEntries(dst, src) {
264+
Object.entries(src).forEach(entry => {
265+
let key = entry[0];
266+
let value = entry[1];
267+
if (dst[key]) {
268+
copyUsingEntries(dst[key], value);
269+
} else {
270+
dst[key] = value; // NOT OK
271+
}
272+
});
273+
}

0 commit comments

Comments
 (0)