From bf2187d141bb46a807174779f6cbd3a16eb680f1 Mon Sep 17 00:00:00 2001 From: Taco de Wolff Date: Mon, 19 Jun 2023 15:13:17 -0400 Subject: [PATCH] JS: don't convert \0,\x00,\u0000 to literal NULL as it breaks RegExp; convert \x00,\u0000 to \0 only if it can't possibly be an octal escape sequence (see #577); fixes #585 --- js/js_test.go | 11 ++++++----- js/util.go | 47 +++++++++++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/js/js_test.go b/js/js_test.go index 74eb3e3b63..6e0c2684c0 100644 --- a/js/js_test.go +++ b/js/js_test.go @@ -149,9 +149,10 @@ func TestJS(t *testing.T) { {`"string\t\f\v\bstring"`, "\"string\t\f\v\bstring\""}, {`"string\a\c\'string"`, `"stringac'string"`}, {`"string\∀string"`, `"string∀string"`}, - {`"string\0\uFFFFstring"`, "\"string\x00￿string\""}, - {`"string\x00\x55\x0A\x0D\x22\x27string"`, "\"string\x00U\\n\\r\\\"'string\""}, - {`"string\000\12\015\042\47\411string"`, "\"string\x00\\n\\r\\\"'!1string\""}, + {`"string\0\uFFFFstring"`, "\"string\\0\uffffstring\""}, + {`"string\x00\x55\x0A\x0D\x22\x27string"`, `"string\x00U\n\r\"'string"`}, + {`"string\000\12\015\042\47\411string"`, "\"string\\0\\n\\r\\\"'!1string\""}, + {`"\x005"`, `"\x005"`}, {"'string\\n\\rstring'", "`string\n\rstring`"}, {"'string\\\r\nstring\\\nstring\\\rstring\\\u2028string\\\u2029string'", `"stringstringstringstringstringstring"`}, {`"\x7H\u877H"`, `"\x7H\u877H"`}, @@ -796,8 +797,8 @@ func TestJS(t *testing.T) { {`var a=5;({});var b=class{c(){3}}`, `var b,a=5;({},b=class{c(){3}})`}, // #494 {`({});a={b(){3}}`, `({},a={b(){3}})`}, // #494 {`export default function Foo(){a}Foo.prototype.bar=b`, `export default function Foo(){a}Foo.prototype.bar=b`}, // #525 - {`(e=1,e=2)`, `e=1,e=2`}, // #528 - {`"\x00\x31 \0\u0000"`, "\"\x001 \x00\x00\""}, // #577 + {`(e=1,e=2)`, `e=1,e=2`}, // #528 + {`"\x00\x31 \0\u0000"`, `"\x001 \0\x00"`}, // #577 } m := minify.New() diff --git a/js/util.go b/js/util.go index b58f83e358..6883d93c9b 100644 --- a/js/util.go +++ b/js/util.go @@ -983,7 +983,7 @@ func replaceEscapes(b []byte, quote byte, prefix, suffix int) []byte { for i := prefix; i < len(b)-suffix; i++ { if c := b[i]; c == '\\' { c = b[i+1] - if c == quote || c == '\\' || quote != '`' && (c == 'n' || c == 'r') { + if c == quote || c == '\\' || quote != '`' && (c == 'n' || c == 'r') || c == '0' && (i+2 == len(b)-1 || b[i+2] < '0' || '7' < b[i+2]) { // keep escape sequence i++ continue @@ -999,11 +999,12 @@ func replaceEscapes(b []byte, quote byte, prefix, suffix int) []byte { n = 2 } } else if c == 'x' { - if i+3 < len(b)-1 && isHexDigit(b[i+2]) && b[i+2] < '8' && isHexDigit(b[i+3]) { + if i+3 < len(b)-1 && isHexDigit(b[i+2]) && b[i+2] < '8' && isHexDigit(b[i+3]) && (!(b[i+2] == '0' && b[i+3] == '0') || i+3 == len(b) || b[i+3] != '\\' && (b[i+3] < '0' && '7' < b[i+3])) { + // don't convert \x00 to \0 if it may be an octal number // hexadecimal escapes _, _ = hex.Decode(b[i+3:i+4:i+4], b[i+2:i+4]) n = 3 - if b[i+3] == '\\' || b[i+3] == quote || b[i+3] == '\n' || b[i+3] == '\r' { + if b[i+3] == '\\' || b[i+3] == quote || b[i+3] == '\n' || b[i+3] == '\r' || b[i+3] == 0 { if b[i+3] == '\n' { b[i+3] = 'n' } else if b[i+3] == '\r' { @@ -1037,22 +1038,32 @@ func replaceEscapes(b []byte, quote byte, prefix, suffix int) []byte { continue } - // decode unicode character to UTF-8 and put at the end of the escape sequence - // then skip the first part of the escape sequence until the decoded character - n = 2 + r - l - if b[i+2] == '{' { - n += 2 - } - m := utf8.RuneLen(rune(num)) - if m == -1 { - i++ - continue + if num == 0 { + // don't convert NULL to literal NULL (gives JS parsing problems) + if r == len(b) || b[r] != '\\' && (b[r] < '0' && '7' < b[r]) { + b[r-2] = '\\' + n = r - l + } else { + // don't convert NULL to \0 (may be an octal number) + b[r-4] = '\\' + b[r-3] = 'x' + n = r - l - 2 + } + } else { + // decode unicode character to UTF-8 and put at the end of the escape sequence + // then skip the first part of the escape sequence until the decoded character + n = 2 + r - l + if b[i+2] == '{' { + n += 2 + } + m := utf8.RuneLen(rune(num)) + if m == -1 { + i++ + continue + } + utf8.EncodeRune(b[i+n-m:], rune(num)) + n -= m } - utf8.EncodeRune(b[i+n-m:], rune(num)) - n -= m - } else if c == '0' && (i+2 == len(b)-1 || b[i+2] < '0' || '7' < b[i+2]) { - // \0 (NULL) - b[i+1] = '\x00' } else if '0' <= c && c <= '7' { // octal escapes (legacy), \0 already handled num := c - '0'