VYPR
Moderate severityNVD Advisory· Published Mar 6, 2026· Updated Mar 9, 2026

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

CVE-2026-3419

Description

Fastify incorrectly accepts malformed Content-Type headers containing trailing characters after the subtype token, in violation of RFC 9110 §8.3.1(https://httpwg.org/specs/rfc9110.html#field.content-type). 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.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
fastifynpm
>= 5.7.2, < 5.8.15.8.1

Affected products

1

Patches

1
67f6c9b32cb3

Merge commit from fork

https://github.com/fastify/fastifyJames SumnersMar 5, 2026via ghsa
2 files changed · +47 3
  • lib/content-type.js+9 3 modified
    @@ -11,7 +11,9 @@ const keyValuePairsReg = /([\w!#$%&'*+.^`|~-]+)=([^;]*)/gm
     
     /**
      * typeNameReg is used to validate that the first part of the media-type
    - * does not use disallowed characters.
    + * does not use disallowed characters. Types must consist solely of
    + * characters that match the specified character class. It must terminate
    + * with a matching character.
      *
      * @see https://httpwg.org/specs/rfc9110.html#rule.token.separators
      * @type {RegExp}
    @@ -20,12 +22,16 @@ const typeNameReg = /^[\w!#$%&'*+.^`|~-]+$/
     
     /**
      * subtypeNameReg is used to validate that the second part of the media-type
    - * does not use disallowed characters.
    + * does not use disallowed characters. Subtypes must consist solely of
    + * characters that match the specified character class, and optionally
    + * terminated with any amount of whitespace characters. Without the terminating
    + * anchor (`$`), the regular expression will match the leading portion of a
    + * string instead of the whole string.
      *
      * @see https://httpwg.org/specs/rfc9110.html#rule.token.separators
      * @type {RegExp}
      */
    -const subtypeNameReg = /^[\w!#$%&'*+.^`|~-]+\s*/
    +const subtypeNameReg = /^[\w!#$%&'*+.^`|~-]+\s*$/
     
     /**
      * ContentType parses and represents the value of the content-type header.
    
  • test/content-type.test.js+38 0 modified
    @@ -74,6 +74,44 @@ describe('ContentType class', () => {
         found = new ContentType('foo/π; param=1')
         t.assert.equal(found.isEmpty, true)
         t.assert.equal(found.isValid, false)
    +
    +    found = new ContentType('application/json<script>alert(1)</script>')
    +    t.assert.equal(found.isEmpty, true)
    +    t.assert.equal(found.isValid, false)
    +
    +    found = new ContentType('application/json/extra/slashes')
    +    t.assert.equal(found.isEmpty, true)
    +    t.assert.equal(found.isValid, false)
    +
    +    found = new ContentType('application/json(garbage)')
    +    t.assert.equal(found.isEmpty, true)
    +    t.assert.equal(found.isValid, false)
    +
    +    found = new ContentType('application/json@evil')
    +    t.assert.equal(found.isEmpty, true)
    +    t.assert.equal(found.isValid, false)
    +
    +    found = new ContentType('application/json\x00garbage')
    +    t.assert.equal(found.isEmpty, true)
    +    t.assert.equal(found.isValid, false)
    +  })
    +
    +  test('subtype with multiple fields validates as incorrect', (t) => {
    +    let found = new ContentType('application/json whatever')
    +    t.assert.equal(found.isValid, false)
    +    t.assert.equal(found.isEmpty, true)
    +
    +    found = new ContentType('application/    json whatever')
    +    t.assert.equal(found.isValid, false)
    +    t.assert.equal(found.isEmpty, true)
    +
    +    found = new ContentType('application/json whatever; foo=bar')
    +    t.assert.equal(found.isValid, false)
    +    t.assert.equal(found.isEmpty, true)
    +
    +    found = new ContentType('application/    json whatever; foo=bar')
    +    t.assert.equal(found.isValid, false)
    +    t.assert.equal(found.isEmpty, true)
       })
     
       test('returns a plain media type instance', (t) => {
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.