summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/tests/unit/console_test.ts12
-rw-r--r--cli/tests/unit/headers_test.ts15
-rw-r--r--cli/tests/unit/urlpattern_test.ts15
-rw-r--r--core/00_primordials.js9
-rw-r--r--core/internal.d.ts1
-rw-r--r--ext/console/01_colors.js6
-rw-r--r--ext/console/02_console.js85
-rw-r--r--ext/fetch/20_headers.js3
-rw-r--r--ext/fetch/21_formdata.js34
-rw-r--r--ext/fetch/23_response.js4
-rw-r--r--ext/url/01_urlpattern.js4
-rw-r--r--ext/web/00_infra.js20
-rw-r--r--ext/web/09_file.js5
-rw-r--r--ext/webidl/00_webidl.js7
-rw-r--r--runtime/js/06_util.js11
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",
+ ),
);
}