KEVWire

Security intelligence, with context.

Response snapshot Oct 27, 2026, 05:58 UTC

Back to the wire
Permalink
VulnerabilityCVE-2026-111755

follow-redirects sensitiveHeaders regex escaping can expose credential headers

```js this._headerFilter = new RegExp("^(?:" + sensitiveHeaders.concat(options.sensitiveHeaders).map(escapeRegex).join("|") + ")$", "i"); ``` `escapeRegex` at `index.js:703-705`:

At a glance

Exploited
Not known
Severity
8.2 High (CVSS 4.0)
Chance of exploitation
0.5% in the next 30 days
Sources
2 independent sources

What to do

GitHub lists first patched versions: follow-redirects: 1.16.1. Confirm the affected range and upgrade guidance in the exact advisory.

Read GitHub-reviewed advisory GHSA-8r9p-f939-6h3c (opens a new tab)

What happened

### Summary follow-redirects 1.16.0 introduced the `sensitiveHeaders` option (the fix for CVE-2026-40895 / GHSA-r4q5-vmmm-2653), which lets an application declare extra header names that must be removed when a redirect crosses to a different host or downgrades protocol.

The filter is a single case-insensitive regular expression built by concatenating the declared header names into an alternation after passing each through `escapeRegex`. `escapeRegex` escapes only a partial set of metacharacters and leaves `^`, `|`, `[`, `{`, and `}` unescaped.

Header names may legitimately contain `^` and `|` (both are valid RFC 9110 token characters that Node accepts and sends), and for a declared name containing either, the generated pattern can never match the intended header.

As a result, an application that adopts this option to protect a custom credential header, then follows an attacker-controlled cross-origin redirect, forwards that credential to the attacker with no error or warning, while believing it is protected. The failure is silent: the protection simply does not exist for the affected names.

Continue reading

The built-in list (Authorization, Proxy-Authorization, Cookie) contains no metacharacters and is not affected by this defect.

### Details The filter is constructed once, in the `RedirectableRequest` constructor at `index.js:118-120`:

```js this._headerFilter = new RegExp("^(?:" + sensitiveHeaders.concat(options.sensitiveHeaders).map(escapeRegex).join("|") + ")$", "i"); ```

`escapeRegex` at `index.js:703-705`:

```js function escapeRegex(regex) { return regex.replace(/[\]\\/()*+?.$]/g, "\\$&"); // escapes ] \ / ( ) * + ? . $ -- NOT ^ | [ { } } ```

