Skip to content

Code injection via unescaped spec-derived strings in generated clients (RCE from an untrusted OpenAPI spec) #2809

Description

@carfeii

Summary

Several templates interpolate OpenAPI-spec-derived strings directly into a single-quoted JS string literal in the generated output, with no escaping. A single quote in the spec value closes the string literal early and the remainder is evaluated as live JS. Affected fields: the request path, each path/query/header/cookie parameter's wire name, the request-body media type, the response header name, the first server URL, and the API version.

Affected versions: confirmed on main at commit d7c2a99 (2026-09-04, latest at time of report), all HTTP clients (fetch/axios/xhr/node/angular).

Details

exportService.hbs emits url: '{{{path}}}', where path is getServices.ts's url variable, the raw key from openApi.paths (for (const url in openApi.paths)), unsanitized. The same file emits '{{{prop}}}': {{{name}}}, for path/query/header/cookie parameters, where prop is getOperationParameter.ts's parameter.name, again the raw spec value.

client.hbs and core/OpenAPI.hbs emit BASE: '{{{server}}}' / VERSION: '{{{version}}}', where server is getServer.ts's return value (openApi.servers[0].url) and version is openApi.info.version, both raw. core/OpenAPI.ts is always generated and is imported by every generated service file.

This is the same class of bug as several vulnerabilities already reported and fixed against other OpenAPI-to-TypeScript code generators (e.g. Orval's GHSA advisories for its request-URL template literal, and acacode/swagger-typescript-api's escapeJsTemplateLiteralWithPathParams), just via single-quoted string interpolation instead of a template literal.

POC

(available upon request)

Impact

Code execution in any environment that generates or runs a client from an attacker-controlled or attacker-influenced OpenAPI description (a developer running the generator locally, a CI pipeline that regenerates a client from a remote spec, or the application itself if it fetches and runs a spec-driven client dynamically).

Two distinct severities depending on which field is exploited:

  • Per-method-call RCE via the request path or a parameter name: the injected code runs every time the affected generated method is called.
  • Import-time RCE via the server URL or API version: the injected code runs as soon as core/OpenAPI.ts (or any generated service file, which imports it) is loaded, before any method is called at all.

I have a fix ready and will open a PR shortly.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions