Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d1c9d80

Browse filesBrowse files
meixgmarco-ippolito
authored andcommitted
repl: add isValidParentheses check before wrap input
PR-URL: #59607 Backport-PR-URL: #60066 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 1788fb5 commit d1c9d80
Copy full SHA for d1c9d80

File tree

Expand file treeCollapse file tree

4 files changed

+121
-4
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+121
-4
lines changed
Open diff view settings
Collapse file

‎lib/internal/repl/utils.js‎

Copy file name to clipboardExpand all lines: lib/internal/repl/utils.js
+21-1Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
296296
function getInputPreview(input, callback) {
297297
// For similar reasons as `defaultEval`, wrap expressions starting with a
298298
// curly brace with parenthesis.
299-
if (!wrapped && input[0] === '{' && input[input.length - 1] !== ';') {
299+
if (!wrapped && input[0] === '{' && input[input.length - 1] !== ';' && isValidSyntax(input)) {
300300
input = `(${input})`;
301301
wrapped = true;
302302
}
@@ -741,6 +741,25 @@ function setupReverseSearch(repl) {
741741

742742
const startsWithBraceRegExp = /^\s*{/;
743743
const endsWithSemicolonRegExp = /;\s*$/;
744+
function isValidSyntax(input) {
745+
try {
746+
AcornParser.parse(input, {
747+
ecmaVersion: 'latest',
748+
allowAwaitOutsideFunction: true,
749+
});
750+
return true;
751+
} catch {
752+
try {
753+
AcornParser.parse(`_=${input}`, {
754+
ecmaVersion: 'latest',
755+
allowAwaitOutsideFunction: true,
756+
});
757+
return true;
758+
} catch {
759+
return false;
760+
}
761+
}
762+
}
744763

745764
/**
746765
* Checks if some provided code represents an object literal.
@@ -763,4 +782,5 @@ module.exports = {
763782
setupPreview,
764783
setupReverseSearch,
765784
isObjectLiteral,
785+
isValidSyntax,
766786
};
Collapse file

‎lib/repl.js‎

Copy file name to clipboardExpand all lines: lib/repl.js
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ const {
172172
setupPreview,
173173
setupReverseSearch,
174174
isObjectLiteral,
175+
isValidSyntax,
175176
} = require('internal/repl/utils');
176177
const {
177178
constants: {
@@ -442,7 +443,7 @@ function REPLServer(prompt,
442443
let awaitPromise = false;
443444
const input = code;
444445

445-
if (isObjectLiteral(code)) {
446+
if (isObjectLiteral(code) && isValidSyntax(code)) {
446447
// Add parentheses to make sure `code` is parsed as an expression
447448
code = `(${StringPrototypeTrim(code)})\n`;
448449
wrappedCmd = true;
@@ -1859,6 +1860,7 @@ module.exports = {
18591860
REPL_MODE_SLOPPY,
18601861
REPL_MODE_STRICT,
18611862
Recoverable,
1863+
isValidSyntax,
18621864
};
18631865

18641866
ObjectDefineProperty(module.exports, 'builtinModules', {
Collapse file

‎test/parallel/test-repl-preview.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-repl-preview.js
+84-2Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,83 @@ async function tests(options) {
157157
'\x1B[90m1\x1B[39m\x1B[12G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
158158
'\x1B[33m1\x1B[39m',
159159
]
160+
}, {
161+
input: 'aaaa',
162+
noPreview: 'Uncaught ReferenceError: aaaa is not defined',
163+
preview: [
164+
'aaaa\r',
165+
'Uncaught ReferenceError: aaaa is not defined',
166+
]
167+
}, {
168+
input: '/0',
169+
noPreview: '/0',
170+
preview: [
171+
'/0\r',
172+
'/0',
173+
'^',
174+
'',
175+
'Uncaught SyntaxError: Invalid regular expression: missing /',
176+
]
177+
}, {
178+
input: '{})',
179+
noPreview: '{})',
180+
preview: [
181+
'{})\r',
182+
'{})',
183+
' ^',
184+
'',
185+
"Uncaught SyntaxError: Unexpected token ')'",
186+
],
187+
}, {
188+
input: "{ a: '{' }",
189+
noPreview: "{ a: \x1B[32m'{'\x1B[39m }",
190+
preview: [
191+
"{ a: '{' }\r",
192+
"{ a: \x1B[32m'{'\x1B[39m }",
193+
],
194+
}, {
195+
input: "{'{':0}",
196+
noPreview: "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }",
197+
preview: [
198+
"{'{':0}",
199+
"\x1B[90m{ '{': 0 }\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r",
200+
"{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }",
201+
],
202+
}, {
203+
input: '{[Symbol.for("{")]: 0 }',
204+
noPreview: '{ [\x1B[32mSymbol({)\x1B[39m]: \x1B[33m0\x1B[39m }',
205+
preview: [
206+
'{[Symbol.for("{")]: 0 }\r',
207+
'{ [\x1B[32mSymbol({)\x1B[39m]: \x1B[33m0\x1B[39m }',
208+
],
209+
}, {
210+
input: '{},{}',
211+
noPreview: '{}',
212+
preview: [
213+
'{},{}',
214+
'\x1B[90m{}\x1B[39m\x1B[13G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
215+
'{}',
216+
],
217+
}, {
218+
input: '{} //',
219+
noPreview: 'repl > ',
220+
preview: [
221+
'{} //\r',
222+
],
223+
}, {
224+
input: '{} //;',
225+
noPreview: 'repl > ',
226+
preview: [
227+
'{} //;\r',
228+
],
229+
}, {
230+
input: '{throw 0}',
231+
noPreview: 'Uncaught \x1B[33m0\x1B[39m',
232+
preview: [
233+
'{throw 0}',
234+
'\x1B[90m0\x1B[39m\x1B[17G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
235+
'Uncaught \x1B[33m0\x1B[39m',
236+
],
160237
}];
161238

162239
const hasPreview = repl.terminal &&
@@ -177,8 +254,13 @@ async function tests(options) {
177254
assert.deepStrictEqual(lines, preview);
178255
} else {
179256
assert.ok(lines[0].includes(noPreview), lines.map(inspect));
180-
if (preview.length !== 1 || preview[0] !== `${input}\r`)
181-
assert.strictEqual(lines.length, 2);
257+
if (preview.length !== 1 || preview[0] !== `${input}\r`) {
258+
if (preview[preview.length - 1].includes('Uncaught SyntaxError')) {
259+
assert.strictEqual(lines.length, 5);
260+
} else {
261+
assert.strictEqual(lines.length, 2);
262+
}
263+
}
182264
}
183265
}
184266
}
Collapse file

‎test/parallel/test-repl.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-repl.js
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,19 @@ const errorTests = [
321321
expect: '[Function (anonymous)]'
322322
},
323323
// Multiline object
324+
{
325+
send: '{}),({}',
326+
expect: '... ',
327+
},
328+
{
329+
send: '}',
330+
expect: [
331+
'{}),({}',
332+
kArrow,
333+
'',
334+
/^Uncaught SyntaxError: /,
335+
]
336+
},
324337
{
325338
send: '{ a: ',
326339
expect: '... '

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.