Oct 1, 2026·6 min read·7 visits
An API contract collision in Fastify's validation runner allows attackers to bypass JSON Schema validation by embedding nested, unvalidated payloads inside a root-level 'value' key, which is then promoted to the root request body.
An API contract mismatch in the Fastify web framework allows remote attackers to bypass schema validation when asynchronous schema validators are used. When a route uses async validation, the validator resolves with the raw request body. If the body contains a root-level key named 'value', the validation runner interprets this as a synchronous wrapper envelope, extracting and promoting the unvalidated nested content to the root level of request.body.
The Fastify web framework relies on an internal validation runner to parse and validate incoming client requests. This component compiles route-specific schemas to sanitize input before the payload reaches downstream application controllers. Under default conditions, this architecture prevents malicious or malformed data from causing type confusion, parameter injection, or logic flaws within handlers.
To accommodate a wide ecosystem of custom schema compilers, Fastify supports both synchronous and asynchronous validation interfaces. Synchronous compilers, such as Joi, return structured validation results inside an envelope object. In contrast, asynchronous compilers, such as Ajv when compiled with the $async: true directive, resolve directly with the successfully validated dataset rather than an envelope.
This architectural difference creates a critical attack surface within the request runner. When processing successful asynchronous resolutions, the framework's internal runner fails to isolate the raw validated payload from legacy synchronous envelope-parsing rules. Consequently, attackers can transmit structured payloads that deliberately collide with internal validation properties, resulting in complete validation bypass.
The core of the vulnerability lies in the legacy unwrapping function named answer() in Fastify's validation execution path. This function is designed to handle custom synchronous validators that return a metadata wrapper structured as { value, error }. If answer() detects an error key on its argument, it signals a validation failure, whereas if it detects a value key, it extracts the nested value to replace the current request.body object.
When a route uses an asynchronous validation compiler, the validator returns a Promise that resolves directly with the raw validated payload upon success. The validation runner handles this resolved payload by passing it straight into the legacy answer() wrapper function. This creates an unhandled type collision because the user-controlled, successfully validated request payload is now evaluated as if it were a synchronous compiler envelope.
If an attacker structures an input payload to contain a root-level property named value, the asynchronous schema validator verifies that the outer object meets the schema criteria and passes the object to the resolver. The runner then processes this validated object through answer(). Because the object contains a root-level value property, answer() extracts its nested contents and assigns them directly to request.body, bypassing all structural checks on the nested fields.
The flaw is located in lib/validation.js where asynchronous validation promises are resolved and returned to the request lifecycle. The vulnerable path executes answer(res) on the resolved data, assuming it represents a validation wrapper.
Below is the code comparison between the vulnerable implementation and the patch:
// === VULNERABLE CODE ===
if (ret && typeof ret.then === 'function') {
return ret
.then((res) => { return answer(res) }) // Vulnerable wrapper processing
.catch(err => { return err })
}
// === PATCHED CODE ===
if (ret && typeof ret.then === 'function') {
// An async validator resolves with the validated data itself,
// not a { value, error } result envelope. Its properties must
// not be unwrapped. Treat the async result as pass/fail only.
return ret
.then((res) => { return res === false ? validatorFunction.errors : false })
.catch(err => { return err })
}In the patched version, the call to answer() is eliminated from the promise resolution chain. The runner now evaluates the resolved promise solely as a indicator of validation success or failure. If res is strictly false, validation is treated as failed and errors are returned. Otherwise, it returns false to indicate that no validation errors occurred, completely bypassing the legacy unwrapping logic.
To exploit this vulnerability, an attacker must identify a route that utilizes an asynchronous JSON Schema compiler, such as Ajv with asynchronous schemas. The endpoint must also permit additional properties on the input schema, or specifically permit a root-level parameter named value.
The exploit payload is structured with a root-level value property containing arbitrary, unvalidated keys. When submitted, the validator verifies only the top-level keys. Upon validation success, Fastify extracts the contents of value and promotes them to the root of request.body. This mechanism completely evades schema enforcement, allowing the injection of forbidden configuration properties, administrative roles, or restricted identifiers.
The impact of this vulnerability is classified as high severity with a CVSS v3.1 score of 8.1. By systematically bypassing JSON schema validation, remote attackers can achieve parameter injection, unauthorized modification of application state, and type confusion in backend databases or business logic.
In typical production applications, schema validation serves as the primary barrier against privilege escalation. For example, if a profile update route forbids the role parameter via schema constraints, an attacker can bypass this restriction by passing { "username": "user", "value": { "role": "admin" } }. This allows the attacker to gain administrative capabilities, modify access controls, or corrupt data integrity.
Furthermore, because the newly substituted request body never undergoes validation, database insertion operations may receive unexpected data structures. This can trigger unexpected application crashes, unhandled database exceptions, or secondary SQL injection and NoSQL injection vulnerabilities in downstream logic.
The primary remediation for this vulnerability is to upgrade Fastify to version 5.12.2 or later, which completely decouples the asynchronous execution path from the legacy synchronous unwrapping logic.
If immediate upgrading is not feasible, developers should enforce additionalProperties: false on all routes employing asynchronous validation. This configuration forces the schema validator to reject any payloads containing the unexpected root-level value property before the validation runner processes the result.
Alternatively, a global preValidation hook can be integrated to detect and drop payloads containing root-level value or error keys on affected routes. This ensures that potentially malicious collision payloads are discarded at the beginning of the request lifecycle.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
fastify fastify | < 5.12.2 | 5.12.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-20 |
| Attack Vector | Network |
| CVSS Score | 8.1 (High) |
| Exploit Status | poc |
| KEV Status | Not Listed |
| Impact | Validation Bypass / Parameter Injection |
The product does not validate or incorrectly validates input that can affect the control flow or data flow of a program.
An authorization bypass and path traversal vulnerability exists in the SiYuan knowledge workspace platform. The vulnerability is located in the '/api/file/getUniqueFilename' endpoint inside the 'github.com/siyuan-note/siyuan/kernel' package. Under default configurations, this route is exposed to users who satisfy basic authentication middleware checks, which includes anonymous readers in publish mode. By supplying unvalidated absolute paths, remote attackers can verify the existence of files and directories across the host operating system, establishing a high-fidelity file existence oracle.
A highly critical Regular Expression Denial of Service (ReDoS) vulnerability in basic-ftp, an FTP client library for Node.js. In versions prior to 6.2.1, a malicious or compromised FTP server can exploit this vulnerability to force the FTP client to consume quadratic CPU time during directory parsing. This issue blocks the single-threaded Node.js event loop, freezing the application process and leading to a complete Denial of Service (DoS).
An uncontrolled resource consumption vulnerability in the russh library allows remote authenticated attackers to exhaust server memory (heap) by flooding channel open requests during a stalled key re-exchange (rekeying) process, causing a denial of service via Out-of-Memory (OOM) termination.
A critical memory handling vulnerability exists in the pageant crate, a workspace component of the Rust-based russh SSH client library, during communication with the PuTTY Pageant SSH agent on Windows systems. Prior to version 0.2.3, the library's shared memory parsing logic blindly trusted a peer-controlled, 32-bit big-endian response length field. This allows local attackers running within the same user session to trigger out-of-bounds reads or execute an out-of-memory crash of the client application.
A validation bypass vulnerability exists in Fastify web framework prior to version 5.12.2. The flaw stems from shallow normalization of header validation schemas, which fails to lowercase nested or conditional schema rules (like JSON Schema dependencies or dependentRequired) defined in mixed or canonical casing. Consequently, because Node.js normalizes incoming HTTP request headers to lowercase, the compiled validator fails to match these headers against the un-normalized mixed-case schema triggers, silently skipping conditional checks and allowing unauthenticated attackers to bypass authorization or security headers.
CVE-2026-84469 is a high-severity request validation bypass vulnerability in the Fastify Node.js web framework. In versions prior to 5.12.2, Fastify uses loose truthiness checks to decide whether to compile request schemas. When a component (such as the body) is explicitly configured with a boolean 'false' schema—which under JSON Schema Draft 7 acts as a 'deny-all' constraint—Fastify's internal logic evaluates this as a falsy value and skips compilation entirely. This allows unauthenticated remote attackers to send arbitrary payloads to these endpoints, bypassing validation checks and directly executing backend route handlers.