diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index c959c056fece45..8e14c4de8def8f 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -282,16 +282,14 @@ const meta = [ ]; // Regex used for ansi escape code splitting -// Ref: https://github.com/chalk/ansi-regex/blob/f338e1814144efb950276aac84135ff86b72dc8e/index.js +// Ref: https://github.com/chalk/ansi-regex/blob/72bc570/index.js // License: MIT by Sindre Sorhus // Matches all ansi escape code sequences in a string const ansi = new RegExp( - '[\\u001B\\u009B][[\\]()#;?]*' + - '(?:(?:(?:(?:;[-a-zA-Z\\d\\/\\#&.:=?%@~_]+)*' + - '|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/\\#&.:=?%@~_]*)*)?' + - '(?:\\u0007|\\u001B\\u005C|\\u009C))' + - '|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?' + - '[\\dA-PR-TZcf-nq-uy=><~]))', 'g', + '(?:\\u001B\\][\\s\\S]*?(?:\\u0007|\\u001B\\u005C|\\u009C))' + + '|[\\u001B\\u009B][[\\]()#;?]*' + + '(?:(?:\\d{1,4}(?:[;:]\\d{0,4})*)?' + + '[\\dA-PR-TZcf-nq-uy=><~])', 'g', ); let getStringWidth; diff --git a/test/parallel/test-util-stripvtcontrolcharacters.js b/test/parallel/test-util-stripvtcontrolcharacters.js index a33d18d26dbc51..0d23dd926aa579 100644 --- a/test/parallel/test-util-stripvtcontrolcharacters.js +++ b/test/parallel/test-util-stripvtcontrolcharacters.js @@ -18,9 +18,31 @@ for (const ST of ['\u0007', '\u001B\u005C', '\u009C']) { tests.push( [`\u001B]8;;mailto:no-replay@mail.com${ST}mail\u001B]8;;${ST}`, 'mail'], [`\u001B]8;k=v;https://example-a.com/?a_b=1&c=2#tit%20le${ST}click\u001B]8;;${ST}`, 'click'], + [`\u001B]8;;https://example.com/(foo${ST}label\u001B]8;;${ST}`, 'label'], + [`\u001B]8;;https://example.com/foo)bar${ST}label\u001B]8;;${ST}`, 'label'], + [`\u001B]8;;https://example.com/!foo${ST}label\u001B]8;;${ST}`, 'label'], + [`\u001B]8;;https://example.com/foo+bar${ST}label\u001B]8;;${ST}`, 'label'], + [`\u001B]8;;https://example.com/[foo]${ST}label\u001B]8;;${ST}`, 'label'], + [`\u001B]8;;https://example.com/foo$bar${ST}label\u001B]8;;${ST}`, 'label'], + [`\u001B]8;;https://example.com/foo'bar${ST}label\u001B]8;;${ST}`, 'label'], + [`\u001B]8;;https://example.com/foo*bar${ST}label\u001B]8;;${ST}`, 'label'], + [`\u001B]8;;https://example.com/foo,bar${ST}label\u001B]8;;${ST}`, 'label'], ); } +// Colon-delimited CSI sub-parameters (SGR) should be stripped like the +// semicolon-delimited form. +tests.push( + ['\u001B[38:2:255:0:0mHello\u001B[0m', 'Hello'], + ['\u001B[4:3mUnderline\u001B[4:0m', 'Underline'], +); + +// Malformed/truncated OSC sequences without any string terminator are left +// unmatched rather than being partially stripped. +tests.push( + ['\u001B]8;;https://example.com/no-terminator', 'ttps://example.com/no-terminator'], +); + test('util.stripVTControlCharacters', (t) => { for (const [before, expected] of tests) { t.assert.strictEqual(util.stripVTControlCharacters(before), expected);