Skip to content

Commit c172310

Browse files
authored
readline: improve robustness against prototype mutation
PR-URL: #45614 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 3ff724d commit c172310

File tree

1 file changed

+34
-22
lines changed

1 file changed

+34
-22
lines changed

lib/internal/readline/interface.js

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,10 @@ const {
2222
NumberIsNaN,
2323
ObjectSetPrototypeOf,
2424
RegExpPrototypeExec,
25-
RegExpPrototypeSymbolReplace,
26-
RegExpPrototypeSymbolSplit,
2725
StringPrototypeCodePointAt,
2826
StringPrototypeEndsWith,
2927
StringPrototypeRepeat,
3028
StringPrototypeSlice,
31-
StringPrototypeSplit,
3229
StringPrototypeStartsWith,
3330
StringPrototypeTrim,
3431
Symbol,
@@ -75,7 +72,7 @@ const kHistorySize = 30;
7572
const kMaxUndoRedoStackSize = 2048;
7673
const kMincrlfDelay = 100;
7774
// \r\n, \n, or \r followed by something other than \n
78-
const lineEnding = /\r?\n|\r(?!\n)/;
75+
const lineEnding = /\r?\n|\r(?!\n)/g;
7976

8077
const kLineObjectStream = Symbol('line object stream');
8178
const kQuestionCancel = Symbol('kQuestionCancel');
@@ -589,31 +586,40 @@ class Interface extends InterfaceConstructor {
589586
this[kSawReturnAt] &&
590587
DateNow() - this[kSawReturnAt] <= this.crlfDelay
591588
) {
592-
string = RegExpPrototypeSymbolReplace(/^\n/, string, '');
589+
if (StringPrototypeCodePointAt(string) === 10) string = StringPrototypeSlice(string, 1);
593590
this[kSawReturnAt] = 0;
594591
}
595592

596593
// Run test() on the new string chunk, not on the entire line buffer.
597-
const newPartContainsEnding = RegExpPrototypeExec(lineEnding, string) !== null;
598-
599-
if (this[kLine_buffer]) {
600-
string = this[kLine_buffer] + string;
601-
this[kLine_buffer] = null;
602-
}
603-
if (newPartContainsEnding) {
594+
let newPartContainsEnding = RegExpPrototypeExec(lineEnding, string);
595+
if (newPartContainsEnding !== null) {
596+
if (this[kLine_buffer]) {
597+
string = this[kLine_buffer] + string;
598+
this[kLine_buffer] = null;
599+
newPartContainsEnding = RegExpPrototypeExec(lineEnding, string);
600+
}
604601
this[kSawReturnAt] = StringPrototypeEndsWith(string, '\r') ?
605602
DateNow() :
606603
0;
607604

608-
// Got one or more newlines; process into "line" events
609-
const lines = StringPrototypeSplit(string, lineEnding);
605+
const indexes = [0, newPartContainsEnding.index, lineEnding.lastIndex];
606+
let nextMatch;
607+
while ((nextMatch = RegExpPrototypeExec(lineEnding, string)) !== null) {
608+
ArrayPrototypePush(indexes, nextMatch.index, lineEnding.lastIndex);
609+
}
610+
const lastIndex = indexes.length - 1;
610611
// Either '' or (conceivably) the unfinished portion of the next line
611-
string = ArrayPrototypePop(lines);
612-
this[kLine_buffer] = string;
613-
for (let n = 0; n < lines.length; n++) this[kOnLine](lines[n]);
612+
this[kLine_buffer] = StringPrototypeSlice(string, indexes[lastIndex]);
613+
for (let i = 1; i < lastIndex; i += 2) {
614+
this[kOnLine](StringPrototypeSlice(string, indexes[i - 1], indexes[i]));
615+
}
614616
} else if (string) {
615617
// No newlines this time, save what we have for next time
616-
this[kLine_buffer] = string;
618+
if (this[kLine_buffer]) {
619+
this[kLine_buffer] += string;
620+
} else {
621+
this[kLine_buffer] = string;
622+
}
617623
}
618624
}
619625

@@ -1321,12 +1327,18 @@ class Interface extends InterfaceConstructor {
13211327
// falls through
13221328
default:
13231329
if (typeof s === 'string' && s) {
1324-
const lines = RegExpPrototypeSymbolSplit(/\r\n|\n|\r/, s);
1325-
for (let i = 0, len = lines.length; i < len; i++) {
1326-
if (i > 0) {
1330+
let nextMatch = RegExpPrototypeExec(lineEnding, s);
1331+
if (nextMatch !== null) {
1332+
this[kInsertString](StringPrototypeSlice(s, 0, nextMatch.index));
1333+
let { lastIndex } = lineEnding;
1334+
while ((nextMatch = RegExpPrototypeExec(lineEnding, s)) !== null) {
13271335
this[kLine]();
1336+
this[kInsertString](StringPrototypeSlice(s, lastIndex, nextMatch.index));
1337+
({ lastIndex } = lineEnding);
13281338
}
1329-
this[kInsertString](lines[i]);
1339+
if (lastIndex === s.length) this[kLine]();
1340+
} else {
1341+
this[kInsertString](s);
13301342
}
13311343
}
13321344
}

0 commit comments

Comments
 (0)