diff options
author | Antoine du Hamel <duhamelantoine1995@gmail.com> | 2022-12-10 17:51:51 +0100 |
---|---|---|
committer | Danielle Adams <adamzdanielle@gmail.com> | 2023-01-04 20:31:54 -0500 |
commit | 6a5b9c51bbe59835d3a8974a49a2e17100a85080 (patch) | |
tree | 1bb693e74b957d22adee68d44daf4d74be6da01c | |
parent | bf8f638958932f82825a0cf36de82f1014c9df09 (diff) | |
download | node-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.js | 56 |
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); } } } |