Exposure of Sensitive Information to an Unauthorized Actor in node-fetch/node-fetch
Description
node-fetch is vulnerable to Exposure of Sensitive Information to an Unauthorized Actor
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
node-fetch v2 and v3 before specific patches forward sensitive headers (e.g. Cookie, Authorization) to third-party hosts during redirects, leaking credentials.
Vulnerability
node-fetch, a popular Fetch API implementation for Node.js, contains a vulnerability in its handling of HTTP redirects. When a request is redirected to a different origin, the Headers class incorrectly forwards secure headers such as Cookie and Authorization to the third-party host. This affects node-fetch versions earlier than 2.6.7 (branch 2.x) and versions earlier than 3.1.1 (branch 3.x) [1][2][3]. The issue was introduced because the code did not strip sensitive headers when following a redirect to an untrusted domain.
Exploitation
To exploit this vulnerability, an attacker needs to control a server that the victim's application is redirected to, or be able to intercept a redirect response. The victim's application must make a request using a vulnerable version of node-fetch, and the server must respond with an HTTP redirect (3xx status) to a different origin. No additional authentication or user interaction beyond the initial request is required; the vulnerable code path is automatically triggered by the redirect. The fix (pull request #1449) adds a check using isDomainOrSubdomain to prevent forwarding secure headers to a different host [2][3].
Impact
Successful exploitation leads to the exposure of sensitive information, specifically HTTP headers that may contain session cookies, authentication tokens, or other credentials. An unauthorized third-party host can capture these headers, potentially allowing session hijacking, credential theft, or unauthorized access to the original target service. The confidentiality of the victim's requests is compromised.
Mitigation
The vulnerability is fixed in node-fetch version 2.6.7 (released January 22, 2022) and version 3.1.1. Users should upgrade to these or later versions immediately. No workaround is available for earlier versions; applying the patch by updating the dependency is the only effective mitigation. The issue is also tracked in the NVD and Siemens advisory [4]. Users of branch 2.x should update to 2.6.7+, and users of branch 3.x to 3.1.1+.
- GitHub - node-fetch/node-fetch: A light-weight module that brings the Fetch API to Node.js
- fix(Headers): don't forward secure headers to 3th party by jimmywarting · Pull Request #1449 · node-fetch/node-fetch
- backport of #1449 by jimmywarting · Pull Request #1453 · node-fetch/node-fetch
- NVD - CVE-2022-0235
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
node-fetchnpm | >= 3.0.0, < 3.1.1 | 3.1.1 |
node-fetchnpm | < 2.6.7 | 2.6.7 |
Affected products
58- ghsa-coords57 versionspkg:npm/node-fetchpkg:rpm/almalinux/nodejspkg:rpm/almalinux/nodejs-develpkg:rpm/almalinux/nodejs-docspkg:rpm/almalinux/nodejs-full-i18npkg:rpm/almalinux/nodejs-nodemonpkg:rpm/almalinux/nodejs-packagingpkg:rpm/almalinux/npmpkg:rpm/opensuse/nodejs10&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/nodejs10&distro=openSUSE%20Leap%2015.4pkg:rpm/opensuse/nodejs12&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/nodejs12&distro=openSUSE%20Leap%2015.4pkg:rpm/opensuse/nodejs14&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/nodejs14&distro=openSUSE%20Leap%2015.4pkg:rpm/opensuse/nodejs8&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/nodejs8&distro=openSUSE%20Leap%2015.4pkg:rpm/suse/nodejs10&distro=SUSE%20Enterprise%20Storage%206pkg:rpm/suse/nodejs10&distro=SUSE%20Enterprise%20Storage%207pkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-ESPOSpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-LTSSpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP2-ESPOSpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP2-LTSSpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015-ESPOSpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015-LTSSpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-BCLpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-LTSSpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP2-BCLpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP2-LTSSpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20Server%2015-LTSSpkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015pkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP1pkg:rpm/suse/nodejs10&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP2pkg:rpm/suse/nodejs10&distro=SUSE%20Manager%20Proxy%204.1pkg:rpm/suse/nodejs10&distro=SUSE%20Manager%20Retail%20Branch%20Server%204.1pkg:rpm/suse/nodejs10&distro=SUSE%20Manager%20Server%204.1pkg:rpm/suse/nodejs12&distro=SUSE%20Enterprise%20Storage%207pkg:rpm/suse/nodejs12&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP2-ESPOSpkg:rpm/suse/nodejs12&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP2-LTSSpkg:rpm/suse/nodejs12&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Web%20and%20Scripting%2012pkg:rpm/suse/nodejs12&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Web%20and%20Scripting%2015%20SP3pkg:rpm/suse/nodejs12&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP2-BCLpkg:rpm/suse/nodejs12&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP2-LTSSpkg:rpm/suse/nodejs12&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP2pkg:rpm/suse/nodejs12&distro=SUSE%20Manager%20Proxy%204.1pkg:rpm/suse/nodejs12&distro=SUSE%20Manager%20Retail%20Branch%20Server%204.1pkg:rpm/suse/nodejs12&distro=SUSE%20Manager%20Server%204.1pkg:rpm/suse/nodejs14&distro=SUSE%20Enterprise%20Storage%207pkg:rpm/suse/nodejs14&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP2-ESPOSpkg:rpm/suse/nodejs14&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP2-LTSSpkg:rpm/suse/nodejs14&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Web%20and%20Scripting%2012pkg:rpm/suse/nodejs14&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Web%20and%20Scripting%2015%20SP3pkg:rpm/suse/nodejs14&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP2-BCLpkg:rpm/suse/nodejs14&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP2-LTSSpkg:rpm/suse/nodejs14&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP2pkg:rpm/suse/nodejs14&distro=SUSE%20Manager%20Proxy%204.1pkg:rpm/suse/nodejs14&distro=SUSE%20Manager%20Retail%20Branch%20Server%204.1pkg:rpm/suse/nodejs14&distro=SUSE%20Manager%20Server%204.1
>= 3.0.0, < 3.1.1+ 56 more
- (no CPE)range: >= 3.0.0, < 3.1.1
- (no CPE)range: < 1:14.21.1-2.module_el8.7.0+3373+a4c18c43
- (no CPE)range: < 1:14.21.1-2.module_el8.7.0+3373+a4c18c43
- (no CPE)range: < 1:14.21.1-2.module_el8.7.0+3373+a4c18c43
- (no CPE)range: < 1:14.21.1-2.module_el8.7.0+3373+a4c18c43
- (no CPE)range: < 2.0.20-2.module_el8.7.0+3373+a4c18c43
- (no CPE)range: < 23-3.module_el8.4.0+2522+3bd42762
- (no CPE)range: < 1:6.14.17-1.14.21.1.2.module_el8.7.0+3373+a4c18c43
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 8.17.0-150200.10.22.1
- (no CPE)range: < 8.17.0-150200.10.22.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 10.24.1-150000.1.44.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 12.22.12-1.48.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 12.22.12-150200.4.32.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 14.19.1-6.28.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- (no CPE)range: < 14.19.1-150200.15.31.1
- node-fetch/node-fetch/node-fetchv5Range: unspecified
Patches
336e47e8a64063.1.1 release (#1451)
2 files changed · +25 −4
docs/CHANGELOG.md+24 −3 modified@@ -4,9 +4,30 @@ All notable changes will be recorded here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased -* fix(request): fix crash when an invalid redirection URL is encountered https://github.com/node-fetch/node-fetch/pull/1387 -* fix: handle errors from the request body stream by @mdmitry01 in https://github.com/node-fetch/node-fetch/pull/1392 +## What's Changed +* core: update fetch-blob by @jimmywarting in https://github.com/node-fetch/node-fetch/pull/1371 +* docs: Fix typo around sending a file by @jimmywarting in https://github.com/node-fetch/node-fetch/pull/1381 +* core: (http.request): Cast URL to string before sending it to NodeJS core by @jimmywarting in https://github.com/node-fetch/node-fetch/pull/1378 +* core: handle errors from the request body stream by @mdmitry01 in https://github.com/node-fetch/node-fetch/pull/1392 +* core: Better handle wrong redirect header in a response by @tasinet in https://github.com/node-fetch/node-fetch/pull/1387 +* core: Don't use buffer to make a blob by @jimmywarting in https://github.com/node-fetch/node-fetch/pull/1402 +* docs: update readme for TS @types/node-fetch by @adamellsworth in https://github.com/node-fetch/node-fetch/pull/1405 +* core: Fix logical operator priority to disallow GET/HEAD with non-empty body by @maxshirshin in https://github.com/node-fetch/node-fetch/pull/1369 +* core: Don't use global buffer by @jimmywarting in https://github.com/node-fetch/node-fetch/pull/1422 +* ci: fix main branch by @dnalborczyk in https://github.com/node-fetch/node-fetch/pull/1429 +* core: use more node: protocol imports by @dnalborczyk in https://github.com/node-fetch/node-fetch/pull/1428 +* core: Warn when using data by @jimmywarting in https://github.com/node-fetch/node-fetch/pull/1421 +* docs: Create SECURITY.md by @JamieSlome in https://github.com/node-fetch/node-fetch/pull/1445 +* core: don't forward secure headers to 3th party by @jimmywarting in https://github.com/node-fetch/node-fetch/pull/1449 + +## New Contributors +* @mdmitry01 made their first contribution in https://github.com/node-fetch/node-fetch/pull/1392 +* @tasinet made their first contribution in https://github.com/node-fetch/node-fetch/pull/1387 +* @adamellsworth made their first contribution in https://github.com/node-fetch/node-fetch/pull/1405 +* @maxshirshin made their first contribution in https://github.com/node-fetch/node-fetch/pull/1369 +* @JamieSlome made their first contribution in https://github.com/node-fetch/node-fetch/pull/1445 + +**Full Changelog**: https://github.com/node-fetch/node-fetch/compare/v3.1.0...v3.1.2 ## 3.1.0
package.json+1 −1 modified@@ -1,6 +1,6 @@ { "name": "node-fetch", - "version": "3.1.0", + "version": "3.1.1", "description": "A light-weight module that brings Fetch API to node.js", "main": "./src/index.js", "sideEffects": false,
1ef4b560a17ebackport of #1449 (#1453)
4 files changed · +94 −11
package.json+1 −1 modified@@ -1,6 +1,6 @@ { "name": "node-fetch", - "version": "2.6.6", + "version": "2.6.7", "description": "A light-weight module that brings window.fetch to node.js", "main": "lib/index.js", "browser": "./browser.js",
src/index.js+40 −9 modified@@ -13,16 +13,29 @@ import https from 'https'; import zlib from 'zlib'; import Stream from 'stream'; -import Body, { writeToStream, getTotalBytes } from './body'; -import Response from './response'; -import Headers, { createHeadersLenient } from './headers'; -import Request, { getNodeRequestOptions } from './request'; -import FetchError from './fetch-error'; -import AbortError from './abort-error'; +import Body, { writeToStream, getTotalBytes } from './body.js'; +import Response from './response.js'; +import Headers, { createHeadersLenient } from './headers.js'; +import Request, { getNodeRequestOptions } from './request.js'; +import FetchError from './fetch-error.js'; +import AbortError from './abort-error.js'; + +import whatwgUrl from 'whatwg-url'; + +const URL = Url.URL || whatwgUrl.URL; // fix an issue where "PassThrough", "resolve" aren't a named export for node <10 const PassThrough = Stream.PassThrough; -const resolve_url = Url.resolve; + +const isDomainOrSubdomain = (destination, original) => { + const orig = new URL(original).hostname; + const dest = new URL(destination).hostname; + + return orig === dest || ( + orig[orig.length - dest.length - 1] === '.' && orig.endsWith(dest) + ); +}; + /** * Fetch function @@ -109,7 +122,19 @@ export default function fetch(url, opts) { const location = headers.get('Location'); // HTTP fetch step 5.3 - const locationURL = location === null ? null : resolve_url(request.url, location); + let locationURL = null; + try { + locationURL = location === null ? null : new URL(location, request.url).toString(); + } catch (err) { + // error here can only be invalid URL in Location: header + // do not throw when options.redirect == manual + // let the user extract the errorneous redirect URL + if (request.redirect !== 'manual') { + reject(new FetchError(`uri requested responds with an invalid redirect URL: ${location}`, 'invalid-redirect')); + finalize(); + return; + } + } // HTTP fetch step 5.5 switch (request.redirect) { @@ -154,9 +179,15 @@ export default function fetch(url, opts) { body: request.body, signal: request.signal, timeout: request.timeout, - size: request.size + size: request.size }; + if (!isDomainOrSubdomain(request.url, locationURL)) { + for (const name of ['authorization', 'www-authenticate', 'cookie', 'cookie2']) { + requestOpts.headers.delete(name); + } + } + // HTTP-redirect fetch step 9 if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) { reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect'));
test/server.js+6 −1 modified@@ -1,7 +1,6 @@ import * as http from 'http'; import { parse } from 'url'; import * as zlib from 'zlib'; -import * as stream from 'stream'; import { multipart as Multipart } from 'parted'; let convert; @@ -66,6 +65,12 @@ export default class TestServer { })); } + if (p.startsWith('/redirect-to/3')) { + res.statusCode = p.slice(13, 16); + res.setHeader('Location', p.slice(17)); + res.end(); + } + if (p === '/gzip') { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain');
test/test.js+47 −0 modified@@ -1569,6 +1569,53 @@ describe('node-fetch', () => { }); }); + it('should not forward secure headers to 3th party', () => { + return fetch(`${base}redirect-to/302/https://httpbin.org/get`, { + headers: new Headers({ + cookie: 'gets=removed', + cookie2: 'gets=removed', + authorization: 'gets=removed', + 'www-authenticate': 'gets=removed', + 'other-safe-headers': 'stays', + 'x-foo': 'bar' + }) + }).then(res => res.json()).then(json => { + const headers = new Headers(json.headers); + // Safe headers are not removed + expect(headers.get('other-safe-headers')).to.equal('stays'); + expect(headers.get('x-foo')).to.equal('bar'); + // Unsafe headers should not have been sent to httpbin + expect(headers.get('cookie')).to.equal(null); + expect(headers.get('cookie2')).to.equal(null); + expect(headers.get('www-authenticate')).to.equal(null); + expect(headers.get('authorization')).to.equal(null); + }); + }); + + it('should forward secure headers to same host', () => { + return fetch(`${base}redirect-to/302/${base}inspect`, { + headers: new Headers({ + cookie: 'is=cookie', + cookie2: 'is=cookie2', + authorization: 'is=authorization', + 'other-safe-headers': 'stays', + 'www-authenticate': 'is=www-authenticate', + 'x-foo': 'bar' + }) + }).then(res => res.json().then(json => { + const headers = new Headers(json.headers); + // Safe headers are not removed + expect(res.url).to.equal(`${base}inspect`); + expect(headers.get('other-safe-headers')).to.equal('stays'); + expect(headers.get('x-foo')).to.equal('bar'); + // Unsafe headers should not have been sent to httpbin + expect(headers.get('cookie')).to.equal('is=cookie'); + expect(headers.get('cookie2')).to.equal('is=cookie2'); + expect(headers.get('www-authenticate')).to.equal('is=www-authenticate'); + expect(headers.get('authorization')).to.equal('is=authorization'); + })); + }); + it('should allow PATCH request', function() { const url = `${base}inspect`; const opts = {
5c32f002fdd6fix(Headers): don't forward secure headers to 3th party
4 files changed · +92 −0
src/index.js+13 −0 modified@@ -21,6 +21,7 @@ import Request, {getNodeRequestOptions} from './request.js'; import {FetchError} from './errors/fetch-error.js'; import {AbortError} from './errors/abort-error.js'; import {isRedirect} from './utils/is-redirect.js'; +import {isDomainOrSubdomain} from './utils/is.js'; import {parseReferrerPolicyFromHeader} from './utils/referrer.js'; export {Headers, Request, Response, FetchError, AbortError, isRedirect}; @@ -188,6 +189,18 @@ export default async function fetch(url, options_) { referrerPolicy: request.referrerPolicy }; + // when forwarding sensitive headers like "Authorization", + // "WWW-Authenticate", and "Cookie" to untrusted targets. + // These headers will be ignored when following a redirect to a domain + // that is not a subdomain match or exact match of the initial domain. + // For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com" + // will forward the sensitive headers, but a redirect to "bar.com" will not. + if (!isDomainOrSubdomain(request.url, locationURL)) { + for (const name of ['authorization', 'www-authenticate', 'cookie', 'cookie2']) { + requestOptions.headers.delete(name); + } + } + // HTTP-redirect fetch step 9 if (response_.statusCode !== 303 && request.body && options_.body instanceof Stream.Readable) { reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect'));
src/utils/is.js+26 −0 modified@@ -56,3 +56,29 @@ export const isAbortSignal = object => { ) ); }; + +/** + * isDomainOrSubdomain reports whether sub is a subdomain (or exact match) of + * the parent domain. + * + * Both domains must already be in canonical form. + * @param {string|URL} sub + * @param {string|URL} parent + */ +export const isDomainOrSubdomain = (sub, parent) => { + const a = new URL(sub).hostname; + const b = new URL(parent).hostname; + + if (a === b) { + return true; + } + + // If sub is "foo.example.com" and parent is "example.com", + // that means sub must end in "."+parent. + // Do it without allocating. + if (!a.endsWith(b)) { + return false; + } + + return a[a.length - b.length - 1] === '.'; +};
test/main.js+47 −0 modified@@ -496,6 +496,53 @@ describe('node-fetch', () => { }); }); + it('should not forward secure headers to 3th party', async () => { + const res = await fetch(`${base}redirect-to/302/https://httpbin.org/get`, { + headers: new Headers({ + cookie: 'gets=removed', + cookie2: 'gets=removed', + authorization: 'gets=removed', + 'www-authenticate': 'gets=removed', + 'other-safe-headers': 'stays', + 'x-foo': 'bar' + }) + }); + + const headers = new Headers((await res.json()).headers); + // Safe headers are not removed + expect(headers.get('other-safe-headers')).to.equal('stays'); + expect(headers.get('x-foo')).to.equal('bar'); + // Unsafe headers should not have been sent to httpbin + expect(headers.get('cookie')).to.equal(null); + expect(headers.get('cookie2')).to.equal(null); + expect(headers.get('www-authenticate')).to.equal(null); + expect(headers.get('authorization')).to.equal(null); + }); + + it('should forward secure headers to same host', async () => { + const res = await fetch(`${base}redirect-to/302/${base}inspect`, { + headers: new Headers({ + cookie: 'is=cookie', + cookie2: 'is=cookie2', + authorization: 'is=authorization', + 'other-safe-headers': 'stays', + 'www-authenticate': 'is=www-authenticate', + 'x-foo': 'bar' + }) + }); + + const headers = new Headers((await res.json()).headers); + // Safe headers are not removed + expect(res.url).to.equal(`${base}inspect`); + expect(headers.get('other-safe-headers')).to.equal('stays'); + expect(headers.get('x-foo')).to.equal('bar'); + // Unsafe headers should not have been sent to httpbin + expect(headers.get('cookie')).to.equal('is=cookie'); + expect(headers.get('cookie2')).to.equal('is=cookie2'); + expect(headers.get('www-authenticate')).to.equal('is=www-authenticate'); + expect(headers.get('authorization')).to.equal('is=authorization'); + }); + it('should treat broken redirect as ordinary response (follow)', () => { const url = `${base}redirect/no-location`; return fetch(url).then(res => {
test/utils/server.js+6 −0 modified@@ -245,6 +245,12 @@ export default class TestServer { res.end(); } + if (p.startsWith('/redirect-to/3')) { + res.statusCode = p.slice(13, 16); + res.setHeader('Location', p.slice(17)); + res.end(); + } + if (p === '/redirect/302') { res.statusCode = 302; res.setHeader('Location', '/inspect');
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-r683-j2x4-v87gghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-0235ghsaADVISORY
- cert-portal.siemens.com/productcert/pdf/ssa-637483.pdfghsaWEB
- github.com/node-fetch/node-fetch/commit/1ef4b560a17e644a02a3bfdea7631ffeee578b35ghsaWEB
- github.com/node-fetch/node-fetch/commit/36e47e8a6406185921e4985dcbeff140d73eaa10ghsaWEB
- github.com/node-fetch/node-fetch/commit/5c32f002fdd65b1c6a8f1e3620210813d45c7e60ghsaWEB
- github.com/node-fetch/node-fetch/pull/1449/commits/5c32f002fdd65b1c6a8f1e3620210813d45c7e60ghsaWEB
- github.com/node-fetch/node-fetch/pull/1453ghsaWEB
- huntr.dev/bounties/d26ab655-38d6-48b3-be15-f9ad6b6ae6f7ghsaWEB
- lists.debian.org/debian-lts-announce/2022/12/msg00007.htmlghsamailing-listWEB
News mentions
0No linked articles in our index yet.