Skip to content

Commit

Permalink
lib: add brand checks to AbortController and AbortSignal
Browse files Browse the repository at this point in the history
PR-URL: #37720
Backport-PR-URL: #38386
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
  • Loading branch information
MattiasBuelens authored and targos committed Apr 30, 2021
1 parent 397d937 commit b1507c4
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 3 deletions.
32 changes: 29 additions & 3 deletions lib/internal/abort_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ const {
emitExperimentalWarning
} = require('internal/util');
const { inspect } = require('internal/util/inspect');
const {
codes: {
ERR_INVALID_THIS,
}
} = require('internal/errors');

const kAborted = Symbol('kAborted');

Expand All @@ -37,13 +42,21 @@ function customInspect(self, obj, depth, options) {
return `${self.constructor.name} ${inspect(obj, opts)}`;
}

function validateAbortSignal(obj) {
if (obj?.[kAborted] === undefined)
throw new ERR_INVALID_THIS('AbortSignal');
}

class AbortSignal extends EventTarget {
constructor() {
// eslint-disable-next-line no-restricted-syntax
throw new TypeError('Illegal constructor');
}

get aborted() { return !!this[kAborted]; }
get aborted() {
validateAbortSignal(this);
return !!this[kAborted];
}

[customInspectSymbol](depth, options) {
return customInspect(this, {
Expand Down Expand Up @@ -89,14 +102,27 @@ function abortSignal(signal) {
// initializers for now:
// https://bugs.chromium.org/p/v8/issues/detail?id=10704
const kSignal = Symbol('signal');

function validateAbortController(obj) {
if (obj?.[kSignal] === undefined)
throw new ERR_INVALID_THIS('AbortController');
}

class AbortController {
constructor() {
this[kSignal] = createAbortSignal();
emitExperimentalWarning('AbortController');
}

get signal() { return this[kSignal]; }
abort() { abortSignal(this[kSignal]); }
get signal() {
validateAbortController(this);
return this[kSignal];
}

abort() {
validateAbortController(this);
abortSignal(this[kSignal]);
}

[customInspectSymbol](depth, options) {
return customInspect(this, {
Expand Down
60 changes: 60 additions & 0 deletions test/parallel/test-abortcontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,63 @@ const { Event } = require('internal/event_target');
const signal = AbortSignal.abort();
ok(signal.aborted);
}

{
// Test that AbortController properties and methods validate the receiver
const acSignalGet = Object.getOwnPropertyDescriptor(
AbortController.prototype,
'signal'
).get;
const acAbort = AbortController.prototype.abort;

const goodController = new AbortController();
ok(acSignalGet.call(goodController));
acAbort.call(goodController);

const badAbortControllers = [
null,
undefined,
0,
NaN,
true,
'AbortController',
Object.create(AbortController.prototype)
];
for (const badController of badAbortControllers) {
throws(
() => acSignalGet.call(badController),
{ code: 'ERR_INVALID_THIS', name: 'TypeError' }
);
throws(
() => acAbort.call(badController),
{ code: 'ERR_INVALID_THIS', name: 'TypeError' }
);
}
}

{
// Test that AbortSignal properties validate the receiver
const signalAbortedGet = Object.getOwnPropertyDescriptor(
AbortSignal.prototype,
'aborted'
).get;

const goodSignal = new AbortController().signal;
strictEqual(signalAbortedGet.call(goodSignal), false);

const badAbortSignals = [
null,
undefined,
0,
NaN,
true,
'AbortSignal',
Object.create(AbortSignal.prototype)
];
for (const badSignal of badAbortSignals) {
throws(
() => signalAbortedGet.call(badSignal),
{ code: 'ERR_INVALID_THIS', name: 'TypeError' }
);
}
}

0 comments on commit b1507c4

Please sign in to comment.