UNPKG

axios

Version:

Promise based HTTP client for the browser and node.js

89 lines (77 loc) 3.15 kB
'use strict'; import AxiosError, { REDACTED } from './AxiosError.js'; import isAbsoluteURL from '../helpers/isAbsoluteURL.js'; import combineURLs from '../helpers/combineURLs.js'; 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 ); } } } /** * Creates a new URL by combining the baseURL with the requestedURL, * only when the requestedURL is not already an absolute URL. * If the requestURL is absolute, this function returns the requestedURL untouched. * * @param {string} baseURL The base URL * @param {string} requestedURL Absolute or relative URL to combine * * @returns {string} The combined full path */ export default 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; }