Skip to content

Commit 6962ef0

Browse files
aduh95targos
authored andcommitted
readline: improve robustness against prototype mutation
PR-URL: #45614 Reviewed-By: James M Snell <[email protected]>
1 parent 691f58e commit 6962ef0

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');
@@ -591,31 +588,40 @@ class Interface extends InterfaceConstructor {
591588
this[kSawReturnAt] &&
592589
DateNow() - this[kSawReturnAt] <= this.crlfDelay
593590
) {
594-
string = RegExpPrototypeSymbolReplace(/^\n/, string, '');
591+
if (StringPrototypeCodePointAt(string) === 10) string = StringPrototypeSlice(string, 1);
595592
this[kSawReturnAt] = 0;
596593
}
597594

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

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

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

0 commit comments

Comments
 (0)