Skip to content

🚨 [security] Update fastify 4.25.0 → 5.12.1 (major) - #614

Open
depfu[bot] wants to merge 1 commit into
mainfrom
depfu/update/pnpm/fastify-5.12.1
Open

🚨 [security] Update fastify 4.25.0 → 5.12.1 (major)#614
depfu[bot] wants to merge 1 commit into
mainfrom
depfu/update/pnpm/fastify-5.12.1

Conversation

@depfu

@depfu depfu Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🚨 Your current dependencies have known security vulnerabilities 🚨

This dependency update fixes known security vulnerabilities. Please see the details below and assess their impact carefully. We recommend to merge and deploy this as soon as possible!


Here is everything you need to know about this upgrade. Please take a good look at what changed and the test results before merging this pull request.

What changed?

✳️ fastify (4.25.0 → 5.12.1) · Repo

Security Advisories 🚨

🚨 fastify vulnerable to X-Forwarded-* spoofing under trustProxy hop-count

Impact

The fix for CVE-2026-3635 (GHSA-444r-cwp2-x5xf) added a proxyFn(socket.remoteAddress, 0) guard on the X-Forwarded-* reads in request.host, request.protocol, request.hostname, request.ip, and request.ips. That guard closes the IP, CIDR, and custom-function forms of trustProxy correctly because those forms compile to predicates that inspect the connecting address. The hop-count form (trustProxy: <number>) compiles to a predicate that structurally ignores the address argument, so the guard reduces to 0 < tp, always true for any tp >= 1.

Applications configured with trustProxy: <number> (documented as "behind N reverse proxies", trustProxy: 1 being the canonical single-proxy setting) remain vulnerable. An attacker who can reach the Fastify origin directly, bypassing the front-facing proxy, can spoof the request fields exactly as in the unpatched version. Impact class matches the parent CVE-2026-3635: host injection in generated URLs, HTTPS-enforcement bypass, secure-cookie / CSRF-origin bypass, host-based routing and cache poisoning.

Patches

Patched in fastify 5.12.1. The numeric form of trustProxy is now disabled at runtime and removed from the TypeScript type union.

Workarounds

  • Migrate to an IP / CIDR / custom-function trustProxy value that validates the connecting address. Custom functions must inspect the address argument, not only the hop index.
  • Ensure the Fastify origin is only reachable through the trusted proxy chain (no direct network path).

🚨 fastify vulnerable to schema validation bypass via root primitive coercion mismatch

Impact

fastify before 5.12.1, when a route uses a root-level primitive body schema (for example an integer with a minimum and maximum) and the default type coercion, validates the coerced value but exposes the original, uncoerced value to the route handler. For example, a JSON body "10" is coerced to the number 10 and passes an integer 1 to 10 schema, but request.body stays the string "10". An application that trusts the validated type is handed a value that did not satisfy the schema, which can bypass limits the application enforces on that typed value. Object and array body schemas are not affected, they coerce their members in place.

Patches

Upgrade to fastify 5.12.1.

Workarounds

Until you can upgrade, avoid relying on the validated type of a root primitive body. Wrap the value in an object schema (object properties are coerced in place), for example accept { "value": 10 } and read request.body.value, or re-check the type in the handler.

🚨 Fastify has a Body Schema Validation Bypass via Leading Space in Content-Type Header

Summary

A validation bypass vulnerability exists in Fastify v5.x where request body validation schemas specified via schema.body.content can be completely circumvented by prepending a single space character (\x20) to the Content-Type header. The body is still parsed correctly as JSON (or any other content type), but schema validation is entirely skipped.
This is a regression introduced by commit f3d2bcb (fix for CVE-2025-32442).

Details

The vulnerability is a parser-validator differential between two independent code paths that process the raw Content-Type header differently.
Parser path (lib/content-type.js, line ~67) applies trimStart() before processing:

const type = headerValue.slice(0, sepIdx).trimStart().toLowerCase()
// ' application/json' → trimStart() → 'application/json' → body is parsed ✓

Validator path (lib/validation.js, line 272) splits on /[ ;]/ before trimming:

function getEssenceMediaType(header) {
  if (!header) return ''
  return header.split(/[ ;]/, 1)[0].trim().toLowerCase()
}
// ' application/json'.split(/[ ;]/, 1) → ['']  (splits on the leading space!)
// ''.trim() → ''
// context[bodySchema][''] → undefined → NO validator found → validation skipped!

The ContentType class applies trimStart() before processing, so the parser correctly identifies application/json and parses the body. However, getEssenceMediaType splits on /[ ;]/ before trimming, so the leading space becomes a split point, producing an empty string. The validator looks up a schema for content-type "", finds nothing, and skips validation entirely.
Regression source: Commit f3d2bcb (April 18, 2025) changed the split delimiter from ';' to /[ ;]/ to fix CVE-2025-32442. The old code (header.split(';', 1)[0].trim()) was not vulnerable to this vector because .trim() would correctly handle the leading space. The new regex-based split introduced the regression.

