Summary
orval's zod client emits each query parameter name as a double-quoted key in the generated zod.object({...}) request-validation schema WITHOUT escaping the double quote. A " in the query parameter name closes the key and lands in object-literal context, where an injected computed property key [expr] is evaluated when zod.object({...}) runs -- at MODULE IMPORT (export const OpQueryParams = zod.object({...}) executes on load) -> import-time RCE. The query parameter name is a pure data field. Verified on orval 8.19.0 / Node.
Details
export const OpQueryParams = zod.object({ "a":zod.string(),[require("fs").writeFileSync("PWNED","")]:zod.string(),"b": ... })
Also affects the hono client (reuses zod generation). orval escapes values in zod arrays but not keys in zod.object. Sibling fields: schema property name, header parameter name (CVE-97) (separate reports). Distinct from orval's $ref / route-path / server-url / zod-default findings.
PoC
reproduce.sh (+ makespec.py) attached: a query parameter name that breaks the zod.object key and injects a computed key; evaluating it (= importing the module) writes the marker. Verified on 8.19.0.
Impact
JavaScript / OS command execution at import time for anyone who generates an orval zod client from an attacker-controlled spec and imports it.
Suggested fix
Escape the query parameter name for the JS string key (JSON.stringify) in the zod.object key generation; never interpolate a raw name adjacent to [ ] in object-literal position. maintainer-report.txt makespec.py reproduce.sh
Summary
Orval emits the OpenAPI path into the generated request URL as a TEMPLATE LITERAL ( /users/... ) without escaping the backtick character. A path containing a backtick closes the template literal and injects a concatenation expression that is evaluated when the generated URL/request/key function is called, executing attacker-controlled code. Affects the axios, fetch, react-query, and swr clients. Verified on Orval 8.19.0; survives Orval's default OpenAPI validation.
Details
ts // generated URL builder (fetch client) return /users/ + (globalThis.X = require("fs").writeFileSync("/marker","pwned")) + /list;
The backtick in the path terminates the ... literal; + (<code>) + runs as a live expression; a trailing backtick reopens a template literal so the file stays valid. The code runs whenever the generated request function, URL builder, or query-key function is called.
This is distinct from Orval's four published CVEs: CVE-2026-22785 (summary -> MCP server), CVE-2026-23947 / CVE-2026-25141 (x-enumDescriptions), and CVE-2026-24132 (const -> mock files). This is a different field (the route path) and a different sink (the request-URL template literal), which those advisories and their fixes do not cover.
PoC
reproduce.sh (+ makespec.py) attached: generates a fetch client with default validation, bundles it, calls the generated functions, and shows a marker file written. Verified on 8.19.0.
Impact
Code execution in any environment that calls a client generated from an attacker-controlled or attacker-influenced OpenAPI description (developer, CI, or the running application).
Suggested fix
Escape the path before emitting it into the URL template literal (escape backtick and ${, or build the URL with an encoder that treats the path as data), and validate path characters.
Summary
orval's zod client emits each schema property name as a double-quoted key in the generated zod.object({...}) WITHOUT escaping the double quote. A " in a property name closes the key and lands in object-literal context, where an injected computed property key [expr] is evaluated when zod.object({...}) runs -- which is at MODULE IMPORT (the export const X = zod.object({...}) executes on load) -> import-time RCE. The property name is a pure data field. Verified on orval 8.19.0 / Node. CWE-94 / CWE-95 / CWE-116.
Details
ts export const OpBody = zod.object({ "a":zod.string(),[require("fs").writeFileSync("PWNED","")]:zod.string(),"b": zod.string().optional() })
Sibling: the MSW mock uses a single-quoted key (' breakout, call-time) -- separate report. The TS interface key is a type (DoS only). Distinct from orval's $ref / route-path / server-url / zod-default findings.
PoC
reproduce.sh (+ makespec.py) attached: a property name a":zod.string(),[require("fs").writeFileSync("<marker>","")]:zod.string(),"b -> zod.object; evaluating it (= importing the module) writes the marker. Verified on 8.19.0.
Impact
JavaScript / OS command execution (via childprocess) at import time for anyone who generates an orval zod client from an attacker-controlled spec and imports it. Estimated Critical, e.g. CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N.
Suggested fix
Escape the property name for the JS string key (JSON.stringify) in the zod.object key generation; never interpolate a raw property name adjacent to [ ] in object-literal position. maintainer-report.txt makespec.py reproduce.sh
Impact The MCP server generation logic relies on string manipulation that incorporates the summary field from the OpenAPI specification without proper validation or escaping. This allows an attacker to "break out" of the string literal and inject arbitrary code.
Here is an example OpenAPI with the exploit
yaml openapi: 3.0.4 info: title: Swagger Petstore - OpenAPI 3.0 description: |- This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about Swagger at https://swagger.io. In the third iteration of the pet store, we've switched to the design first approach! You can now help us improve the API whether it's by making changes to the definition itself or to the code. That way, with time, we can improve the API in general, and expose some of the new features in OAS3.
Some useful links: - The Pet Store repository - The source API definition for the Pet Store termsOfService: https://swagger.io/terms/ contact: email: apiteam@swagger.io license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html version: 1.0.27-SNAPSHOT externalDocs: description: Find out more about Swagger url: https://swagger.io servers: - url: https://petstore3.swagger.io/api/v3 tags: - name: pet description: Everything about your Pets externalDocs: description: Find out more url: https://swagger.io - name: store description: Access to Petstore orders externalDocs: description: Find out more about our store url: https://swagger.io - name: user description: Operations about user paths: /pet/findByStatus: get: tags: - pet summary: Finds Pets by status.' + require('childprocess').execSync("open -a Calculator").toString(),// description: Multiple status values can be provided with comma separated strings. operationId: findPetsByStatus parameters: - name: status in: query description: Status values that need to be considered for filter schema: type: string responses: '200': description: successful operation content: application/json: schema: type: string '400': description: Invalid status value default: description: Unexpected error security: - petstoreauth: - write:pets - read:pets
Patches This is fixed in version 7.18.0 or higher
Workarounds Do check your generated OpenAPI yaml/json before running through Orval CLI and correct it if it has injection.