diff --git a/dist/index.js b/dist/index.js index ee67967..f528531 100644 --- a/dist/index.js +++ b/dist/index.js @@ -3447,6 +3447,241 @@ function copyFile(srcFile, destFile, force) { /***/ }), +/***/ 8207: +/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +const events_1 = __nccwpck_require__(4434); +const debug_1 = __importDefault(__nccwpck_require__(2830)); +const promisify_1 = __importDefault(__nccwpck_require__(8067)); +const debug = debug_1.default('agent-base'); +function isAgent(v) { + return Boolean(v) && typeof v.addRequest === 'function'; +} +function isSecureEndpoint() { + const { stack } = new Error(); + if (typeof stack !== 'string') + return false; + return stack.split('\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1); +} +function createAgent(callback, opts) { + return new createAgent.Agent(callback, opts); +} +(function (createAgent) { + /** + * Base `http.Agent` implementation. + * No pooling/keep-alive is implemented by default. + * + * @param {Function} callback + * @api public + */ + class Agent extends events_1.EventEmitter { + constructor(callback, _opts) { + super(); + let opts = _opts; + if (typeof callback === 'function') { + this.callback = callback; + } + else if (callback) { + opts = callback; + } + // Timeout for the socket to be returned from the callback + this.timeout = null; + if (opts && typeof opts.timeout === 'number') { + this.timeout = opts.timeout; + } + // These aren't actually used by `agent-base`, but are required + // for the TypeScript definition files in `@types/node` :/ + this.maxFreeSockets = 1; + this.maxSockets = 1; + this.maxTotalSockets = Infinity; + this.sockets = {}; + this.freeSockets = {}; + this.requests = {}; + this.options = {}; + } + get defaultPort() { + if (typeof this.explicitDefaultPort === 'number') { + return this.explicitDefaultPort; + } + return isSecureEndpoint() ? 443 : 80; + } + set defaultPort(v) { + this.explicitDefaultPort = v; + } + get protocol() { + if (typeof this.explicitProtocol === 'string') { + return this.explicitProtocol; + } + return isSecureEndpoint() ? 'https:' : 'http:'; + } + set protocol(v) { + this.explicitProtocol = v; + } + callback(req, opts, fn) { + throw new Error('"agent-base" has no default implementation, you must subclass and override `callback()`'); + } + /** + * Called by node-core's "_http_client.js" module when creating + * a new HTTP request with this Agent instance. + * + * @api public + */ + addRequest(req, _opts) { + const opts = Object.assign({}, _opts); + if (typeof opts.secureEndpoint !== 'boolean') { + opts.secureEndpoint = isSecureEndpoint(); + } + if (opts.host == null) { + opts.host = 'localhost'; + } + if (opts.port == null) { + opts.port = opts.secureEndpoint ? 443 : 80; + } + if (opts.protocol == null) { + opts.protocol = opts.secureEndpoint ? 'https:' : 'http:'; + } + if (opts.host && opts.path) { + // If both a `host` and `path` are specified then it's most + // likely the result of a `url.parse()` call... we need to + // remove the `path` portion so that `net.connect()` doesn't + // attempt to open that as a unix socket file. + delete opts.path; + } + delete opts.agent; + delete opts.hostname; + delete opts._defaultAgent; + delete opts.defaultPort; + delete opts.createConnection; + // Hint to use "Connection: close" + // XXX: non-documented `http` module API :( + req._last = true; + req.shouldKeepAlive = false; + let timedOut = false; + let timeoutId = null; + const timeoutMs = opts.timeout || this.timeout; + const onerror = (err) => { + if (req._hadError) + return; + req.emit('error', err); + // For Safety. Some additional errors might fire later on + // and we need to make sure we don't double-fire the error event. + req._hadError = true; + }; + const ontimeout = () => { + timeoutId = null; + timedOut = true; + const err = new Error(`A "socket" was not created for HTTP request before ${timeoutMs}ms`); + err.code = 'ETIMEOUT'; + onerror(err); + }; + const callbackError = (err) => { + if (timedOut) + return; + if (timeoutId !== null) { + clearTimeout(timeoutId); + timeoutId = null; + } + onerror(err); + }; + const onsocket = (socket) => { + if (timedOut) + return; + if (timeoutId != null) { + clearTimeout(timeoutId); + timeoutId = null; + } + if (isAgent(socket)) { + // `socket` is actually an `http.Agent` instance, so + // relinquish responsibility for this `req` to the Agent + // from here on + debug('Callback returned another Agent instance %o', socket.constructor.name); + socket.addRequest(req, opts); + return; + } + if (socket) { + socket.once('free', () => { + this.freeSocket(socket, opts); + }); + req.onSocket(socket); + return; + } + const err = new Error(`no Duplex stream was returned to agent-base for \`${req.method} ${req.path}\``); + onerror(err); + }; + if (typeof this.callback !== 'function') { + onerror(new Error('`callback` is not defined')); + return; + } + if (!this.promisifiedCallback) { + if (this.callback.length >= 3) { + debug('Converting legacy callback function to promise'); + this.promisifiedCallback = promisify_1.default(this.callback); + } + else { + this.promisifiedCallback = this.callback; + } + } + if (typeof timeoutMs === 'number' && timeoutMs > 0) { + timeoutId = setTimeout(ontimeout, timeoutMs); + } + if ('port' in opts && typeof opts.port !== 'number') { + opts.port = Number(opts.port); + } + try { + debug('Resolving socket for %o request: %o', opts.protocol, `${req.method} ${req.path}`); + Promise.resolve(this.promisifiedCallback(req, opts)).then(onsocket, callbackError); + } + catch (err) { + Promise.reject(err).catch(callbackError); + } + } + freeSocket(socket, opts) { + debug('Freeing socket %o %o', socket.constructor.name, opts); + socket.destroy(); + } + destroy() { + debug('Destroying agent %o', this.constructor.name); + } + } + createAgent.Agent = Agent; + // So that `instanceof` works correctly + createAgent.prototype = createAgent.Agent.prototype; +})(createAgent || (createAgent = {})); +module.exports = createAgent; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 8067: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +function promisify(fn) { + return function (req, opts) { + return new Promise((resolve, reject) => { + fn.call(this, req, opts, (err, rtn) => { + if (err) { + reject(err); + } + else { + resolve(rtn); + } + }); + }); + }; +} +exports["default"] = promisify; +//# sourceMappingURL=promisify.js.map + +/***/ }), + /***/ 1324: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { @@ -7336,6 +7571,284 @@ var bind = __nccwpck_require__(7564); module.exports = bind.call(call, $hasOwn); +/***/ }), + +/***/ 6904: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const net_1 = __importDefault(__nccwpck_require__(9278)); +const tls_1 = __importDefault(__nccwpck_require__(4756)); +const url_1 = __importDefault(__nccwpck_require__(7016)); +const assert_1 = __importDefault(__nccwpck_require__(2613)); +const debug_1 = __importDefault(__nccwpck_require__(2830)); +const agent_base_1 = __nccwpck_require__(8207); +const parse_proxy_response_1 = __importDefault(__nccwpck_require__(7943)); +const debug = debug_1.default('https-proxy-agent:agent'); +/** + * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to + * the specified "HTTP(s) proxy server" in order to proxy HTTPS requests. + * + * Outgoing HTTP requests are first tunneled through the proxy server using the + * `CONNECT` HTTP request method to establish a connection to the proxy server, + * and then the proxy server connects to the destination target and issues the + * HTTP request from the proxy server. + * + * `https:` requests have their socket connection upgraded to TLS once + * the connection to the proxy server has been established. + * + * @api public + */ +class HttpsProxyAgent extends agent_base_1.Agent { + constructor(_opts) { + let opts; + if (typeof _opts === 'string') { + opts = url_1.default.parse(_opts); + } + else { + opts = _opts; + } + if (!opts) { + throw new Error('an HTTP(S) proxy server `host` and `port` must be specified!'); + } + debug('creating new HttpsProxyAgent instance: %o', opts); + super(opts); + const proxy = Object.assign({}, opts); + // If `true`, then connect to the proxy server over TLS. + // Defaults to `false`. + this.secureProxy = opts.secureProxy || isHTTPS(proxy.protocol); + // Prefer `hostname` over `host`, and set the `port` if needed. + proxy.host = proxy.hostname || proxy.host; + if (typeof proxy.port === 'string') { + proxy.port = parseInt(proxy.port, 10); + } + if (!proxy.port && proxy.host) { + proxy.port = this.secureProxy ? 443 : 80; + } + // ALPN is supported by Node.js >= v5. + // attempt to negotiate http/1.1 for proxy servers that support http/2 + if (this.secureProxy && !('ALPNProtocols' in proxy)) { + proxy.ALPNProtocols = ['http 1.1']; + } + if (proxy.host && proxy.path) { + // If both a `host` and `path` are specified then it's most likely + // the result of a `url.parse()` call... we need to remove the + // `path` portion so that `net.connect()` doesn't attempt to open + // that as a Unix socket file. + delete proxy.path; + delete proxy.pathname; + } + this.proxy = proxy; + } + /** + * Called when the node-core HTTP client library is creating a + * new HTTP request. + * + * @api protected + */ + callback(req, opts) { + return __awaiter(this, void 0, void 0, function* () { + const { proxy, secureProxy } = this; + // Create a socket connection to the proxy server. + let socket; + if (secureProxy) { + debug('Creating `tls.Socket`: %o', proxy); + socket = tls_1.default.connect(proxy); + } + else { + debug('Creating `net.Socket`: %o', proxy); + socket = net_1.default.connect(proxy); + } + const headers = Object.assign({}, proxy.headers); + const hostname = `${opts.host}:${opts.port}`; + let payload = `CONNECT ${hostname} HTTP/1.1\r\n`; + // Inject the `Proxy-Authorization` header if necessary. + if (proxy.auth) { + headers['Proxy-Authorization'] = `Basic ${Buffer.from(proxy.auth).toString('base64')}`; + } + // The `Host` header should only include the port + // number when it is not the default port. + let { host, port, secureEndpoint } = opts; + if (!isDefaultPort(port, secureEndpoint)) { + host += `:${port}`; + } + headers.Host = host; + headers.Connection = 'close'; + for (const name of Object.keys(headers)) { + payload += `${name}: ${headers[name]}\r\n`; + } + const proxyResponsePromise = parse_proxy_response_1.default(socket); + socket.write(`${payload}\r\n`); + const { statusCode, buffered } = yield proxyResponsePromise; + if (statusCode === 200) { + req.once('socket', resume); + if (opts.secureEndpoint) { + // The proxy is connecting to a TLS server, so upgrade + // this socket connection to a TLS connection. + debug('Upgrading socket connection to TLS'); + const servername = opts.servername || opts.host; + return tls_1.default.connect(Object.assign(Object.assign({}, omit(opts, 'host', 'hostname', 'path', 'port')), { socket, + servername })); + } + return socket; + } + // Some other status code that's not 200... need to re-play the HTTP + // header "data" events onto the socket once the HTTP machinery is + // attached so that the node core `http` can parse and handle the + // error status code. + // Close the original socket, and a new "fake" socket is returned + // instead, so that the proxy doesn't get the HTTP request + // written to it (which may contain `Authorization` headers or other + // sensitive data). + // + // See: https://hackerone.com/reports/541502 + socket.destroy(); + const fakeSocket = new net_1.default.Socket({ writable: false }); + fakeSocket.readable = true; + // Need to wait for the "socket" event to re-play the "data" events. + req.once('socket', (s) => { + debug('replaying proxy buffer for failed request'); + assert_1.default(s.listenerCount('data') > 0); + // Replay the "buffered" Buffer onto the fake `socket`, since at + // this point the HTTP module machinery has been hooked up for + // the user. + s.push(buffered); + s.push(null); + }); + return fakeSocket; + }); + } +} +exports["default"] = HttpsProxyAgent; +function resume(socket) { + socket.resume(); +} +function isDefaultPort(port, secure) { + return Boolean((!secure && port === 80) || (secure && port === 443)); +} +function isHTTPS(protocol) { + return typeof protocol === 'string' ? /^https:?$/i.test(protocol) : false; +} +function omit(obj, ...keys) { + const ret = {}; + let key; + for (key in obj) { + if (!keys.includes(key)) { + ret[key] = obj[key]; + } + } + return ret; +} +//# sourceMappingURL=agent.js.map + +/***/ }), + +/***/ 3669: +/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +const agent_1 = __importDefault(__nccwpck_require__(6904)); +function createHttpsProxyAgent(opts) { + return new agent_1.default(opts); +} +(function (createHttpsProxyAgent) { + createHttpsProxyAgent.HttpsProxyAgent = agent_1.default; + createHttpsProxyAgent.prototype = agent_1.default.prototype; +})(createHttpsProxyAgent || (createHttpsProxyAgent = {})); +module.exports = createHttpsProxyAgent; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 7943: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const debug_1 = __importDefault(__nccwpck_require__(2830)); +const debug = debug_1.default('https-proxy-agent:parse-proxy-response'); +function parseProxyResponse(socket) { + return new Promise((resolve, reject) => { + // we need to buffer any HTTP traffic that happens with the proxy before we get + // the CONNECT response, so that if the response is anything other than an "200" + // response code, then we can re-play the "data" events on the socket once the + // HTTP parser is hooked up... + let buffersLength = 0; + const buffers = []; + function read() { + const b = socket.read(); + if (b) + ondata(b); + else + socket.once('readable', read); + } + function cleanup() { + socket.removeListener('end', onend); + socket.removeListener('error', onerror); + socket.removeListener('close', onclose); + socket.removeListener('readable', read); + } + function onclose(err) { + debug('onclose had error %o', err); + } + function onend() { + debug('onend'); + } + function onerror(err) { + cleanup(); + debug('onerror %o', err); + reject(err); + } + function ondata(b) { + buffers.push(b); + buffersLength += b.length; + const buffered = Buffer.concat(buffers, buffersLength); + const endOfHeaders = buffered.indexOf('\r\n\r\n'); + if (endOfHeaders === -1) { + // keep buffering + debug('have not received end of HTTP headers yet...'); + read(); + return; + } + const firstLine = buffered.toString('ascii', 0, buffered.indexOf('\r\n')); + const statusCode = +firstLine.split(' ')[1]; + debug('got proxy server response: %o', firstLine); + resolve({ + statusCode, + buffered + }); + } + socket.on('error', onerror); + socket.on('close', onclose); + socket.on('end', onend); + read(); + }); +} +exports["default"] = parseProxyResponse; +//# sourceMappingURL=parse-proxy-response.js.map + /***/ }), /***/ 5641: @@ -36885,12 +37398,13 @@ module.exports = require("zlib"); /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -/*! Axios v1.16.0 Copyright (c) 2026 Matt Zabriskie and contributors */ +/*! Axios v1.19.0 Copyright (c) 2026 Matt Zabriskie and contributors */ var FormData$1 = __nccwpck_require__(6454); var crypto = __nccwpck_require__(6982); var url = __nccwpck_require__(7016); +var HttpsProxyAgent = __nccwpck_require__(3669); var http = __nccwpck_require__(8611); var https = __nccwpck_require__(5692); var http2 = __nccwpck_require__(5675); @@ -36926,6 +37440,52 @@ const { iterator, toStringTag } = Symbol; + +/* Creating a function that will check if an object has a property. */ +const hasOwnProperty = (({ + hasOwnProperty +}) => (obj, prop) => hasOwnProperty.call(obj, prop))(Object.prototype); + +/** + * Walk the prototype chain (excluding the shared Object.prototype) looking for + * an own `prop`. This distinguishes genuine own/inherited members — including + * class accessors and template prototypes — from members injected via + * Object.prototype pollution (e.g. `Object.prototype.username = '...'`), which + * live on Object.prototype itself and are therefore never matched. + * + * @param {*} thing The value whose chain to inspect + * @param {string|symbol} prop The property key to look for + * + * @returns {boolean} True when `prop` is owned below Object.prototype + */ +const hasOwnInPrototypeChain = (thing, prop) => { + let obj = thing; + const seen = []; + while (obj != null && obj !== Object.prototype) { + if (seen.indexOf(obj) !== -1) { + return false; + } + seen.push(obj); + if (hasOwnProperty(obj, prop)) { + return true; + } + obj = getPrototypeOf(obj); + } + return false; +}; + +/** + * Read `obj[prop]` only when it is safe from Object.prototype pollution. Own + * properties and members inherited from a non-Object.prototype source (a class + * instance or template object) are honored; a value reachable only through a + * polluted Object.prototype is ignored and `undefined` is returned. + * + * @param {*} obj The source object + * @param {string|symbol} prop The property key to read + * + * @returns {*} The resolved value, or undefined when unsafe/absent + */ +const getSafeProp = (obj, prop) => obj != null && hasOwnInPrototypeChain(obj, prop) ? obj[prop] : undefined; const kindOf = (cache => thing => { const str = toString.call(thing); return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase()); @@ -37044,11 +37604,15 @@ const isBoolean = thing => thing === true || thing === false; * @returns {boolean} True if value is a plain Object, otherwise false */ const isPlainObject = val => { - if (kindOf(val) !== 'object') { + if (!isObject(val)) { return false; } const prototype = getPrototypeOf(val); - return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(toStringTag in val) && !(iterator in val); + return (prototype === null || prototype === Object.prototype || getPrototypeOf(prototype) === null) && + // Treat any genuine (non-Object.prototype-polluted) Symbol.toStringTag or + // Symbol.iterator as evidence the value is a tagged/iterable type rather + // than a plain object, while ignoring keys injected onto Object.prototype. + !hasOwnInPrototypeChain(val, toStringTag) && !hasOwnInPrototypeChain(val, iterator); }; /** @@ -37131,6 +37695,7 @@ const isBlob = kindOfTest('Blob'); * @returns {boolean} True if value is a FileList, otherwise false */ const isFileList = kindOfTest('FileList'); +const isSet = kindOfTest('Set'); /** * Determine if a value is a Stream @@ -37303,7 +37868,10 @@ function merge(...objs) { if (key === '__proto__' || key === 'constructor' || key === 'prototype') { return; } - const targetKey = caseless && findKey(result, key) || key; + + // findKey lowercases the key, so caseless lookup only applies to strings — + // symbol keys are identity-matched. + const targetKey = caseless && typeof key === 'string' && findKey(result, key) || key; // Read via own-prop only — a bare `result[targetKey]` walks the prototype // chain, so a polluted Object.prototype value could surface here and get // copied into the merged result. @@ -37319,7 +37887,21 @@ function merge(...objs) { } }; for (let i = 0, l = objs.length; i < l; i++) { - objs[i] && forEach(objs[i], assignValue); + const source = objs[i]; + if (!source || isBuffer(source)) { + continue; + } + forEach(source, assignValue); + if (typeof source !== 'object' || isArray(source)) { + continue; + } + const symbols = Object.getOwnPropertySymbols(source); + for (let j = 0; j < symbols.length; j++) { + const symbol = symbols[j]; + if (propertyIsEnumerable.call(source, symbol)) { + assignValue(source[symbol], symbol); + } + } } return result; } @@ -37531,11 +38113,9 @@ const toCamelCase = str => { return p1.toUpperCase() + p2; }); }; - -/* Creating a function that will check if an object has a property. */ -const hasOwnProperty = (({ - hasOwnProperty -}) => (obj, prop) => hasOwnProperty.call(obj, prop))(Object.prototype); +const { + propertyIsEnumerable +} = Object.prototype; /** * Determine if a value is a RegExp object @@ -37624,10 +38204,10 @@ function isSpecCompliantForm(thing) { * @returns {Object} The JSON-compatible object. */ const toJSONObject = obj => { - const stack = new Array(10); - const visit = (source, i) => { + const visited = new WeakSet(); + const visit = source => { if (isObject(source)) { - if (stack.indexOf(source) >= 0) { + if (visited.has(source)) { return; } @@ -37636,19 +38216,29 @@ const toJSONObject = obj => { return source; } if (!('toJSON' in source)) { - stack[i] = source; - const target = isArray(source) ? [] : {}; - forEach(source, (value, key) => { - const reducedValue = visit(value, i + 1); - !isUndefined(reducedValue) && (target[key] = reducedValue); - }); - stack[i] = undefined; + // add-on descent / delete-on-ascent: preserves path semantics, so DAG nodes serialise at every occurrence (see #7230). + visited.add(source); + let target; + if (isSet(source)) { + target = []; + for (const value of source) { + const reducedValue = visit(value); + !isUndefined(reducedValue) && target.push(reducedValue); + } + } else { + target = isArray(source) ? [] : {}; + forEach(source, (value, key) => { + const reducedValue = visit(value); + !isUndefined(reducedValue) && (target[key] = reducedValue); + }); + } + visited.delete(source); return target; } } return source; }; - return visit(obj, 0); + return visit(obj); }; /** @@ -37709,6 +38299,19 @@ const asap = typeof queueMicrotask !== 'undefined' ? queueMicrotask.bind(_global // ********************* const isIterable = thing => thing != null && isFunction$1(thing[iterator]); + +/** + * Determine if a value is iterable via an iterator that is NOT sourced solely + * from a polluted Object.prototype. Use this instead of `isIterable` whenever + * the iterable comes from untrusted input (e.g. user-supplied header sources), + * so `Object.prototype[Symbol.iterator] = ...` cannot turn an ordinary object + * into an attacker-controlled entries iterator. + * + * @param {*} thing The value to test + * + * @returns {boolean} True if value has a non-polluted iterator + */ +const isSafeIterable = thing => thing != null && hasOwnInPrototypeChain(thing, iterator) && isIterable(thing); var utils$1 = { isArray, isArrayBuffer, @@ -37754,6 +38357,8 @@ var utils$1 = { hasOwnProperty, hasOwnProp: hasOwnProperty, // an alias to avoid ESLint no-prototype-builtins detection + hasOwnInPrototypeChain, + getSafeProp, reduceDescriptors, freezeMethods, toObjectSet, @@ -37769,7 +38374,8 @@ var utils$1 = { isThenable, setImmediate: _setImmediate, asap, - isIterable + isIterable, + isSafeIterable }; // RawAxiosHeaders whose duplicates are ignored by node @@ -37799,24 +38405,23 @@ var parseHeaders = rawHeaders => { i = line.indexOf(':'); key = line.substring(0, i).trim().toLowerCase(); val = line.substring(i + 1).trim(); - if (!key || parsed[key] && ignoreDuplicateOf[key]) { + const hasKey = utils$1.hasOwnProp(parsed, key); + if (!key || hasKey && utils$1.hasOwnProp(ignoreDuplicateOf, key)) { return; } if (key === 'set-cookie') { - if (parsed[key]) { + if (hasKey) { parsed[key].push(val); } else { parsed[key] = [val]; } } else { - parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + parsed[key] = hasKey ? parsed[key] + ', ' + val : val; } }); return parsed; }; -const $internals = Symbol('internals'); -const INVALID_HEADER_VALUE_CHARS_RE = /[^\x09\x20-\x7E\x80-\xFF]/g; function trimSPorHTAB(str) { let start = 0; let end = str.length; @@ -37836,12 +38441,32 @@ function trimSPorHTAB(str) { } return start === 0 && end === str.length ? str : str.slice(start, end); } + +// The control-code ranges are intentional: header sanitization strips C0/DEL bytes. +// eslint-disable-next-line no-control-regex +const INVALID_UNICODE_HEADER_VALUE_CHARS = new RegExp('[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+', 'g'); +// eslint-disable-next-line no-control-regex +const INVALID_BYTE_STRING_HEADER_VALUE_CHARS = new RegExp('[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+', 'g'); +function sanitizeValue(value, invalidChars) { + if (utils$1.isArray(value)) { + return value.map(item => sanitizeValue(item, invalidChars)); + } + return trimSPorHTAB(String(value).replace(invalidChars, '')); +} +const sanitizeHeaderValue = value => sanitizeValue(value, INVALID_UNICODE_HEADER_VALUE_CHARS); +const sanitizeByteStringHeaderValue = value => sanitizeValue(value, INVALID_BYTE_STRING_HEADER_VALUE_CHARS); +function toByteStringHeaderObject(headers) { + const byteStringHeaders = Object.create(null); + utils$1.forEach(headers.toJSON(), (value, header) => { + byteStringHeaders[header] = sanitizeByteStringHeaderValue(value); + }); + return byteStringHeaders; +} + +const $internals = Symbol('internals'); function normalizeHeader(header) { return header && String(header).trim().toLowerCase(); } -function sanitizeHeaderValue(str) { - return trimSPorHTAB(str.replace(INVALID_HEADER_VALUE_CHARS_RE, '')); -} function normalizeValue(value) { if (value === false || value == null) { return value; @@ -37857,6 +38482,90 @@ function parseTokens(str) { } return tokens; } +const parameterNameRE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/; +function trimOWS(value) { + let start = 0; + let end = value.length; + while (start < end) { + const code = value.charCodeAt(start); + if (code !== 0x09 && code !== 0x20) { + break; + } + start += 1; + } + while (end > start) { + const code = value.charCodeAt(end - 1); + if (code !== 0x09 && code !== 0x20) { + break; + } + end -= 1; + } + return start === 0 && end === value.length ? value : value.slice(start, end); +} +function decodeQuotedString(value) { + const last = value.length - 1; + if (last < 1 || value.charCodeAt(0) !== 0x22 || value.charCodeAt(last) !== 0x22) { + return value; + } + let decoded = ''; + for (let i = 1; i < last; i++) { + const code = value.charCodeAt(i); + if (code === 0x22) { + return value; + } + if (code === 0x5c) { + i += 1; + if (i >= last) { + return value; + } + } + decoded += value[i]; + } + return decoded; +} +function parseParameters(value) { + const parameters = Object.create(null); + const str = String(value); + let start = 0; + let quoted = false; + let escaped = false; + function parseParameter(end) { + const part = trimOWS(str.slice(start, end)); + const equals = part.indexOf('='); + if (equals < 1) { + return; + } + const name = trimOWS(part.slice(0, equals)); + if (!parameterNameRE.test(name)) { + return; + } + const normalizedName = name.toLowerCase(); + if (normalizedName === '__proto__' || normalizedName === 'constructor' || normalizedName === 'prototype') { + return; + } + const parameterValue = trimOWS(part.slice(equals + 1)); + parameters[normalizedName] = decodeQuotedString(parameterValue); + } + for (let i = 0; i < str.length; i++) { + const code = str.charCodeAt(i); + if (quoted) { + if (escaped) { + escaped = false; + } else if (code === 0x5c) { + escaped = true; + } else if (code === 0x22) { + quoted = false; + } + } else if (code === 0x22) { + quoted = true; + } else if (code === 0x2c || code === 0x3b) { + parseParameter(i); + start = i + 1; + } + } + parseParameter(str.length); + return parameters; +} const isValidHeaderName = str => /^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(str.trim()); function matchHeaderValue(context, value, header, filter, isHeaderNameFilter) { if (utils$1.isFunction(filter)) { @@ -37901,7 +38610,7 @@ class AxiosHeaders { function setHeader(_value, _header, _rewrite) { const lHeader = normalizeHeader(_header); if (!lHeader) { - throw new Error('header name must be a non-empty string'); + return; } const key = utils$1.findKey(self, lHeader); if (!key || self[key] === undefined || _rewrite === true || _rewrite === undefined && self[key] !== false) { @@ -37913,15 +38622,21 @@ class AxiosHeaders { setHeaders(header, valueOrRewrite); } else if (utils$1.isString(header) && (header = header.trim()) && !isValidHeaderName(header)) { setHeaders(parseHeaders(header), valueOrRewrite); - } else if (utils$1.isObject(header) && utils$1.isIterable(header)) { - let obj = {}, + } else if (utils$1.isObject(header) && utils$1.isSafeIterable(header)) { + let obj = Object.create(null), dest, key; for (const entry of header) { if (!utils$1.isArray(entry)) { - throw TypeError('Object iterator must return a key-value pair'); + throw new TypeError('Object iterator must return a key-value pair'); + } + key = entry[0]; + if (utils$1.hasOwnProp(obj, key)) { + dest = obj[key]; + obj[key] = utils$1.isArray(dest) ? [...dest, entry[1]] : [dest, entry[1]]; + } else { + obj[key] = entry[1]; } - obj[key = entry[0]] = (dest = obj[key]) ? utils$1.isArray(dest) ? [...dest, entry[1]] : [dest, entry[1]] : entry[1]; } setHeaders(obj, valueOrRewrite); } else { @@ -38028,7 +38743,8 @@ class AxiosHeaders { return Object.entries(this.toJSON()).map(([header, value]) => header + ': ' + value).join('\n'); } getSetCookie() { - return this.get('set-cookie') || []; + const value = this.get('set-cookie'); + return utils$1.isArray(value) ? value : value == null || value === false ? [] : [value]; } get [Symbol.toStringTag]() { return 'AxiosHeaders'; @@ -38036,6 +38752,9 @@ class AxiosHeaders { static from(thing) { return thing instanceof this ? thing : new this(thing); } + static parseParameters(value) { + return parseParameters(value); + } static concat(first, ...targets) { const computed = new this(first); targets.forEach(target => computed.set(target)); @@ -38130,10 +38849,46 @@ function redactConfig(config, redactKeys) { }; return visit(config); } +function stringifySafely$1(value) { + try { + return String(value); + } catch (err) { + return ''; + } +} +function aggregateErrorMessage(error) { + const message = error.errors.map(entry => { + try { + return entry && entry.message ? stringifySafely$1(entry.message) : stringifySafely$1(entry); + } catch (err) { + return ''; + } + }).filter(Boolean).join('; '); + return message || error.name || 'AggregateError'; +} class AxiosError extends Error { static from(error, code, config, request, response, customProps) { - const axiosError = new AxiosError(error.message, code || error.code, config, request, response); - axiosError.cause = error; + // `AggregateError` (thrown by Node on dual-stack/Happy-Eyeballs connection + // failures) has an empty `message`; its detail lives in `errors[]`. Without + // this, the wrapped error surfaces with a blank message (see #6721). + let message = error.message; + if (!message && utils$1.isArray(error.errors) && error.errors.length) { + message = aggregateErrorMessage(error); + } + const axiosError = new AxiosError(message, code || error.code, config, request, response); + // Match native `Error` `cause` semantics: non-enumerable. The wrapped + // error often carries circular internals (sockets, requests, agents), so + // an enumerable `cause` makes structured loggers (pino/winston) and any + // own-property walk throw "Converting circular structure to JSON". + // Regression from #6982; see #7205. `__proto__: null` mirrors the + // `message` descriptor below (prototype-pollution-safe descriptor). + Object.defineProperty(axiosError, 'cause', { + __proto__: null, + value: error, + writable: true, + enumerable: false, + configurable: true + }); axiosError.name = error.name; // Preserve status from the original error if not already set from response @@ -38224,6 +38979,19 @@ AxiosError.ERR_NOT_SUPPORT = 'ERR_NOT_SUPPORT'; AxiosError.ERR_INVALID_URL = 'ERR_INVALID_URL'; AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED = 'ERR_FORM_DATA_DEPTH_EXCEEDED'; +var PlatformBuffer = { + isBufferAvailable() { + return typeof Buffer !== 'undefined'; + }, + from(value) { + return Buffer.from(value); + } +}; + +// Default nesting limit shared with the inverse transform (formDataToJSON) so +// the FormData <-> JSON round-trip stays symmetric. +const DEFAULT_FORM_DATA_MAX_DEPTH = 100; + /** * Determines if the given thing is a array or js object. * @@ -38324,8 +39092,9 @@ function toFormData(obj, formData, options) { const dots = options.dots; const indexes = options.indexes; const _Blob = options.Blob || typeof Blob !== 'undefined' && Blob; - const maxDepth = options.maxDepth === undefined ? 100 : options.maxDepth; + const maxDepth = options.maxDepth === undefined ? DEFAULT_FORM_DATA_MAX_DEPTH : options.maxDepth; const useBlob = _Blob && utils$1.isSpecCompliantForm(formData); + const stack = []; if (!utils$1.isFunction(visitor)) { throw new TypeError('visitor must be a function'); } @@ -38341,10 +39110,38 @@ function toFormData(obj, formData, options) { throw new AxiosError('Blob is not supported. Use a Buffer instead.'); } if (utils$1.isArrayBuffer(value) || utils$1.isTypedArray(value)) { - return useBlob && typeof Blob === 'function' ? new Blob([value]) : Buffer.from(value); + if (useBlob && typeof _Blob === 'function') { + return new _Blob([value]); + } + if (PlatformBuffer && PlatformBuffer.isBufferAvailable()) { + return PlatformBuffer.from(value); + } + throw new AxiosError('Blob is not supported. Use a Buffer instead.', AxiosError.ERR_NOT_SUPPORT); } return value; } + function throwIfMaxDepthExceeded(depth) { + if (depth > maxDepth) { + throw new AxiosError('Object is too deeply nested (' + depth + ' levels). Max depth: ' + maxDepth, AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED); + } + } + function stringifyWithDepthLimit(value, depth) { + if (maxDepth === Infinity) { + return JSON.stringify(value); + } + const ancestors = []; + return JSON.stringify(value, function limitDepth(_key, currentValue) { + if (!utils$1.isObject(currentValue)) { + return currentValue; + } + while (ancestors.length && ancestors[ancestors.length - 1] !== this) { + ancestors.pop(); + } + ancestors.push(currentValue); + throwIfMaxDepthExceeded(depth + ancestors.length - 1); + return currentValue; + }); + } /** * Default visitor. @@ -38367,7 +39164,7 @@ function toFormData(obj, formData, options) { // eslint-disable-next-line no-param-reassign key = metaTokens ? key : key.slice(0, -2); // eslint-disable-next-line no-param-reassign - value = JSON.stringify(value); + value = stringifyWithDepthLimit(value, 1); } else if (utils$1.isArray(value) && isFlatArray(value) || (utils$1.isFileList(value) || utils$1.endsWith(key, '[]')) && (arr = utils$1.toArray(value))) { // eslint-disable-next-line no-param-reassign key = removeBrackets(key); @@ -38385,7 +39182,6 @@ function toFormData(obj, formData, options) { formData.append(renderKey(path, key, dots), convertValue(value)); return false; } - const stack = []; const exposedHelpers = Object.assign(predicates, { defaultVisitor, convertValue, @@ -38393,11 +39189,9 @@ function toFormData(obj, formData, options) { }); function build(value, path, depth = 0) { if (utils$1.isUndefined(value)) return; - if (depth > maxDepth) { - throw new AxiosError('Object is too deeply nested (' + depth + ' levels). Max depth: ' + maxDepth, AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED); - } + throwIfMaxDepthExceeded(depth); if (stack.indexOf(value) !== -1) { - throw Error('Circular reference detected in ' + path.join('.')); + throw new Error('Circular reference detected in ' + path.join('.')); } stack.push(value); utils$1.forEach(value, function each(el, key) { @@ -38454,9 +39248,7 @@ prototype.append = function append(name, value) { this._pairs.push([name, value]); }; prototype.toString = function toString(encoder) { - const _encode = encoder ? function (value) { - return encoder.call(this, value, encode$1); - } : encode$1; + const _encode = encoder ? value => encoder.call(this, value, encode$1) : encode$1; return this._pairs.map(function each(pair) { return _encode(pair[0]) + '=' + _encode(pair[1]); }, '').join('&'); @@ -38487,11 +39279,16 @@ function buildURL(url, params, options) { if (!params) { return url; } - const _encode = options && options.encode || encode; + url = url || ''; const _options = utils$1.isFunction(options) ? { serialize: options } : options; - const serializeFn = _options && _options.serialize; + + // Read serializer options pollution-safely: own properties and methods on a + // class/template prototype are honored, but values injected onto a polluted + // Object.prototype are ignored. + const _encode = utils$1.getSafeProp(_options, 'encode') || encode; + const serializeFn = utils$1.getSafeProp(_options, 'serialize'); let serializedParams; if (serializeFn) { serializedParams = serializeFn(params, _options); @@ -38579,7 +39376,9 @@ var transitionalDefaults = { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false, - legacyInterceptorReqResOrdering: true + legacyInterceptorReqResOrdering: true, + advertiseZstdAcceptEncoding: false, + validateStatusUndefinedResolves: true }; var URLSearchParams = url.URLSearchParams; @@ -38680,6 +39479,13 @@ function toURLEncodedForm(data, options) { }); } +const MAX_DEPTH = DEFAULT_FORM_DATA_MAX_DEPTH; +function throwIfDepthExceeded(index) { + if (index > MAX_DEPTH) { + throw new AxiosError('FormData field is too deeply nested (' + index + ' levels). Max depth: ' + MAX_DEPTH, AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED); + } +} + /** * It takes a string like `foo[x][y][z]` and returns an array like `['foo', 'x', 'y', 'z'] * @@ -38688,13 +39494,24 @@ function toURLEncodedForm(data, options) { * @returns An array of strings. */ function parsePropPath(name) { - // foo[x][y][z] - // foo.x.y.z - // foo-x-y-z - // foo x y z - return utils$1.matchAll(/\w+|\[(\w*)]/g, name).map(match => { - return match[0] === '[]' ? '' : match[1] || match[0]; - }); + // foo[x][y][z] -> ['foo', 'x', 'y', 'z'] + // foo.x.y.z -> ['foo', 'x', 'y', 'z'] + // A path is split on `.` and on `[...]` groups. A segment — whether written + // in dot notation or captured inside brackets — may contain any character + // except `.`, `[` and `]`, so a key like `user-name` or `user name` is kept + // literal instead of being split (#5402). `.`, `[` and `]` keep their existing + // meaning, e.g. `foo[bar.baz]` -> ['foo', 'bar', 'baz'] and `[]` is an array push. + // Excluding `[` from the bracket group also makes the match fail fast at the + // next `[`, so a malformed name cannot rescan to the end of the string from + // every unmatched `[` — parsing stays linear in the length of the name. + const path = []; + const pattern = /[^.[\]]+|\[([^.[\]]*)]/g; + let match; + while ((match = pattern.exec(name)) !== null) { + throwIfDepthExceeded(path.length); + path.push(match[0] === '[]' ? '' : match[1] || match[0]); + } + return path; } /** @@ -38726,6 +39543,7 @@ function arrayToObject(arr) { */ function formDataToJSON(formData) { function buildPath(path, value, target, index) { + throwIfDepthExceeded(index); let name = path[index++]; if (name === '__proto__') return true; const isNumericKey = Number.isFinite(+name); @@ -38739,7 +39557,7 @@ function formDataToJSON(formData) { } return !isNumericKey; } - if (!target[name] || !utils$1.isObject(target[name])) { + if (!utils$1.hasOwnProp(target, name) || !utils$1.isObject(target[name])) { target[name] = []; } const result = buildPath(path, value, target[name], index); @@ -38963,7 +39781,62 @@ function isAbsoluteURL(url) { * @returns {string} The combined URL */ function combineURLs(baseURL, relativeURL) { - return relativeURL ? baseURL.replace(/\/?\/$/, '') + '/' + relativeURL.replace(/^\/+/, '') : baseURL; + if (!relativeURL) { + return baseURL; + } + let end = baseURL.length; + while (end > 0 && baseURL.charCodeAt(end - 1) === 47) { + end--; + } + return baseURL.slice(0, end) + '/' + relativeURL.replace(/^\/+/, ''); +} + +const malformedHttpProtocol = /^https?:(?!\/\/)/i; +const httpProtocolControlCharacters = /[\t\n\r]/g; +function stripLeadingC0ControlOrSpace(url) { + let i = 0; + while (i < url.length && url.charCodeAt(i) <= 0x20) { + i++; + } + return url.slice(i); +} +function normalizeURLForProtocolCheck(url) { + return stripLeadingC0ControlOrSpace(url).replace(httpProtocolControlCharacters, ''); +} + +// Redact the parts of a URL that can carry secrets before it is embedded in an +// error message. AxiosError.toJSON() serializes `message` verbatim and errors +// are commonly logged, while the opt-in `config.redact` model only cleans +// config keys — it cannot reach the message. Redact only the genuinely +// sensitive substrings — userinfo (credentials), query parameter values and +// fragment contents — with the same REDACTED marker the config redaction uses, +// while keeping the scheme, host, path and parameter names so the offending +// request stays accurately identifiable. +function redactFragment(fragment) { + if (!fragment) { + return fragment; + } + return fragment.replace(/(^|&)([^=&]*=)?[^&]+/g, (match, separator, parameterName = '') => { + return `${separator}${parameterName}${REDACTED}`; + }); +} +function redactSensitiveURLParts(url) { + const redactedURL = url.replace(/^(https?:\/{0,2})[^/?#]*@/i, `$1${REDACTED}@`); + const fragmentIndex = redactedURL.indexOf('#'); + const urlWithoutFragment = fragmentIndex === -1 ? redactedURL : redactedURL.slice(0, fragmentIndex); + const redactedURLWithoutFragment = urlWithoutFragment.replace(/([?&][^=&#]*=)[^&#]*/g, `$1${REDACTED}`); + if (fragmentIndex === -1) { + return redactedURLWithoutFragment; + } + return `${redactedURLWithoutFragment}#${redactFragment(redactedURL.slice(fragmentIndex + 1))}`; +} +function assertValidHttpProtocolURL(url, config) { + if (typeof url === 'string') { + const normalizedURL = normalizeURLForProtocolCheck(url); + if (malformedHttpProtocol.test(normalizedURL)) { + throw new AxiosError(`Invalid URL ${JSON.stringify(redactSensitiveURLParts(normalizedURL))}: missing "//" after protocol`, AxiosError.ERR_INVALID_URL, config); + } + } } /** @@ -38976,9 +39849,11 @@ function combineURLs(baseURL, relativeURL) { * * @returns {string} The combined full path */ -function buildFullPath(baseURL, requestedURL, allowAbsoluteUrls) { +function buildFullPath(baseURL, requestedURL, allowAbsoluteUrls, config) { + assertValidHttpProtocolURL(requestedURL, config); let isRelativeUrl = !isAbsoluteURL(requestedURL); if (baseURL && (isRelativeUrl || allowAbsoluteUrls === false)) { + assertValidHttpProtocolURL(baseURL, config); return combineURLs(baseURL, requestedURL); } return requestedURL; @@ -39080,14 +39955,16 @@ function getEnv(key) { return process.env[key.toLowerCase()] || process.env[key.toUpperCase()] || ''; } -const VERSION = "1.16.0"; +const VERSION = "1.19.0"; function parseProtocol(url) { const match = /^([-+\w]{1,25}):(?:\/\/)?/.exec(url); return match && match[1] || ''; } -const DATA_URL_PATTERN = /^(?:([^;]+);)?(?:[^;]+;)?(base64|),([\s\S]*)$/; +// RFC 2397: data:[][;base64], +// mediatype = type/subtype followed by optional ;name=value parameters +const DATA_URL_PATTERN = /^([^,;]+\/[^,;]+)?((?:;[^,;=]+=[^,;]+)*)(;base64)?,([\s\S]*)$/; /** * Parse data uri to a Buffer or Blob @@ -39111,10 +39988,20 @@ function fromDataURI(uri, asBlob, options) { if (!match) { throw new AxiosError('Invalid URL', AxiosError.ERR_INVALID_URL); } - const mime = match[1]; - const isBase64 = match[2]; - const body = match[3]; - const buffer = Buffer.from(decodeURIComponent(body), isBase64 ? 'base64' : 'utf8'); + const type = match[1]; + const params = match[2]; + const encoding = match[3] ? 'base64' : 'utf8'; + const body = match[4]; + + // RFC 2397 section 3: default mediatype is text/plain;charset=US-ASCII + // Bare `data:,` leaves mime undefined; Blob normalises that to "" per spec. + let mime = ''; + if (type) { + mime = params ? type + params : type; + } else if (params) { + mime = 'text/plain' + params; + } + const buffer = encoding === 'base64' ? Buffer.from(body, 'base64') : Buffer.from(decodeURIComponent(body), encoding); if (asBlob) { if (!_Blob) { throw new AxiosError('Blob is not supported', AxiosError.ERR_NOT_SUPPORT); @@ -39128,6 +40015,31 @@ function fromDataURI(uri, asBlob, options) { throw new AxiosError('Unsupported protocol ' + protocol, AxiosError.ERR_NOT_SUPPORT); } +const FORM_DATA_CONTENT_HEADERS = ['content-type', 'content-length']; + +/** + * Apply the headers generated by a FormData implementation to the request headers, + * honoring the `formDataHeaderPolicy` option: with 'content-only', copy only the + * content-* headers; otherwise merge all of them. + * + * @param {AxiosHeaders} headers - the request headers to mutate + * @param {Object | null | undefined} formHeaders - headers produced by the FormData implementation + * @param {String} [policy] - the resolved `formDataHeaderPolicy` config value + * + * @returns {void} + */ +function setFormDataHeaders(headers, formHeaders, policy) { + if (policy !== 'content-only') { + headers.set(formHeaders); + return; + } + Object.entries(formHeaders || {}).forEach(([key, val]) => { + if (FORM_DATA_CONTENT_HEADERS.includes(key.toLowerCase())) { + headers.set(key, val); + } + }); +} + const kInternals = Symbol('internals'); class AxiosTransformStream extends stream.Transform { constructor(options) { @@ -39307,10 +40219,10 @@ const formDataToStream = (form, headersHandler, options) => { boundary = tag + '-' + platform.generateString(size, BOUNDARY_ALPHABET) } = options || {}; if (!utils$1.isFormData(form)) { - throw TypeError('FormData instance required'); + throw new TypeError('FormData instance required'); } if (boundary.length < 1 || boundary.length > 70) { - throw Error('boundary must be 1-70 characters long'); + throw new Error('boundary must be 1-70 characters long'); } const boundaryBytes = textEncoder.encode('--' + boundary + CRLF); const footerBytes = textEncoder.encode('--' + boundary + '--' + CRLF); @@ -39360,6 +40272,84 @@ class ZlibHeaderTransformStream extends stream.Transform { } } +class Http2Sessions { + constructor() { + this.sessions = Object.create(null); + } + getSession(authority, options) { + options = Object.assign({ + sessionTimeout: 1000 + }, options); + let authoritySessions = this.sessions[authority]; + if (authoritySessions) { + let len = authoritySessions.length; + for (let i = 0; i < len; i++) { + const [sessionHandle, sessionOptions] = authoritySessions[i]; + if (!sessionHandle.destroyed && !sessionHandle.closed && util.isDeepStrictEqual(sessionOptions, options)) { + return sessionHandle; + } + } + } + const session = http2.connect(authority, options); + let removed; + let timer; + const removeSession = () => { + if (removed) { + return; + } + removed = true; + if (timer) { + clearTimeout(timer); + timer = null; + } + let entries = authoritySessions, + len = entries.length, + i = len; + while (i--) { + if (entries[i][0] === session) { + if (len === 1) { + delete this.sessions[authority]; + } else { + entries.splice(i, 1); + } + if (!session.closed) { + session.close(); + } + return; + } + } + }; + const originalRequestFn = session.request; + const { + sessionTimeout + } = options; + if (sessionTimeout != null) { + let streamsCount = 0; + session.request = function () { + const stream = originalRequestFn.apply(this, arguments); + streamsCount++; + if (timer) { + clearTimeout(timer); + timer = null; + } + stream.once('close', () => { + if (! --streamsCount) { + timer = setTimeout(() => { + timer = null; + removeSession(); + }, sessionTimeout); + } + }); + return stream; + }; + } + session.once('close', removeSession); + let entry = [session, options]; + authoritySessions ? authoritySessions.push(entry) : authoritySessions = this.sessions[authority] = [entry]; + return session; + } +} + const callbackify = (fn, reducer) => { return utils$1.isAsyncFn(fn) ? function (...args) { const cb = args.pop(); @@ -39373,13 +40363,132 @@ const callbackify = (fn, reducer) => { } : fn; }; -const LOOPBACK_HOSTNAMES = new Set(['localhost']); +const LOOPBACK_HOSTNAMES = new Set(['localhost', '0.0.0.0']); const isIPv4Loopback = host => { const parts = host.split('.'); if (parts.length !== 4) return false; if (parts[0] !== '127') return false; return parts.every(p => /^\d+$/.test(p) && Number(p) >= 0 && Number(p) <= 255); }; + +/** + * Canonicalize an IPv4 address written in shorthand, octal, or hex form into + * dotted-decimal. IPv6 addresses and non-IP strings are returned unchanged so + * the existing IPv4-mapped IPv6 unmap path and the isLoopback path can still + * see them. + * + * Shorthand expansion mirrors Node's URL parser: literal parts fill from the + * left, the final part fills the remaining octets from the right with + * zero-padding on the left. + * 127.1 -> 127.0.0.1 + * 127.0.1 -> 127.0.0.1 + * 1.2.3 -> 1.2.0.3 + * + * Each octet is parsed with an explicit base: 16 for `0x`/`0X` prefix, 8 for + * zero-prefixed multi-digit all-`0-7` parts, 10 otherwise. Zero-prefixed + * decimal-looking parts that contain `8` or `9` are rejected to match Node's + * URL parser, and the comparison layer falls through to non-bypass if either + * side rejects the form (fail-safe). + * + * Returns the input unchanged on any parse failure, out-of-range octet, or + * unusual shape (1-part, 5+ parts) so the comparison layer fails closed. + */ +const parseIPv4Octet = text => { + if (/^0[xX][0-9a-fA-F]+$/.test(text)) { + const n = parseInt(text.slice(2), 16); + return Number.isFinite(n) ? n : null; + } + if (text.length > 1 && /^0[0-7]+$/.test(text)) { + const n = parseInt(text, 8); + return Number.isFinite(n) ? n : null; + } + if (text.length > 1 && /^0[0-9]+$/.test(text)) { + return null; + } + if (/^[0-9]+$/.test(text)) { + const n = parseInt(text, 10); + return Number.isFinite(n) ? n : null; + } + return null; +}; +const normalizeIPAddress = host => { + if (typeof host !== 'string' || !host || host.indexOf(':') !== -1) { + return host; + } + let h = host; + if (h.charAt(0) === '[' && h.charAt(h.length - 1) === ']') { + h = h.slice(1, -1); + } + h = h.replace(/\.+$/, ''); + + // Allowed characters for any IPv4 shape: digits, dot, 'x', 'X', hex digits. + if (!/^[0-9.xXa-fA-F]+$/.test(h)) return host; + const parts = h.split('.'); + + // No part may be empty (e.g. "127..0.1" or "127.0.0."). Trailing dots are + // already stripped above; this guards against the empty-middle case. + if (parts.some(p => p === '')) return host; + if (parts.length === 4) { + // Full IPv4 form: each part is an octet. + const octets = parts.map(parseIPv4Octet); + if (octets.some(n => n === null || n < 0 || n > 255)) return host; + return octets.join('.'); + } + if (parts.length > 4) { + return host; + } + + // Shorthand: 1..3 parts. Node's URL parser treats a 1-part input as a 32-bit + // integer split into octets, which has surprising semantics (e.g. "127" -> + // "0.0.0.127"). Reject 1-part inputs to keep the helper predictable: the + // fail-safe returns the input unchanged and the comparison layer falls + // through to non-bypass. + if (parts.length === 1) return host; + + // 2..3 parts: literal parts fill from the left, tail fills remaining octets + // from the right with zero-padding. + const literalOctets = parts.slice(0, -1); + const tail = parts[parts.length - 1]; + const tailSlots = 4 - literalOctets.length; + + // Tail is parsed as a full IPv4 number (hex/octal/decimal) and packed + // low-byte-right into the remaining octets, matching Node's URL parser. + // e.g. 127.65535 (tail 0xFFFF into 3 slots) -> 127.0.255.255; + // 127.0x00ff (tail 0xFF into 3 slots) -> 127.0.0.255; + // 127.0.65535 (tail 0xFFFF into 2 slots) -> 127.0.255.255. + const tailValue = parseIPv4Octet(tail); + if (tailValue === null) return host; + const maxTail = (1 << 8 * tailSlots) - 1; + if (tailValue < 0 || tailValue > maxTail) return host; + const tailOctets = new Array(tailSlots).fill(0); + for (let i = tailSlots - 1, v = tailValue; i >= 0; i--, v >>= 8) { + tailOctets[i] = v & 0xff; + } + const literal = literalOctets.map(parseIPv4Octet); + if (literal.some(n => n === null || n < 0 || n > 255)) return host; + return [...literal, ...tailOctets].join('.'); +}; +const isIPv6ZeroGroup = group => /^0{1,4}$/.test(group); + +// The unspecified address (IPv4 0.0.0.0 / IPv6 ::) resolves to the local host +// for outbound connections, so treat it as loopback-equivalent for NO_PROXY +// matching. 0.0.0.0 is covered by LOOPBACK_HOSTNAMES; this handles compressed +// and full IPv6 all-zero forms so both families bypass symmetrically. +const isIPv6Unspecified = host => { + if (host === '::') return true; + const compressionIndex = host.indexOf('::'); + if (compressionIndex !== -1) { + if (compressionIndex !== host.lastIndexOf('::')) return false; + const left = host.slice(0, compressionIndex); + const right = host.slice(compressionIndex + 2); + const leftGroups = left ? left.split(':') : []; + const rightGroups = right ? right.split(':') : []; + const explicitGroups = leftGroups.length + rightGroups.length; + return explicitGroups < 8 && leftGroups.every(isIPv6ZeroGroup) && rightGroups.every(isIPv6ZeroGroup); + } + const groups = host.split(':'); + return groups.length === 8 && groups.every(isIPv6ZeroGroup); +}; const isIPv6Loopback = host => { // Collapse all-zero groups: any form of ::1 / 0:0:...:0:1 // First, strip any leading "::" by normalising with Set lookup of common forms, @@ -39412,6 +40521,7 @@ const isLoopback = host => { if (!host) return false; if (LOOPBACK_HOSTNAMES.has(host)) return true; if (isIPv4Loopback(host)) return true; + if (isIPv6Unspecified(host)) return true; return isIPv6Loopback(host); }; const DEFAULT_PORTS = { @@ -39471,7 +40581,15 @@ const normalizeNoProxyHost = hostname => { if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') { hostname = hostname.slice(1, -1); } - return unmapIPv4MappedIPv6(hostname.replace(/\.+$/, '')); + const trimmed = hostname.replace(/\.+$/, ''); + + // IPv4 shorthand/octal/hex → dotted-decimal; helper is a no-op for inputs + // containing ':' (IPv6 and IPv4-mapped IPv6) so we fall through to unmap. + const ipv4 = normalizeIPAddress(trimmed); + if (ipv4 !== trimmed) { + return ipv4; + } + return unmapIPv4MappedIPv6(trimmed); }; function shouldBypassProxy(location) { let parsed; @@ -39493,6 +40611,9 @@ function shouldBypassProxy(location) { if (!entry) { return false; } + if (entry === '*') { + return true; + } let [entryHost, entryPort] = parseNoProxyEntry(entry); entryHost = normalizeNoProxyHost(entryHost); if (!entryHost) { @@ -39594,9 +40715,12 @@ const progressEventReducer = (listener, isDownloadStream, freq = 3) => { let bytesNotified = 0; const _speedometer = speedometer(50, 250); return throttle(e => { + if (!e || typeof e.loaded !== 'number') { + return; + } const rawLoaded = e.loaded; const total = e.lengthComputable ? e.total : undefined; - const loaded = total != null ? Math.min(rawLoaded, total) : rawLoaded; + const loaded = Math.max(0, total != null ? Math.min(rawLoaded, total) : rawLoaded); const progressBytes = Math.max(0, loaded - bytesNotified); const rate = _speedometer(progressBytes); bytesNotified = Math.max(bytesNotified, loaded); @@ -39622,18 +40746,85 @@ const progressEventDecorator = (total, throttled) => { loaded }), throttled[1]]; }; -const asyncDecorator = fn => (...args) => utils$1.asap(() => fn(...args)); +const asyncDecorator = (fn, scheduler = utils$1.asap) => (...args) => scheduler(() => fn(...args)); + +/** + * Estimate data: URL byte lengths *without* allocating large buffers. + * - Fetch percent-decodes a base64 body before decoding it. + * - Node's Buffer.from(body, 'base64') sizes its backing allocation from the + * raw body, including ignored characters and content after padding. + * - Non-base64 data is percent-decoded and then encoded as UTF-8. + */ +const isHexDigit = charCode => charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 70 || charCode >= 97 && charCode <= 102; +const isPercentEncodedByte = (str, i, len) => i + 2 < len && isHexDigit(str.charCodeAt(i + 1)) && isHexDigit(str.charCodeAt(i + 2)); +const hexValue = charCode => charCode <= 57 ? charCode - 48 : (charCode & 0xdf) - 55; +const isBase64Char = charCode => charCode >= 65 && charCode <= 90 || +// A-Z +charCode >= 97 && charCode <= 122 || +// a-z +charCode >= 48 && charCode <= 57 || +// 0-9 +charCode === 43 || +// + +charCode === 47 || +// / +charCode === 45 || +// - (base64url) +charCode === 95; // _ (base64url) + +const isBase64Whitespace = charCode => charCode === 9 || charCode === 10 || charCode === 12 || charCode === 13 || charCode === 32; +const base64Bytes = significant => { + const groups = Math.floor(significant / 4); + const remainder = significant % 4; + return groups * 3 + (remainder === 2 ? 1 : remainder === 3 ? 2 : 0); +}; -/** - * Estimate decoded byte length of a data:// URL *without* allocating large buffers. - * - For base64: compute exact decoded size using length and padding; - * handle %XX at the character-count level (no string allocation). - * - For non-base64: use UTF-8 byteLength of the encoded body as a safe upper bound. - * - * @param {string} url - * @returns {number} - */ -function estimateDataURLDecodedBytes(url) { +// Buffer.byteLength(body, 'base64') uses the raw string length as an allocation +// upper bound even when Buffer.from later ignores characters or stops at '='. +const estimateBase64BufferAllocation = body => { + const len = body.length; + let padding = 0; + if (len > 0 && body.charCodeAt(len - 1) === 61 /* '=' */) { + padding++; + if (len > 1 && body.charCodeAt(len - 2) === 61 /* '=' */) { + padding++; + } + } + return Math.floor((len - padding) * 3 / 4); +}; +const estimatePercentDecodedBase64Bytes = body => { + const len = body.length; + let significant = 0; + let padding = 0; + let invalid = false; + for (let i = 0; i < len; i++) { + let code = body.charCodeAt(i); + if (code === 37 /* '%' */ && isPercentEncodedByte(body, i, len)) { + code = hexValue(body.charCodeAt(i + 1)) * 16 + hexValue(body.charCodeAt(i + 2)); + i += 2; + } + if (isBase64Whitespace(code)) { + continue; + } + if (code === 61 /* '=' */) { + padding++; + continue; + } + if (!isBase64Char(code) || padding > 0) { + invalid = true; + continue; + } + significant++; + } + + // Fetch rejects malformed forgiving-base64 input. Returning the raw-size + // allocation bound keeps that invalid input from becoming a pre-check bypass. + if (invalid || padding > 2 || padding > 0 && (significant + padding) % 4 !== 0 || significant % 4 === 1) { + return estimateBase64BufferAllocation(body); + } + return base64Bytes(significant); +}; +const estimateDataURLBytes = (url, estimateBase64) => { if (!url || typeof url !== 'string') return 0; if (!url.startsWith('data:')) return 0; const comma = url.indexOf(','); @@ -39642,60 +40833,20 @@ function estimateDataURLDecodedBytes(url) { const body = url.slice(comma + 1); const isBase64 = /;base64/i.test(meta); if (isBase64) { - let effectiveLen = body.length; - const len = body.length; // cache length - - for (let i = 0; i < len; i++) { - if (body.charCodeAt(i) === 37 /* '%' */ && i + 2 < len) { - const a = body.charCodeAt(i + 1); - const b = body.charCodeAt(i + 2); - const isHex = (a >= 48 && a <= 57 || a >= 65 && a <= 70 || a >= 97 && a <= 102) && (b >= 48 && b <= 57 || b >= 65 && b <= 70 || b >= 97 && b <= 102); - if (isHex) { - effectiveLen -= 2; - i += 2; - } - } - } - let pad = 0; - let idx = len - 1; - const tailIsPct3D = j => j >= 2 && body.charCodeAt(j - 2) === 37 && - // '%' - body.charCodeAt(j - 1) === 51 && ( - // '3' - body.charCodeAt(j) === 68 || body.charCodeAt(j) === 100); // 'D' or 'd' - - if (idx >= 0) { - if (body.charCodeAt(idx) === 61 /* '=' */) { - pad++; - idx--; - } else if (tailIsPct3D(idx)) { - pad++; - idx -= 3; - } - } - if (pad === 1 && idx >= 0) { - if (body.charCodeAt(idx) === 61 /* '=' */) { - pad++; - } else if (tailIsPct3D(idx)) { - pad++; - } - } - const groups = Math.floor(effectiveLen / 4); - const bytes = groups * 3 - (pad || 0); - return bytes > 0 ? bytes : 0; - } - if (typeof Buffer !== 'undefined' && typeof Buffer.byteLength === 'function') { - return Buffer.byteLength(body, 'utf8'); + return estimateBase64(body); } // Compute UTF-8 byte length directly from UTF-16 code units without allocating // a byte buffer (TextEncoder.encode would defeat the DoS guard on large bodies). - // Using body.length here would undercount non-ASCII (e.g. '€' is 1 code unit - // but 3 UTF-8 bytes). + // Valid %XX triplets count as one decoded byte; this matches the bytes that + // decodeURIComponent(body) would produce before Buffer re-encodes the string. let bytes = 0; for (let i = 0, len = body.length; i < len; i++) { const c = body.charCodeAt(i); - if (c < 0x80) { + if (c === 37 /* '%' */ && isPercentEncodedByte(body, i, len)) { + bytes += 1; + i += 2; + } else if (c < 0x80) { bytes += 1; } else if (c < 0x800) { bytes += 2; @@ -39712,6 +40863,28 @@ function estimateDataURLDecodedBytes(url) { } } return bytes; +}; + +/** + * Estimate the percent-decoded payload size used by Fetch data: URLs. + * + * @param {string} url + * @returns {number} + */ +function estimateDataURLDecodedBytes(url) { + // Fetch removes URL fragments before processing a data: URL. + const fragmentIndex = typeof url === 'string' ? url.indexOf('#') : -1; + return estimateDataURLBytes(fragmentIndex === -1 ? url : url.slice(0, fragmentIndex), estimatePercentDecodedBase64Bytes); +} + +/** + * Estimate the Buffer backing allocation used by Node's raw base64 decoder. + * + * @param {string} url + * @returns {number} + */ +function estimateDataURLBufferAllocation(url) { + return estimateDataURLBytes(url, estimateBase64BufferAllocation); } const zlibOptions = { @@ -39722,29 +40895,97 @@ const brotliOptions = { flush: zlib.constants.BROTLI_OPERATION_FLUSH, finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH }; +const zstdOptions = { + flush: zlib.constants.ZSTD_e_flush, + finishFlush: zlib.constants.ZSTD_e_flush +}; const isBrotliSupported = utils$1.isFunction(zlib.createBrotliDecompress); +const isZstdSupported = utils$1.isFunction(zlib.createZstdDecompress); +const ACCEPT_ENCODING = 'gzip, compress, deflate' + (isBrotliSupported ? ', br' : ''); +const ACCEPT_ENCODING_WITH_ZSTD = ACCEPT_ENCODING + (isZstdSupported ? ', zstd' : ''); +const scheduleProgress = typeof process !== 'undefined' && process.nextTick ? process.nextTick.bind(process) : utils$1.asap; const { http: httpFollow, https: httpsFollow } = followRedirects; const isHttps = /https:?/; -const FORM_DATA_CONTENT_HEADERS$1 = ['content-type', 'content-length']; -function setFormDataHeaders$1(headers, formHeaders, policy) { - if (policy !== 'content-only') { - headers.set(formHeaders); - return; - } - Object.entries(formHeaders).forEach(([key, val]) => { - if (FORM_DATA_CONTENT_HEADERS$1.includes(key.toLowerCase())) { - headers.set(key, val); - } - }); -} // Symbols used to bind a single 'error' listener to a pooled socket and track // the request currently owning that socket across keep-alive reuse (issue #10780). const kAxiosSocketListener = Symbol('axios.http.socketListener'); const kAxiosCurrentReq = Symbol('axios.http.currentReq'); + +// Tags HttpsProxyAgent instances installed by setProxy() so the redirect path +// can strip them without clobbering a user-supplied agent that happens to be +// an HttpsProxyAgent. +const kAxiosInstalledTunnel = Symbol('axios.http.installedTunnel'); + +// Cache of CONNECT-tunneling agents keyed by proxy config so repeat requests +// through the same proxy reuse a single agent (and its socket pool). The +// keyspace is bounded by the set of distinct proxy configs the process uses, +// so unbounded growth is not a concern in practice. +const tunnelingAgentCache = new Map(); +const tunnelingAgentCacheUser = new WeakMap(); +// Minimum minor versions where Node's HTTP Agent supports native proxyEnv +// handling. Checking the selected agent below also covers startup modes such +// as NODE_OPTIONS=--use-env-proxy and --no-use-env-proxy precedence. +const NODE_NATIVE_ENV_PROXY_SUPPORT = { + 22: 21, + 24: 5 +}; +function isNodeNativeEnvProxySupported(nodeVersion = process.versions && process.versions.node) { + if (!nodeVersion) { + return false; + } + const [major, minor] = nodeVersion.split('.').map(part => Number(part)); + if (!Number.isInteger(major) || !Number.isInteger(minor)) { + return false; + } + if (major > 24) { + return true; + } + return NODE_NATIVE_ENV_PROXY_SUPPORT[major] != null && minor >= NODE_NATIVE_ENV_PROXY_SUPPORT[major]; +} +function isNodeEnvProxyEnabled(agent, nodeVersion = process.versions && process.versions.node) { + if (!isNodeNativeEnvProxySupported(nodeVersion)) { + return false; + } + const agentOptions = agent && agent.options; + return Boolean(agentOptions && utils$1.hasOwnProp(agentOptions, 'proxyEnv') && agentOptions.proxyEnv != null); +} +function getProxyEnvAgent(options, configHttpAgent, configHttpsAgent) { + return isHttps.test(options.protocol) ? configHttpsAgent || https.globalAgent : configHttpAgent || http.globalAgent; +} +function getTunnelingAgent(agentOptions, userHttpsAgent) { + const key = agentOptions.protocol + '//' + agentOptions.hostname + ':' + (agentOptions.port || '') + '#' + (agentOptions.auth || ''); + const cache = userHttpsAgent ? tunnelingAgentCacheUser.get(userHttpsAgent) || tunnelingAgentCacheUser.set(userHttpsAgent, new Map()).get(userHttpsAgent) : tunnelingAgentCache; + let agent = cache.get(key); + if (agent) return agent; + // Forward the user's TLS options (custom CA, rejectUnauthorized, client cert, + // etc.) into the tunneling agent so they apply to the origin TLS upgrade + // performed after CONNECT. Our proxy fields take precedence on conflict. + const merged = userHttpsAgent && userHttpsAgent.options ? { + ...userHttpsAgent.options, + ...agentOptions + } : agentOptions; + agent = new HttpsProxyAgent(merged); + if (userHttpsAgent && userHttpsAgent.options) { + const originTLSOptions = { + ...userHttpsAgent.options + }; + const callback = agent.callback; + agent.callback = function axiosTunnelingAgentCallback(req, opts) { + // HttpsProxyAgent v5 reads callback opts for the post-CONNECT origin TLS upgrade. + return callback.call(this, req, { + ...originTLSOptions, + ...opts + }); + }; + } + agent[kAxiosInstalledTunnel] = true; + cache.set(key, agent); + return agent; +} const supportedProtocols = platform.protocols.map(protocol => { return protocol + ':'; }); @@ -39753,7 +40994,7 @@ const supportedProtocols = platform.protocols.map(protocol => { // Decode before composing the `auth` option so credentials such as // `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the // original value for malformed input so a bad encoding never throws. -const decodeURIComponentSafe = value => { +const decodeURIComponentSafe$1 = value => { if (!utils$1.isString(value)) { return value; } @@ -39767,84 +41008,11 @@ const flushOnFinish = (stream, [throttled, flush]) => { stream.on('end', flush).on('error', flush); return throttled; }; -class Http2Sessions { - constructor() { - this.sessions = Object.create(null); - } - getSession(authority, options) { - options = Object.assign({ - sessionTimeout: 1000 - }, options); - let authoritySessions = this.sessions[authority]; - if (authoritySessions) { - let len = authoritySessions.length; - for (let i = 0; i < len; i++) { - const [sessionHandle, sessionOptions] = authoritySessions[i]; - if (!sessionHandle.destroyed && !sessionHandle.closed && util.isDeepStrictEqual(sessionOptions, options)) { - return sessionHandle; - } - } - } - const session = http2.connect(authority, options); - let removed; - const removeSession = () => { - if (removed) { - return; - } - removed = true; - let entries = authoritySessions, - len = entries.length, - i = len; - while (i--) { - if (entries[i][0] === session) { - if (len === 1) { - delete this.sessions[authority]; - } else { - entries.splice(i, 1); - } - if (!session.closed) { - session.close(); - } - return; - } - } - }; - const originalRequestFn = session.request; - const { - sessionTimeout - } = options; - if (sessionTimeout != null) { - let timer; - let streamsCount = 0; - session.request = function () { - const stream = originalRequestFn.apply(this, arguments); - streamsCount++; - if (timer) { - clearTimeout(timer); - timer = null; - } - stream.once('close', () => { - if (! --streamsCount) { - timer = setTimeout(() => { - timer = null; - removeSession(); - }, sessionTimeout); - } - }); - return stream; - }; - } - session.once('close', removeSession); - let entry = [session, options]; - authoritySessions ? authoritySessions.push(entry) : authoritySessions = this.sessions[authority] = [entry]; - return session; - } -} const http2Sessions = new Http2Sessions(); /** - * If the proxy or config beforeRedirects functions are defined, call them with the options - * object. + * If the proxy, auth, sensitive header, or config beforeRedirects functions are defined, + * call them with the options object. * * @param {Object} options - The options object that was passed to the request. * @@ -39854,10 +41022,37 @@ function dispatchBeforeRedirect(options, responseDetails, requestDetails) { if (options.beforeRedirects.proxy) { options.beforeRedirects.proxy(options); } + if (options.beforeRedirects.auth) { + options.beforeRedirects.auth(options); + } + if (options.beforeRedirects.sensitiveHeaders) { + options.beforeRedirects.sensitiveHeaders(options, requestDetails); + } if (options.beforeRedirects.config) { options.beforeRedirects.config(options, responseDetails, requestDetails); } } +function stripMatchingHeaders(headers, sensitiveSet) { + if (!headers) { + return; + } + Object.keys(headers).forEach(header => { + if (sensitiveSet.has(header.toLowerCase())) { + delete headers[header]; + } + }); +} +function isSameOriginRedirect(redirectOptions, requestDetails) { + if (!requestDetails) { + return false; + } + try { + return new URL(requestDetails.url).origin === new URL(redirectOptions.href).origin; + } catch (e) { + // If origin comparison fails, treat the redirect as unsafe. + return false; + } +} /** * If the proxy or config afterRedirects functions are defined, call them with the options @@ -39868,9 +41063,10 @@ function dispatchBeforeRedirect(options, responseDetails, requestDetails) { * * @returns {http.ClientRequestArgs} */ -function setProxy(options, configProxy, location, isRedirect) { +function setProxy(options, configProxy, location, isRedirect, configHttpsAgent, configHttpAgent) { let proxy = configProxy; - if (!proxy && proxy !== false) { + const proxyEnvAgent = getProxyEnvAgent(options, configHttpAgent, configHttpsAgent); + if (!proxy && proxy !== false && !isNodeEnvProxyEnabled(proxyEnvAgent)) { const proxyUrl = getProxyForUrl(location); if (proxyUrl) { if (!shouldBypassProxy(location)) { @@ -39889,6 +41085,13 @@ function setProxy(options, configProxy, location, isRedirect) { } } } + // Strip any tunneling agent we installed for the previous hop so a redirect + // that drops the proxy or crosses an HTTPS↔HTTP boundary doesn't reuse a + // stale one. Match on our Symbol marker so a user-supplied HttpsProxyAgent + // (which won't carry the marker) is left alone. + if (isRedirect && options.agent && options.agent[kAxiosInstalledTunnel]) { + options.agent = undefined; + } if (proxy) { // Read proxy fields without traversing the prototype chain. URL instances expose // username/password/hostname/host/port/protocol via getters on URL.prototype (so @@ -39921,37 +41124,84 @@ function setProxy(options, configProxy, location, isRedirect) { proxy }); } - const base64 = Buffer.from(proxyAuth, 'utf8').toString('base64'); - options.headers['Proxy-Authorization'] = 'Basic ' + base64; } + const targetIsHttps = isHttps.test(options.protocol); + if (targetIsHttps) { + // CONNECT-tunneling path for HTTPS targets. Preserves end-to-end TLS to + // the origin so the proxy cannot inspect the URL, headers, or body — the + // behavior already promised by THREATMODEL.md (T-R9). HttpsProxyAgent + // sends Proxy-Authorization on the CONNECT request only, never on the + // wrapped TLS request, which is why we don't stamp it onto + // options.headers here. If the user already supplied an HttpsProxyAgent, + // they own tunneling end-to-end and we leave them alone; otherwise we + // install our own tunneling agent and forward their TLS options (if any) + // so a custom httpsAgent for cert pinning / rejectUnauthorized still + // applies to the origin TLS upgrade. + if (!(configHttpsAgent instanceof HttpsProxyAgent)) { + const proxyHost = readProxyField('hostname') || readProxyField('host'); + const proxyPort = readProxyField('port'); + const rawProxyProtocol = readProxyField('protocol'); + const normalizedProtocol = rawProxyProtocol ? rawProxyProtocol.includes(':') ? rawProxyProtocol : `${rawProxyProtocol}:` : 'http:'; + // Bracket IPv6 literals for URL parsing; URL.hostname strips the + // brackets again on read so the agent receives the raw form. + const proxyHostForURL = proxyHost && proxyHost.includes(':') && !proxyHost.startsWith('[') ? `[${proxyHost}]` : proxyHost; + const proxyURL = new URL(`${normalizedProtocol}//${proxyHostForURL}${proxyPort ? ':' + proxyPort : ''}`); + const agentOptions = { + protocol: proxyURL.protocol, + hostname: proxyURL.hostname.replace(/^\[|\]$/g, ''), + port: proxyURL.port, + auth: proxyAuth && typeof proxyAuth === 'string' ? proxyAuth : undefined + }; + if (proxyURL.protocol === 'https:') { + agentOptions.ALPNProtocols = ['http/1.1']; + } + const tunnelingAgent = getTunnelingAgent(agentOptions, configHttpsAgent); + // Set both: `options.agent` is consumed by the native https.request path + // (maxRedirects === 0); `options.agents.https` is consumed by + // follow-redirects, which ignores `options.agent` when `options.agents` + // is present. + options.agent = tunnelingAgent; + if (options.agents) { + options.agents.https = tunnelingAgent; + } + } + } else { + // Forward-proxy mode for plaintext HTTP targets. The request line carries + // the absolute URL and the proxy sees everything — acceptable for plain + // HTTP since the wire was already plaintext. + if (proxyAuth) { + const base64 = Buffer.from(proxyAuth, 'utf8').toString('base64'); + options.headers['Proxy-Authorization'] = 'Basic ' + base64; + } - // Preserve a user-supplied Host header (case-insensitive) so callers can override - // the value forwarded to the proxy; otherwise default to the request URL's host. - let hasUserHostHeader = false; - for (const name of Object.keys(options.headers)) { - if (name.toLowerCase() === 'host') { - hasUserHostHeader = true; - break; + // Preserve a user-supplied Host header (case-insensitive) so callers can override + // the value forwarded to the proxy; otherwise default to the request URL's host. + let hasUserHostHeader = false; + for (const name of Object.keys(options.headers)) { + if (name.toLowerCase() === 'host') { + hasUserHostHeader = true; + break; + } + } + if (!hasUserHostHeader) { + options.headers.host = options.hostname + (options.port ? ':' + options.port : ''); + } + const proxyHost = readProxyField('hostname') || readProxyField('host'); + options.hostname = proxyHost; + // Replace 'host' since options is not a URL object + options.host = proxyHost; + options.port = readProxyField('port'); + options.path = location; + const proxyProtocol = readProxyField('protocol'); + if (proxyProtocol) { + options.protocol = proxyProtocol.includes(':') ? proxyProtocol : `${proxyProtocol}:`; } - } - if (!hasUserHostHeader) { - options.headers.host = options.hostname + (options.port ? ':' + options.port : ''); - } - const proxyHost = readProxyField('hostname') || readProxyField('host'); - options.hostname = proxyHost; - // Replace 'host' since options is not a URL object - options.host = proxyHost; - options.port = readProxyField('port'); - options.path = location; - const proxyProtocol = readProxyField('protocol'); - if (proxyProtocol) { - options.protocol = proxyProtocol.includes(':') ? proxyProtocol : `${proxyProtocol}:`; } } options.beforeRedirects.proxy = function beforeRedirect(redirectOptions) { // Configure proxy for redirected request, passing the original config proxy to apply // the exact same logic as if the redirected request was performed by axios directly. - setProxy(redirectOptions, configProxy, redirectOptions.href, true); + setProxy(redirectOptions, configProxy, redirectOptions.href, true, configHttpsAgent, configHttpAgent); }; } const isHttpAdapterSupported = typeof process !== 'undefined' && utils$1.kindOf(process) === 'process'; @@ -40034,16 +41284,30 @@ const http2Transport = { /*eslint consistent-return:0*/ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) { - const own = key => utils$1.hasOwnProp(config, key) ? config[key] : undefined; + // Read config pollution-safely: own properties and members inherited from + // a non-Object.prototype source (e.g. an Object.create(defaults) template) + // are honored, but values injected onto a polluted Object.prototype are + // ignored. All behavior-affecting reads in this adapter go through own() + // so the protection boundary stays consistent. + const own = key => utils$1.getSafeProp(config, key); + const transitional = own('transitional') || transitionalDefaults; let data = own('data'); let lookup = own('lookup'); let family = own('family'); let httpVersion = own('httpVersion'); if (httpVersion === undefined) httpVersion = 1; let http2Options = own('http2Options'); + const httpAgent = own('httpAgent'); + const httpsAgent = own('httpsAgent'); + const configProxy = own('proxy'); const responseType = own('responseType'); const responseEncoding = own('responseEncoding'); - const method = config.method.toUpperCase(); + const socketPath = own('socketPath'); + const method = own('method').toUpperCase(); + const maxRedirects = own('maxRedirects'); + const maxBodyLength = own('maxBodyLength'); + const maxContentLength = own('maxContentLength'); + const decompress = own('decompress'); let isDone; let rejected = false; let req; @@ -40074,7 +41338,7 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { try { abortEmitter.emit('abort', !reason || reason.type ? new CanceledError(null, config, req) : reason); } catch (err) { - console.warn('emit error', err); + // ignore emit errors } } function clearConnectPhaseTimer() { @@ -40084,10 +41348,11 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { } } function createTimeoutError() { - let timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded'; - const transitional = config.transitional || transitionalDefaults; - if (config.timeoutErrorMessage) { - timeoutErrorMessage = config.timeoutErrorMessage; + const configTimeout = own('timeout'); + let timeoutErrorMessage = configTimeout ? 'timeout of ' + configTimeout + 'ms exceeded' : 'timeout exceeded'; + const configTimeoutErrorMessage = own('timeoutErrorMessage'); + if (configTimeoutErrorMessage) { + timeoutErrorMessage = configTimeoutErrorMessage; } return new AxiosError(timeoutErrorMessage, transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED, config, req); } @@ -40130,17 +41395,22 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { }); // Parse url - const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls); - const parsed = new URL(fullPath, platform.hasBrowserEnv ? platform.origin : undefined); + const fullPath = buildFullPath(own('baseURL'), own('url'), own('allowAbsoluteUrls'), config); + // Unix-socket requests (own socketPath) commonly pass a path-only url + // like '/foo'; supply a synthetic base so new URL() can still parse it. + // Use the own-property value (not config.socketPath) so a polluted + // prototype cannot influence URL base selection. + const urlBase = socketPath ? 'http://localhost' : platform.hasBrowserEnv ? platform.origin : undefined; + const parsed = new URL(fullPath, urlBase); const protocol = parsed.protocol || supportedProtocols[0]; if (protocol === 'data:') { // Apply the same semantics as HTTP: only enforce if a finite, non-negative cap is set. - if (config.maxContentLength > -1) { - // Use the exact string passed to fromDataURI (config.url); fall back to fullPath if needed. - const dataUrl = String(config.url || fullPath || ''); - const estimated = estimateDataURLDecodedBytes(dataUrl); - if (estimated > config.maxContentLength) { - return reject(new AxiosError('maxContentLength size of ' + config.maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config)); + if (maxContentLength > -1) { + // Use the exact string passed to fromDataURI (the configured url); fall back to fullPath if needed. + const dataUrl = String(own('url') || fullPath || ''); + const estimated = estimateDataURLBufferAllocation(dataUrl); + if (estimated > maxContentLength) { + return reject(new AxiosError('maxContentLength size of ' + maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config)); } } let convertedData; @@ -40153,7 +41423,7 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { }); } try { - convertedData = fromDataURI(config.url, responseType === 'blob', { + convertedData = fromDataURI(own('url'), responseType === 'blob', { Blob: config.env && config.env.Blob }); } catch (err) { @@ -40204,7 +41474,7 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { }); // support for https://www.npmjs.com/package/form-data api } else if (utils$1.isFormData(data) && utils$1.isFunction(data.getHeaders) && data.getHeaders !== Object.prototype.getHeaders) { - setFormDataHeaders$1(headers, data.getHeaders(), own('formDataHeaderPolicy')); + setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy')); if (!headers.hasContentLength()) { try { const knownLength = await util.promisify(data.getLength).call(data); @@ -40227,7 +41497,7 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { // Add Content-Length header if data exists headers.setContentLength(data.length, false); - if (config.maxBodyLength > -1 && data.length > config.maxBodyLength) { + if (maxBodyLength > -1 && data.length > maxBodyLength) { return reject(new AxiosError('Request body larger than maxBodyLength limit', AxiosError.ERR_BAD_REQUEST, config)); } } @@ -40247,44 +41517,43 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { data = stream.pipeline([data, new AxiosTransformStream({ maxRate: utils$1.toFiniteNumber(maxUploadRate) })], utils$1.noop); - onUploadProgress && data.on('progress', flushOnFinish(data, progressEventDecorator(contentLength, progressEventReducer(asyncDecorator(onUploadProgress), false, 3)))); + onUploadProgress && data.on('progress', flushOnFinish(data, progressEventDecorator(contentLength, progressEventReducer(asyncDecorator(onUploadProgress, scheduleProgress), false, 3)))); } // HTTP basic authentication let auth = undefined; const configAuth = own('auth'); if (configAuth) { - const username = configAuth.username || ''; - const password = configAuth.password || ''; + const username = utils$1.getSafeProp(configAuth, 'username') || ''; + const password = utils$1.getSafeProp(configAuth, 'password') || ''; auth = username + ':' + password; } - if (!auth && parsed.username) { - const urlUsername = decodeURIComponentSafe(parsed.username); - const urlPassword = decodeURIComponentSafe(parsed.password); + if (!auth && (parsed.username || parsed.password)) { + const urlUsername = decodeURIComponentSafe$1(parsed.username); + const urlPassword = decodeURIComponentSafe$1(parsed.password); auth = urlUsername + ':' + urlPassword; } auth && headers.delete('authorization'); let path$1; try { - path$1 = buildURL(parsed.pathname + parsed.search, config.params, config.paramsSerializer).replace(/^\?/, ''); + path$1 = buildURL(parsed.pathname + parsed.search, own('params'), own('paramsSerializer')).replace(/^\?/, ''); } catch (err) { - const customErr = new Error(err.message); - customErr.config = config; - customErr.url = config.url; - customErr.exists = true; - return reject(customErr); + return reject(AxiosError.from(err, AxiosError.ERR_BAD_REQUEST, config, null, null, { + url: own('url'), + exists: true + })); } - headers.set('Accept-Encoding', 'gzip, compress, deflate' + (isBrotliSupported ? ', br' : ''), false); + headers.set('Accept-Encoding', utils$1.hasOwnProp(transitional, 'advertiseZstdAcceptEncoding') && transitional.advertiseZstdAcceptEncoding === true ? ACCEPT_ENCODING_WITH_ZSTD : ACCEPT_ENCODING, false); // Null-prototype to block prototype pollution gadgets on properties read // directly by Node's http.request (e.g. insecureHTTPParser, lookup). const options = Object.assign(Object.create(null), { path: path$1, method: method, - headers: headers.toJSON(), + headers: toByteStringHeaderObject(headers), agents: { - http: config.httpAgent, - https: config.httpsAgent + http: httpAgent, + https: httpsAgent }, auth, protocol, @@ -40296,52 +41565,105 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { // cacheable-lookup integration hotfix !utils$1.isUndefined(lookup) && (options.lookup = lookup); - if (config.socketPath) { - if (typeof config.socketPath !== 'string') { + if (socketPath) { + if (typeof socketPath !== 'string') { return reject(new AxiosError('socketPath must be a string', AxiosError.ERR_BAD_OPTION_VALUE, config)); } - if (config.allowedSocketPaths != null) { - const allowed = Array.isArray(config.allowedSocketPaths) ? config.allowedSocketPaths : [config.allowedSocketPaths]; - const resolvedSocket = path.resolve(config.socketPath); + const allowedSocketPaths = own('allowedSocketPaths'); + if (allowedSocketPaths != null) { + const allowed = Array.isArray(allowedSocketPaths) ? allowedSocketPaths : [allowedSocketPaths]; + const resolvedSocket = path.resolve(socketPath); const isAllowed = allowed.some(entry => typeof entry === 'string' && path.resolve(entry) === resolvedSocket); if (!isAllowed) { - return reject(new AxiosError(`socketPath "${config.socketPath}" is not permitted by allowedSocketPaths`, AxiosError.ERR_BAD_OPTION_VALUE, config)); + return reject(new AxiosError(`socketPath "${socketPath}" is not permitted by allowedSocketPaths`, AxiosError.ERR_BAD_OPTION_VALUE, config)); } } - options.socketPath = config.socketPath; + options.socketPath = socketPath; } else { options.hostname = parsed.hostname.startsWith('[') ? parsed.hostname.slice(1, -1) : parsed.hostname; options.port = parsed.port; - setProxy(options, config.proxy, protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path); + setProxy(options, configProxy, protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path, false, httpsAgent, httpAgent); } let transport; let isNativeTransport = false; + // True only for the follow-redirects transport, which applies + // options.maxBodyLength itself. Every other transport (http2, native + // http/https, a user-supplied custom transport) needs the explicit + // byte-counting pipeline below to enforce maxBodyLength on streamed uploads. + let transportEnforcesMaxBodyLength = false; const isHttpsRequest = isHttps.test(options.protocol); - options.agent = isHttpsRequest ? config.httpsAgent : config.httpAgent; + // Don't clobber a CONNECT-tunneling agent installed by setProxy() for an + // HTTPS target. + if (options.agent == null) { + options.agent = isHttpsRequest ? httpsAgent : httpAgent; + } if (isHttp2) { transport = http2Transport; } else { const configTransport = own('transport'); if (configTransport) { transport = configTransport; - } else if (config.maxRedirects === 0) { + } else if (maxRedirects === 0) { transport = isHttpsRequest ? https : http; isNativeTransport = true; } else { - if (config.maxRedirects) { - options.maxRedirects = config.maxRedirects; + transportEnforcesMaxBodyLength = true; + options.sensitiveHeaders = []; + if (maxRedirects) { + options.maxRedirects = maxRedirects; } const configBeforeRedirect = own('beforeRedirect'); if (configBeforeRedirect) { options.beforeRedirects.config = configBeforeRedirect; } + if (auth) { + // Restore HTTP Basic credentials on same-origin redirects only. + // follow-redirects >= 1.15.8 strips Authorization on every redirect (see #6929); + // cross-origin stripping is the documented mitigation for T-R2 in THREATMODEL.md + // and is preserved by deliberately not restoring on origin change. + const requestOrigin = parsed.origin; + const authToRestore = auth; + options.beforeRedirects.auth = function beforeRedirectAuth(redirectOptions) { + try { + if (new URL(redirectOptions.href).origin === requestOrigin) { + redirectOptions.auth = authToRestore; + } + } catch (e) { + // ignore malformed URL: leaving auth stripped is fail-safe + } + }; + } + const sensitiveHeaders = own('sensitiveHeaders'); + if (sensitiveHeaders != null) { + if (!utils$1.isArray(sensitiveHeaders)) { + return reject(new AxiosError('sensitiveHeaders must be an array of strings', AxiosError.ERR_BAD_OPTION_VALUE, config)); + } + const sensitiveSet = new Set(); + for (const header of sensitiveHeaders) { + if (!utils$1.isString(header)) { + return reject(new AxiosError('sensitiveHeaders must be an array of strings', AxiosError.ERR_BAD_OPTION_VALUE, config)); + } + sensitiveSet.add(header.toLowerCase()); + } + if (sensitiveSet.size) { + options.sensitiveHeaders = Array.from(sensitiveSet); + options.beforeRedirects.sensitiveHeaders = function beforeRedirectSensitiveHeaders(redirectOptions, requestDetails) { + if (!isSameOriginRedirect(redirectOptions, requestDetails)) { + stripMatchingHeaders(redirectOptions.headers, sensitiveSet); + } + }; + } + } transport = isHttpsRequest ? httpsFollow : httpFollow; } } - if (config.maxBodyLength > -1) { - options.maxBodyLength = config.maxBodyLength; + + // Set an explicit maxBodyLength option for transports that inspect it. + // When maxBodyLength is -1 (default/unlimited), use Infinity so + // follow-redirects does not fall back to its own 10MB default. + if (maxBodyLength > -1) { + options.maxBodyLength = maxBodyLength; } else { - // follow-redirects does not skip comparison, so it should always succeed for axios -1 unlimited options.maxBodyLength = Infinity; } @@ -40360,7 +41682,7 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { const transformStream = new AxiosTransformStream({ maxRate: utils$1.toFiniteNumber(maxDownloadRate) }); - onDownloadProgress && transformStream.on('progress', flushOnFinish(transformStream, progressEventDecorator(responseLength, progressEventReducer(asyncDecorator(onDownloadProgress), true, 3)))); + onDownloadProgress && transformStream.on('progress', flushOnFinish(transformStream, progressEventDecorator(responseLength, progressEventReducer(asyncDecorator(onDownloadProgress, scheduleProgress), true, 3)))); streams.push(transformStream); } @@ -40371,7 +41693,7 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { const lastRequest = res.req || req; // if decompress disabled we should not decompress - if (config.decompress !== false && res.headers['content-encoding']) { + if (decompress !== false && res.headers['content-encoding']) { // if no content, but headers still say that it is encoded, // remove the header not confuse downstream operations if (method === 'HEAD' || res.statusCode === 204) { @@ -40403,6 +41725,13 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { streams.push(zlib.createBrotliDecompress(brotliOptions)); delete res.headers['content-encoding']; } + break; + case 'zstd': + if (isZstdSupported) { + streams.push(zlib.createZstdDecompress(zstdOptions)); + delete res.headers['content-encoding']; + } + break; } } responseStream = streams.length > 1 ? stream.pipeline(streams, utils$1.noop) : streams[0]; @@ -40416,8 +41745,8 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { if (responseType === 'stream') { // Enforce maxContentLength on streamed responses; previously this // was applied only to buffered responses. - if (config.maxContentLength > -1) { - const limit = config.maxContentLength; + if (maxContentLength > -1) { + const limit = maxContentLength; const source = responseStream; async function* enforceMaxContentLength() { let totalResponseBytes = 0; @@ -40443,11 +41772,11 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { totalResponseBytes += chunk.length; // make sure the content length is not over the maxContentLength if specified - if (config.maxContentLength > -1 && totalResponseBytes > config.maxContentLength) { + if (maxContentLength > -1 && totalResponseBytes > maxContentLength) { // stream.destroy() emit aborted event before calling reject() on Node.js v16 rejected = true; responseStream.destroy(); - abort(new AxiosError('maxContentLength size of ' + config.maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config, lastRequest)); + abort(new AxiosError('maxContentLength size of ' + maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config, lastRequest)); } }); responseStream.on('aborted', function handlerStreamAborted() { @@ -40510,7 +41839,11 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { const boundSockets = new Set(); req.on('socket', function handleRequestSocket(socket) { // default interval of sending ack packet is 1 minute - socket.setKeepAlive(true, 1000 * 60); + // proxy agents (e.g. agent-base) may return a generic Duplex stream + // that doesn't have setKeepAlive, so guard before calling + if (typeof socket.setKeepAlive === 'function') { + socket.setKeepAlive(true, 1000 * 60); + } // Install a single 'error' listener per socket (not per request) to avoid // accumulating listeners on pooled keep-alive sockets that get reassigned @@ -40540,9 +41873,9 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { }); // Handle request timeout - if (config.timeout) { + if (own('timeout')) { // This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types. - const timeout = parseInt(config.timeout, 10); + const timeout = parseInt(own('timeout'), 10); if (Number.isNaN(timeout)) { abort(new AxiosError('error trying to parse `config.timeout` to int', AxiosError.ERR_BAD_OPTION_VALUE, config, req)); return; @@ -40586,12 +41919,13 @@ var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { } }); - // Enforce maxBodyLength for streamed uploads on the native http/https - // transport (maxRedirects === 0); follow-redirects enforces it on the - // other path. + // Enforce maxBodyLength for streamed uploads on every transport that + // does not apply options.maxBodyLength itself (native http/https, http2, + // and user-supplied custom transports). The follow-redirects transport + // enforces it on the redirected HTTP/1 path. let uploadStream = data; - if (config.maxBodyLength > -1 && config.maxRedirects === 0) { - const limit = config.maxBodyLength; + if (maxBodyLength > -1 && !transportEnforcesMaxBodyLength) { + const limit = maxBodyLength; let bytesSent = 0; uploadStream = stream.pipeline([data, new stream.Transform({ transform(chunk, _enc, cb) { @@ -40654,7 +41988,11 @@ var cookies = platform.hasStandardBrowserEnv ? const cookie = cookies[i].replace(/^\s+/, ''); const eq = cookie.indexOf('='); if (eq !== -1 && cookie.slice(0, eq) === name) { - return decodeURIComponent(cookie.slice(eq + 1)); + try { + return decodeURIComponent(cookie.slice(eq + 1)); + } catch (e) { + return cookie.slice(eq + 1); + } } } return null; @@ -40675,6 +42013,12 @@ var cookies = platform.hasStandardBrowserEnv ? const headersToObject = thing => thing instanceof AxiosHeaders ? { ...thing } : thing; +const ownEnumerableKeys = thing => { + if (Object.getOwnPropertySymbols && Object.getOwnPropertyDescriptor) { + return Object.keys(thing).concat(Object.getOwnPropertySymbols(thing).filter(symbol => Object.getOwnPropertyDescriptor(thing, symbol).enumerable)); + } + return Object.keys(thing); +}; /** * Config-specific merge-function which creates a new config-object @@ -40687,6 +42031,7 @@ const headersToObject = thing => thing instanceof AxiosHeaders ? { */ function mergeConfig(config1, config2) { // eslint-disable-next-line no-param-reassign + config1 = config1 || {}; config2 = config2 || {}; // Use a null-prototype object so that downstream reads such as `config.auth` @@ -40738,6 +42083,23 @@ function mergeConfig(config1, config2) { return getMergedValue(undefined, a); } } + function getMergedTransitionalOption(prop) { + const transitional2 = utils$1.hasOwnProp(config2, 'transitional') ? config2.transitional : undefined; + if (!utils$1.isUndefined(transitional2)) { + if (utils$1.isPlainObject(transitional2)) { + if (utils$1.hasOwnProp(transitional2, prop)) { + return transitional2[prop]; + } + } else { + return undefined; + } + } + const transitional1 = utils$1.hasOwnProp(config1, 'transitional') ? config1.transitional : undefined; + if (utils$1.isPlainObject(transitional1) && utils$1.hasOwnProp(transitional1, prop)) { + return transitional1[prop]; + } + return undefined; + } // eslint-disable-next-line consistent-return function mergeDirectKeys(a, b, prop) { @@ -40779,7 +42141,7 @@ function mergeConfig(config1, config2) { validateStatus: mergeDirectKeys, headers: (a, b, prop) => mergeDeepProperties(headersToObject(a), headersToObject(b), prop, true) }; - utils$1.forEach(Object.keys({ + utils$1.forEach(ownEnumerableKeys({ ...config1, ...config2 }), function computeConfigValue(prop) { @@ -40790,20 +42152,14 @@ function mergeConfig(config1, config2) { const configValue = merge(a, b, prop); utils$1.isUndefined(configValue) && merge !== mergeDirectKeys || (config[prop] = configValue); }); - return config; -} - -const FORM_DATA_CONTENT_HEADERS = ['content-type', 'content-length']; -function setFormDataHeaders(headers, formHeaders, policy) { - if (policy !== 'content-only') { - headers.set(formHeaders); - return; - } - Object.entries(formHeaders).forEach(([key, val]) => { - if (FORM_DATA_CONTENT_HEADERS.includes(key.toLowerCase())) { - headers.set(key, val); + if (utils$1.hasOwnProp(config2, 'validateStatus') && utils$1.isUndefined(config2.validateStatus) && getMergedTransitionalOption('validateStatusUndefinedResolves') === false) { + if (utils$1.hasOwnProp(config1, 'validateStatus')) { + config.validateStatus = getMergedValue(undefined, config1.validateStatus); + } else { + delete config.validateStatus; } - }); + } + return config; } /** @@ -40814,8 +42170,8 @@ function setFormDataHeaders(headers, formHeaders, policy) { * * @returns {string} UTF-8 bytes as a Latin-1 string */ -const encodeUTF8 = str => encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16))); -var resolveConfig = config => { +const encodeUTF8$1 = str => encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16))); +function resolveConfig(config) { const newConfig = mergeConfig({}, config); // Read only own properties to prevent prototype pollution gadgets @@ -40831,15 +42187,21 @@ var resolveConfig = config => { const allowAbsoluteUrls = own('allowAbsoluteUrls'); const url = own('url'); newConfig.headers = headers = AxiosHeaders.from(headers); - newConfig.url = buildURL(buildFullPath(baseURL, url, allowAbsoluteUrls), config.params, config.paramsSerializer); + newConfig.url = buildURL(buildFullPath(baseURL, url, allowAbsoluteUrls, newConfig), own('params'), own('paramsSerializer')); // HTTP basic authentication if (auth) { - headers.set('Authorization', 'Basic ' + btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8(auth.password) : ''))); + const username = utils$1.getSafeProp(auth, 'username') || ''; + const password = utils$1.getSafeProp(auth, 'password') || ''; + try { + headers.set('Authorization', 'Basic ' + btoa(username + ':' + (password ? encodeUTF8$1(password) : ''))); + } catch (e) { + throw AxiosError.from(e, AxiosError.ERR_BAD_OPTION_VALUE, config); + } } if (utils$1.isFormData(data)) { - if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv) { - headers.setContentType(undefined); // browser handles it + if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv || utils$1.isReactNative(data)) { + headers.setContentType(undefined); // browser/web worker/RN handles it } else if (utils$1.isFunction(data.getHeaders)) { // Node.js FormData (like form-data package) setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy')); @@ -40867,7 +42229,7 @@ var resolveConfig = config => { } } return newConfig; -}; +} const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined'; var xhrAdapter = isXHRAdapterSupported && function (config) { @@ -40989,7 +42351,7 @@ var xhrAdapter = isXHRAdapterSupported && function (config) { // Add headers to the request if ('setRequestHeader' in request) { - utils$1.forEach(requestHeaders.toJSON(), function setRequestHeader(val, key) { + utils$1.forEach(toByteStringHeaderObject(requestHeaders), function setRequestHeader(val, key) { request.setRequestHeader(key, val); }); } @@ -41036,6 +42398,7 @@ var xhrAdapter = isXHRAdapterSupported && function (config) { const protocol = parseProtocol(_config.url); if (protocol && !platform.protocols.includes(protocol)) { reject(new AxiosError('Unsupported protocol ' + protocol + ':', AxiosError.ERR_BAD_REQUEST, config)); + done(); return; } @@ -41045,41 +42408,52 @@ var xhrAdapter = isXHRAdapterSupported && function (config) { }; const composeSignals = (signals, timeout) => { - const { - length - } = signals = signals ? signals.filter(Boolean) : []; - if (timeout || length) { - let controller = new AbortController(); - let aborted; - const onabort = function (reason) { - if (!aborted) { - aborted = true; - unsubscribe(); - const err = reason instanceof Error ? reason : this.reason; - controller.abort(err instanceof AxiosError ? err : new CanceledError(err instanceof Error ? err.message : err)); - } - }; - let timer = timeout && setTimeout(() => { - timer = null; - onabort(new AxiosError(`timeout of ${timeout}ms exceeded`, AxiosError.ETIMEDOUT)); - }, timeout); - const unsubscribe = () => { - if (signals) { - timer && clearTimeout(timer); - timer = null; - signals.forEach(signal => { - signal.unsubscribe ? signal.unsubscribe(onabort) : signal.removeEventListener('abort', onabort); - }); - signals = null; - } - }; - signals.forEach(signal => signal.addEventListener('abort', onabort)); - const { - signal - } = controller; - signal.unsubscribe = () => utils$1.asap(unsubscribe); - return signal; + signals = signals ? signals.filter(Boolean) : []; + if (!timeout && !signals.length) { + return; } + const controller = new AbortController(); + let aborted = false; + const onabort = function (reason) { + if (!aborted) { + aborted = true; + unsubscribe(); + const err = reason instanceof Error ? reason : this.reason; + controller.abort(err instanceof AxiosError ? err : new CanceledError(err instanceof Error ? err.message : err)); + } + }; + let timer = timeout && setTimeout(() => { + timer = null; + onabort(new AxiosError(`timeout of ${timeout}ms exceeded`, AxiosError.ETIMEDOUT)); + }, timeout); + const unsubscribe = () => { + if (!signals) { + return; + } + timer && clearTimeout(timer); + timer = null; + signals.forEach(signal => { + signal.unsubscribe ? signal.unsubscribe(onabort) : signal.removeEventListener('abort', onabort); + }); + signals = null; + }; + signals.forEach(signal => { + if (aborted) { + return; + } + if (signal.aborted) { + onabort.call(signal); + return; + } + signal.addEventListener('abort', onabort, { + once: true + }); + }); + const { + signal + } = controller; + signal.unsubscribe = () => utils$1.asap(unsubscribe); + return signal; }; const streamChunk = function* (chunk, chunkSize) { @@ -41168,6 +42542,31 @@ const DEFAULT_CHUNK_SIZE = 64 * 1024; const { isFunction } = utils$1; + +/** + * Encode a UTF-8 string to a Latin-1 byte string for use with btoa(). + * This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern. + * + * @param {string} str The string to encode + * + * @returns {string} UTF-8 bytes as a Latin-1 string + */ +const encodeUTF8 = str => encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16))); + +// Node's WHATWG URL parser returns `username` and `password` percent-encoded. +// Decode before composing the `auth` option so credentials such as +// `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the +// original value for malformed input so a bad encoding never throws. +const decodeURIComponentSafe = value => { + if (!utils$1.isString(value)) { + return value; + } + try { + return decodeURIComponent(value); + } catch (error) { + return value; + } +}; const test = (fn, ...args) => { try { return !!fn(...args); @@ -41175,9 +42574,16 @@ const test = (fn, ...args) => { return false; } }; +const maybeWithAuthCredentials = url => { + const protocolIndex = url.indexOf('://'); + let urlToCheck = url; + if (protocolIndex !== -1) { + urlToCheck = urlToCheck.slice(protocolIndex + 3); + } + return urlToCheck.includes('@') || urlToCheck.includes(':'); +}; const factory = env => { - var _utils$global; - const globalObject = (_utils$global = utils$1.global) !== null && _utils$global !== void 0 ? _utils$global : globalThis; + const globalObject = utils$1.global !== undefined && utils$1.global !== null ? utils$1.global : globalThis; const { ReadableStream, TextEncoder @@ -41279,6 +42685,7 @@ const factory = env => { } = resolveConfig(config); const hasMaxContentLength = utils$1.isNumber(maxContentLength) && maxContentLength > -1; const hasMaxBodyLength = utils$1.isNumber(maxBodyLength) && maxBodyLength > -1; + const own = key => utils$1.hasOwnProp(config, key) ? config[key] : undefined; let _fetch = envFetch || fetch; responseType = responseType ? (responseType + '').toLowerCase() : 'text'; let composedSignal = composeSignals([signal, cancelToken && cancelToken.toAbortSignal()], timeout); @@ -41287,7 +42694,46 @@ const factory = env => { composedSignal.unsubscribe(); }); let requestContentLength; + + // AxiosError we raise while the request body is being streamed. Captured + // by identity so the catch block can surface it directly, regardless of + // how the runtime wraps the resulting fetch rejection (undici exposes it + // as `err.cause`; some browsers drop the original error entirely). + let pendingBodyError = null; + const maxBodyLengthError = () => new AxiosError('Request body larger than maxBodyLength limit', AxiosError.ERR_BAD_REQUEST, config, request); try { + // HTTP basic authentication + let auth = undefined; + const configAuth = own('auth'); + if (configAuth) { + const username = utils$1.getSafeProp(configAuth, 'username') || ''; + const password = utils$1.getSafeProp(configAuth, 'password') || ''; + auth = { + username, + password + }; + } + if (maybeWithAuthCredentials(url)) { + const parsedURL = new URL(url, platform.origin); + if (!auth && (parsedURL.username || parsedURL.password)) { + const urlUsername = decodeURIComponentSafe(parsedURL.username); + const urlPassword = decodeURIComponentSafe(parsedURL.password); + auth = { + username: urlUsername, + password: urlPassword + }; + } + if (parsedURL.username || parsedURL.password) { + parsedURL.username = ''; + parsedURL.password = ''; + url = parsedURL.href; + } + } + if (auth) { + headers.delete('authorization'); + headers.set('Authorization', 'Basic ' + btoa(encodeUTF8((auth.username || '') + ':' + (auth.password || '')))); + } + // Enforce maxContentLength for data: URLs up-front so we never materialize // an oversized payload. The HTTP adapter applies the same check (see http.js // "if (protocol === 'data:')" branch). @@ -41298,30 +42744,54 @@ const factory = env => { } } - // Enforce maxBodyLength against the outbound request body before dispatch. - // Mirrors http.js behavior (ERR_BAD_REQUEST / 'Request body larger than - // maxBodyLength limit'). Skip when the body length cannot be determined - // (e.g. a live ReadableStream supplied by the caller). + // Enforce maxBodyLength against known-size bodies before dispatch using + // the body's *actual* size — never a caller-declared Content-Length, + // which could under-report to slip an oversized body past the check. + // Unknown-size streams return undefined here and are counted per-chunk + // below as fetch consumes them. if (hasMaxBodyLength && method !== 'get' && method !== 'head') { - const outboundLength = await resolveBodyLength(headers, data); - if (typeof outboundLength === 'number' && isFinite(outboundLength) && outboundLength > maxBodyLength) { - throw new AxiosError('Request body larger than maxBodyLength limit', AxiosError.ERR_BAD_REQUEST, config, request); + const outboundLength = await getBodyLength(data); + if (typeof outboundLength === 'number' && isFinite(outboundLength)) { + requestContentLength = outboundLength; + if (outboundLength > maxBodyLength) { + throw maxBodyLengthError(); + } } } - if (onUploadProgress && supportsRequestStream && method !== 'get' && method !== 'head' && (requestContentLength = await resolveBodyLength(headers, data)) !== 0) { - let _request = new Request(url, { - method: 'POST', - body: data, - duplex: 'half' - }); - let contentTypeHeader; - if (utils$1.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) { - headers.setContentType(contentTypeHeader); + + // A streamed body under maxBodyLength must be counted as fetch consumes + // it; its size is never trusted from a caller-declared Content-Length. + const mustEnforceStreamBody = hasMaxBodyLength && (utils$1.isReadableStream(data) || utils$1.isStream(data)); + const trackRequestStream = (stream, onProgress, flush) => trackStream(stream, DEFAULT_CHUNK_SIZE, loadedBytes => { + if (hasMaxBodyLength && loadedBytes > maxBodyLength) { + throw pendingBodyError = maxBodyLengthError(); } - if (_request.body) { - const [onProgress, flush] = progressEventDecorator(requestContentLength, progressEventReducer(asyncDecorator(onUploadProgress))); - data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush); + onProgress && onProgress(loadedBytes); + }, flush); + if (supportsRequestStream && method !== 'get' && method !== 'head' && (onUploadProgress || mustEnforceStreamBody)) { + requestContentLength = requestContentLength == null ? await resolveBodyLength(headers, data) : requestContentLength; + + // A declared length of 0 is only trusted to skip the wrap when we are + // not enforcing a stream limit (which must not rely on that header). + if (requestContentLength !== 0 || mustEnforceStreamBody) { + let _request = new Request(url, { + method: 'POST', + body: data, + duplex: 'half' + }); + let contentTypeHeader; + if (utils$1.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) { + headers.setContentType(contentTypeHeader); + } + if (_request.body) { + const [onProgress, flush] = onUploadProgress && progressEventDecorator(requestContentLength, progressEventReducer(asyncDecorator(onUploadProgress))) || []; + data = trackRequestStream(_request.body, onProgress, flush); + } } + } else if (mustEnforceStreamBody && !isRequestSupported && isReadableStreamSupported && method !== 'get' && method !== 'head') { + data = trackRequestStream(data); + } else if (mustEnforceStreamBody && isRequestSupported && !supportsRequestStream && method !== 'get' && method !== 'head') { + throw new AxiosError('Stream request bodies are not supported by the current fetch implementation', AxiosError.ERR_NOT_SUPPORT, config, request); } if (!utils$1.isString(withCredentials)) { withCredentials = withCredentials ? 'include' : 'omit'; @@ -41346,18 +42816,19 @@ const factory = env => { ...fetchOptions, signal: composedSignal, method: method.toUpperCase(), - headers: headers.normalize().toJSON(), + headers: toByteStringHeaderObject(headers.normalize()), body: data, duplex: 'half', credentials: isCredentialsSupported ? withCredentials : undefined }; request = isRequestSupported && new Request(url, resolvedOptions); let response = await (isRequestSupported ? _fetch(request, fetchOptions) : _fetch(url, resolvedOptions)); + const responseHeaders = AxiosHeaders.from(response.headers); // Cheap pre-check: if the server honestly declares a content-length that // already exceeds the cap, reject before we start streaming. if (hasMaxContentLength) { - const declaredLength = utils$1.toFiniteNumber(response.headers.get('content-length')); + const declaredLength = utils$1.toFiniteNumber(responseHeaders.getContentLength()); if (declaredLength != null && declaredLength > maxContentLength) { throw new AxiosError('maxContentLength size of ' + maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config, request); } @@ -41368,7 +42839,7 @@ const factory = env => { ['status', 'statusText', 'headers'].forEach(prop => { options[prop] = response[prop]; }); - const responseContentLength = utils$1.toFiniteNumber(response.headers.get('content-length')); + const responseContentLength = utils$1.toFiniteNumber(responseHeaders.getContentLength()); const [onProgress, flush] = onDownloadProgress && progressEventDecorator(responseContentLength, progressEventReducer(asyncDecorator(onDownloadProgress), true)) || []; let bytesRead = 0; const onChunkProgress = loadedBytes => { @@ -41427,13 +42898,48 @@ const factory = env => { const canceledError = composedSignal.reason; canceledError.config = config; request && (canceledError.request = request); - err !== canceledError && (canceledError.cause = err); + if (err !== canceledError) { + // Non-enumerable to match native Error `cause` semantics so loggers + // don't recurse into circular fetch internals (see #7205). + Object.defineProperty(canceledError, 'cause', { + __proto__: null, + value: err, + writable: true, + enumerable: false, + configurable: true + }); + } throw canceledError; } + + // Surface a maxBodyLength violation we raised while the request body was + // being streamed. Matching by identity (rather than reading + // `err.cause.isAxiosError`) keeps the error deterministic across runtimes + // and avoids both prototype-pollution reads and mis-attributing a foreign + // AxiosError that merely happened to land in `err.cause`. + if (pendingBodyError) { + request && !pendingBodyError.request && (pendingBodyError.request = request); + throw pendingBodyError; + } + + // Re-throw AxiosErrors we raised synchronously (data: URL / content-length + // pre-checks, response size enforcement) without re-wrapping them. + if (err instanceof AxiosError) { + request && !err.request && (err.request = request); + throw err; + } if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) { - throw Object.assign(new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request, err && err.response), { - cause: err.cause || err + const networkError = new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request, err && err.response); + // Non-enumerable to match native Error `cause` semantics so loggers + // don't recurse into circular fetch internals (see #7205). + Object.defineProperty(networkError, 'cause', { + __proto__: null, + value: err.cause || err, + writable: true, + enumerable: false, + configurable: true }); + throw networkError; } throw AxiosError.from(err, err && err.code, config, request, err && err.response); } @@ -41552,7 +43058,7 @@ function getAdapter(adapters, config) { if (!adapter) { const reasons = Object.entries(rejectedReasons).map(([id, state]) => `adapter ${id} ` + (state === false ? 'is not supported by the environment' : 'is not available in the build')); let s = length ? reasons.length > 1 ? 'since :\n' + reasons.map(renderReason).join('\n') : ' ' + renderReason(reasons[0]) : 'as no adapter specified'; - throw new AxiosError(`There is no suitable adapter to dispatch the request ` + s, 'ERR_NOT_SUPPORT'); + throw new AxiosError(`There is no suitable adapter to dispatch the request ` + s, AxiosError.ERR_NOT_SUPPORT); } return adapter; } @@ -41695,7 +43201,7 @@ validators$1.spelling = function spelling(correctSpelling) { */ function assertOptions(options, schema, allowUnknown) { - if (typeof options !== 'object') { + if (typeof options !== 'object' || options === null) { throw new AxiosError('options must be an object', AxiosError.ERR_BAD_OPTION_VALUE); } const keys = Object.keys(options); @@ -41804,7 +43310,9 @@ class Axios { silentJSONParsing: validators.transitional(validators.boolean), forcedJSONParsing: validators.transitional(validators.boolean), clarifyTimeoutError: validators.transitional(validators.boolean), - legacyInterceptorReqResOrdering: validators.transitional(validators.boolean) + legacyInterceptorReqResOrdering: validators.transitional(validators.boolean), + advertiseZstdAcceptEncoding: validators.transitional(validators.boolean), + validateStatusUndefinedResolves: validators.transitional(validators.boolean) }, false); } if (paramsSerializer != null) { @@ -41881,16 +43389,29 @@ class Axios { const onFulfilled = requestInterceptorChain[i++]; const onRejected = requestInterceptorChain[i++]; try { - newConfig = onFulfilled(newConfig); + newConfig = onFulfilled ? onFulfilled(newConfig) : newConfig; } catch (error) { - onRejected.call(this, error); + if (!onRejected) { + promise = Promise.reject(error); + break; + } + try { + const rejectedResult = onRejected.call(this, error); + if (utils$1.isThenable(rejectedResult)) { + promise = Promise.resolve(rejectedResult).then(() => dispatchRequest.call(this, newConfig)); + } + } catch (rejectedError) { + promise = Promise.reject(rejectedError); + } break; } } - try { - promise = dispatchRequest.call(this, newConfig); - } catch (error) { - return Promise.reject(error); + if (!promise) { + try { + promise = dispatchRequest.call(this, newConfig); + } catch (error) { + promise = Promise.reject(error); + } } i = 0; len = responseInterceptorChain.length; @@ -41901,7 +43422,7 @@ class Axios { } getUri(config) { config = mergeConfig(this.defaults, config); - const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls); + const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls, config); return buildURL(fullPath, config.params, config.paramsSerializer); } } @@ -41913,7 +43434,7 @@ utils$1.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoDa return this.request(mergeConfig(config || {}, { method, url, - data: (config || {}).data + data: config && utils$1.hasOwnProp(config, 'data') ? config.data : undefined })); }; }); @@ -42156,6 +43677,7 @@ const HttpStatusCode = { LoopDetected: 508, NotExtended: 510, NetworkAuthenticationRequired: 511, + WebServerReturnsAnUnknownError: 520, WebServerIsDown: 521, ConnectionTimedOut: 522, OriginIsUnreachable: 523, @@ -42232,7 +43754,6 @@ axios.HttpStatusCode = HttpStatusCode; axios.default = axios; module.exports = axios; -//# sourceMappingURL=axios.cjs.map /***/ }), diff --git a/package-lock.json b/package-lock.json index 8aed09e..34fc0df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@actions/core": "^2.0.3", "@actions/exec": "^2.0.0", - "axios": "1.16.0" + "axios": "^1.16.0" }, "devDependencies": { "@vercel/ncc": "^0.38.1", @@ -298,6 +298,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", @@ -345,13 +357,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", - "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, @@ -471,7 +484,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -968,6 +980,19 @@ "node": ">= 0.4" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1173,7 +1198,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/natural-compare": { diff --git a/package.json b/package.json index 085a87e..0f89eac 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dependencies": { "@actions/core": "^2.0.3", "@actions/exec": "^2.0.0", - "axios": "1.16.0" + "axios": "^1.16.0" }, "devDependencies": { "@vercel/ncc": "^0.38.1",