{"ghsa_id":"GHSA-pjwm-pj3p-43mv","cve_id":"CVE-2026-44492","url":"https://api.github.com/advisories/GHSA-pjwm-pj3p-43mv","html_url":"https://github.com/advisories/GHSA-pjwm-pj3p-43mv","summary":"axios's shouldBypassProxy does not recognize IPv4-mapped IPv6 addresses, allowing NO_PROXY bypass (incomplete fix for CVE-2025-62718)","description":"### Summary\nshouldBypassProxy, introduced in v1.15.0 to fix CVE-2025-62718, does not normalise IPv4-mapped IPv6 addresses. When NO_PROXY lists an IPv4 address such as `127.0.0.1` or `169.254.169.254`, a request URL using the IPv4-mapped IPv6 form (`::ffff:7f00:1`, `::ffff:a9fe:a9fe`) still routes through the configured proxy. Node.js resolves these addresses to the underlying IPv4 host, so the request reaches the internal service via the proxy rather than being blocked.\n\n### Details\nlib/helpers/shouldBypassProxy.js (v1.15.0):                                                                                                                                   \n\n```javascript                                                                                                                                                                              \n  const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']);                                                                                                      \n  const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host);                                                                                                                    \n                                                                                                                                                                                \n  // normalizeNoProxyHost strips brackets and trailing dots, but not ::ffff: prefix                                                                                             \n  return hostname === entryHost || (isLoopback(hostname) && isLoopback(entryHost));                                                                                             \n```\n                                                                                                                                                                                \nThe WHATWG URL parser canonicalises `http://[::ffff:127.0.0.1]/` to hostname `[::ffff:7f00:1]`. After bracket-stripping: `::ffff:7f00:1`. This string does not match 127.0.0.1 in NO_PROXY and is not in LOOPBACK_ADDRESSES, so shouldBypassProxy returns false and the proxy is used.  proxy-from-env (called before shouldBypassProxy) has the same gap - it does not equate ::ffff:7f00:1 with 127.0.0.1 - so neither layer catches the bypass.\n\n### PoC\n```javascript\n\n// NO_PROXY=127.0.0.1,localhost,::1  HTTP_PROXY=http://attacker:8080\nimport shouldBypassProxy from 'axios/lib/helpers/shouldBypassProxy.js';                                                                                                       \n                                                                                                                                                                              \n// All three should return true (bypass proxy). Only the first two do.                                                                                                        \nconsole.log(shouldBypassProxy('http://127.0.0.1/'));          // true  [OK]                                                                                                     \nconsole.log(shouldBypassProxy('http://[::1]/'));               // true  [OK]                                                                                                     \nconsole.log(shouldBypassProxy('http://[::ffff:127.0.0.1]/')); // false <- bypass                                                                                             \nconsole.log(shouldBypassProxy('http://[::ffff:7f00:1]/'));     // false <- bypass\n\n```                                                                                              \n                                                                                                                                                                              \nNode.js routes ::ffff:7f00:1 to 127.0.0.1:                                                                                                                                    \n\n```                                                                                                                                                                              \n// net.connect({ host: '::ffff:7f00:1', port: 80 }) reaches a service                                                                                                       \n// bound to 127.0.0.1:80 — confirmed on Node.js v24, Linux and macOS.                                                                                                         \n```                                                                                                                                                                              \nCloud metadata SSRF: ::ffff:a9fe:a9fe = ::ffff:169.254.169.254. If NO_PROXY=169.254.169.254 is set to block IMDS access, a request to http://[::ffff:a9fe:a9fe]/latest/meta-data/ bypasses it.                                                                                                                      \n                                                                                                                                                                            \n#### Fix                                                                                                                                                                           \n                                                                                                                                                                            \nCanonicalise IPv4-mapped IPv6 in normalizeNoProxyHost before any comparison:                                                                                                  \n \n ```javascript                                                                                                                                                                           \nconst ipv4MappedDotted = /^::ffff:(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})$/i;                                                                                                    \nconst ipv4MappedHex    = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i;                                                                                                         \n                                                                                                                                                                              \nfunction hexToIPv4(a, b) {                                                                                                                                                    \n  const hi = parseInt(a, 16), lo = parseInt(b, 16);                                                                                                                           \n  return `${hi >> 8}.${hi & 0xff}.${lo >> 8}.${lo & 0xff}`;                                                                                                                   \n}                                                                                                                                                                             \n                                                                                                                                                                              \nconst normalizeNoProxyHost = (hostname) => {                                                                                                                                  \n  if (!hostname) return hostname;                                                                                                                                           \n  if (hostname[0] === '[' && hostname.at(-1) === ']')\n    hostname = hostname.slice(1, -1);                                                                                                                                         \n  hostname = hostname.replace(/\\.+$/, '').toLowerCase();\n                                                                                                                                                                              \n  let m;                                                                                                                                                                    \n  if ((m = hostname.match(ipv4MappedDotted))) return m[1];                                                                                                                    \n  if ((m = hostname.match(ipv4MappedHex)))    return hexToIPv4(m[1], m[2]);                                                                                                   \n  return hostname;                                                                                                                                                            \n};\n\n```\n\n### Impact\nAny application that sets NO_PROXY to exclude internal or metadata endpoints and uses an HTTP/HTTPS proxy can have those exclusions bypassed by a URL using IPv4-mapped IPv6 notation. The attacker must control the request URL. In cloud environments with instance metadata services, this can lead to credential exfiltration.","type":"reviewed","severity":"high","repository_advisory_url":"https://api.github.com/repos/axios/axios/security-advisories/GHSA-pjwm-pj3p-43mv","source_code_location":"https://github.com/axios/axios","identifiers":[{"value":"GHSA-pjwm-pj3p-43mv","type":"GHSA"},{"value":"CVE-2026-44492","type":"CVE"}],"references":["https://github.com/axios/axios/security/advisories/GHSA-pjwm-pj3p-43mv","https://nvd.nist.gov/vuln/detail/CVE-2025-62718","https://nvd.nist.gov/vuln/detail/CVE-2026-44492","https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-44492.json","https://bugzilla.redhat.com/show_bug.cgi?id=2487938","https://access.redhat.com/security/cve/CVE-2026-44492","https://access.redhat.com/errata/RHSA-2026:36108","https://access.redhat.com/errata/RHSA-2026:33574","https://access.redhat.com/errata/RHSA-2026:33183","https://access.redhat.com/errata/RHSA-2026:33173","https://access.redhat.com/errata/RHSA-2026:33163","https://access.redhat.com/errata/RHSA-2026:33160","https://access.redhat.com/errata/RHSA-2026:33155","https://access.redhat.com/errata/RHSA-2026:33005","https://access.redhat.com/errata/RHSA-2026:30651","https://access.redhat.com/errata/RHSA-2026:30650","https://access.redhat.com/errata/RHSA-2026:29197","https://access.redhat.com/errata/RHSA-2026:29082","https://access.redhat.com/errata/RHSA-2026:28964","https://access.redhat.com/errata/RHSA-2026:27063","https://access.redhat.com/errata/RHSA-2026:27044","https://access.redhat.com/errata/RHSA-2026:26234","https://access.redhat.com/errata/RHSA-2026:20938","https://access.redhat.com/errata/RHSA-2026:20889","https://access.redhat.com/errata/RHSA-2026:36883","https://access.redhat.com/errata/RHSA-2026:36882","https://access.redhat.com/errata/RHSA-2026:36820","https://access.redhat.com/errata/RHSA-2026:36754","https://access.redhat.com/errata/RHSA-2026:34766","https://access.redhat.com/errata/RHSA-2026:36611","https://access.redhat.com/errata/RHSA-2026:40119","https://access.redhat.com/errata/RHSA-2026:40138","https://access.redhat.com/errata/RHSA-2026:40262","https://access.redhat.com/errata/RHSA-2026:41031","https://access.redhat.com/errata/RHSA-2026:41066","https://access.redhat.com/errata/RHSA-2026:41055","https://access.redhat.com/errata/RHSA-2026:41064","https://github.com/advisories/GHSA-pjwm-pj3p-43mv"],"published_at":"2026-05-29T15:59:30Z","updated_at":"2026-07-20T12:34:08Z","github_reviewed_at":"2026-05-29T15:59:30Z","nvd_published_at":"2026-06-11T17:16:33Z","withdrawn_at":null,"vulnerabilities":[{"package":{"ecosystem":"npm","name":"axios"},"vulnerable_version_range":"<= 0.31.1","first_patched_version":"0.32.0","vulnerable_functions":[]},{"package":{"ecosystem":"npm","name":"axios"},"vulnerable_version_range":">= 1.15.0, < 1.16.0","first_patched_version":"1.16.0","vulnerable_functions":[]}],"cvss_severities":{"cvss_v3":{"vector_string":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N","score":8.6},"cvss_v4":{"vector_string":null,"score":0.0}},"cwes":[{"cwe_id":"CWE-289","name":"Authentication Bypass by Alternate Name"},{"cwe_id":"CWE-918","name":"Server-Side Request Forgery (SSRF)"}],"credits":[{"user":{"login":"HamdaanAliQuatil","id":96776914,"node_id":"U_kgDOBcSy0g","avatar_url":"https://avatars.githubusercontent.com/u/96776914?v=4","gravatar_id":"","url":"https://api.github.com/users/HamdaanAliQuatil","html_url":"https://github.com/HamdaanAliQuatil","followers_url":"https://api.github.com/users/HamdaanAliQuatil/followers","following_url":"https://api.github.com/users/HamdaanAliQuatil/following{/other_user}","gists_url":"https://api.github.com/users/HamdaanAliQuatil/gists{/gist_id}","starred_url":"https://api.github.com/users/HamdaanAliQuatil/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/HamdaanAliQuatil/subscriptions","organizations_url":"https://api.github.com/users/HamdaanAliQuatil/orgs","repos_url":"https://api.github.com/users/HamdaanAliQuatil/repos","events_url":"https://api.github.com/users/HamdaanAliQuatil/events{/privacy}","received_events_url":"https://api.github.com/users/HamdaanAliQuatil/received_events","type":"User","user_view_type":"public","site_admin":false},"type":"reporter"},{"user":{"login":"jasonsaayman","id":4814473,"node_id":"MDQ6VXNlcjQ4MTQ0NzM=","avatar_url":"https://avatars.githubusercontent.com/u/4814473?v=4","gravatar_id":"","url":"https://api.github.com/users/jasonsaayman","html_url":"https://github.com/jasonsaayman","followers_url":"https://api.github.com/users/jasonsaayman/followers","following_url":"https://api.github.com/users/jasonsaayman/following{/other_user}","gists_url":"https://api.github.com/users/jasonsaayman/gists{/gist_id}","starred_url":"https://api.github.com/users/jasonsaayman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jasonsaayman/subscriptions","organizations_url":"https://api.github.com/users/jasonsaayman/orgs","repos_url":"https://api.github.com/users/jasonsaayman/repos","events_url":"https://api.github.com/users/jasonsaayman/events{/privacy}","received_events_url":"https://api.github.com/users/jasonsaayman/received_events","type":"User","user_view_type":"public","site_admin":false},"type":"analyst"}],"cvss":{"vector_string":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N","score":8.6},"epss":{"percentage":0.0087,"percentile":0.56656}}