04:23
<bakkot>
function safePromiseCapability() {
  let { promise, resolve, reject } = Promise.withResolvers();
  function safeResolve(val) {
    if (Object.hasOwn(val, 'then')) throw new TypeError('you are not supposed to use this with thenables');
    Object.defineProperty(val, 'then', { configurable: true, value: void 0 });
    resolve(val);
    delete val.then;
  }
  return { promise, safeResolve, reject };
}

let evilProto = { get then() { throw 'boom'; } };

let { promise, safeResolve } = safePromiseCapability();
safeResolve({ __proto__: evilProto, key: 'value' });
console.log((await promise).key); // no boom

turns out we already have the "safe promise resolve" capability with no changes to the language

04:30
<TabAtkins>
I... don't understand how that's safe. Doesn't that override/delete a .then property on the element, potentially wiping out an existing .then property?
04:32
<bakkot>
sorry, yes, the assumption is that you are only using this with an object you've just created yourself, which you know not to have such a property
04:32
<bakkot>
and you're trying to avoid the problem where someone might have put a .then on Object.prototype
10:54
<nicolo-ribaudo>
This example makes my concern about having .then being called with a thenable (and thus .then(x => x) not being an identity) moot, since it's already possible
15:27
<Richard Gibson>
"only using this with an [extensible] object you've just created yourself" is an enormous restriction
15:39
<Aki>
TC39 your individual contributor RFTC license/permission form is so good I'm adapting it for all of Ecma
15:47
<nicolo-ribaudo>
Yes, but it's exactly the case that matters for the security problems in web specs that were presented
17:04
<bakkot>
I should say, basic idea is due to Justin Ridgewell https://github.com/tc39/proposal-thenable-curtailment/issues/5#issuecomment-3145520373
17:06
<bakkot>
"extensible" isn't necessary since you can make it non-extensible after doing this step, but yes, still a pretty significant restriction. but as nicolo says it does cover most of the actual CVEs
17:06
<bakkot>
also the "just" part of "just created yourself" can be dropped if you're willing to have it throw if the object has a .then property that the user put on it, which I think is a perfectly fine restriction; I updated my snippet to do that. as long as you know the object is one you created, even if you've since handed it out to a user (as in the Animation CVE in the repo) then you can rely on it not being a proxy and so the hasOwn check not triggering user code
17:12
<bakkot>
I wonder if we ought to do this for async iterator result objects https://github.com/tc39/Reflector/issues/535
17:32
<Justin Ridgewell>
There’s also the potential to introduce a Fulfilled wrapper which could short-circuit the resolution recursion.
17:34
<Justin Ridgewell>
await Promise.resolve(new Fulfilled(thenable)) === thenable