diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index e0d8fc4..66c1d4e 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -10550,12 +10550,7 @@ exports.defaultUserAgent = defaultUserAgent; /***/ }), /***/ 4274: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -var xmlParser = __nccwpck_require__(3343); +/***/ ((__unused_webpack_module, exports) => { const ATTR_ESCAPE_RE = /[&<>"]/g; const ATTR_ESCAPE_MAP = { @@ -10681,40 +10676,254 @@ class XmlNode { } } -exports.parseXML = xmlParser.parseXML; +function writeKey(obj) { + Object.defineProperty(obj, "__proto__", { value: undefined, writable: true, enumerable: true, configurable: true }); +} +function parseXML(xml) { + const state = new AwsXmlParser(xml); + return state.parse(); +} +class AwsXmlParser { + x; + i = 0; + z; + constructor(x) { + this.x = x; + this.x = x.replace(/\r\n?/g, "\n"); + this.z = this.x.length; + } + parse() { + const p = this; + const { z } = p; + while (p.i < z) { + p.trim(); + if (p.i >= z) { + break; + } + if (p.isNext(""); + p.trim(); + } + else if (p.isNext(""); + p.trim(); + } + else if (p.isNext("/".includes(p.x[p.i])) { + tag += p.x[p.i++]; + } + let hasAttrs = false; + const attrs = {}; + while (p.i < p.z) { + p.trim(); + if (">/".includes(p.x[p.i])) { + break; + } + let name = ""; + while (p.i < p.z && !"= \t\r\n>/?".includes(p.x[p.i])) { + name += p.x[p.i++]; + } + p.trim(); + if (p.x[p.i] !== "=") { + break; + } + ++p.i; + p.trim(); + if (name === "__proto__") { + writeKey(attrs); + } + attrs[name] = p.readAttrValue(); + hasAttrs = true; + } + if (p.i >= p.z) { + throw new Error("@aws-sdk XML parse error: unexpected end of input."); + } + if (p.x[p.i] === "/") { + ++p.i; + if (p.i >= p.z || p.x[p.i] !== ">") { + throw new Error("@aws-sdk XML parse error: expected > at the end of self-closing tag."); + } + ++p.i; + return { tag, value: hasAttrs ? attrs : "" }; + } + if (p.x[p.i] !== ">") { + throw new Error("@aws-sdk XML parse error: expected > at the end of opening tag."); + } + ++p.i; + const textParts = []; + const childTags = []; + let hasElementChild = false; + while (p.i < p.z) { + if (p.isNext(""); + } + else if (p.isNext("")); + } + else if (p.isNext(""); + } + else { + hasElementChild = true; + childTags.push(p.parseTag()); + } + } + else { + let text = ""; + while (p.i < p.z && p.x[p.i] !== "<") { + text += p.x[p.i++]; + } + textParts.push(p.decodeEntities(text)); + } + } + if (!p.isNext(".`); + } + p.i += 2; + const closeTag = p.readTo(">").trim(); + if (closeTag !== tag) { + throw new Error(`@aws-sdk XML parse error: mismatched tags <${tag}> and .`); + } + if (!hasAttrs && textParts.length === 0 && !hasElementChild) { + return { tag, value: "" }; + } + if (!hasAttrs && !hasElementChild) { + const text = textParts.length === 1 ? textParts[0] : textParts.join(""); + if (text.trim() === "" && text.includes("\n")) { + return { tag, value: "" }; + } + return { tag, value: text }; + } + const obj = {}; + for (const text of textParts) { + if (text.trim() === "" && text.includes("\n")) { + continue; + } + obj["#text"] = "#text" in obj ? obj["#text"] + text : text; + } + for (const child of childTags) { + if (child.tag === "__proto__") { + writeKey(obj); + } + if (child.tag in obj) { + if (Array.isArray(obj[child.tag])) { + obj[child.tag].push(child.value); + } + else { + obj[child.tag] = [obj[child.tag], child.value]; + } + } + else { + obj[child.tag] = child.value; + } + } + for (const [k, v] of Object.entries(attrs)) { + if (k === "__proto__") { + writeKey(obj); + } + obj[k] = v; + } + return { tag, value: obj }; + } + static ENTITIES = { + amp: "&", + lt: "<", + gt: ">", + quot: '"', + apos: "'", + }; + skipDoctype() { + const p = this; + p.i += 9; + let depth = 0; + while (p.i < p.z) { + const c = p.x[p.i]; + if (c === "[") { + ++depth; + } + else if (c === "]") { + --depth; + } + else if (c === ">" && depth === 0) { + ++p.i; + return; + } + ++p.i; + } + throw new Error("@aws-sdk XML parse error: unclosed DOCTYPE."); + } + decodeEntities(s) { + return s.replace(/&(?:#x([0-9a-fA-F]{1,6})|#(\d{1,7})|([a-zA-Z][a-zA-Z0-9]{0,30}));/g, (_, hex, dec, named) => { + if (hex) { + return String.fromCharCode(parseInt(hex, 16)); + } + if (dec) { + return String.fromCharCode(parseInt(dec, 10)); + } + return AwsXmlParser.ENTITIES[named] ?? ""; + }); + } +} + exports.XmlNode = XmlNode; exports.XmlText = XmlText; - - -/***/ }), - -/***/ 3343: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); exports.parseXML = parseXML; -const fast_xml_parser_1 = __nccwpck_require__(591); -const parser = new fast_xml_parser_1.XMLParser({ - attributeNamePrefix: "", - processEntities: { - enabled: true, - maxTotalExpansions: Infinity, - }, - htmlEntities: true, - ignoreAttributes: false, - ignoreDeclaration: true, - parseTagValue: false, - trimValues: false, - tagValueProcessor: (_, val) => (val.trim() === "" && val.includes("\n") ? "" : undefined), - maxNestedTags: Infinity, -}); -parser.addEntity("#xD", "\r"); -parser.addEntity("#10", "\n"); -function parseXML(xmlString) { - return parser.parse(xmlString, true); -} /***/ }), @@ -25254,46 +25463,43 @@ Object.keys(serde).forEach(function (k) { /***/ 690: /***/ ((__unused_webpack_module, exports) => { -"use strict"; - - -exports.HttpAuthLocation = void 0; +var HttpAuthLocation; (function (HttpAuthLocation) { HttpAuthLocation["HEADER"] = "header"; HttpAuthLocation["QUERY"] = "query"; -})(exports.HttpAuthLocation || (exports.HttpAuthLocation = {})); +})(HttpAuthLocation || (HttpAuthLocation = {})); -exports.HttpApiKeyAuthLocation = void 0; +var HttpApiKeyAuthLocation; (function (HttpApiKeyAuthLocation) { HttpApiKeyAuthLocation["HEADER"] = "header"; HttpApiKeyAuthLocation["QUERY"] = "query"; -})(exports.HttpApiKeyAuthLocation || (exports.HttpApiKeyAuthLocation = {})); +})(HttpApiKeyAuthLocation || (HttpApiKeyAuthLocation = {})); -exports.EndpointURLScheme = void 0; +var EndpointURLScheme; (function (EndpointURLScheme) { EndpointURLScheme["HTTP"] = "http"; EndpointURLScheme["HTTPS"] = "https"; -})(exports.EndpointURLScheme || (exports.EndpointURLScheme = {})); +})(EndpointURLScheme || (EndpointURLScheme = {})); -exports.AlgorithmId = void 0; +var AlgorithmId; (function (AlgorithmId) { AlgorithmId["MD5"] = "md5"; AlgorithmId["CRC32"] = "crc32"; AlgorithmId["CRC32C"] = "crc32c"; AlgorithmId["SHA1"] = "sha1"; AlgorithmId["SHA256"] = "sha256"; -})(exports.AlgorithmId || (exports.AlgorithmId = {})); +})(AlgorithmId || (AlgorithmId = {})); const getChecksumConfiguration = (runtimeConfig) => { const checksumAlgorithms = []; if (runtimeConfig.sha256 !== undefined) { checksumAlgorithms.push({ - algorithmId: () => exports.AlgorithmId.SHA256, + algorithmId: () => AlgorithmId.SHA256, checksumConstructor: () => runtimeConfig.sha256, }); } if (runtimeConfig.md5 != undefined) { checksumAlgorithms.push({ - algorithmId: () => exports.AlgorithmId.MD5, + algorithmId: () => AlgorithmId.MD5, checksumConstructor: () => runtimeConfig.md5, }); } @@ -25321,28 +25527,35 @@ const resolveDefaultRuntimeConfig = (config) => { return resolveChecksumRuntimeConfig(config); }; -exports.FieldPosition = void 0; +var FieldPosition; (function (FieldPosition) { FieldPosition[FieldPosition["HEADER"] = 0] = "HEADER"; FieldPosition[FieldPosition["TRAILER"] = 1] = "TRAILER"; -})(exports.FieldPosition || (exports.FieldPosition = {})); +})(FieldPosition || (FieldPosition = {})); const SMITHY_CONTEXT_KEY = "__smithy_context"; -exports.IniSectionType = void 0; +var IniSectionType; (function (IniSectionType) { IniSectionType["PROFILE"] = "profile"; IniSectionType["SSO_SESSION"] = "sso-session"; IniSectionType["SERVICES"] = "services"; -})(exports.IniSectionType || (exports.IniSectionType = {})); +})(IniSectionType || (IniSectionType = {})); -exports.RequestHandlerProtocol = void 0; +var RequestHandlerProtocol; (function (RequestHandlerProtocol) { RequestHandlerProtocol["HTTP_0_9"] = "http/0.9"; RequestHandlerProtocol["HTTP_1_0"] = "http/1.0"; RequestHandlerProtocol["TDS_8_0"] = "tds/8.0"; -})(exports.RequestHandlerProtocol || (exports.RequestHandlerProtocol = {})); - +})(RequestHandlerProtocol || (RequestHandlerProtocol = {})); + +exports.AlgorithmId = AlgorithmId; +exports.EndpointURLScheme = EndpointURLScheme; +exports.FieldPosition = FieldPosition; +exports.HttpApiKeyAuthLocation = HttpApiKeyAuthLocation; +exports.HttpAuthLocation = HttpAuthLocation; +exports.IniSectionType = IniSectionType; +exports.RequestHandlerProtocol = RequestHandlerProtocol; exports.SMITHY_CONTEXT_KEY = SMITHY_CONTEXT_KEY; exports.getDefaultClientConfiguration = getDefaultClientConfiguration; exports.resolveDefaultRuntimeConfig = resolveDefaultRuntimeConfig; @@ -42316,19 +42529,7 @@ const DEFAULT_DELIMITER = "/"; const NOOP_VALUE = (value) => value; const ID_START = /^[$_\p{ID_Start}]$/u; const ID_CONTINUE = /^[$\u200c\u200d\p{ID_Continue}]$/u; -const SIMPLE_TOKENS = { - // Groups. - "{": "{", - "}": "}", - // Reserved. - "(": "(", - ")": ")", - "[": "[", - "]": "]", - "+": "+", - "?": "?", - "!": "!", -}; +const ID = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u; /** * Escape text for stringify to path. */ @@ -42371,96 +42572,91 @@ __webpack_unused_export__ = PathError; function parse(str, options = {}) { const { encodePath = NOOP_VALUE } = options; const chars = [...str]; - const tokens = []; let index = 0; - let pos = 0; - function name() { - let value = ""; - if (ID_START.test(chars[index])) { - do { - value += chars[index++]; - } while (ID_CONTINUE.test(chars[index])); - } - else if (chars[index] === '"') { - let quoteStart = index; - while (index++ < chars.length) { - if (chars[index] === '"') { - index++; - quoteStart = 0; - break; - } - // Increment over escape characters. - if (chars[index] === "\\") - index++; - value += chars[index]; - } - if (quoteStart) { - throw new PathError(`Unterminated quote at index ${quoteStart}`, str); - } - } - if (!value) { - throw new PathError(`Missing parameter name at index ${index}`, str); - } - return value; - } - while (index < chars.length) { - const value = chars[index]; - const type = SIMPLE_TOKENS[value]; - if (type) { - tokens.push({ type, index: index++, value }); - } - else if (value === "\\") { - tokens.push({ type: "escape", index: index++, value: chars[index++] }); - } - else if (value === ":") { - tokens.push({ type: "param", index: index++, value: name() }); - } - else if (value === "*") { - tokens.push({ type: "wildcard", index: index++, value: name() }); - } - else { - tokens.push({ type: "char", index: index++, value }); - } - } - tokens.push({ type: "end", index, value: "" }); - function consumeUntil(endType) { + function consumeUntil(end) { const output = []; - while (true) { - const token = tokens[pos++]; - if (token.type === endType) - break; - if (token.type === "char" || token.type === "escape") { - let path = token.value; - let cur = tokens[pos]; - while (cur.type === "char" || cur.type === "escape") { - path += cur.value; - cur = tokens[++pos]; + let path = ""; + function writePath() { + if (!path) + return; + output.push({ + type: "text", + value: encodePath(path), + }); + path = ""; + } + while (index < chars.length) { + const value = chars[index++]; + if (value === end) { + writePath(); + return output; + } + if (value === "\\") { + if (index === chars.length) { + throw new PathError(`Unexpected end after \\ at index ${index}`, str); } - output.push({ - type: "text", - value: encodePath(path), - }); + path += chars[index++]; continue; } - if (token.type === "param" || token.type === "wildcard") { - output.push({ - type: token.type, - name: token.value, - }); + if (value === ":" || value === "*") { + const type = value === ":" ? "param" : "wildcard"; + let name = ""; + if (ID_START.test(chars[index])) { + do { + name += chars[index++]; + } while (ID_CONTINUE.test(chars[index])); + } + else if (chars[index] === '"') { + let quoteStart = index; + while (index < chars.length) { + if (chars[++index] === '"') { + index++; + quoteStart = 0; + break; + } + // Increment over escape characters. + if (chars[index] === "\\") + index++; + name += chars[index]; + } + if (quoteStart) { + throw new PathError(`Unterminated quote at index ${quoteStart}`, str); + } + } + if (!name) { + throw new PathError(`Missing parameter name at index ${index}`, str); + } + writePath(); + output.push({ type, name }); continue; } - if (token.type === "{") { + if (value === "{") { + writePath(); output.push({ type: "group", tokens: consumeUntil("}"), }); continue; } - throw new PathError(`Unexpected ${token.type} at index ${token.index}, expected ${endType}`, str); + if (value === "}" || + value === "(" || + value === ")" || + value === "[" || + value === "]" || + value === "+" || + value === "?" || + value === "!") { + throw new PathError(`Unexpected ${value} at index ${index - 1}`, str); + } + path += value; } + if (end) { + throw new PathError(`Unexpected end at index ${index}, expected ${end}`, str); + } + writePath(); return output; } - return new TokenData(consumeUntil("end"), str); + return new TokenData(consumeUntil(""), str); } /** * Compile a string to a template function for the path. @@ -42470,7 +42666,8 @@ function compile(path, options = {}) { const data = typeof path === "object" ? path : parse(path, options); const fn = tokensToFunction(data.tokens, delimiter, encode); return function path(params = {}) { - const [path, ...missing] = fn(params); + const missing = []; + const path = fn(params, missing); if (missing.length) { throw new TypeError(`Missing parameters: ${missing.join(", ")}`); } @@ -42479,12 +42676,10 @@ function compile(path, options = {}) { } function tokensToFunction(tokens, delimiter, encode) { const encoders = tokens.map((token) => tokenToFunction(token, delimiter, encode)); - return (data) => { - const result = [""]; + return (data, missing) => { + let result = ""; for (const encoder of encoders) { - const [value, ...extras] = encoder(data); - result[0] += value; - result.push(...extras); + result += encoder(data, missing); } return result; }; @@ -42494,45 +42689,51 @@ function tokensToFunction(tokens, delimiter, encode) { */ function tokenToFunction(token, delimiter, encode) { if (token.type === "text") - return () => [token.value]; + return () => token.value; if (token.type === "group") { const fn = tokensToFunction(token.tokens, delimiter, encode); - return (data) => { - const [value, ...missing] = fn(data); - if (!missing.length) - return [value]; - return [""]; + return (data, missing) => { + const len = missing.length; + const value = fn(data, missing); + if (missing.length === len) + return value; + missing.length = len; // Reset optional group. + return ""; }; } const encodeValue = encode || NOOP_VALUE; if (token.type === "wildcard" && encode !== false) { - return (data) => { + return (data, missing) => { const value = data[token.name]; - if (value == null) - return ["", token.name]; + if (value == null) { + missing.push(token.name); + return ""; + } if (!Array.isArray(value) || value.length === 0) { throw new TypeError(`Expected "${token.name}" to be a non-empty array`); } - return [ - value - .map((value, index) => { - if (typeof value !== "string") { - throw new TypeError(`Expected "${token.name}/${index}" to be a string`); - } - return encodeValue(value); - }) - .join(delimiter), - ]; + let result = ""; + for (let i = 0; i < value.length; i++) { + if (typeof value[i] !== "string") { + throw new TypeError(`Expected "${token.name}/${i}" to be a string`); + } + if (i > 0) + result += delimiter; + result += encodeValue(value[i]); + } + return result; }; } - return (data) => { + return (data, missing) => { const value = data[token.name]; - if (value == null) - return ["", token.name]; + if (value == null) { + missing.push(token.name); + return ""; + } if (typeof value !== "string") { throw new TypeError(`Expected "${token.name}" to be a string`); } - return [encodeValue(value)]; + return encodeValue(value); }; } /** @@ -42564,54 +42765,53 @@ function match(path, options = {}) { return { path, params }; }; } +/** + * Transform a path into a regular expression and capture keys. + */ function pathToRegexp(path, options = {}) { const { delimiter = DEFAULT_DELIMITER, end = true, sensitive = false, trailing = true, } = options; const keys = []; - const flags = sensitive ? "" : "i"; - const sources = []; - for (const input of pathsToArray(path, [])) { - const data = typeof input === "object" ? input : parse(input, options); - for (const tokens of flatten(data.tokens, 0, [])) { - sources.push(toRegExpSource(tokens, delimiter, keys, data.originalPath)); + let source = ""; + let combinations = 0; + function process(path) { + if (Array.isArray(path)) { + for (const p of path) + process(p); + return; } + const data = typeof path === "object" ? path : parse(path, options); + flatten(data.tokens, 0, [], (tokens) => { + if (combinations >= 256) { + throw new PathError("Too many path combinations", data.originalPath); + } + if (combinations > 0) + source += "|"; + source += toRegExpSource(tokens, delimiter, keys, data.originalPath); + combinations++; + }); } - let pattern = `^(?:${sources.join("|")})`; + process(path); + let pattern = `^(?:${source})`; if (trailing) - pattern += `(?:${escape(delimiter)}$)?`; - pattern += end ? "$" : `(?=${escape(delimiter)}|$)`; - const regexp = new RegExp(pattern, flags); - return { regexp, keys }; -} -/** - * Convert a path or array of paths into a flat array. - */ -function pathsToArray(paths, init) { - if (Array.isArray(paths)) { - for (const p of paths) - pathsToArray(p, init); - } - else { - init.push(paths); - } - return init; + pattern += "(?:" + escape(delimiter) + "$)?"; + pattern += end ? "$" : "(?=" + escape(delimiter) + "|$)"; + return { regexp: new RegExp(pattern, sensitive ? "" : "i"), keys }; } /** * Generate a flat list of sequence tokens from the given tokens. */ -function* flatten(tokens, index, init) { - if (index === tokens.length) { - return yield init; - } - const token = tokens[index]; - if (token.type === "group") { - for (const seq of flatten(token.tokens, 0, init.slice())) { - yield* flatten(tokens, index + 1, seq); +function flatten(tokens, index, result, callback) { + while (index < tokens.length) { + const token = tokens[index++]; + if (token.type === "group") { + const len = result.length; + flatten(token.tokens, 0, result, (seq) => flatten(tokens, index, seq, callback)); + result.length = len; + continue; } + result.push(token); } - else { - init.push(token); - } - yield* flatten(tokens, index + 1, init); + callback(result); } /** * Transform a flat sequence of tokens into a regular expression. @@ -42619,72 +42819,111 @@ function* flatten(tokens, index, init) { function toRegExpSource(tokens, delimiter, keys, originalPath) { let result = ""; let backtrack = ""; - let isSafeSegmentParam = true; - for (const token of tokens) { + let wildcardBacktrack = ""; + let prevCaptureType = 0; + let hasSegmentCapture = 0; + let index = 0; + function hasInSegment(index, type) { + while (index < tokens.length) { + const token = tokens[index++]; + if (token.type === type) + return true; + if (token.type === "text") { + if (token.value.includes(delimiter)) + break; + } + } + return false; + } + function peekText(index) { + let result = ""; + while (index < tokens.length) { + const token = tokens[index++]; + if (token.type !== "text") + break; + result += token.value; + } + return result; + } + while (index < tokens.length) { + const token = tokens[index++]; if (token.type === "text") { result += escape(token.value); backtrack += token.value; - isSafeSegmentParam || (isSafeSegmentParam = token.value.includes(delimiter)); + if (prevCaptureType === 2) + wildcardBacktrack += token.value; + if (token.value.includes(delimiter)) + hasSegmentCapture = 0; continue; } if (token.type === "param" || token.type === "wildcard") { - if (!isSafeSegmentParam && !backtrack) { + if (prevCaptureType && !backtrack) { throw new PathError(`Missing text before "${token.name}" ${token.type}`, originalPath); } if (token.type === "param") { - result += `(${negate(delimiter, isSafeSegmentParam ? "" : backtrack)}+)`; + result += + hasSegmentCapture & 2 // Seen wildcard in segment. + ? `(${negate(delimiter, backtrack)}+)` + : hasInSegment(index, "wildcard") // See wildcard later in segment. + ? `(${negate(delimiter, peekText(index))}+)` + : hasSegmentCapture & 1 // Seen parameter in segment. + ? `(${negate(delimiter, backtrack)}+|${escape(backtrack)})` + : `(${negate(delimiter, "")}+)`; + hasSegmentCapture |= prevCaptureType = 1; } else { - result += `([\\s\\S]+)`; + result += + hasSegmentCapture & 2 // Seen wildcard in segment. + ? `(${negate(backtrack, "")}+)` + : wildcardBacktrack // No capture in segment, seen wildcard in path. + ? `(${negate(wildcardBacktrack, "")}+|${negate(delimiter, "")}+)` + : `([^]+)`; + wildcardBacktrack = ""; + hasSegmentCapture |= prevCaptureType = 2; } keys.push(token); backtrack = ""; - isSafeSegmentParam = false; continue; } + throw new TypeError(`Unknown token type: ${token.type}`); } return result; } /** - * Block backtracking on previous text and ignore delimiter string. - */ -function negate(delimiter, backtrack) { - if (backtrack.length < 2) { - if (delimiter.length < 2) - return `[^${escape(delimiter + backtrack)}]`; - return `(?:(?!${escape(delimiter)})[^${escape(backtrack)}])`; - } - if (delimiter.length < 2) { - return `(?:(?!${escape(backtrack)})[^${escape(delimiter)}])`; - } - return `(?:(?!${escape(backtrack)}|${escape(delimiter)})[\\s\\S])`; + * Block backtracking on previous text/delimiter. + */ +function negate(a, b) { + if (b.length > a.length) + return negate(b, a); // Longest string first. + if (a === b) + b = ""; // Cleaner regex strings, no duplication. + if (b.length > 1) + return `(?:(?!${escape(a)}|${escape(b)})[^])`; + if (a.length > 1) + return `(?:(?!${escape(a)})[^${escape(b)}])`; + return `[^${escape(a + b)}]`; } /** * Stringify an array of tokens into a path string. */ -function stringifyTokens(tokens) { +function stringifyTokens(tokens, index) { let value = ""; - let i = 0; - function name(value) { - const isSafe = isNameSafe(value) && isNextNameSafe(tokens[i]); - return isSafe ? value : JSON.stringify(value); - } - while (i < tokens.length) { - const token = tokens[i++]; + while (index < tokens.length) { + const token = tokens[index++]; if (token.type === "text") { value += escapeText(token.value); continue; } if (token.type === "group") { - value += `{${stringifyTokens(token.tokens)}}`; + value += "{" + stringifyTokens(token.tokens, 0) + "}"; continue; } if (token.type === "param") { - value += `:${name(token.name)}`; + value += ":" + stringifyName(token.name, tokens[index]); continue; } if (token.type === "wildcard") { - value += `*${name(token.name)}`; + value += "*" + stringifyName(token.name, tokens[index]); continue; } throw new TypeError(`Unknown token type: ${token.type}`); @@ -42695,22 +42934,18 @@ function stringifyTokens(tokens) { * Stringify token data into a path string. */ function stringify(data) { - return stringifyTokens(data.tokens); + return stringifyTokens(data.tokens, 0); } /** - * Validate the parameter name contains valid ID characters. + * Stringify a parameter name, escaping when it cannot be emitted directly. */ -function isNameSafe(name) { - const [first, ...rest] = name; - return ID_START.test(first) && rest.every((char) => ID_CONTINUE.test(char)); -} -/** - * Validate the next token does not interfere with the current param name. - */ -function isNextNameSafe(token) { - if (token && token.type === "text") - return !ID_CONTINUE.test(token.value[0]); - return true; +function stringifyName(name, next) { + if (!ID.test(name)) + return JSON.stringify(name); + if ((next === null || next === void 0 ? void 0 : next.type) === "text" && ID_CONTINUE.test(next.value[0])) { + return JSON.stringify(name); + } + return name; } //# sourceMappingURL=index.js.map @@ -53349,7 +53584,13 @@ function processHeader (request, key, val) { } else if (typeof val[i] === 'object') { throw new InvalidArgumentError(`invalid ${key} header`) } else { - arr.push(`${val[i]}`) + // Coerce primitives (and reject unsafe coercions such as functions + // with a crafted toString/Symbol.toPrimitive). + const str = `${val[i]}` + if (!isValidHeaderValue(str)) { + throw new InvalidArgumentError(`invalid ${key} header`) + } + arr.push(str) } } val = arr @@ -53360,7 +53601,12 @@ function processHeader (request, key, val) { } else if (val === null) { val = '' } else { + // Coerce primitives (and reject unsafe coercions such as functions + // with a crafted toString/Symbol.toPrimitive). val = `${val}` + if (!isValidHeaderValue(val)) { + throw new InvalidArgumentError(`invalid ${key} header`) + } } if (headerName === 'host') { @@ -54397,8 +54643,6 @@ function defaultFactory (origin, opts) { class Agent extends DispatcherBase { constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) { - super() - if (typeof factory !== 'function') { throw new InvalidArgumentError('factory must be a function.') } @@ -54411,6 +54655,8 @@ class Agent extends DispatcherBase { throw new InvalidArgumentError('maxRedirections must be a positive number') } + super(options) + if (connect && typeof connect !== 'function') { connect = { ...connect } } @@ -54737,6 +54983,7 @@ const { RequestContentLengthMismatchError, ResponseContentLengthMismatchError, RequestAbortedError, + InvalidArgumentError, HeadersTimeoutError, HeadersOverflowError, SocketError, @@ -54784,6 +55031,9 @@ const EMPTY_BUF = Buffer.alloc(0) const FastBuffer = Buffer[Symbol.species] const addListener = util.addListener const removeAllListeners = util.removeAllListeners +const kIdleSocketValidation = Symbol('kIdleSocketValidation') +const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout') +const kSocketUsed = Symbol('kSocketUsed') let extractBody @@ -55006,29 +55256,71 @@ class Parser { const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr - if (ret === constants.ERROR.PAUSED_UPGRADE) { - this.onUpgrade(data.slice(offset)) - } else if (ret === constants.ERROR.PAUSED) { - this.paused = true - socket.unshift(data.slice(offset)) - } else if (ret !== constants.ERROR.OK) { - const ptr = llhttp.llhttp_get_error_reason(this.ptr) - let message = '' - /* istanbul ignore else: difficult to make a test case for */ - if (ptr) { - const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0) - message = - 'Response does not match the HTTP/1.1 protocol (' + - Buffer.from(llhttp.memory.buffer, ptr, len).toString() + - ')' - } - throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset)) + if (ret !== constants.ERROR.OK) { + const body = data.subarray(offset) + + if (ret === constants.ERROR.PAUSED_UPGRADE) { + this.onUpgrade(body) + } else if (ret === constants.ERROR.PAUSED) { + this.paused = true + socket.unshift(body) + } else { + throw this.createError(ret, body) + } } } catch (err) { util.destroy(socket, err) } } + finish () { + assert(currentParser === null) + assert(this.ptr != null) + assert(!this.paused) + + const { llhttp } = this + + let ret + + try { + currentParser = this + ret = llhttp.llhttp_finish(this.ptr) + } finally { + currentParser = null + } + + if (ret === constants.ERROR.OK) { + return null + } + + if (ret === constants.ERROR.PAUSED || ret === constants.ERROR.PAUSED_UPGRADE) { + this.paused = true + return null + } + + return this.createError(ret, EMPTY_BUF) + } + + createError (ret, data) { + const { llhttp, contentLength, bytesRead } = this + + if (contentLength && bytesRead !== parseInt(contentLength, 10)) { + return new ResponseContentLengthMismatchError() + } + + const ptr = llhttp.llhttp_get_error_reason(this.ptr) + let message = '' + if (ptr) { + const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0) + message = + 'Response does not match the HTTP/1.1 protocol (' + + Buffer.from(llhttp.memory.buffer, ptr, len).toString() + + ')' + } + + return new HTTPParserError(message, constants.ERROR[ret], data) + } + destroy () { assert(this.ptr != null) assert(currentParser == null) @@ -55056,6 +55348,11 @@ class Parser { return -1 } + if (client[kRunning] === 0) { + util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket))) + return -1 + } + const request = client[kQueue][client[kRunningIdx]] if (!request) { return -1 @@ -55159,6 +55456,11 @@ class Parser { return -1 } + if (client[kRunning] === 0) { + util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket))) + return -1 + } + const request = client[kQueue][client[kRunningIdx]] /* istanbul ignore next: difficult to make a test case for */ @@ -55332,6 +55634,7 @@ class Parser { request.onComplete(headers) client[kQueue][client[kRunningIdx]++] = null + socket[kSocketUsed] = true if (socket[kWriting]) { assert(client[kRunning] === 0) @@ -55390,6 +55693,9 @@ async function connectH1 (client, socket) { socket[kWriting] = false socket[kReset] = false socket[kBlocking] = false + socket[kIdleSocketValidation] = 0 + socket[kIdleSocketValidationTimeout] = null + socket[kSocketUsed] = false socket[kParser] = new Parser(client, socket, llhttpInstance) addListener(socket, 'error', function (err) { @@ -55400,8 +55706,11 @@ async function connectH1 (client, socket) { // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded // to the user. if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so for as a valid response. - parser.onMessageComplete() + const parserErr = parser.finish() + if (parserErr) { + this[kError] = parserErr + this[kClient][kOnError](parserErr) + } return } @@ -55420,8 +55729,10 @@ async function connectH1 (client, socket) { const parser = this[kParser] if (parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so far as a valid response. - parser.onMessageComplete() + const parserErr = parser.finish() + if (parserErr) { + util.destroy(this, parserErr) + } return } @@ -55431,10 +55742,11 @@ async function connectH1 (client, socket) { const client = this[kClient] const parser = this[kParser] + clearIdleSocketValidation(this) + if (parser) { if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so far as a valid response. - parser.onMessageComplete() + this[kError] = parser.finish() || this[kError] } this[kParser].destroy() @@ -55497,7 +55809,7 @@ async function connectH1 (client, socket) { return socket.destroyed }, busy (request) { - if (socket[kWriting] || socket[kReset] || socket[kBlocking]) { + if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) { return true } @@ -55535,6 +55847,31 @@ async function connectH1 (client, socket) { } } +function clearIdleSocketValidation (socket) { + if (socket[kIdleSocketValidationTimeout]) { + clearTimeout(socket[kIdleSocketValidationTimeout]) + socket[kIdleSocketValidationTimeout] = null + } + + socket[kIdleSocketValidation] = 0 +} + +function scheduleIdleSocketValidation (client, socket) { + socket[kIdleSocketValidation] = 1 + socket[kIdleSocketValidationTimeout] = setTimeout(() => { + socket[kIdleSocketValidationTimeout] = null + socket[kIdleSocketValidation] = 2 + + if (client[kSocket] === socket && !socket.destroyed) { + client[kResume]() + } + }, 0) + socket[kIdleSocketValidationTimeout].unref?.() +} + +/** + * @param {import('./client.js')} client + */ function resumeH1 (client) { const socket = client[kSocket] @@ -55549,6 +55886,32 @@ function resumeH1 (client) { socket[kNoRef] = false } + if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) { + if (socket[kIdleSocketValidation] === 0) { + scheduleIdleSocketValidation(client, socket) + socket[kParser].readMore() + if (socket.destroyed) { + return + } + return + } + + if (socket[kIdleSocketValidation] === 1) { + socket[kParser].readMore() + if (socket.destroyed) { + return + } + return + } + } + + if (client[kRunning] === 0) { + socket[kParser].readMore() + if (socket.destroyed) { + return + } + } + if (client[kSize] === 0) { if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) { socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE) @@ -55604,8 +55967,16 @@ function writeH1 (client, request) { } body = bodyStream.stream contentLength = bodyStream.length - } else if (util.isBlobLike(body) && request.contentType == null && body.type) { - headers.push('content-type', body.type) + } else if (util.isBlobLike(body) && request.contentType == null) { + const contentType = body.type + if (contentType) { + const contentTypeValue = `${contentType}` + if (!util.isValidHeaderValue(contentTypeValue)) { + util.errorRequest(client, request, new InvalidArgumentError('invalid content-type header')) + return false + } + headers.push('content-type', contentTypeValue) + } } if (body && typeof body.read === 'function') { @@ -55642,6 +56013,7 @@ function writeH1 (client, request) { } const socket = client[kSocket] + clearIdleSocketValidation(socket) const abort = (err) => { if (request.aborted || request.completed) { @@ -56963,9 +57335,10 @@ class Client extends DispatcherBase { autoSelectFamilyAttemptTimeout, // h2 maxConcurrentStreams, - allowH2 + allowH2, + webSocket } = {}) { - super() + super({ webSocket }) if (keepAlive !== undefined) { throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead') @@ -57498,15 +57871,24 @@ const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = __nc const kOnDestroyed = Symbol('onDestroyed') const kOnClosed = Symbol('onClosed') const kInterceptedDispatch = Symbol('Intercepted Dispatch') +const kWebSocketOptions = Symbol('webSocketOptions') class DispatcherBase extends Dispatcher { - constructor () { + constructor (opts) { super() this[kDestroyed] = false this[kOnDestroyed] = null this[kClosed] = false this[kOnClosed] = [] + this[kWebSocketOptions] = opts?.webSocket ?? {} + } + + get webSocketOptions () { + return { + maxFragments: this[kWebSocketOptions].maxFragments ?? 131072, + maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024 + } } get destroyed () { @@ -58070,8 +58452,8 @@ const kRemoveClient = Symbol('remove client') const kStats = Symbol('stats') class PoolBase extends DispatcherBase { - constructor () { - super() + constructor (opts) { + super(opts) this[kQueue] = new FixedQueue() this[kClients] = [] @@ -58331,8 +58713,6 @@ class Pool extends PoolBase { allowH2, ...options } = {}) { - super() - if (connections != null && (!Number.isFinite(connections) || connections < 0)) { throw new InvalidArgumentError('invalid connections') } @@ -58357,6 +58737,8 @@ class Pool extends PoolBase { }) } + super(options) + this[kInterceptors] = options.interceptors?.Pool && Array.isArray(options.interceptors.Pool) ? options.interceptors.Pool : [] @@ -59081,6 +59463,28 @@ function calculateRetryAfterHeader (retryAfter) { return new Date(retryAfter).getTime() - current } +function validatePartialResponseContentLength (headers, range, statusCode, retryCount) { + const contentLength = headers['content-length'] + if (contentLength == null) { + return null + } + + if (!Number.isFinite(range.start) || !Number.isFinite(range.end)) { + return null + } + + const length = Number(contentLength) + const expectedLength = range.end - range.start + 1 + if (!Number.isFinite(length) || length !== expectedLength) { + return new RequestRetryError('Content-Length mismatch', statusCode, { + headers, + data: { count: retryCount } + }) + } + + return null +} + class RetryHandler { constructor (opts, handlers) { const { retryOptions, ...dispatchOpts } = opts @@ -59295,6 +59699,12 @@ class RetryHandler { return false } + const contentLengthError = validatePartialResponseContentLength(headers, contentRange, statusCode, this.retryCount) + if (contentLengthError != null) { + this.abort(contentLengthError) + return false + } + const { start, size, end = size - 1 } = contentRange assert(this.start === start, 'content-range mismatch') @@ -59318,6 +59728,12 @@ class RetryHandler { ) } + const contentLengthError = validatePartialResponseContentLength(headers, range, statusCode, this.retryCount) + if (contentLengthError != null) { + this.abort(contentLengthError) + return false + } + const { start, size, end = size - 1 } = range assert( start != null && Number.isFinite(start), @@ -63441,32 +63857,25 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {}) // If the attribute-name case-insensitively matches the string // "SameSite", the user agent MUST process the cookie-av as follows: - // 1. Let enforcement be "Default". - let enforcement = 'Default' - const attributeValueLowercase = attributeValue.toLowerCase() - // 2. If cookie-av's attribute-value is a case-insensitive match for - // "None", set enforcement to "None". - if (attributeValueLowercase.includes('none')) { - enforcement = 'None' - } - // 3. If cookie-av's attribute-value is a case-insensitive match for - // "Strict", set enforcement to "Strict". - if (attributeValueLowercase.includes('strict')) { - enforcement = 'Strict' + // 1. If cookie-av's attribute-value is a case-insensitive match for + // "None", append an attribute to the cookie-attribute-list with an + // attribute-name of "SameSite" and an attribute-value of "None". + if (attributeValueLowercase === 'none') { + cookieAttributeList.sameSite = 'None' + } else if (attributeValueLowercase === 'strict') { + // 2. If cookie-av's attribute-value is a case-insensitive match for + // "Strict", append an attribute to the cookie-attribute-list with + // an attribute-name of "SameSite" and an attribute-value of + // "Strict". + cookieAttributeList.sameSite = 'Strict' + } else if (attributeValueLowercase === 'lax') { + // 3. If cookie-av's attribute-value is a case-insensitive match for + // "Lax", append an attribute to the cookie-attribute-list with an + // attribute-name of "SameSite" and an attribute-value of "Lax". + cookieAttributeList.sameSite = 'Lax' } - - // 4. If cookie-av's attribute-value is a case-insensitive match for - // "Lax", set enforcement to "Lax". - if (attributeValueLowercase.includes('lax')) { - enforcement = 'Lax' - } - - // 5. Append an attribute to the cookie-attribute-list with an - // attribute-name of "SameSite" and an attribute-value of - // enforcement. - cookieAttributeList.sameSite = enforcement } else { cookieAttributeList.unparsed ??= [] @@ -63596,7 +64005,7 @@ function validateCookiePath (path) { if ( code < 0x20 || // exclude CTLs (0-31) - code === 0x7F || // DEL + code > 0x7E || // exclude DEL and non-ascii code === 0x3B // ; ) { throw new Error('Invalid cookie path') @@ -63605,16 +64014,80 @@ function validateCookiePath (path) { } /** - * I have no idea why these values aren't allowed to be honest, - * but Deno tests these. - Khafra + * ::= | + * + * ::= any one of the 52 alphabetic characters A through Z in + * upper case and a through z in lower case + * + * ::= any one of the ten digits 0 through 9r + * + * @see https://www.rfc-editor.org/rfc/rfc1034#section-3.5 + * @param {number} code + */ +function isLetterOrDigit (code) { + return ( + (code >= 0x30 && code <= 0x39) || // 0-9 + (code >= 0x41 && code <= 0x5A) || // A-Z + (code >= 0x61 && code <= 0x7A) // a-z + ) +} + +/** + * Validates a cookie domain against the "preferred name syntax". + * + * ::= | " " + * ::=