Sep 9, 2026·7 min read·2 visits
Unsanitized path interpolation in Microsoft TypeSpec allows malicious specs to write files outside of the emitterOutputDir via directory traversal sequences.
A path traversal vulnerability (CWE-22) in the Microsoft TypeSpec compiler core and associated emitter packages permits unvalidated user input to escape the designated output directory, resulting in arbitrary JSON and YAML file creation or modification on the host system.
This report details a directory traversal vulnerability (CWE-22) affecting the Microsoft TypeSpec (formerly ADL / CADL) compiler ecosystem, tracked under advisory ID GHSA-2Q42-4Q24-7RGV. The flaw is located within the core path interpolation mechanisms and multiple official code generator packages. Affected packages include the central compiler @typespec/compiler, the OpenAPI generator @typespec/openapi3, the JSON Schema generator @typespec/json-schema, and the @typespec/asset-emitter asset management library.
TypeSpec compilers rely on emitters to translate high-level API design files (.tsp) into serialized output definitions (such as OpenAPI v3 yaml or JSON schemas). The compiler environment defines a base output boundary, designated via emitterOutputDir, to isolate generated schemas. However, the path generation routines fail to neutralize traversal patterns embedded in user-controlled metadata. When a developer or automated build agent compiles a maliciously crafted specification file, the execution path escapes this isolated directory and modifies arbitrary paths on the host system.
The attack surface is present in development environments, build systems, and continuous integration pipelines that automatically process TypeSpec files from external sources. Because TypeSpec allows backticked identifiers to override syntactic character rules, an adversary can embed directory traversal paths in version enums, namespace names, and model structures. The vulnerability has no corresponding CVE ID and remains cataloged solely via the GitHub Security Advisory database, which can bypass some automated legacy scanners.
The root cause of GHSA-2Q42-4Q24-7RGV is the complete absence of input validation and path sanitization on variables interpolated into output paths. Emitters utilize dynamic template layouts to construct file outputs. In the OpenAPI 3 emitter, the output pattern is defined dynamically using variables such as {version}, {service-name}, and {service-name-if-multiple}. In the JSON Schema emitter, model declaration names are directly converted to output filenames.
To construct the destination file path, the compiler core joins the root directory (emitterOutputDir) with the path template and passes the string to an interpolatePath helper function. This helper executes direct string substitutions using parsed metadata strings directly extracted from the source .tsp file. Because this replacement is performed on the combined path string, any directory traversal segments (such as ../ or ..\) are resolved relative to the entire filesystem hierarchy rather than remaining constrained to the destination directory.
Furthermore, the system's support for backticked identifiers expands the input vector. Backticked identifiers allow model structures to bypass typical variable constraints, accepting characters like slashes, null bytes, and drive indicators. When these model names are processed as filenames by the JSON Schema emitter, they are processed raw, allowing an attacker to generate arbitrary filesystem locations and perform file overwrite operations.
The vulnerability was mitigated in the Microsoft TypeSpec repository in Pull Request #11777 (Commit e0f67bdf3c5a0875dfa98b475648af37caac71a6). The patch introduces a validation and sanitization utility within the path interpolation engine of the core compiler, located at packages/compiler/src/core/helpers/path-interpolation.ts.
// Defensive sanitization helper added in packages/compiler/src/core/helpers/path-interpolation.ts
const UnsafePathSegmentCharsRegex = /[/\\:\0]/g;
const OnlyDotsRegex = /^\.+$/;
/**
* Sanitize a value so it is safe to use as a single segment of a path.
* Drive letter separators, path separators, and null bytes are replaced with '_'.
* Segments consisting entirely of periods (e.g. '.' or '..') are neutralized to '_'.
*/
export function sanitizePathSegment(value: string): string {
const sanitized = value.replace(UnsafePathSegmentCharsRegex, "_");
return OnlyDotsRegex.test(sanitized) ? "_" : sanitized;
}The sanitization routine replaces POSIX and Windows slashes (/, \), Windows drive letters (:), and null characters (\0) with underscores. It also checks if the path segment consists entirely of periods, neutralizing traversal sequences like .. to _. This prevents dynamic input strings from escaping their targeted directories.
Corresponding modifications were applied to individual emitters. The @typespec/openapi3 emitter was modified to sanitize dynamic variables before substitution. Similarly, the @typespec/json-schema compiler was updated to pass model declaration names through the sanitizer before allocating the new file scope.
// Patched JSON Schema emitter behavior in packages/json-schema/src/json-schema-emitter.ts
#newFileScope(type: JsonSchemaDeclaration) {
const sourceFile = this.emitter.createSourceFile(
`${sanitizePathSegment(this.declarationName(type)!)}.${this.#fileExtension()}`,
);
// ...
}Within @typespec/asset-emitter, path resolution is guarded using a dedicated containment utility that splits the path and filters relative traversal segments prior to invoking sanitization.
// Contained path resolution in packages/asset-emitter/src/asset-emitter.ts
function resolveContainedPath(path: string): string {
return path
.split(/[\\\/]/)
.filter((segment) => segment !== "" && segment !== "." && segment !== "..")
.map(sanitizePathSegment)
.join("/");
}Exploitation of this vulnerability requires local or remote delivery of a crafted .tsp file that is compiled by the target environment. The trigger sequence differs depending on the active emitter package.
In the @typespec/openapi3 scenario, the attack is structured around the @versioned decorator. An attacker can construct a version string containing a relative climbing path:
import "@typespec/openapi3";
import "@typespec/versioning";
using TypeSpec.Versioning;
@versioned(Versions)
@service(#{ title: "Vulnerable Service" })
namespace Svc {
enum Versions {
v1: "../../../../../../tmp/PWNED/pwn"
}
}When the compiler executes the OpenAPI emitter against this specification, it processes the template configuration. Because the version enum is parsed and integrated into the destination path without sanitization, the compiler writes the resulting YAML output directly to /tmp/PWNED/pwn.yaml instead of the expected output directory.
For systems utilizing the JSON Schema emitter, exploitation relies on backticked identifiers. The compiler processes backticked model names as standard identifiers while retaining arbitrary symbol characters:
import "@typespec/json-schema";
model `../../escaped` {
id: string;
}Upon compilation, the JSON schema generator attempts to write the resulting file as ../../escaped.json relative to the build root, bypassing local folder boundaries.
The practical impact of GHSA-2Q42-4Q24-7RGV is arbitrary file write and file overwrite on the system compiling the TypeSpec source code. If the compilation process is performed within automated environments with elevated write permissions, the vulnerability can lead to code execution or complete system compromise. An attacker can overwrite startup scripts, shell configuration files, dependency configurations, or pipeline steps, causing subsequent steps to execute arbitrary binaries.
The vulnerability is assessed with a CVSS v3.1 vector of CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:H, giving it a score of 7.9. The scope parameter is changed to Changed (S:C) because escaping the target build output directory alters the security context of the host operating system. The user interaction requirement (UI:R) is fulfilled when a user, administrator, or CI engine executes the compiler on the untrusted codebase.
Because no CVE ID was assigned, standard security alerting systems that rely purely on CVE indicators will not flag this vulnerability in dependency audits. This creates a risk of silent exposure within development pipelines that depend on older, unpatched releases of the TypeSpec toolchain.
The mitigation introduced in PR #11777 effectively addresses the core path traversal vectors associated with direct variable interpolation in standard operating systems. By converting all path separators, drive indicators, and period-only sequences into underscores, the compiler prevents directories from being traversed or altered through dynamic parameters.
Security researchers analyzing downstream systems must monitor several potential edge-case scenarios:
%2e%2e%2f) after the sanitizePathSegment function has run, the path validation can be bypassed. Ensuring that input is fully decoded before passing through sanitization limits this risk./ or \ to standardized slashes) after sanitization is completed, path traversal may still occur. Developers should ensure the host platform's path resolving library operates on sanitized, normalized strings.CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
@typespec/compiler Microsoft | < fixed release containing PR #11777 | Fixed in release containing PR #11777 |
@typespec/openapi3 Microsoft | < fixed release containing PR #11777 | Fixed in release containing PR #11777 |
@typespec/json-schema Microsoft | < fixed release containing PR #11777 | Fixed in release containing PR #11777 |
@typespec/asset-emitter Microsoft | < fixed release containing PR #11777 | Fixed in release containing PR #11777 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Local (via malicious TypeSpec file compilation) |
| CVSS Score | 7.9 (Estimated) |
| EPSS Score | N/A |
| Impact | Arbitrary file creation and write outside of output directory |
| Exploit Status | Proof of Concept Available |
| KEV Status | Not Listed |
The software uses external input to construct a pathname that is intended to identify a file or directory that is located under a restricted parent directory, but the software does not properly neutralize special elements within the pathname.
A resource consumption vulnerability exists in the multer library version 2.2.0 when utilizing the disk storage engine. When a remote client aborts or truncates an in-progress file upload, multer removes the partial file from the disk but fails to properly close the active write stream. This behavior leaves the underlying file descriptor open in the operating system, allowing a remote attacker to systematically exhaust the server's file descriptor limits and trigger a Denial of Service.
CVE-2026-77078 is a critical denial of service vulnerability in the multer Node.js package, allowing unauthenticated remote attackers to crash the runtime process using a single crafted multipart/form-data HTTP payload.
A critical parser differential vulnerability exists in Nodemailer prior to version 9.1.0. An attacker can bypass recipient-domain validation checks by utilizing RFC 5322 comments, leading to unauthorized email routing.
An algorithmic complexity vulnerability in Nodemailer before version 9.1.0 allows remote attackers to block the Node.js event loop. This denial of service is triggered by processing large or complex lists of email addresses, leading to quadratic resource consumption.
Nodemailer (prior to version 9.1.0) is vulnerable to an IDN/Punycode domain allow-list bypass due to an interpretation conflict between legacy RFC-3492 codecs and modern UTS-46 Unicode parsers.
A missing authorization vulnerability (CWE-862) exists in n8n where AI Agent workflows executing as tools bypass the Sub-Workflow Caller Policy settings, allowing authenticated users with agent creation privileges to invoke unauthorized sub-workflows across project boundaries.