summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine du Hamel <duhamelantoine1995@gmail.com>2022-12-10 17:51:51 +0100
committerDanielle Adams <adamzdanielle@gmail.com>2023-01-04 20:31:54 -0500
commit6a5b9c51bbe59835d3a8974a49a2e17100a85080 (patch)
tree1bb693e74b957d22adee68d44daf4d74be6da01c
parentbf8f638958932f82825a0cf36de82f1014c9df09 (diff)
downloadnode-new-6a5b9c51bbe59835d3a8974a49a2e17100a85080.tar.gz
readline: improve robustness against prototype mutation
PR-URL: https://github.com/nodejs/node/pull/45614 Reviewed-By: James M Snell <jasnell@gmail.com>
-rw-r--r--lib/internal/readline/interface.js56
1 files changed, 34 insertions, 22 deletions
diff --git a/lib/internal/readline/interface.js b/lib/internal/readline/interface.js
index 41e84099cf..937f426615 100644
--- a/lib/internal/readline/interface.js
+++ b/lib/internal/readline/interface.js
@@ -22,13 +22,10 @@ const {
NumberIsNaN,
ObjectSetPrototypeOf,
RegExpPrototypeExec,
- RegExpPrototypeSymbolReplace,
- RegExpPrototypeSymbolSplit,
StringPrototypeCodePointAt,
StringPrototypeEndsWith,
StringPrototypeRepeat,
StringPrototypeSlice,
- StringPrototypeSplit,
StringPrototypeStartsWith,
StringPrototypeTrim,
Symbol,
@@ -77,7 +74,7 @@ const kHistorySize = 30;
const kMaxUndoRedoStackSize = 2048;
const kMincrlfDelay = 100;
// \r\n, \n, or \r followed by something other than \n
-const lineEnding = /\r?\n|\r(?!\n)/;
+const lineEnding = /\r?\n|\r(?!\n)/g;
const kLineObjectStream = Symbol('line object stream');
const kQuestionCancel = Symbol('kQuestionCancel');
@@ -590,31 +587,40 @@ class Interface extends InterfaceConstructor {
this[kSawReturnAt] &&
DateNow() - this[kSawReturnAt] <= this.crlfDelay
) {
- string = RegExpPrototypeSymbolReplace(/^\n/, string, '');
+ if (StringPrototypeCodePointAt(string) === 10) string = StringPrototypeSlice(string, 1);
this[kSawReturnAt] = 0;
}
// Run test() on the new string chunk, not on the entire line buffer.
- const newPartContainsEnding = RegExpPrototypeExec(lineEnding, string) !== null;
-
- if (this[kLine_buffer]) {
- string = this[kLine_buffer] + string;
- this[kLine_buffer] = null;
- }
- if (newPartContainsEnding) {
+ let newPartContainsEnding = RegExpPrototypeExec(lineEnding, string);
+ if (newPartContainsEnding !== null) {
+ if (this[kLine_buffer]) {
+ string = this[kLine_buffer] + string;
+ this[kLine_buffer] = null;
+ newPartContainsEnding = RegExpPrototypeExec(lineEnding, string);
+ }
this[kSawReturnAt] = StringPrototypeEndsWith(string, '\r') ?
DateNow() :
0;
- // Got one or more newlines; process into "line" events
- const lines = StringPrototypeSplit(string, lineEnding);
+ const indexes = [0, newPartContainsEnding.index, lineEnding.lastIndex];
+ let nextMatch;
+ while ((nextMatch = RegExpPrototypeExec(lineEnding, string)) !== null) {
+ ArrayPrototypePush(indexes, nextMatch.index, lineEnding.lastIndex);
+ }
+ const lastIndex = indexes.length - 1;
// Either '' or (conceivably) the unfinished portion of the next line
- string = ArrayPrototypePop(lines);
- this[kLine_buffer] = string;
- for (let n = 0; n < lines.length; n++) this[kOnLine](lines[n]);
+ this[kLine_buffer] = StringPrototypeSlice(string, indexes[lastIndex]);
+ for (let i = 1; i < lastIndex; i += 2) {
+ this[kOnLine](StringPrototypeSlice(string, indexes[i - 1], indexes[i]));
+ }
} else if (string) {
// No newlines this time, save what we have for next time
- this[kLine_buffer] = string;
+ if (this[kLine_buffer]) {
+ this[kLine_buffer] += string;
+ } else {
+ this[kLine_buffer] = string;
+ }
}
}
@@ -1322,12 +1328,18 @@ class Interface extends InterfaceConstructor {
// falls through
default:
if (typeof s === 'string' && s) {
- const lines = RegExpPrototypeSymbolSplit(/\r\n|\n|\r/, s);
- for (let i = 0, len = lines.length; i < len; i++) {
- if (i > 0) {
+ let nextMatch = RegExpPrototypeExec(lineEnding, s);
+ if (nextMatch !== null) {
+ this[kInsertString](StringPrototypeSlice(s, 0, nextMatch.index));
+ let { lastIndex } = lineEnding;
+ while ((nextMatch = RegExpPrototypeExec(lineEnding, s)) !== null) {
this[kLine]();
+ this[kInsertString](StringPrototypeSlice(s, lastIndex, nextMatch.index));
+ ({ lastIndex } = lineEnding);
}
- this[kInsertString](lines[i]);
+ if (lastIndex === s.length) this[kLine]();
+ } else {
+ this[kInsertString](s);
}
}
}