diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 1a029c7cf761..8e0b6eeed5f0 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -286,7 +286,17 @@ const meta = [ // License: MIT by Sindre Sorhus // Matches all ansi escape code sequences in a string const ansi = new RegExp( + // Sequences that run until a string terminator, such as OSC 8 hyperlinks. '(?:\\u001B\\][\\s\\S]*?(?:\\u0007|\\u001B\\u005C|\\u009C))' + + // Control sequences, using the structure ECMA-48 5.4 defines: CSI, then any + // number of parameter bytes (0x30-0x3F), then any number of intermediate + // bytes (0x20-0x2F), then a single final byte (0x40-0x7E). The list of final + // bytes below covers neither parameter bytes such as `<`, used by mouse + // reports, nor final bytes such as `@`, so it stops in the middle of the + // sequence and leaves the rest in the output. + '|(?:\\u001B\\[|\\u009B)[\\u0030-\\u003F]*[\\u0020-\\u002F]*[\\u0040-\\u007E]' + + // Everything else the reference implementation matched, including sequences + // whose last byte is a digit, such as `ESC 7`. '|[\\u001B\\u009B][[\\]()#;?]*' + '(?:\\d{1,4}(?:[;:]\\d{0,4})*)?' + '[\\dA-PR-TZcf-nq-uy=><~]', 'g', diff --git a/test/parallel/test-util-stripvtcontrolcharacters.js b/test/parallel/test-util-stripvtcontrolcharacters.js index efda16687821..022776362641 100644 --- a/test/parallel/test-util-stripvtcontrolcharacters.js +++ b/test/parallel/test-util-stripvtcontrolcharacters.js @@ -37,6 +37,27 @@ tests.push( ['\u001B[4:3mUnderline\u001B[4:0m', 'Underline'], ); +// Ref: ECMA-48 5.4. A control sequence is CSI, then any number of parameter +// bytes (0x30-0x3F), then any number of intermediate bytes (0x20-0x2F), then a +// single final byte (0x40-0x7E). Every sequence below is well formed and in use +// by terminals today, and every one of them has its tail left behind today. +tests.push( + // Parameter bytes other than digits, `;` and `:`. + ['a\u001B[<35;10;20Mb', 'ab'], // SGR mouse report + ['a\u001B[>1;2cb', 'ab'], // Secondary device attributes + ['a\u001B[=5hb', 'ab'], + ['a\u009B<35;10;20Mb', 'ab'], // Same, with the 8 bit CSI introducer + // Final bytes that the reference implementation does not list. + ['a\u001B[3@b', 'ab'], // ICH, insert character + ['a\u001B[3Xb', 'ab'], // ECH, erase character + ['a\u001B[5db', 'ab'], // VPA, line position absolute + ['a\u001B[3bb', 'ab'], // REP, repeat preceding character + ['a\u001B[2ab', 'ab'], // HPR, character position forward + // Intermediate bytes. + ['a\u001B[2 qb', 'ab'], // DECSCUSR, set cursor style + ['a\u001B[!pb', 'ab'], // DECSTR, soft terminal reset +); + // Unterminated OSC does not match the OSC alternative; the CSI alternative may // still consume a short prefix (here ESC ] 8 ;; h), leaving the remainder. tests.push(