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

fix(webidl): do not throw if Object.prototype has been mutated #16407

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions cli/tests/unit/file_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,77 @@ Deno.test(function fileUsingNumberFileName() {
Deno.test(function fileUsingEmptyStringFileName() {
testSecondArgument("", "");
});

Deno.test(function fileConstructorOptionsValidation() {
// deno-lint-ignore ban-types
type AnyFunction = Function;
const assert = {
strictEqual: assertEquals,
throws(fn: AnyFunction, expected: AnyFunction) {
try {
fn();
throw new Error("Missing expected exception");
} catch (e) {
if (e instanceof expected) {
return;
}

throw new Error("Wrong type of error", { cause: e });
}
},
};

function mustCall(fn: AnyFunction, nb = 1) {
const timeout = setTimeout(() => {
if (nb !== 0) throw new Error(`Expected ${nb} more calls`);
}, 999);
return function (this: unknown) {
nb--;
if (nb === 0) clearTimeout(timeout);
else if (nb < 0) {
throw new Error("Function has been called more times than expected");
}
return Reflect.apply(fn, this, arguments);
};
}

[undefined, null, Object.create(null), { lastModified: undefined }, {
get lastModified() {
return undefined;
},
}].forEach((options) => {
assert.strictEqual(
new File([], "", options).lastModified,
new File([], "").lastModified,
);
});

Reflect.defineProperty(Object.prototype, "get", {
// @ts-ignore __proto__ null is important here to avoid prototype pollution.
__proto__: null,
configurable: true,
get() {
throw new Error();
},
});
Reflect.defineProperty(Object.prototype, "lastModified", {
// @ts-ignore __proto__ null is important here to avoid prototype pollution.
__proto__: null,
configurable: true,
get: mustCall(() => 3, 7),
});

[{}, [], () => {}, Number, new Number(), new String(), new Boolean()].forEach(
(options) => {
// @ts-ignore We want to test an options object that doesn't meet the typical types.
assert.strictEqual(new File([], "", options).lastModified, 3);
},
);
[0, "", true, Symbol(), 0n].forEach((options) => {
// @ts-ignore We want to test an options object that doesn't meet the typical types.
assert.throws(() => new File([], "", options), TypeError);
});

// @ts-ignore cleaning up.
delete Object.prototype.lastModified;
});
3 changes: 1 addition & 2 deletions ext/webidl/00_webidl.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ const {
NumberMAX_SAFE_INTEGER,
// deno-lint-ignore camelcase
NumberMIN_SAFE_INTEGER,
ObjectAssign,
ObjectCreate,
ObjectDefineProperties,
ObjectDefineProperty,
Expand Down Expand Up @@ -698,7 +697,7 @@ function createDictionaryConverter(name, ...dictionaries) {
}
const esDict = V;

const idlDict = ObjectAssign({}, defaultValues);
const idlDict = { __proto__: null, ...defaultValues };
aapoalas marked this conversation as resolved.
Show resolved Hide resolved

// NOTE: fast path Null and Undefined.
if ((V === undefined || V === null) && !hasRequiredKey) {
Expand Down