Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 29814dc

Browse files
committed
Make the check for ZoneAwarePromise more stringent
`resolvePromise` assumes that if a value is an `instanceof` ZoneAwarePromise then it has the properties "__zone_symbol__state" and "__zone_symbol__value" and it _is_ a true ZoneAwarePromise; however, a user can construct a value that breaks this assumption by inheriting from ZoneAwarePromise without actually having those properties or being a true ZoneAwarePromise (for example, by attempting to subclass Promise). We can fix this by adding checks for "__zone_symbol__state" and "__zone_symbol__value" to `resolvePromise`.
1 parent 21935e8 commit 29814dc

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

lib/zone.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,10 @@ const Zone: ZoneType = (function(global: any) {
10301030
function resolvePromise(
10311031
promise: ZoneAwarePromise<any>, state: boolean, value: any): ZoneAwarePromise<any> {
10321032
if (promise[symbolState] === UNRESOLVED) {
1033-
if (value instanceof ZoneAwarePromise && value[symbolState] !== UNRESOLVED) {
1033+
if (value instanceof ZoneAwarePromise &&
1034+
value.hasOwnProperty(symbolState) &&
1035+
value.hasOwnProperty(symbolValue) &&
1036+
value[symbolState] !== UNRESOLVED) {
10341037
clearRejectedNoCatch(<Promise<any>>value);
10351038
resolvePromise(promise, value[symbolState], value[symbolValue]);
10361039
} else if (isThenable(value)) {

test/common/Promise.spec.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,53 @@ describe(
331331
});
332332
});
333333

334+
describe('Promise subclasses', function() {
335+
function MyPromise(init) {
336+
this._promise = new Promise(init);
337+
}
338+
339+
MyPromise.prototype.catch = function _catch() {
340+
return this._promise.catch.apply(this._promise, arguments);
341+
};
342+
343+
MyPromise.prototype.then = function then() {
344+
return this._promise.then.apply(this._promise, arguments);
345+
};
346+
347+
var setPrototypeOf = (Object as any).setPrototypeOf || function(obj, proto) {
348+
obj.__proto__ = proto;
349+
return obj;
350+
}
351+
352+
setPrototypeOf(MyPromise.prototype, Promise.prototype);
353+
354+
it('should reject if the Promise subclass rejects', function() {
355+
var myPromise = new MyPromise(function(resolve, reject) {
356+
reject('foo');
357+
});
358+
359+
return Promise.resolve().then(function() {
360+
return myPromise;
361+
}).then(function() {
362+
throw new Error('Unexpected resolution');
363+
}, function(result) {
364+
expect(result).toBe('foo');
365+
});
366+
});
367+
368+
it('should resolve if the Promise subclass resolves', function() {
369+
var myPromise = new MyPromise(function(resolve, reject) {
370+
resolve('foo');
371+
});
372+
373+
return Promise.resolve().then(function() {
374+
return myPromise;
375+
}).then(function(result) {
376+
expect(result).toBe('foo');
377+
});
378+
});
379+
});
380+
334381
describe('fetch', ifEnvSupports('fetch', function() {
335382
it('should work for text response', function(done) {
336383
testZone.run(function() {

0 commit comments

Comments
 (0)