Developer platforms, tools and supply chain
openapi-typescript-codegen: CVE-2026-108551 code injection check
openapi-typescript-codegen code injection check
A crafted OpenAPI document can turn the TypeScript client that openapi-typescript-codegen generates into a way to run code. The generator copies strings from the document into single-quoted JavaScript literals without escaping them: path keys, parameter names, request media types, response header names, servers[0].url and info.version. One single quote ends the literal, and whatever follows runs as code.
The package has no fixed release. Remove it, or generate only from documents you control. This tool finds where it is used and scans the code it generated.
| CVE | CVE-2026-108551 (CWE-94, code injection) |
| Severity | 9.8 Critical (CVSS 3.1, AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), 9.3 Critical (CVSS 4.0) |
| Affected | openapi-typescript-codegen 0.31.0 and earlier, which is every release |
| Fixed | Nothing. 0.31.0 is the latest release and is affected; the tracking issue, #2809, was still open on 23 October 2026 |
| Reported on | main at commit d7c2a99, all HTTP clients the generator supports (fetch, axios, xhr, node, angular) |
When it runs. A value in servers[0].url or info.version runs the moment the generated client is imported, because core/OpenAPI.ts is always generated and every generated service file imports it. A value in a request path or parameter name runs each time the affected method is called.
Who is at risk. Anyone who generates or runs a client from a document someone else can influence. That includes a developer running the generator locally, a CI job that regenerates a client from a remote or third-party specification, and an application that fetches a specification and runs a client built from it. This is a supply-chain and developer-tooling problem, not a bug in a server that listens for requests, even though the CVSS vector says network. The same class of bug has been reported and fixed in other OpenAPI generators, so apply the same discipline to them.
Public exploit. As of 23 October no exploit was public and the reporter offered a proof of concept on request. A public proof of concept on GitHub has since been reported. It is not linked here, and this tool contains no exploit logic: it reads files and never imports, compiles or runs generated code.
Quick start
-
Download the script. The repository is mirrored on GitHub and Codeberg; pick a base URL.
# BASE from GitHub: https://raw.githubusercontent.com/PrivacySafe/security-patches/refs/heads/main # BASE from Codeberg: https://codeberg.org/PrivacySafe/security-patches/raw/branch/main # curl -fLO "$BASE/openapi/openapi-codegen-check.sh" curl -fLO https://raw.githubusercontent.com/PrivacySafe/security-patches/refs/heads/main/openapi/openapi-codegen-check.sh -
Read it before running it, then make it executable.
less openapi-codegen-check.sh chmod +x openapi-codegen-check.sh -
See where the package is used. This changes nothing. Point
ROOTSat your projects if they are not under the defaults.ROOTS="/srv /home/ci" ./openapi-codegen-check.sh check -
Check what the generator already produced, before anything imports it.
./openapi-codegen-check.sh scan ./src/api ./generated
Commands
| Command | What it does |
|---|---|
check (default) |
Finds openapi-typescript-codegen in package.json, package-lock.json, npm-shrinkwrap.json, yarn.lock and pnpm-lock.yaml, in installed node_modules copies, and in the CI workflows, Makefiles, shell scripts and package.json scripts that run it, and marks a job whose input is a URL. Changes nothing, root optional. Exits 0 if nothing is found, 1 if the package is in use, 2 if something couldn't be judged |
scan DIR... |
Reads generated .ts, .js, .mjs and .cjs files under each directory and reports a spec-derived value that is not exactly one well-formed single-quoted literal. Exits 0 if nothing is found, 1 if it found something, 2 for a bad argument |
version |
Prints the script version. Every command prints it on its first line too |
Environment: ROOTS (where check looks; default /home /root /srv /var/www /opt /usr/local) and MAX_DEPTH (how far it descends, default 9).
What check judges
Every published release is affected and none is fixed, so any version at or below 0.31.0 reads as exposed, whether it comes from a lockfile, an installed copy or a version range in package.json. A resolved version newer than 0.31.0 reads as unknown, not safe: look at the tracking issue to see whether a fix has shipped. A job that fetches its specification from a URL is marked, because that is the case where a third party's text becomes code in your build.
What scan looks for
The generator writes each spec-derived value inside one single-quoted literal and nothing else on that line. So scan accepts only that shape: an opening quote, characters that are not an unescaped quote, a closing quote, and an optional trailing comma. It does not look for a list of dangerous characters. Code after the closing quote, or a bare quote in the middle of the value, does not fit and is reported, while a properly escaped apostrophe in a path or parameter name does not trigger it. It checks the url, BASE, VERSION, mediaType and responseHeader entries and quoted property keys, on lines laid out the way the generator lays them out.
It reports a breakout in BASE, VERSION, url, a parameter name or mediaType, and does not flag keys and paths that contain escaped apostrophes.
Before you run it
- The remedy. Remove the package, or control every document it reads.
scanreads one generator's output layout. A clean result covers that generator's output. It does not see a value placed on a line the generator would not have written, and hand-written code is out of scope.checkreads files, not running systems. It does not look inside container images or other machines. Remote build services that run the generator are yours to find.- GNU/Linux and a POSIX shell. It needs bash, find and grep, and nothing from Node.js. It never runs
npm,nodeor any generated file. - It also checks running docker and podman containers, unless you name a directory or set
SCAN_CONTAINERS=0. A read-only probe inside each running docker and podman container copies thepackage.jsonand lockfiles that mentionopenapi-typescript-codegen, whole, and the installednode_modulescopy'spackage.json, into a private temporary directory, and the script runs once per container in isolation. Workflows, Makefiles and shell scripts are not read inside containers. SettingROOTSyourself skips the containers.
If you use it
- Stop generating from documents you do not control. Treat an OpenAPI document as source code: review it, pin it by commit or hash, and fetch it only from a trusted, authenticated place.
- Move to a maintained generator. The upstream tracking issue points to
@hey-api/openapi-ts. Check what it does with the same values before you rely on it. - Until then, contain the job. Generate in an ephemeral job with no secrets, a read-only token and restricted network access. Review the generated diff, and do not import freshly generated output in a privileged process before that review.
- Run
scanon what you already have , in particular output generated from a remote or third-party specification. - If a hit is real, rotate every secret the generating job or developer machine could reach, rebuild from a clean checkout, check published packages built from that output, and look at outbound traffic from the runner.
Sources: the tracking issue, the VulnCheck advisory and the CVE record. The fix status above was current on 23 October 2026. Check the issue before relying on it.
The Security Patches software and this documentation are dedicated to the public domain under CC0 1.0 Universal. Other pages on privacysafe.app/docs are CC BY-SA 4.0 unless the page says otherwise.