GHSA-x36r-4347-pm5x swagger-typescript-api vulnerable to Server-Side Request Forgery via spec `$ref`
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`:The fetch itself:…and the only URL-shape filter: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).PoCSelf-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`):**Steps:****Observed (control vs payload):**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:**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.**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`.**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)
CVSS v3.1 score: 6.1