Skip to content

Commit a55f5d5

Browse files
dario-piotrowiczRafaelGSS
authored andcommitted
readline: add stricter validation for functions called after closed
PR-URL: #57680 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Edy Silva <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]>
1 parent 87fd838 commit a55f5d5

7 files changed

+73
-14
lines changed

lib/internal/readline/interface.js

+9
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,9 @@ class Interface extends InterfaceConstructor {
599599
* @returns {void | Interface}
600600
*/
601601
pause() {
602+
if (this.closed) {
603+
throw new ERR_USE_AFTER_CLOSE('readline');
604+
}
602605
if (this.paused) return;
603606
this.input.pause();
604607
this.paused = true;
@@ -611,6 +614,9 @@ class Interface extends InterfaceConstructor {
611614
* @returns {void | Interface}
612615
*/
613616
resume() {
617+
if (this.closed) {
618+
throw new ERR_USE_AFTER_CLOSE('readline');
619+
}
614620
if (!this.paused) return;
615621
this.input.resume();
616622
this.paused = false;
@@ -631,6 +637,9 @@ class Interface extends InterfaceConstructor {
631637
* @returns {void}
632638
*/
633639
write(d, key) {
640+
if (this.closed) {
641+
throw new ERR_USE_AFTER_CLOSE('readline');
642+
}
634643
if (this.paused) this.resume();
635644
if (this.terminal) {
636645
this[kTtyWrite](d, key);

test/parallel/test-readline-interface.js

+41
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,47 @@ for (let i = 0; i < 12; i++) {
12021202
fi.emit('data', 'Node.js\n');
12031203
}
12041204

1205+
// Call write after close
1206+
{
1207+
const [rli, fi] = getInterface({ terminal });
1208+
rli.question('What\'s your name?', common.mustCall((name) => {
1209+
assert.strictEqual(name, 'Node.js');
1210+
rli.close();
1211+
assert.throws(() => {
1212+
rli.write('I said Node.js');
1213+
}, {
1214+
name: 'Error',
1215+
code: 'ERR_USE_AFTER_CLOSE'
1216+
});
1217+
}));
1218+
fi.emit('data', 'Node.js\n');
1219+
}
1220+
1221+
// Call pause/resume after close
1222+
{
1223+
const [rli, fi] = getInterface({ terminal });
1224+
rli.question('What\'s your name?', common.mustCall((name) => {
1225+
assert.strictEqual(name, 'Node.js');
1226+
rli.close();
1227+
// No 'resume' nor 'pause' event should be emitted after close
1228+
rli.on('resume', common.mustNotCall());
1229+
rli.on('pause', common.mustNotCall());
1230+
assert.throws(() => {
1231+
rli.pause();
1232+
}, {
1233+
name: 'Error',
1234+
code: 'ERR_USE_AFTER_CLOSE'
1235+
});
1236+
assert.throws(() => {
1237+
rli.resume();
1238+
}, {
1239+
name: 'Error',
1240+
code: 'ERR_USE_AFTER_CLOSE'
1241+
});
1242+
}));
1243+
fi.emit('data', 'Node.js\n');
1244+
}
1245+
12051246
// Can create a new readline Interface with a null output argument
12061247
{
12071248
const [rli, fi] = getInterface({ output: null, terminal });

test/parallel/test-readline-promises-interface.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ function assertCursorRowsAndCols(rli, rows, cols) {
204204
fi.emit('data', character);
205205
}
206206
fi.emit('data', '\n');
207-
rli.close();
207+
fi.end();
208208
}
209209

210210
// \t when there is no completer function should behave like an ordinary

test/parallel/test-readline-promises-tab-complete.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ if (process.env.TERM === 'dumb') {
8080
output = '';
8181
});
8282
}
83-
rli.close();
83+
fi.end();
8484
});
8585
});
8686
});
@@ -114,5 +114,5 @@ if (process.env.TERM === 'dumb') {
114114
assert.match(output, /^Tab completion error: Error: message/);
115115
output = '';
116116
});
117-
rli.close();
117+
fi.end();
118118
}

test/parallel/test-repl-import-referrer.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,24 @@ const args = ['--interactive'];
88
const opts = { cwd: fixtures.path('es-modules') };
99
const child = cp.spawn(process.execPath, args, opts);
1010

11-
let output = '';
11+
const outputs = [];
1212
child.stdout.setEncoding('utf8');
1313
child.stdout.on('data', (data) => {
14-
output += data;
14+
outputs.push(data);
15+
if (outputs.length === 3) {
16+
// All the expected outputs have been received
17+
// so we can close the child process's stdin
18+
child.stdin.end();
19+
}
1520
});
1621

1722
child.on('exit', common.mustCall(() => {
18-
const results = output.replace(/^> /mg, '').split('\n').slice(2);
19-
assert.deepStrictEqual(
23+
const results = outputs[2].split('\n')[0];
24+
assert.strictEqual(
2025
results,
21-
['[Module: null prototype] { message: \'A message\' }', '']
26+
'[Module: null prototype] { message: \'A message\' }'
2227
);
2328
}));
2429

2530
child.stdin.write('await import(\'./message.mjs\');\n');
2631
child.stdin.write('.exit');
27-
child.stdin.end();
+9-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
'use strict';
22
const common = require('../common');
3-
3+
const ArrayStream = require('../common/arraystream');
44
const repl = require('repl');
5-
const r = repl.start({ terminal: false });
6-
r.setupHistory('/nonexistent/file', common.mustSucceed());
7-
process.stdin.unref?.();
5+
6+
const stream = new ArrayStream();
7+
8+
const replServer = repl.start({ terminal: false, input: stream, output: stream });
9+
10+
replServer.setupHistory('/nonexistent/file', common.mustSucceed(() => {
11+
replServer.close();
12+
}));

test/parallel/test-repl-uncaught-exception-async.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ r.write(
3434
' throw new RangeError("abc");\n' +
3535
'}, 1);console.log()\n'
3636
);
37-
r.close();
3837

3938
setTimeout(() => {
39+
r.close();
4040
const len = process.listenerCount('uncaughtException');
4141
process.removeAllListeners('uncaughtException');
4242
assert.strictEqual(len, 0);

0 commit comments

Comments
 (0)