1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 08:33:43 -05:00

fix(core): SafePromiseAll to be unaffected by Array#@@iterator (#17542)

This commit is contained in:
Kenta Moriuchi 2023-03-13 19:24:31 +09:00 committed by GitHub
parent bcb6ee9d08
commit e5673f5ed8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 20 deletions

View file

@ -851,10 +851,11 @@ Deno.test(
Deno.test( Deno.test(
{ permissions: { read: true, run: true } }, { permissions: { read: true, run: true } },
async function commandWithPromisePrototypeThenOverride() { async function commandWithPrototypePollution() {
const originalThen = Promise.prototype.then; const originalThen = Promise.prototype.then;
const originalSymbolIterator = Array.prototype[Symbol.iterator];
try { try {
Promise.prototype.then = () => { Promise.prototype.then = Array.prototype[Symbol.iterator] = () => {
throw new Error(); throw new Error();
}; };
await new Deno.Command(Deno.execPath(), { await new Deno.Command(Deno.execPath(), {
@ -862,6 +863,7 @@ Deno.test(
}).output(); }).output();
} finally { } finally {
Promise.prototype.then = originalThen; Promise.prototype.then = originalThen;
Array.prototype[Symbol.iterator] = originalSymbolIterator;
} }
}, },
); );

View file

@ -2272,10 +2272,11 @@ Deno.test(
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function serveWithPromisePrototypeThenOverride() { async function serveWithPrototypePollution() {
const originalThen = Promise.prototype.then; const originalThen = Promise.prototype.then;
const originalSymbolIterator = Array.prototype[Symbol.iterator];
try { try {
Promise.prototype.then = () => { Promise.prototype.then = Array.prototype[Symbol.iterator] = () => {
throw new Error(); throw new Error();
}; };
const ac = new AbortController(); const ac = new AbortController();
@ -2292,6 +2293,7 @@ Deno.test(
await server; await server;
} finally { } finally {
Promise.prototype.then = originalThen; Promise.prototype.then = originalThen;
Array.prototype[Symbol.iterator] = originalSymbolIterator;
} }
}, },
); );

View file

@ -329,10 +329,11 @@
return SafeIterator; return SafeIterator;
}; };
primordials.SafeArrayIterator = createSafeIterator( const SafeArrayIterator = createSafeIterator(
primordials.ArrayPrototypeSymbolIterator, primordials.ArrayPrototypeSymbolIterator,
primordials.ArrayIteratorPrototypeNext, primordials.ArrayIteratorPrototypeNext,
); );
primordials.SafeArrayIterator = SafeArrayIterator;
primordials.SafeSetIterator = createSafeIterator( primordials.SafeSetIterator = createSafeIterator(
primordials.SetPrototypeSymbolIterator, primordials.SetPrototypeSymbolIterator,
primordials.SetIteratorPrototypeNext, primordials.SetIteratorPrototypeNext,
@ -479,29 +480,80 @@
primordials.PromisePrototypeCatch = (thisPromise, onRejected) => primordials.PromisePrototypeCatch = (thisPromise, onRejected) =>
PromisePrototypeThen(thisPromise, undefined, onRejected); PromisePrototypeThen(thisPromise, undefined, onRejected);
const arrayToSafePromiseIterable = (array) =>
new SafeArrayIterator(
ArrayPrototypeMap(
array,
(p) => {
if (ObjectPrototypeIsPrototypeOf(PromisePrototype, p)) {
return new SafePromise((c, d) => PromisePrototypeThen(p, c, d));
}
return p;
},
),
);
/** /**
* Creates a Promise that is resolved with an array of results when all of the * Creates a Promise that is resolved with an array of results when all of the
* provided Promises resolve, or rejected when any Promise is rejected. * provided Promises resolve, or rejected when any Promise is rejected.
* @param {unknown[]} values An array of Promises. * @template T
* @returns A new Promise. * @param {Array<T | PromiseLike<T>>} values
* @returns {Promise<Awaited<T>[]>}
*/ */
primordials.SafePromiseAll = (values) => primordials.SafePromiseAll = (values) =>
// Wrapping on a new Promise is necessary to not expose the SafePromise // Wrapping on a new Promise is necessary to not expose the SafePromise
// prototype to user-land. // prototype to user-land.
new Promise((a, b) => new Promise((a, b) =>
SafePromise.all( SafePromise.all(arrayToSafePromiseIterable(values)).then(a, b)
ArrayPrototypeMap(
values,
(p) => {
if (ObjectPrototypeIsPrototypeOf(PromisePrototype, p)) {
return new SafePromise((c, d) => PromisePrototypeThen(p, c, d));
}
return p;
},
),
).then(a, b)
); );
// NOTE: Uncomment the following functions when you need to use them
// /**
// * Creates a Promise that is resolved with an array of results when all
// * of the provided Promises resolve or reject.
// * @template T
// * @param {Array<T | PromiseLike<T>>} values
// * @returns {Promise<PromiseSettledResult<T>[]>}
// */
// primordials.SafePromiseAllSettled = (values) =>
// // Wrapping on a new Promise is necessary to not expose the SafePromise
// // prototype to user-land.
// new Promise((a, b) =>
// SafePromise.allSettled(arrayToSafePromiseIterable(values)).then(a, b)
// );
// /**
// * The any function returns a promise that is fulfilled by the first given
// * promise to be fulfilled, or rejected with an AggregateError containing
// * an array of rejection reasons if all of the given promises are rejected.
// * It resolves all elements of the passed iterable to promises as it runs
// * this algorithm.
// * @template T
// * @param {T} values
// * @returns {Promise<Awaited<T[number]>>}
// */
// primordials.SafePromiseAny = (values) =>
// // Wrapping on a new Promise is necessary to not expose the SafePromise
// // prototype to user-land.
// new Promise((a, b) =>
// SafePromise.any(arrayToSafePromiseIterable(values)).then(a, b)
// );
// /**
// * Creates a Promise that is resolved or rejected when any of the provided
// * Promises are resolved or rejected.
// * @template T
// * @param {T} values
// * @returns {Promise<Awaited<T[number]>>}
// */
// primordials.SafePromiseRace = (values) =>
// // Wrapping on a new Promise is necessary to not expose the SafePromise
// // prototype to user-land.
// new Promise((a, b) =>
// SafePromise.race(arrayToSafePromiseIterable(values)).then(a, b)
// );
/** /**
* Attaches a callback that is invoked when the Promise is settled (fulfilled or * Attaches a callback that is invoked when the Promise is settled (fulfilled or
* rejected). The resolved value cannot be modified from the callback. * rejected). The resolved value cannot be modified from the callback.

View file

@ -21,6 +21,7 @@
PromisePrototypeThen, PromisePrototypeThen,
RangeError, RangeError,
ReferenceError, ReferenceError,
SafeArrayIterator,
SafePromisePrototypeFinally, SafePromisePrototypeFinally,
setQueueMicrotask, setQueueMicrotask,
StringPrototypeSlice, StringPrototypeSlice,
@ -198,7 +199,7 @@
const id = rollPromiseId(); const id = rollPromiseId();
let promise = PromisePrototypeThen(setPromise(id), unwrapOpResult); let promise = PromisePrototypeThen(setPromise(id), unwrapOpResult);
try { try {
ops[name](id, ...args); ops[name](id, ...new SafeArrayIterator(args));
} catch (err) { } catch (err) {
// Cleanup the just-created promise // Cleanup the just-created promise
getPromise(id); getPromise(id);

4
core/internal.d.ts vendored
View file

@ -75,6 +75,10 @@ declare namespace __bootstrap {
typeof globalThis.FinalizationRegistry; typeof globalThis.FinalizationRegistry;
export const SafeWeakRef: typeof globalThis.WeakRef; export const SafeWeakRef: typeof globalThis.WeakRef;
export const SafePromiseAll: typeof Promise.all; export const SafePromiseAll: typeof Promise.all;
// NOTE: Uncomment the following functions when you need to use them
// export const SafePromiseAllSettled: typeof Promise.allSettled;
// export const SafePromiseAny: typeof Promise.any;
// export const SafePromiseRace: typeof Promise.race;
export const SafePromisePrototypeFinally: UncurryThis< export const SafePromisePrototypeFinally: UncurryThis<
Promise.prototype.finally Promise.prototype.finally
>; >;

View file

@ -510,7 +510,8 @@ function buildCaseInsensitiveCommaValueFinder(checkText) {
/** @param value {string} */ /** @param value {string} */
function hasWord(value) { function hasWord(value) {
for (const [cLower, cUpper] of charCodes) { for (let j = 0; j < charCodes.length; ++j) {
const { 0: cLower, 1: cUpper } = charCodes[j];
if (cLower === char || cUpper === char) { if (cLower === char || cUpper === char) {
char = StringPrototypeCharCodeAt(value, ++i); char = StringPrototypeCharCodeAt(value, ++i);
} else { } else {