Skip to content

🚨 [security] [boilerplate/ts/infra/rest/fastify] Update fastify 5.7.4 → 5.8.5 (minor)#547

Open
depfu[bot] wants to merge 1 commit intomainfrom
depfu/update/boilerplatetsinfrarestfastify/yarn/fastify-5.8.5
Open

🚨 [security] [boilerplate/ts/infra/rest/fastify] Update fastify 5.7.4 → 5.8.5 (minor)#547
depfu[bot] wants to merge 1 commit intomainfrom
depfu/update/boilerplatetsinfrarestfastify/yarn/fastify-5.8.5

Conversation

@depfu
Copy link
Copy Markdown
Contributor

@depfu depfu Bot commented Apr 15, 2026


🚨 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 update. Please take a good look at what changed and the test results before merging this pull request.

What changed?

✳️ fastify (5.7.4 → 5.8.5) · Repo

Security Advisories 🚨

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

Release Notes

5.8.5

⚠️ Security Release

This fixes CVE CVE-2026-33806 GHSA-247c-9743-5963.

What's Changed

  • chore: Fix port parsing by @jsumners in #6603
  • chore: upgrade to typescript v6.0.2 by @Tony133 in #6605
  • fix: restore trustProxy function for number and string types, add null check for socketAddr by @mcollina in #6613
  • ci: reduce cron scheduled workflows from daily/weekly to monthly by @Fdawgs in #6623
  • chore: Bump pnpm/action-setup from 4.2.0 to 5.0.0 by @dependabot[bot] in #6629
  • chore: Bump markdownlint-cli2 from 0.21.0 to 0.22.0 by @dependabot[bot] in #6632
  • chore: Bump borp from 0.21.0 to 1.0.0 by @dependabot[bot] in #6633
  • chore: Bump actions/dependency-review-action from 4.8.3 to 4.9.0 by @dependabot[bot] in #6630
  • docs(ecosystem): add @pompelmi/fastify-plugin by @SonoTommy in #6610

New Contributors

Full Changelog: v5.8.4...v5.8.5

5.8.4

Full Changelog: v5.8.3...v5.8.4

5.8.3

⚠️ Security Release

This fixes CVE CVE-2026-3635 GHSA-444r-cwp2-x5xf.

What's Changed

New Contributors

Full Changelog: v5.8.2...v5.8.3

5.8.2

What's Changed

New Contributors

Full Changelog: v5.8.1...v5.8.2

5.8.1

⚠️ Security Release

Fixes "Missing End Anchor in "subtypeNameReg" Allows Malformed Content-Types to Pass Validation": GHSA-573f-x89g-hqp9.

CVE-2026-3419

Full Changelog: v5.8.0...v5.8.1

5.8.0

What's Changed

  • docs(request): add host security warning references by @mcollina in #6476
  • docs: fix note style by @Fdawgs in #6487
  • chore: rename deploy website ci by @Eomm in #6492
  • chore: support pino v9 and v10 by @mcollina in #6496
  • chore: update logger types and fix TODO comment by @Tony133 in #6470
  • refactor(test-types): migrate dummy-plugin to FastifyPluginAsync by @Tony133 in #6472
  • docs: fix markdown typo in README.md by @droppingbeans in #6491
  • test: cover non-numeric content-length client error path by @mcollina in #6500
  • ci: remove tests-checker workflow by @Tony133 in #6481
  • ci: remove stale.yml file by @Tony133 in #6504
  • docs(security): remove hackerone references; change note style by @Fdawgs in #6501
  • chore: rename @sinclair/typebox to typebox by @Tony133 in #6494
  • ci(links-check): add external link checker using linkinator-action by @umxr in #6386
  • chore: upgrade borp to v1.0.0 by @Tony133 in #6510
  • docs: Add OpenJS CNA reference to SECURITY.md by @mcollina in #6516
  • fix: avoid mutating shared routerOptions across instances by @mcollina in #6515
  • fix(types): accept async route hooks in shorthand options by @mcollina in #6514
  • docs: Improve shutdown lifecycle documentation by @kibertoad in #6517
  • chore: remove unused tsconfig.eslint.json by @mrazauskas in #6524
  • feat: First-class support for handler-level timeouts by @kibertoad in #6521
  • docs(security): clarify insecureHTTPParser threat model scope by @mcollina in #6533
  • chore(license): standardise license notice by @Fdawgs in #6511
  • docs: clarify anyOf nullable coercion behavior with primitive types by @slegarraga in #6531
  • fix: remove format placeholder from FST_ERR_CTP_INVALID_MEDIA_TYPE message by @super-mcgin in #6528
  • docs(reference/hooks): fix note style by @Fdawgs in #6538
  • chore: Bump lycheeverse/lychee-action from 2.7.0 to 2.8.0 by @dependabot[bot] in #6539
  • chore: Bump actions/dependency-review-action from 4.8.2 to 4.8.3 by @dependabot[bot] in #6540
  • chore: Bump markdownlint-cli2 from 0.20.0 to 0.21.0 by @dependabot[bot] in #6542
  • ci: remove broken links and add ecosystem link validator by @mcollina in #6421
  • ci(validate-ecoystem-links): add job level permission by @Fdawgs in #6545
  • style: remove trailing whitespace by @Fdawgs in #6543

New Contributors

Full Changelog: v5.7.4...v5.8.0

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by 73 commits:


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