Skip to content

Commit 707dd77

Browse files
committed
readline: validate AbortSignals and remove unused event listeners
PR-URL: #37947 Fixes: #37287 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Robert Nagy <ronagy@icloud.com>
1 parent 8122d24 commit 707dd77

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed

lib/readline.js

+22-5
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const {
4848
inspect,
4949
} = require('internal/util/inspect');
5050
const { promisify } = require('internal/util');
51+
const { validateAbortSignal } = require('internal/validators');
5152

5253
/**
5354
* @typedef {import('./stream.js').Readable} Readable
@@ -130,13 +131,22 @@ Interface.prototype.question = function(query, options, cb) {
130131
options = typeof options === 'object' && options !== null ? options : {};
131132

132133
if (options.signal) {
134+
validateAbortSignal(options.signal, 'options.signal');
133135
if (options.signal.aborted) {
134136
return;
135137
}
136138

137-
options.signal.addEventListener('abort', () => {
139+
const onAbort = () => {
138140
this[kQuestionCancel]();
139-
}, { once: true });
141+
};
142+
options.signal.addEventListener('abort', onAbort, { once: true });
143+
const cleanup = () => {
144+
options.signal.removeEventListener(onAbort);
145+
};
146+
cb = typeof cb === 'function' ? (answer) => {
147+
cleanup();
148+
return cb(answer);
149+
} : cleanup;
140150
}
141151

142152
if (typeof cb === 'function') {
@@ -151,13 +161,20 @@ Interface.prototype.question[promisify.custom] = function(query, options) {
151161
}
152162

153163
return new Promise((resolve, reject) => {
154-
this.question(query, options, resolve);
164+
let cb = resolve;
155165

156166
if (options.signal) {
157-
options.signal.addEventListener('abort', () => {
167+
const onAbort = () => {
158168
reject(new AbortError());
159-
}, { once: true });
169+
};
170+
options.signal.addEventListener('abort', onAbort, { once: true });
171+
cb = (answer) => {
172+
options.signal.removeEventListener('abort', onAbort);
173+
resolve(answer);
174+
};
160175
}
176+
177+
this.question(query, options, cb);
161178
});
162179
};
163180

lib/readline/promises.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const {
1616
const {
1717
AbortError,
1818
} = require('internal/errors');
19+
const { validateAbortSignal } = require('internal/validators');
1920

2021
class Interface extends _Interface {
2122
// eslint-disable-next-line no-useless-constructor
@@ -24,18 +25,26 @@ class Interface extends _Interface {
2425
}
2526
question(query, options = {}) {
2627
return new Promise((resolve, reject) => {
27-
if (options.signal) {
28+
let cb = resolve;
29+
30+
if (options?.signal) {
31+
validateAbortSignal(options.signal, 'options.signal');
2832
if (options.signal.aborted) {
2933
return reject(new AbortError());
3034
}
3135

32-
options.signal.addEventListener('abort', () => {
36+
const onAbort = () => {
3337
this[kQuestionCancel]();
3438
reject(new AbortError());
35-
}, { once: true });
39+
};
40+
options.signal.addEventListener('abort', onAbort, { once: true });
41+
cb = (answer) => {
42+
options.signal.removeEventListener('abort', onAbort);
43+
resolve(answer);
44+
};
3645
}
3746

38-
super.question(query, resolve);
47+
super.question(query, cb);
3948
});
4049
}
4150
}

0 commit comments

Comments
 (0)