The character class escapes `]`, `\`, `/`, `(`, `)`, `*`, `+`, `?`, `.`, and `$`. It omits `^`, `|`, `[`, `{`, and `}`. Of these, Node's `http.ClientRequest` accepts `^` and `|` in header names (they are valid `tchar` values under RFC 9110) and rejects `[`, `{`, `}` with `ERR_INVALID_HTTP_TOKEN`, so `^` and `|` are the reachable vectors for a silent credential leak.

Separately, although `[`, `{`, `}` cannot appear as live header names, declaring a `sensitiveHeaders` entry that contains one of them injects an unbalanced token into the alternation (for example an unterminated character class), so the filter `RegExp` throws a `SyntaxError` in the `RedirectableRequest` constructor and the request fails to start.

That case is a loud request-time crash from the same incomplete-escaping root cause rather than a silent bypass, and the canonical escaper below fixes it as well.

For a declared sensitive header name containing an unescaped metacharacter, the alternation is broken in a fail-open way:

- A name containing `^`, for example `X-Api^Key`, yields the alternative `X-Api^Key` inside the pattern. The `^` becomes a mid-pattern start-of-string anchor, so the alternative can never match any real header name. `test("X-Api^Key")` returns `false`, and the header is not stripped. - A name containing `|`, for example `X-Token|Secret`, is split into two separate alternatives `X-Token` and `Secret`.

The real header `X-Token|Secret` is never matched (not stripped), while unrelated headers literally named `X-Token` or `Secret` would be stripped instead.

The generated filter source, reproduced from the exact library logic, is: ``` ^(?:Authorization|Proxy-Authorization|Cookie|X-Api^Key|X-Token|Secret|X-Control-Key)$ ``` Direct matching results against this filter: `X-Api^Key` -> false, `X-Token|Secret` -> false, `X-Control-Key` (normal name) -> true, and the split-off `X-Token` -> true and `Secret` -> true (collateral wrong-stripping). The built-ins Authorization, Proxy-Authorization, Cookie all -> true.

Secondary, related weakness (filter snapshot not refreshed after `beforeRedirect`): the filter is built once at construction and applied at `index.js:490`, which runs before the `beforeRedirect(this._options, ...)` hook and the subsequent `_sanitizeOptions` at `index.js:494-506`. Because the strip happens before the hook and `this._headerFilter` is never rebuilt, any entry a caller adds to `options.

sensitiveHeaders` inside `beforeRedirect` is silently ignored. This facet is lower severity: mutating `sensitiveHeaders` inside `beforeRedirect` is not a documented usage of the option (the documented path is the request option), so it is best treated as a hardening item bundled with the escaping fix rather than an independent control bypass.

It is included here because both issues undermine the same control and share a remediation.

Suggested remediation: two changes close both facets.

1. Escape the complete set of regular-expression metacharacters: ```js function escapeRegex(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } ``` This was verified to restore correct matching for all affected names while preserving the built-in matches.

2. Build the filter at strip time, or rebuild it after the `beforeRedirect` hook re-sanitizes options, so entries added inside the hook take effect. For example, replace the cached `this._headerFilter` with a `this._getHeaderFilter()` that constructs the RegExp from the current `this._options.sensitiveHeaders` on each redirect.

Additionally, since header names have a strict grammar, consider validating each entry of `options.sensitiveHeaders` against the RFC 9110 token character set and throwing an explicit error on invalid names, so misconfiguration fails loudly. Add a unit test asserting that every declared name is actually matched by the generated filter; the current suite only exercises metacharacter-free names (`Sensitive-A`, `x-sensitive-b`), which is why the defect passes CI.

References: - https://github.com/follow-redirects/follow-redirects/security/advisories/GHSA-r4q5-vmmm-2653 - https://github.com/follow-redirects/follow-redirects/pull/284 - https://github.com/follow-redirects/follow-redirects/commit/844c4d302ac963d29bdb5dc1754ec7df3d70d7f9

### PoC Environment: Node.js v24.18.0 on Windows 11. Run each script from a c

Affected package ranges reported by GitHub: follow-redirects: >= 1.16.0, < 1.16.1

Scores and how they were measured

CVSS base severity

8.2

Version 4.0As of

CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N

Measurement source (opens a new tab)

How bad it is if exploited. Not a prediction of exploitation or proof you are exposed.

EPSS probability

0.5%

Scored Percentile 40.5%

Model and measurement source (opens a new tab)

A model's estimate of exploitation in the next 30 days, not certainty or your own risk.

Related technology

Sources

  1. Primary source

    GitHub-reviewed advisory GHSA-8r9p-f939-6h3c (opens a new tab)

    The public advisory supplies the description, affected package/version ranges, and any listed CVSS metric. GitHub review is not local review.

    Published Checked

  2. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-8r9p-f939-6h3c, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  3. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-8r9p-f939-6h3c, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

  4. Primary source

    NVD CVE-2026-111755 (opens a new tab)

    NVD record: description, CVSS (the CNA score is preferred), affected products, and publication date. Not evidence of exploitation.

    Published Checked

  5. Primary source

    NVD CVE API 2.0 (opens a new tab)

    The NVD API page that supplied this CVE record.

    Published Date not suppliedChecked

  6. Primary source

    FIRST measured EPSS score (opens a new tab)

    Measured EPSS probability and percentile for CVE-2026-111755, scored 2026-10-26. This is a model estimate, not proof of exposure.

    Published Checked

Cite this record

For factual claims, cite the primary sources above; cite KEVWire for its compilation, dates, and classifications.

APA

KEVWire. (2026, October 20). follow-redirects sensitiveHeaders regex escaping can expose credential headers (CVE-2026-111755). https://kevwire.com/record/CVE-2026-111755

MLA

"follow-redirects sensitiveHeaders regex escaping can expose credential headers (CVE-2026-111755)." KEVWire, 20 Oct. 2026, kevwire.com/record/CVE-2026-111755.

Chicago

KEVWire. "follow-redirects sensitiveHeaders regex escaping can expose credential headers (CVE-2026-111755)." Last modified October 20, 2026. https://kevwire.com/record/CVE-2026-111755.

Evidence summary (plain text)

follow-redirects sensitiveHeaders regex escaping can expose credential headers (Oct 20, 2026).
```js
this._headerFilter = new RegExp("^(?:" +
 sensitiveHeaders.concat(options.sensitiveHeaders).map(escapeRegex).join("|") +
 ")$", "i");
```

`escapeRegex` at `index.js:703-705`:
CVSS 8.2 (version 4.0; evidence as of Oct 20, 2026; https://github.com/advisories/GHSA-8r9p-f939-6h3c).
EPSS 0.5% for the following 30 days, scored Oct 26, 2026 (https://api.first.org/data/v1/epss?cve=CVE-2026-111755&date=2026-10-26).
Sources:
GitHub-reviewed advisory GHSA-8r9p-f939-6h3c: https://github.com/advisories/GHSA-8r9p-f939-6h3c
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yMVQyMDozMDoyNlrOAAfvxw%3D%3D
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc&after=Y3Vyc29yOnYyOpK0MjAyNi0xMC0yMVQyMDowMzo1N1rOAAfvxQ%3D%3D
NVD CVE-2026-111755: https://nvd.nist.gov/vuln/detail/CVE-2026-111755
NVD CVE API 2.0: https://services.nvd.nist.gov/rest/json/cves/2.0?pubStartDate=2026-10-12T13:35:55.142Z&pubEndDate=2026-10-26T13:35:55.142Z&resultsPerPage=500&startIndex=5500
FIRST measured EPSS score: https://api.first.org/data/v1/epss?cve=CVE-2026-111755&date=2026-10-26
Record ID: CVE-2026-111755.
Record origin: Ingested record.
AI evidence: Not assessed.
Known exploitation: Not assessed.
Zero-day status: Not assessed. KEV membership alone does not establish zero-day status.
About this data

This record was ingested from a data source. Retrieved: Oct 27, 2026, 01:40 UTC. Ingestion is not an independent editorial investigation.

A CISA KEV listing documents known exploitation, not necessarily a zero-day; absence from the catalog does not prove a vulnerability has never been exploited.

Technology tags indicate possible relevance, not verified exposure. Confirm the affected product, version, and configuration against your own inventory; KEVWire does not scan your systems.

A personal reading checklist on this device, not verified remediation.

Record JSON

Your account

Email alerts

Email alerts are not available yet. To follow your stack today, use its RSS feed, which honors your filters, or connect an AI agent through the API.

Suggest an improvement

Ideas, missing sources, corrections, and bugs are all welcome. No account needed. Suggestions are reviewed by a person before anything changes.

Record details

Record detail