Skip to content

Commit d1df251

Browse files
committed
JS: Proto pollution: Add is-plain-object sanitizer
1 parent ee5cf95 commit d1df251

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-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/tests.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,3 +462,14 @@ 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 (dst[key] && isPlainObject(src)) {
470+
copyPlainObject(dst[key], src[key]);
471+
} else {
472+
dst[key] = src[key]; // OK
473+
}
474+
}
475+
}

0 commit comments

Comments
 (0)