PoC

const fastify = require('fastify')({ logger: false });

fastify.post('/transfer', {
schema: {
body: {
content: {
'application/json': {
schema: {
type: 'object',
required: ['amount', 'recipient'],
properties: {
amount: { type: 'number', maximum: 1000 },
recipient: { type: 'string', maxLength: 50 },
admin: { type: 'boolean', enum: [false] }
},
additionalProperties: false
}
}
}
}
}
}, async (request) => {
return { processed: true, data: request.body };
});

(async () => {
await fastify.ready();

// BLOCKED — normal request with invalid payload
const res1 = await fastify.inject({
method: 'POST',
url: '/transfer',
headers: { 'content-type': 'application/json' },
payload: JSON.stringify({ amount: 9999, recipient: 'EVIL', admin: true })
});
console.log('Normal:', res1.statusCode);
// → 400 FST_ERR_VALIDATION

// BYPASS — single leading space
const res2 = await fastify.inject({
method: 'POST',
url: '/transfer',
headers: { 'content-type': ' application/json' },
payload: JSON.stringify({ amount: 9999, recipient: 'EVIL', admin: true })
});
console.log('Leading space:', res2.statusCode);
// → 200 (validation bypassed!)
console.log('Body:', res2.body);

await fastify.close();
})();

Output:

Normal: 400
Leading space: 200
Body: {"processed":true,"data":{"amount":9999,"recipient":"EVIL","admin":true}}

Impact

Any Fastify application that relies on schema.body.content (per-content-type body validation) to enforce data integrity or security constraints is affected. An attacker can bypass all body validation by adding a single space before the Content-Type value. The attack requires no authentication and has zero complexity — it is a single-character modification to an HTTP header.
This vulnerability is distinct from all previously patched content-type bypasses:

CVE Vector Patched in 5.8.4?
CVE-2025-32442 Casing / semicolon whitespace ✅ Yes
CVE-2026-25223 Tab character (\t) ✅ Yes
CVE-2026-3419 Trailing garbage after subtype ✅ Yes
This finding Leading space (\x20) ❌ No

Recommended fix — add trimStart() before the split in getEssenceMediaType:

function getEssenceMediaType(header) {
  if (!header) return ''
  return header.trimStart().split(/[ ;]/, 1)[0].trim().toLowerCase()
}

🚨 fastify: request.protocol and request.host Spoofable via X-Forwarded-Proto/Host from Untrusted Connections

Summary

When trustProxy is configured with a restrictive trust function (e.g., a specific IP like trustProxy: '10.0.0.1', a subnet, a hop count, or a custom function), the request.protocol and request.host getters read X-Forwarded-Proto and X-Forwarded-Host headers from any connection — including connections from untrusted IPs. This allows an attacker connecting directly to Fastify (bypassing the proxy) to spoof both the protocol and host seen by the application.

Affected Versions

fastify <= 5.8.2

Impact

Applications using request.protocol or request.host for security decisions (HTTPS enforcement, secure cookie flags, CSRF origin checks, URL construction, host-based routing) are affected when trustProxy is configured with a restrictive trust function.

When trustProxy: true (trust everything), both host and protocol trust all forwarded headers — this is expected behavior. The vulnerability only manifests with restrictive trust configurations.

🚨 Fastify's Missing End Anchor in "subtypeNameReg" Allows Malformed Content-Types to Pass Validation

Description

Fastify incorrectly accepts malformed Content-Type headers containing trailing characters after the subtype token, in violation of RFC 9110 §8.3.1. For example, a request sent with Content-Type: application/json garbage passes validation and is processed normally, rather than being rejected with 415 Unsupported Media Type.

When regex-based content-type parsers are in use (a documented Fastify feature), the malformed value is matched against registered parsers using the full string including the trailing garbage. This means a request with an invalid content-type may be routed to and processed by a parser it should never have reached.

Impact

An attacker can send requests with RFC-invalid Content-Type headers that bypass validity checks, reach content-type parser matching, and be processed by the server. Requests that should be rejected at the validation stage are instead handled as if the content-type were valid.

Workarounds

Deploy a WAF rule to protect against this

Fix

The fix is available starting with v5.8.1.

🚨 Fastify Vulnerable to DoS via Unbounded Memory Allocation in sendWebStream

Impact

A Denial of Service vulnerability in Fastify’s Web Streams response handling can allow a remote client to exhaust server memory. Applications that return a ReadableStream (or Response with a Web Stream body) via reply.send() are impacted. A slow or non-reading client can trigger unbounded buffering when backpressure is ignored, leading to process crashes or severe degradation.

Patches

The issue is fixed in Fastify 5.7.3. Users should upgrade to 5.7.3 or later.

Workarounds

