Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node: warn for Object.prototype.__* accessors common in security warnings #39824

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
58 changes: 58 additions & 0 deletions lib/internal/bootstrap/pre_execution.js
Expand Up @@ -3,10 +3,13 @@
const {
NumberParseInt,
ObjectDefineProperty,
ObjectGetOwnPropertyDescriptor,
ObjectGetPrototypeOf,
SafeMap,
SafeWeakMap,
StringPrototypeStartsWith,
globalThis,
uncurryThis,
} = primordials;

const {
Expand Down Expand Up @@ -332,6 +335,61 @@ function initializeDeprecations() {
enumerable: false,
configurable: true
});

let warnedForProto = false;
const ObjectPrototype = ObjectGetPrototypeOf({});
bmeck marked this conversation as resolved.
Show resolved Hide resolved
function warnOnProto() {
if (!warnedForProto) {
warnedForProto = true;
process.emitWarning('usage of Object.prototype.__* properties are a' +
bmeck marked this conversation as resolved.
Show resolved Hide resolved
' common security concern, use static methods on Object instead');
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should make these proper runtime deprecations?
/cc @addaleax

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not opposed, is it just adding to the docs a new number to get the DEP###?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added to DEP doc, idk if there is a process to do instead

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look for other DEP codes in the code and you'll see how those are emitted :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:

node/lib/sys.js

Lines 28 to 29 in e46c680

process.emitWarning('sys is deprecated. Use util instead.',
'DeprecationWarning', 'DEP0025');

As soon as this is changed to emit a deprecation warning, I'll launch a CITGM run with --throw-deprecation

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems difficult to wrangle without giving a unique DEP id to each accessor given the other feedback of giving more actionable feedback in #39824 (comment)

}
const legacy = [
'__proto__',
bmeck marked this conversation as resolved.
Show resolved Hide resolved
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__',
];
for (let i = 0; i < legacy.length; i++) {
const prop = legacy[i];
const protoDescriptor = ObjectGetOwnPropertyDescriptor(
ObjectPrototype,
prop);
if (protoDescriptor && 'value' in protoDescriptor) {
let value = protoDescriptor.value;
const writable = protoDescriptor.writable;
ObjectDefineProperty(ObjectPrototype, legacy[i], {
get() {
warnOnProto();
return value;
},
set(v) {
warnOnProto();
if (!writable) return;
value = v;
},
enumerable: false,
configurable: true
});
} else {
const getter = uncurryThis(protoDescriptor.get);
const setter = uncurryThis(protoDescriptor.set);
ObjectDefineProperty(ObjectPrototype, legacy[i], {
get() {
warnOnProto();
return getter(this);
},
set(value) {
warnOnProto();
setter(this, value);
},
enumerable: false,
configurable: true
});
}
}
}

function setupChildProcessIpcChannel() {
Expand Down
11 changes: 11 additions & 0 deletions test/parallel/test-object-proto-accessor-warning.js
@@ -0,0 +1,11 @@
'use strict';

const common = require('../common');

process.on('warning', common.mustCall());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a common.expectWarning() utility.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, but it seems deprecate(...) in lib seems to only fire once per dep code so having specialized messages per accessor seems to require unique DEP codes.


const obj = {};
// eslint-disable-next-line
const _ = obj.__proto__;
// eslint-disable-next-line
obj.__proto__ = null;