Skip to content

Commit ff03ed1

Browse files
aduh95danielleadams
authored andcommitted
readline: improve robustness against prototype mutation
PR-URL: #45614 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 979d837 commit ff03ed1

File tree

1 file changed

+34
-22
lines changed

1 file changed

+34
-22
lines changed

lib/internal/readline/interface.js

+34-22
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,
@@ -77,7 +74,7 @@ const kHistorySize = 30;
7774
const kMaxUndoRedoStackSize = 2048;
7875
const kMincrlfDelay = 100;
7976
// \r\n, \n, or \r followed by something other than \n
80-
const lineEnding = /\r?\n|\r(?!\n)/;
77+
const lineEnding = /\r?\n|\r(?!\n)/g;
8178

8279
const kLineObjectStream = Symbol('line object stream');
8380
const kQuestionCancel = Symbol('kQuestionCancel');
@@ -590,31 +587,40 @@ class Interface extends InterfaceConstructor {
590587
this[kSawReturnAt] &&
591588
DateNow() - this[kSawReturnAt] <= this.crlfDelay
592589
) {
593-
string = RegExpPrototypeSymbolReplace(/^\n/, string, '');
590+
if (StringPrototypeCodePointAt(string) === 10) string = StringPrototypeSlice(string, 1);
594591
this[kSawReturnAt] = 0;
595592
}
596593

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

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

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

0 commit comments

Comments
 (0)