@typespec/openapi3 Path Traversal via Versioned Enum Value in Output Filename
A crafted version string in a TypeSpec versioned enum can smuggle path traversal sequences into the emitted output filename, letting the compiler write or overwrite YAML and JSON files anywhere on…

The problem
The @typespec/openapi3 emitter takes the raw string value of a @versioned enum member and interpolates it directly into the output file path as {version}, with no sanitization of path separators or .. components.
The resulting path is handed to emitFile(), which calls mkdirp() and writeFile() without first checking that the destination sits inside emitterOutputDir. Any .tsp file a developer compiles can therefore write OpenAPI-formatted output to an arbitrary location on disk, constrained only by the process user's filesystem permissions.
Proof of concept
A working proof-of-concept for this issue in @typespec/openapi3, with the exact payload below.
import "@typespec/http";
import "@typespec/versioning";
import "@typespec/openapi3";
using TypeSpec.Http;
using TypeSpec.Versioning;
@service(#{ title: "PwnSvc" })
@versioned(Versions)
namespace Svc;
enum Versions {
v1: "../../../../../../../../../../tmp/TYPESPEC_PWNED/pwn",
}
@route("/ping")
op ping(): void;The version value is captured verbatim from snapshot.version!.value in openapi.ts and passed unchanged into interpolatePath(). Because resolvePath() joins the template to emitterOutputDir before the {version} token is substituted, the traversal sequences are never seen during path resolution, only after it.
The sink in emitter-utils.ts then calls mkdirp() on the attacker-controlled directory and writes the file without any containment check against emitterOutputDir (CWE-22, CWE-73).
The fix, introduced in commit e0f67bdf3c5a0875dfa98b475648af37caac71a6 via PR #11777, resolves the fully interpolated destination path and rejects it if it does not sit under emitterOutputDir, and also strips or rejects traversal components and path separators found inside spec-derived filename tokens.
The fix
Upgrade @typespec/openapi3 to a version that includes commit e0f67bdf3c5a0875dfa98b475648af37caac71a6 (PR #11777) once a patched release is published. As of the advisory date no patched npm release had been identified, so the interim workaround is to audit every @versioned enum string value for path separators and .. sequences before running tsp compile, and to run the compiler under a dedicated low-privilege account with a restricted working directory.
Related research
- high · 7.5CVE-2026-82393CVE-2026-82393: pnpm Scoped-Name Path Traversal Arbitrary File Write
- high · 7.1pnpm pacquet: Trust-Lockfile Dependency Alias Path Traversal
- high · 7.5grok-faf-mcp: Arbitrary File Read via Unconfined Path in FAF Tools
- high · 7.5faf-mcp Arbitrary File Read/Write via Unconfined Path Argument