Avoid sending Web Streams from Fastify responses (e.g., ReadableStream or Response bodies). Use Node.js streams (stream.Readable) or buffered payloads instead until the project can upgrade.

References

🚨 Fastify's Content-Type header tab character allows body validation bypass

Impact

A validation bypass vulnerability exists in Fastify where request body validation schemas specified by Content-Type can be completely circumvented. By appending a tab character (\t) followed by arbitrary content to the Content-Type header, attackers can bypass body validation while the server still processes the body as the original content type.

For example, a request with Content-Type: application/json\ta will bypass JSON schema validation but still be parsed as JSON.

This vulnerability affects all Fastify users who rely on Content-Type-based body validation schemas to enforce data integrity or security constraints. The concrete impact depends on the handler implementation and the level of trust placed in the validated request body, but at the library level, this allows complete bypass of body validation for any handler using Content-Type-discriminated schemas.

This issue is a regression or missed edge case from the fix for a previously reported vulnerability.

Patches

This vulnerability has been patched in Fastify v5.7.2. All users should upgrade to this version or later immediately.

Workarounds

If upgrading is not immediately possible, user can implement a custom onRequest hook to reject requests containing tab characters in the Content-Type header:

fastify.addHook('onRequest', async (request, reply) => {
  const contentType = request.headers['content-type']
  if (contentType && contentType.includes('\t')) {
    reply.code(400).send({ error: 'Invalid Content-Type header' })
  }
})

Resources

🚨 Fastify vulnerable to invalid content-type parsing, which could lead to validation bypass

Impact

In applications that specify different validation strategies for different content types, it's possible to bypass the validation by providing a slightly altered content type such as with different casing or altered whitespacing before ;.

Users using the the following pattern are affected:

fastify.post('/', {
  handler(request, reply) {
    reply.code(200).send(request.body)
  },
  schema: {
    body: {
      content: {
        'application/json': {
          schema: {
            type: 'object',
            properties: {
              'foo': {
                type: 'string',
              }
            },
            required: ['foo']
          }
        },
      }
    }
  }
})

User using the following pattern are not affected:

fastify.post('/', {
  handler(request, reply) {
    reply.code(200).send(request.body)
  },
  schema: {
    body: {
      type: 'object',
      properties: {
        'foo': {
          type: 'string',
        }
      },
      required: ['foo']
    }
  }
})

Patches

This was patched in v5.3.1, but unfortunately it did not cover all problems. This has been fully patched in v5.3.2.
Version v4.9.0 was also affected by this issue. This has been fully patched in v4.9.1.

Workarounds

Do not specify multiple content types in the schema.

References

Are there any links users can visit to find out more?

https://hackerone.com/reports/3087928

🚨 Fastify vulnerable to invalid content-type parsing, which could lead to validation bypass

Impact

In applications that specify different validation strategies for different content types, it's possible to bypass the validation by providing a slightly altered content type such as with different casing or altered whitespacing before ;.

Users using the the following pattern are affected:

fastify.post('/', {
  handler(request, reply) {
    reply.code(200).send(request.body)
  },
  schema: {
    body: {
      content: {
        'application/json': {
          schema: {
            type: 'object',
            properties: {
              'foo': {
                type: 'string',
              }
            },
            required: ['foo']
          }
        },
      }
    }
  }
})

User using the following pattern are not affected:

fastify.post('/', {
  handler(request, reply) {
    reply.code(200).send(request.body)
  },
  schema: {
    body: {
      type: 'object',
      properties: {
        'foo': {
          type: 'string',
        }
      },
      required: ['foo']
    }
  }
})

Patches

This was patched in v5.3.1, but unfortunately it did not cover all problems. This has been fully patched in v5.3.2.
Version v4.9.0 was also affected by this issue. This has been fully patched in v4.9.1.

Workarounds

Do not specify multiple content types in the schema.

References

Are there any links users can visit to find out more?

https://hackerone.com/reports/3087928

Release Notes

Too many releases to show here. View the full release notes.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.


Depfu Status

Depfu will automatically keep this PR conflict-free, as long as you don't add any commits to this branch yourself. You can also trigger a rebase manually by commenting with @depfu rebase.

All Depfu comment commands
@​depfu rebase
Rebases against your default branch and redoes this update
@​depfu recreate
Recreates this PR, overwriting any edits that you've made to it
@​depfu merge
Merges this PR once your tests are passing and conflicts are resolved
@​depfu cancel merge
Cancels automatic merging of this PR
@​depfu close
Closes this PR and deletes the branch
@​depfu reopen
Restores the branch and reopens this PR (if it's closed)
@​depfu pause
Ignores all future updates for this dependency and closes this PR
@​depfu pause [minor|major]
Ignores all future minor/major updates for this dependency and closes this PR
@​depfu resume
Future versions of this dependency will create PRs again (leaves this PR as is)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants