diff options
-rw-r--r-- | cli/tests/unit/console_test.ts | 12 | ||||
-rw-r--r-- | cli/tests/unit/headers_test.ts | 15 | ||||
-rw-r--r-- | cli/tests/unit/urlpattern_test.ts | 15 | ||||
-rw-r--r-- | core/00_primordials.js | 9 | ||||
-rw-r--r-- | core/internal.d.ts | 1 | ||||
-rw-r--r-- | ext/console/01_colors.js | 6 | ||||
-rw-r--r-- | ext/console/02_console.js | 85 | ||||
-rw-r--r-- | ext/fetch/20_headers.js | 3 | ||||
-rw-r--r-- | ext/fetch/21_formdata.js | 34 | ||||
-rw-r--r-- | ext/fetch/23_response.js | 4 | ||||
-rw-r--r-- | ext/url/01_urlpattern.js | 4 | ||||
-rw-r--r-- | ext/web/00_infra.js | 20 | ||||
-rw-r--r-- | ext/web/09_file.js | 5 | ||||
-rw-r--r-- | ext/webidl/00_webidl.js | 7 | ||||
-rw-r--r-- | runtime/js/06_util.js | 11 |
15 files changed, 171 insertions, 60 deletions
diff --git a/cli/tests/unit/console_test.ts b/cli/tests/unit/console_test.ts index 239a8bf26..2b7426b99 100644 --- a/cli/tests/unit/console_test.ts +++ b/cli/tests/unit/console_test.ts @@ -2138,6 +2138,18 @@ Deno.test(async function inspectAggregateError() { } }); +Deno.test(function inspectWithPrototypePollution() { + const originalExec = RegExp.prototype.exec; + try { + RegExp.prototype.exec = () => { + throw Error(); + }; + Deno.inspect("foo"); + } finally { + RegExp.prototype.exec = originalExec; + } +}); + Deno.test(function inspectorMethods() { console.timeStamp("test"); console.profile("test"); diff --git a/cli/tests/unit/headers_test.ts b/cli/tests/unit/headers_test.ts index fda4e81d9..295f03071 100644 --- a/cli/tests/unit/headers_test.ts +++ b/cli/tests/unit/headers_test.ts @@ -325,6 +325,21 @@ Deno.test(function headersInitMultiple() { ]); }); +Deno.test(function headerInitWithPrototypePollution() { + const originalExec = RegExp.prototype.exec; + try { + RegExp.prototype.exec = () => { + throw Error(); + }; + new Headers([ + ["X-Deno", "foo"], + ["X-Deno", "bar"], + ]); + } finally { + RegExp.prototype.exec = originalExec; + } +}); + Deno.test(function headersAppendMultiple() { const headers = new Headers([ ["Set-Cookie", "foo=bar"], diff --git a/cli/tests/unit/urlpattern_test.ts b/cli/tests/unit/urlpattern_test.ts index 1ec64b2fe..9bed09235 100644 --- a/cli/tests/unit/urlpattern_test.ts +++ b/cli/tests/unit/urlpattern_test.ts @@ -43,3 +43,18 @@ Deno.test(function urlPatternFromInit() { assert(pattern.test({ pathname: "/foo/x" })); }); + +Deno.test(function urlPatternWithPrototypePollution() { + const originalExec = RegExp.prototype.exec; + try { + RegExp.prototype.exec = () => { + throw Error(); + }; + const pattern = new URLPattern({ + pathname: "/foo/:bar", + }); + assert(pattern.test("https://deno.land/foo/x")); + } finally { + RegExp.prototype.exec = originalExec; + } +}); diff --git a/core/00_primordials.js b/core/00_primordials.js index 6495a5268..243f40e88 100644 --- a/core/00_primordials.js +++ b/core/00_primordials.js @@ -434,6 +434,15 @@ }, ); + primordials.SafeRegExp = makeSafe( + RegExp, + class SafeRegExp extends RegExp { + constructor(pattern, flags) { + super(pattern, flags); + } + }, + ); + primordials.SafeFinalizationRegistry = makeSafe( FinalizationRegistry, class SafeFinalizationRegistry extends FinalizationRegistry { diff --git a/core/internal.d.ts b/core/internal.d.ts index 004e068ff..a91ac6244 100644 --- a/core/internal.d.ts +++ b/core/internal.d.ts @@ -78,6 +78,7 @@ declare namespace __bootstrap { export const SafePromisePrototypeFinally: UncurryThis< Promise.prototype.finally >; + export const SafeRegExp: typeof RegExp; // safe iterators export const SafeArrayIterator: new <T>(array: T[]) => IterableIterator<T>; diff --git a/ext/console/01_colors.js b/ext/console/01_colors.js index d01edd247..a598db921 100644 --- a/ext/console/01_colors.js +++ b/ext/console/01_colors.js @@ -4,7 +4,7 @@ const primordials = globalThis.__bootstrap.primordials; const { - RegExp, + SafeRegExp, StringPrototypeReplace, ArrayPrototypeJoin, } = primordials; @@ -23,7 +23,7 @@ function code(open, close) { return { open: `\x1b[${open}m`, close: `\x1b[${close}m`, - regexp: new RegExp(`\\x1b\\[${close}m`, "g"), + regexp: new SafeRegExp(`\\x1b\\[${close}m`, "g"), }; } @@ -74,7 +74,7 @@ function magenta(str) { } // https://github.com/chalk/ansi-regex/blob/02fa893d619d3da85411acc8fd4e2eea0e95a9d9/index.js -const ANSI_PATTERN = new RegExp( +const ANSI_PATTERN = new SafeRegExp( ArrayPrototypeJoin([ "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", diff --git a/ext/console/02_console.js b/ext/console/02_console.js index a8b335b94..6f708c71f 100644 --- a/ext/console/02_console.js +++ b/ext/console/02_console.js @@ -17,8 +17,9 @@ const { BooleanPrototype, BooleanPrototypeToString, ObjectKeys, - ObjectCreate, ObjectAssign, + ObjectCreate, + ObjectFreeze, ObjectIs, ObjectValues, ObjectFromEntries, @@ -49,13 +50,13 @@ const { TypeError, NumberIsInteger, NumberParseInt, - RegExp, RegExpPrototype, RegExpPrototypeTest, RegExpPrototypeToString, SafeArrayIterator, SafeStringIterator, SafeSet, + SafeRegExp, SetPrototype, SetPrototypeEntries, SetPrototypeGetSize, @@ -747,31 +748,34 @@ function quoteString(string) { const quote = ArrayPrototypeFind(QUOTES, (c) => !StringPrototypeIncludes(string, c)) ?? QUOTES[0]; - const escapePattern = new RegExp(`(?=[${quote}\\\\])`, "g"); + const escapePattern = new SafeRegExp(`(?=[${quote}\\\\])`, "g"); string = StringPrototypeReplace(string, escapePattern, "\\"); string = replaceEscapeSequences(string); return `${quote}${string}${quote}`; } +const ESCAPE_PATTERN = new SafeRegExp(/([\b\f\n\r\t\v])/g); +const ESCAPE_MAP = ObjectFreeze({ + "\b": "\\b", + "\f": "\\f", + "\n": "\\n", + "\r": "\\r", + "\t": "\\t", + "\v": "\\v", +}); + +// deno-lint-ignore no-control-regex +const ESCAPE_PATTERN2 = new SafeRegExp(/[\x00-\x1f\x7f-\x9f]/g); + // Replace escape sequences that can modify output. function replaceEscapeSequences(string) { - const escapeMap = { - "\b": "\\b", - "\f": "\\f", - "\n": "\\n", - "\r": "\\r", - "\t": "\\t", - "\v": "\\v", - }; - return StringPrototypeReplace( StringPrototypeReplace( string, - /([\b\f\n\r\t\v])/g, - (c) => escapeMap[c], + ESCAPE_PATTERN, + (c) => ESCAPE_MAP[c], ), - // deno-lint-ignore no-control-regex - /[\x00-\x1f\x7f-\x9f]/g, + new SafeRegExp(ESCAPE_PATTERN2), (c) => "\\x" + StringPrototypePadStart( @@ -782,22 +786,33 @@ function replaceEscapeSequences(string) { ); } +const QUOTE_STRING_PATTERN = new SafeRegExp(/^[a-zA-Z_][a-zA-Z_0-9]*$/); + // Surround a string with quotes when it is required (e.g the string not a valid identifier). function maybeQuoteString(string) { - if (RegExpPrototypeTest(/^[a-zA-Z_][a-zA-Z_0-9]*$/, string)) { + if ( + RegExpPrototypeTest(QUOTE_STRING_PATTERN, string) + ) { return replaceEscapeSequences(string); } return quoteString(string); } +const QUOTE_SYMBOL_REG = new SafeRegExp(/^[a-zA-Z_][a-zA-Z_.0-9]*$/); + // Surround a symbol's description in quotes when it is required (e.g the description has non printable characters). function maybeQuoteSymbol(symbol) { if (symbol.description === undefined) { return SymbolPrototypeToString(symbol); } - if (RegExpPrototypeTest(/^[a-zA-Z_][a-zA-Z_.0-9]*$/, symbol.description)) { + if ( + RegExpPrototypeTest( + QUOTE_SYMBOL_REG, + symbol.description, + ) + ) { return SymbolPrototypeToString(symbol); } @@ -980,6 +995,9 @@ function inspectRegExp(value, inspectOptions) { return red(RegExpPrototypeToString(value)); // RegExps are red } +const AGGREGATE_ERROR_HAS_AT_PATTERN = new SafeRegExp(/\s+at/); +const AGGREGATE_ERROR_NOT_EMPTY_LINE_PATTERN = new SafeRegExp(/^(?!\s*$)/gm); + function inspectError(value, cyan) { const causes = [value]; @@ -1012,7 +1030,7 @@ function inspectError(value, cyan) { const stackLines = StringPrototypeSplit(value.stack, "\n"); while (true) { const line = ArrayPrototypeShift(stackLines); - if (RegExpPrototypeTest(/\s+at/, line)) { + if (RegExpPrototypeTest(AGGREGATE_ERROR_HAS_AT_PATTERN, line)) { ArrayPrototypeUnshift(stackLines, line); break; } else if (typeof line === "undefined") { @@ -1028,7 +1046,7 @@ function inspectError(value, cyan) { (error) => StringPrototypeReplace( inspectArgs([error]), - /^(?!\s*$)/gm, + AGGREGATE_ERROR_NOT_EMPTY_LINE_PATTERN, StringPrototypeRepeat(" ", 4), ), ), @@ -1519,12 +1537,25 @@ const colorKeywords = new Map([ ["rebeccapurple", "#663399"], ]); +const HASH_PATTERN = new SafeRegExp( + /^#([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})?$/, +); +const SMALL_HASH_PATTERN = new SafeRegExp( + /^#([\dA-Fa-f])([\dA-Fa-f])([\dA-Fa-f])([\dA-Fa-f])?$/, +); +const RGB_PATTERN = new SafeRegExp( + /^rgba?\(\s*([+\-]?\d*\.?\d+)\s*,\s*([+\-]?\d*\.?\d+)\s*,\s*([+\-]?\d*\.?\d+)\s*(,\s*([+\-]?\d*\.?\d+)\s*)?\)$/, +); +const HSL_PATTERN = new SafeRegExp( + /^hsla?\(\s*([+\-]?\d*\.?\d+)\s*,\s*([+\-]?\d*\.?\d+)%\s*,\s*([+\-]?\d*\.?\d+)%\s*(,\s*([+\-]?\d*\.?\d+)\s*)?\)$/, +); + function parseCssColor(colorString) { if (MapPrototypeHas(colorKeywords, colorString)) { colorString = MapPrototypeGet(colorKeywords, colorString); } // deno-fmt-ignore - const hashMatch = StringPrototypeMatch(colorString, /^#([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})?$/); + const hashMatch = StringPrototypeMatch(colorString, HASH_PATTERN); if (hashMatch != null) { return [ Number(`0x${hashMatch[1]}`), @@ -1533,7 +1564,7 @@ function parseCssColor(colorString) { ]; } // deno-fmt-ignore - const smallHashMatch = StringPrototypeMatch(colorString, /^#([\dA-Fa-f])([\dA-Fa-f])([\dA-Fa-f])([\dA-Fa-f])?$/); + const smallHashMatch = StringPrototypeMatch(colorString, SMALL_HASH_PATTERN); if (smallHashMatch != null) { return [ Number(`0x${smallHashMatch[1]}0`), @@ -1542,7 +1573,7 @@ function parseCssColor(colorString) { ]; } // deno-fmt-ignore - const rgbMatch = StringPrototypeMatch(colorString, /^rgba?\(\s*([+\-]?\d*\.?\d+)\s*,\s*([+\-]?\d*\.?\d+)\s*,\s*([+\-]?\d*\.?\d+)\s*(,\s*([+\-]?\d*\.?\d+)\s*)?\)$/); + const rgbMatch = StringPrototypeMatch(colorString, RGB_PATTERN); if (rgbMatch != null) { return [ MathRound(MathMax(0, MathMin(255, Number(rgbMatch[1])))), @@ -1551,7 +1582,7 @@ function parseCssColor(colorString) { ]; } // deno-fmt-ignore - const hslMatch = StringPrototypeMatch(colorString, /^hsla?\(\s*([+\-]?\d*\.?\d+)\s*,\s*([+\-]?\d*\.?\d+)%\s*,\s*([+\-]?\d*\.?\d+)%\s*(,\s*([+\-]?\d*\.?\d+)\s*)?\)$/); + const hslMatch = StringPrototypeMatch(colorString, HSL_PATTERN); if (hslMatch != null) { // https://www.rapidtables.com/convert/color/hsl-to-rgb.html let h = Number(hslMatch[1]) % 360; @@ -1599,6 +1630,8 @@ function getDefaultCss() { }; } +const SPACE_PATTERN = new SafeRegExp(/\s+/g); + function parseCss(cssString) { const css = getDefaultCss(); @@ -1665,7 +1698,7 @@ function parseCss(cssString) { } } else if (key == "text-decoration-line") { css.textDecorationLine = []; - const lineTypes = StringPrototypeSplit(value, /\s+/g); + const lineTypes = StringPrototypeSplit(value, SPACE_PATTERN); for (let i = 0; i < lineTypes.length; ++i) { const lineType = lineTypes[i]; if ( @@ -1685,7 +1718,7 @@ function parseCss(cssString) { } else if (key == "text-decoration") { css.textDecorationColor = null; css.textDecorationLine = []; - const args = StringPrototypeSplit(value, /\s+/g); + const args = StringPrototypeSplit(value, SPACE_PATTERN); for (let i = 0; i < args.length; ++i) { const arg = args[i]; const maybeColor = parseCssColor(arg); diff --git a/ext/fetch/20_headers.js b/ext/fetch/20_headers.js index b8fd8ab83..a432e76f4 100644 --- a/ext/fetch/20_headers.js +++ b/ext/fetch/20_headers.js @@ -32,6 +32,7 @@ const { ObjectEntries, RegExpPrototypeTest, SafeArrayIterator, + SafeRegExp, Symbol, SymbolFor, SymbolIterator, @@ -88,7 +89,7 @@ function fillHeaders(headers, object) { // Regex matching illegal chars in a header value // deno-lint-ignore no-control-regex -const ILLEGAL_VALUE_CHARS = /[\x00\x0A\x0D]/; +const ILLEGAL_VALUE_CHARS = new SafeRegExp(/[\x00\x0A\x0D]/); /** * https://fetch.spec.whatwg.org/#concept-headers-append diff --git a/ext/fetch/21_formdata.js b/ext/fetch/21_formdata.js index 647c716b3..724511633 100644 --- a/ext/fetch/21_formdata.js +++ b/ext/fetch/21_formdata.js @@ -26,7 +26,9 @@ const { MapPrototypeGet, MapPrototypeSet, MathRandom, + ObjectFreeze, ObjectPrototypeIsPrototypeOf, + SafeRegExp, Symbol, StringFromCharCode, StringPrototypeTrim, @@ -277,19 +279,25 @@ webidl.mixinPairIterable("FormData", FormData, entryList, "name", "value"); webidl.configurePrototype(FormData); const FormDataPrototype = FormData.prototype; -const escape = (str, isFilename) => { - const escapeMap = { - "\n": "%0A", - "\r": "%0D", - '"': "%22", - }; +const ESCAPE_FILENAME_PATTERN = new SafeRegExp(/\r?\n|\r/g); +const ESCAPE_PATTERN = new SafeRegExp(/([\n\r"])/g); +const ESCAPE_MAP = ObjectFreeze({ + "\n": "%0A", + "\r": "%0D", + '"': "%22", +}); +function escape(str, isFilename) { return StringPrototypeReplace( - isFilename ? str : StringPrototypeReplace(str, /\r?\n|\r/g, "\r\n"), - /([\n\r"])/g, - (c) => escapeMap[c], + isFilename + ? str + : StringPrototypeReplace(str, ESCAPE_FILENAME_PATTERN, "\r\n"), + ESCAPE_PATTERN, + (c) => ESCAPE_MAP[c], ); -}; +} + +const FORM_DETA_SERIALIZE_PATTERN = new SafeRegExp(/\r(?!\n)|(?<!\r)\n/g); /** * convert FormData to a Blob synchronous without reading all of the files @@ -313,7 +321,11 @@ function formDataToBlob(formData) { ArrayPrototypePush( chunks, prefix + escape(name) + '"' + CRLF + CRLF + - StringPrototypeReplace(value, /\r(?!\n)|(?<!\r)\n/g, CRLF) + CRLF, + StringPrototypeReplace( + value, + FORM_DETA_SERIALIZE_PATTERN, + CRLF, + ) + CRLF, ); } else { ArrayPrototypePush( diff --git a/ext/fetch/23_response.js b/ext/fetch/23_response.js index 4b579a306..6e8955167 100644 --- a/ext/fetch/23_response.js +++ b/ext/fetch/23_response.js @@ -37,9 +37,9 @@ const { ObjectDefineProperties, ObjectPrototypeIsPrototypeOf, RangeError, - RegExp, RegExpPrototypeTest, SafeArrayIterator, + SafeRegExp, Symbol, SymbolFor, TypeError, @@ -54,7 +54,7 @@ const REASON_PHRASE = [ ...new SafeArrayIterator(OBS_TEXT), ]; const REASON_PHRASE_MATCHER = regexMatcher(REASON_PHRASE); -const REASON_PHRASE_RE = new RegExp(`^[${REASON_PHRASE_MATCHER}]*$`); +const REASON_PHRASE_RE = new SafeRegExp(`^[${REASON_PHRASE_MATCHER}]*$`); const _response = Symbol("response"); const _headers = Symbol("headers"); diff --git a/ext/url/01_urlpattern.js b/ext/url/01_urlpattern.js index c311b5abc..93b77ee6a 100644 --- a/ext/url/01_urlpattern.js +++ b/ext/url/01_urlpattern.js @@ -15,9 +15,9 @@ const { ArrayPrototypeMap, ObjectKeys, ObjectFromEntries, - RegExp, RegExpPrototypeExec, RegExpPrototypeTest, + SafeRegExp, Symbol, SymbolFor, TypeError, @@ -73,7 +73,7 @@ class URLPattern { for (let i = 0; i < keys.length; ++i) { const key = keys[i]; try { - components[key].regexp = new RegExp( + components[key].regexp = new SafeRegExp( components[key].regexpString, "u", ); diff --git a/ext/web/00_infra.js b/ext/web/00_infra.js index 0a8491563..cff0f66d8 100644 --- a/ext/web/00_infra.js +++ b/ext/web/00_infra.js @@ -15,8 +15,8 @@ const { Error, JSONStringify, NumberPrototypeToString, - RegExp, SafeArrayIterator, + SafeRegExp, String, StringPrototypeCharAt, StringPrototypeCharCodeAt, @@ -67,7 +67,7 @@ const HTTP_TOKEN_CODE_POINT = [ "\u007E", ...new SafeArrayIterator(ASCII_ALPHANUMERIC), ]; -const HTTP_TOKEN_CODE_POINT_RE = new RegExp( +const HTTP_TOKEN_CODE_POINT_RE = new SafeRegExp( `^[${regexMatcher(HTTP_TOKEN_CODE_POINT)}]+$`, ); const HTTP_QUOTED_STRING_TOKEN_POINT = [ @@ -75,27 +75,27 @@ const HTTP_QUOTED_STRING_TOKEN_POINT = [ "\u0020-\u007E", "\u0080-\u00FF", ]; -const HTTP_QUOTED_STRING_TOKEN_POINT_RE = new RegExp( +const HTTP_QUOTED_STRING_TOKEN_POINT_RE = new SafeRegExp( `^[${regexMatcher(HTTP_QUOTED_STRING_TOKEN_POINT)}]+$`, ); const HTTP_TAB_OR_SPACE_MATCHER = regexMatcher(HTTP_TAB_OR_SPACE); -const HTTP_TAB_OR_SPACE_PREFIX_RE = new RegExp( +const HTTP_TAB_OR_SPACE_PREFIX_RE = new SafeRegExp( `^[${HTTP_TAB_OR_SPACE_MATCHER}]+`, "g", ); -const HTTP_TAB_OR_SPACE_SUFFIX_RE = new RegExp( +const HTTP_TAB_OR_SPACE_SUFFIX_RE = new SafeRegExp( `[${HTTP_TAB_OR_SPACE_MATCHER}]+$`, "g", ); const HTTP_WHITESPACE_MATCHER = regexMatcher(HTTP_WHITESPACE); -const HTTP_BETWEEN_WHITESPACE = new RegExp( +const HTTP_BETWEEN_WHITESPACE = new SafeRegExp( `^[${HTTP_WHITESPACE_MATCHER}]*(.*?)[${HTTP_WHITESPACE_MATCHER}]*$`, ); -const HTTP_WHITESPACE_PREFIX_RE = new RegExp( +const HTTP_WHITESPACE_PREFIX_RE = new SafeRegExp( `^[${HTTP_WHITESPACE_MATCHER}]+`, "g", ); -const HTTP_WHITESPACE_SUFFIX_RE = new RegExp( +const HTTP_WHITESPACE_SUFFIX_RE = new SafeRegExp( `[${HTTP_WHITESPACE_MATCHER}]+$`, "g", ); @@ -150,6 +150,8 @@ function collectSequenceOfCodepoints(input, position, condition) { return { result: StringPrototypeSlice(input, start, position), position }; } +const LOWERCASE_PATTERN = new SafeRegExp(/[a-z]/g); + /** * @param {string} s * @returns {string} @@ -157,7 +159,7 @@ function collectSequenceOfCodepoints(input, position, condition) { function byteUpperCase(s) { return StringPrototypeReplace( String(s), - /[a-z]/g, + LOWERCASE_PATTERN, function byteUpperCaseReplace(c) { return StringPrototypeToUpperCase(c); }, diff --git a/ext/web/09_file.js b/ext/web/09_file.js index b44537dd4..c2fe7782c 100644 --- a/ext/web/09_file.js +++ b/ext/web/09_file.js @@ -29,6 +29,7 @@ const { RegExpPrototypeTest, // TODO(lucacasonato): add SharedArrayBuffer to primordials // SharedArrayBufferPrototype + SafeRegExp, StringPrototypeCharAt, StringPrototypeToLowerCase, StringPrototypeSlice, @@ -143,13 +144,15 @@ function processBlobParts(parts, endings) { return { parts: processedParts, size }; } +const NORMALIZE_PATTERN = new SafeRegExp(/^[\x20-\x7E]*$/); + /** * @param {string} str * @returns {string} */ function normalizeType(str) { let normalizedType = str; - if (!RegExpPrototypeTest(/^[\x20-\x7E]*$/, str)) { + if (!RegExpPrototypeTest(NORMALIZE_PATTERN, str)) { normalizedType = ""; } return StringPrototypeToLowerCase(normalizedType); diff --git a/ext/webidl/00_webidl.js b/ext/webidl/00_webidl.js index 46d0a2c1d..124c81c73 100644 --- a/ext/webidl/00_webidl.js +++ b/ext/webidl/00_webidl.js @@ -58,6 +58,7 @@ const { ReflectHas, ReflectOwnKeys, RegExpPrototypeTest, + SafeRegExp, Set, SetPrototypeEntries, SetPrototypeForEach, @@ -386,7 +387,7 @@ converters.DOMString = function (V, opts = {}) { }; // deno-lint-ignore no-control-regex -const IS_BYTE_STRING = /^[\x00-\xFF]*$/; +const IS_BYTE_STRING = new SafeRegExp(/^[\x00-\xFF]*$/); converters.ByteString = (V, opts) => { const x = converters.DOMString(V, opts); if (!RegExpPrototypeTest(IS_BYTE_STRING, x)) { @@ -500,7 +501,9 @@ ArrayPrototypeForEach( ], (func) => { const name = func.name; - const article = RegExpPrototypeTest(/^[AEIOU]/, name) ? "an" : "a"; + const article = RegExpPrototypeTest(new SafeRegExp(/^[AEIOU]/), name) + ? "an" + : "a"; converters[name] = (V, opts = {}) => { if (TypedArrayPrototypeGetSymbolToStringTag(V) !== name) { throw makeException( diff --git a/runtime/js/06_util.js b/runtime/js/06_util.js index 435a55a61..c9bd86285 100644 --- a/runtime/js/06_util.js +++ b/runtime/js/06_util.js @@ -7,6 +7,7 @@ const { ObjectPrototypeIsPrototypeOf, Promise, SafeArrayIterator, + SafeRegExp, StringPrototypeReplace, TypeError, } = primordials; @@ -49,7 +50,7 @@ function createResolvable() { function pathFromURLWin32(url) { let p = StringPrototypeReplace( url.pathname, - /^\/*([A-Za-z]:)(\/|$)/, + new SafeRegExp(/^\/*([A-Za-z]:)(\/|$)/), "$1/", ); p = StringPrototypeReplace( @@ -59,7 +60,7 @@ function pathFromURLWin32(url) { ); p = StringPrototypeReplace( p, - /%(?![0-9A-Fa-f]{2})/g, + new SafeRegExp(/%(?![0-9A-Fa-f]{2})/g), "%25", ); let path = decodeURIComponent(p); @@ -79,7 +80,11 @@ function pathFromURLPosix(url) { } return decodeURIComponent( - StringPrototypeReplace(url.pathname, /%(?![0-9A-Fa-f]{2})/g, "%25"), + StringPrototypeReplace( + url.pathname, + new SafeRegExp(/%(?![0-9A-Fa-f]{2})/g), + "%25", + ), ); } |