Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine du Hamel <duhamelantoine1995@gmail.com>2022-06-12 14:52:43 +0300
committerDanielle Adams <adamzdanielle@gmail.com>2022-06-16 04:28:48 +0300
commit812140c65a2fa79079b2be6bb91b80580006ef81 (patch)
tree23309e92704366e00c846b0e3678806e0dd5631c
parentac9599a7182505a78aaf6ab031b62f4408f7cdcd (diff)
tools,doc: add guards against prototype pollution when creating proxies
PR-URL: https://github.com/nodejs/node/pull/43391 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: LiviaMedeiros <livia@cirno.name> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
-rw-r--r--doc/contributing/primordials.md24
-rw-r--r--lib/internal/debugger/inspect.js1
-rw-r--r--lib/internal/http2/core.js1
-rw-r--r--lib/internal/modules/cjs/loader.js4
-rw-r--r--test/parallel/test-eslint-avoid-prototype-pollution.js20
-rw-r--r--tools/eslint-rules/avoid-prototype-pollution.js17
6 files changed, 67 insertions, 0 deletions
diff --git a/doc/contributing/primordials.md b/doc/contributing/primordials.md
index 9a081418981..e93d224495a 100644
--- a/doc/contributing/primordials.md
+++ b/doc/contributing/primordials.md
@@ -705,3 +705,27 @@ class SomeClass {
ObjectDefineProperty(SomeClass.prototype, 'readOnlyProperty', kEnumerableProperty);
console.log(new SomeClass().readOnlyProperty); // genuine data
```
+
+### Defining a `Proxy` handler
+
+When defining a `Proxy`, the handler object could be at risk of prototype
+pollution when using a plain object literal:
+
+```js
+// User-land
+Object.prototype.get = () => 'Unrelated user-provided data';
+
+// Core
+const objectToProxy = { someProperty: 'genuine value' };
+
+const proxyWithPlainObjectLiteral = new Proxy(objectToProxy, {
+ has() { return false; },
+});
+console.log(proxyWithPlainObjectLiteral.someProperty); // Unrelated user-provided data
+
+const proxyWithNullPrototypeObject = new Proxy(objectToProxy, {
+ __proto__: null,
+ has() { return false; },
+});
+console.log(proxyWithNullPrototypeObject.someProperty); // genuine value
+```
diff --git a/lib/internal/debugger/inspect.js b/lib/internal/debugger/inspect.js
index 7f1017886f6..cb5ffa8cef5 100644
--- a/lib/internal/debugger/inspect.js
+++ b/lib/internal/debugger/inspect.js
@@ -117,6 +117,7 @@ function createAgentProxy(domain, client) {
};
return new Proxy(agent, {
+ __proto__: null,
get(target, name) {
if (name in target) return target[name];
return function callVirtualMethod(params) {
diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js
index d6319286fa7..22d9f0f6c0d 100644
--- a/lib/internal/http2/core.js
+++ b/lib/internal/http2/core.js
@@ -987,6 +987,7 @@ function trackAssignmentsTypedArray(typedArray) {
}
return new Proxy(typedArray, {
+ __proto__: null,
get(obj, prop, receiver) {
if (prop === 'copyAssigned') {
return copyAssigned;
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index 9eaac14bcaa..66f8e5f98ca 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -210,6 +210,8 @@ const wrapper = [
];
let wrapperProxy = new Proxy(wrapper, {
+ __proto__: null,
+
set(target, property, value, receiver) {
patched = true;
return ReflectSet(target, property, value, receiver);
@@ -718,6 +720,8 @@ function emitCircularRequireWarning(prop) {
// A Proxy that can be used as the prototype of a module.exports object and
// warns when non-existent properties are accessed.
const CircularRequirePrototypeWarningProxy = new Proxy({}, {
+ __proto__: null,
+
get(target, prop) {
// Allow __esModule access in any case because it is used in the output
// of transpiled code to determine whether something comes from an
diff --git a/test/parallel/test-eslint-avoid-prototype-pollution.js b/test/parallel/test-eslint-avoid-prototype-pollution.js
index 0af4a0a07a2..26b0852c0c2 100644
--- a/test/parallel/test-eslint-avoid-prototype-pollution.js
+++ b/test/parallel/test-eslint-avoid-prototype-pollution.js
@@ -45,6 +45,10 @@ new RuleTester({
'ReflectDefineProperty({}, "key", { "__proto__": null })',
'ObjectDefineProperty({}, "key", { \'__proto__\': null })',
'ReflectDefineProperty({}, "key", { \'__proto__\': null })',
+ 'new Proxy({}, otherObject)',
+ 'new Proxy({}, someFactory())',
+ 'new Proxy({}, { __proto__: null })',
+ 'new Proxy({}, { __proto__: null, ...{} })',
],
invalid: [
{
@@ -183,5 +187,21 @@ new RuleTester({
code: 'StringPrototypeSplit("some string", /some regex/)',
errors: [{ message: /looks up the Symbol\.split property/ }],
},
+ {
+ code: 'new Proxy({}, {})',
+ errors: [{ message: /null-prototype/ }]
+ },
+ {
+ code: 'new Proxy({}, { [`__proto__`]: null })',
+ errors: [{ message: /null-prototype/ }]
+ },
+ {
+ code: 'new Proxy({}, { __proto__: Object.prototype })',
+ errors: [{ message: /null-prototype/ }]
+ },
+ {
+ code: 'new Proxy({}, { ...{ __proto__: null } })',
+ errors: [{ message: /null-prototype/ }]
+ },
]
});
diff --git a/tools/eslint-rules/avoid-prototype-pollution.js b/tools/eslint-rules/avoid-prototype-pollution.js
index 0759960349a..1f71272bd7d 100644
--- a/tools/eslint-rules/avoid-prototype-pollution.js
+++ b/tools/eslint-rules/avoid-prototype-pollution.js
@@ -128,6 +128,23 @@ module.exports = {
...createUnsafeStringMethodReport(context, 'StringPrototypeReplaceAll', 'Symbol.replace'),
...createUnsafeStringMethodReport(context, 'StringPrototypeSearch', 'Symbol.search'),
...createUnsafeStringMethodReport(context, 'StringPrototypeSplit', 'Symbol.split'),
+
+ 'NewExpression[callee.name="Proxy"][arguments.1.type="ObjectExpression"]'(node) {
+ for (const { key, value } of node.arguments[1].properties) {
+ if (
+ key != null && value != null &&
+ ((key.type === 'Identifier' && key.name === '__proto__') ||
+ (key.type === 'Literal' && key.value === '__proto__')) &&
+ value.type === 'Literal' && value.value === null
+ ) {
+ return;
+ }
+ }
+ context.report({
+ node,
+ message: 'Proxy handler must be a null-prototype object'
+ });
+ }
};
},
};