ZeroPath at Black Hat USA 2026

Babel CVE-2026-44728: Brief Summary of Arbitrary Code Execution via SystemJS Module Transform

A brief summary of CVE-2026-44728, a high severity code injection vulnerability in Babel's SystemJS module transform plugin that allows attacker crafted source code to produce compiled output containing arbitrary JavaScript. Includes patch analysis and affected version details.

CVE Analysis

10 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-05-26

Babel CVE-2026-44728: Brief Summary of Arbitrary Code Execution via SystemJS Module Transform
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A flaw in Babel's SystemJS module transform plugin allows specially crafted JavaScript source code to produce compiled output containing arbitrary executable code, turning the build process itself into an attack vector. With roughly 367 million annual npm downloads for babel-core and a four year window of affected versions, this vulnerability sits squarely in the supply chain risk category that security teams have been increasingly focused on.

CVE-2026-44728 carries a CVSS 8.2 (HIGH) score and affects @babel/plugin-transform-modules-systemjs from version 7.12.0 through 7.29.3, as well as the 8.x alpha line from 8.0.0-alpha.0 through 8.0.0-alpha.12. The vulnerability is classified under both CWE-94 (Code Injection) and CWE-843 (Type Confusion), reflecting two distinct weakness mechanisms that converge on the same outcome.

Technical Information

Vulnerable Component

The vulnerable component is @babel/plugin-transform-modules-systemjs, a Babel plugin responsible for transforming ECMAScript module syntax (import/export statements and import() expressions) into SystemJS compatible format. This plugin is also included in @babel/preset-env when configured with the modules: "systemjs" option, meaning projects using preset-env with that setting are transitively affected. No other plugins under the @babel/ namespace are impacted by this specific vulnerability.

Root Cause

The root cause lies in the constructExportCall function inside packages/babel-plugin-transform-modules-systemjs/src/index.ts. When Babel encounters export names that are string specifiers (a feature introduced in ES2022, e.g., export {foo as "default exports"}), the vulnerable code path unconditionally used t.identifier(exportName) to build MemberExpression AST nodes.

The problem is that t.identifier() does not sanitize or validate the string content it receives. An identifier node in the AST is meant to hold valid JavaScript identifiers, not arbitrary strings. When a carefully crafted string export name is passed through t.identifier(), it can break out of the intended AST structure entirely. The resulting compiled output would contain the attacker's string interpreted as raw JavaScript code rather than as a quoted property name.

For example, consider an export like export {foo as "malicious;payload()//"}. In the vulnerable code path, this string would be placed into an identifier node and rendered as dot notation in the output: _exportObj.malicious;payload()//. The semicolon terminates the intended statement, payload() executes as a function call, and // comments out the rest of the line. The compiled output now contains executable code that was never present in the original source.

This is why the vulnerability carries both CWE-94 (Code Injection) and CWE-843 (Type Confusion) classifications. The type confusion occurs when a string specifier (which should be treated as a string literal) is incorrectly processed as an identifier. The code injection is the consequence: attacker controlled data influences the structure of the generated output code.

CVSS Vector Breakdown

The CVSS vector CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H reveals several important characteristics:

ComponentValueMeaning
Attack VectorLocal (AV:L)Attacker must provide code to the Babel compiler locally or through a pipeline
Attack ComplexityLow (AC:L)No special conditions required beyond submitting crafted code
Privileges RequiredLow (PR:L)Minimal access needed to submit source for compilation
User InteractionRequired (UI:R)A user or automated process must compile the malicious code
ScopeChanged (S:C)Impact extends beyond the vulnerable component
ConfidentialityHigh (C:H)Total information disclosure possible
IntegrityHigh (I:H)Total system integrity compromise possible
AvailabilityHigh (A:H)Total availability disruption possible

The "Scope: Changed" designation is particularly significant. It indicates that compromise of the build toolchain can cascade into production environments: the malicious code injected during compilation would be present in the final bundled output served to users or executed on servers.

Attack Flow

  1. Craft malicious source: The attacker creates a JavaScript file containing an export statement with a carefully constructed string specifier, such as export {foo as "crafted_payload"}, where the payload string is designed to produce valid, executable JavaScript when incorrectly placed into an identifier position.

  2. Submit for compilation: The attacker submits this file to a system that compiles it using Babel with the SystemJS module transform enabled. This could be a CI/CD pipeline processing pull requests, a build server handling dependency updates, or any workflow that compiles third party code.

  3. Babel processes the input: During the AST transformation phase, @babel/plugin-transform-modules-systemjs encounters the string export specifier and passes it to t.identifier() instead of t.stringLiteral().

  4. Malicious output generated: The compiled output contains the attacker's payload as executable JavaScript code. When this output is subsequently loaded or executed (in a browser, Node.js runtime, or test environment), the injected code runs with the privileges of the execution context.

Key Constraint

The advisory explicitly states that this vulnerability only affects users who compile untrusted or third party code. Projects that strictly control their input source code and never compile external contributions face significantly lower risk. This distinction is critical for risk assessment and prioritization.

Patch Information

The Babel team addressed CVE-2026-44728 through a targeted fix in commit 5abf7bb (PR #17973), authored by Huáng Jùnliàng (@JLHwung) and reviewed by Nicolò Ribaudo (@nicolo-ribaudo). The fix was published on May 5, 2026, and the advisory (GHSA-fv7c-fp4j-7gwp) was formally released on May 8, 2026.

The fix is precise and addresses the root cause directly. It introduces a check for whether the export name is a string specifier, and when true, wraps the name in t.stringLiteral() (which properly escapes the string) and marks the MemberExpression as computed. Here is the critical portion of the diff:

// In constructExportCall — the member expression branch: for (let i = 0; i < exportNames.length; i++) { const exportName = exportNames[i]; + const computed = stringSpecifiers.has(exportName); + const exportNameNode = computed + ? t.stringLiteral(exportName) + : t.identifier(exportName); const exportValue = exportValues[i]; statements.push( @@ -140,7 +140,8 @@ function constructExportCall( "=", t.memberExpression( t.identifier(exportObj), - t.identifier(exportName), + exportNameNode, + computed, ), exportValue, ),

This means the generated code now uses bracket notation with a properly escaped string literal (_exportObj["default exports"]) instead of the unsafe dot notation (_exportObj.default exports), which would have caused the injected string to be interpreted as executable code.

A parallel fix was applied in the same commit to the object property construction path, ensuring consistent safe handling in both code generation branches:

for (let i = 0; i < exportNames.length; i++) { const exportName = exportNames[i]; + const exportNameNode = stringSpecifiers.has(exportName) + ? t.stringLiteral(exportName) + : t.identifier(exportName); const exportValue = exportValues[i]; - objectProperties.push( - t.objectProperty( - stringSpecifiers.has(exportName) - ? t.stringLiteral(exportName) - : t.identifier(exportName), - exportValue, - ), - ); + objectProperties.push(t.objectProperty(exportNameNode, exportValue));

The commit also includes new test fixtures that validate correct output for string name exports combined with export * re-exports, specifically testing cases like export {foo as "default exports", bar} from "./other.mjs" alongside export * from "./other.mjs".

Fixed Versions

PackageFixed Version
@babel/plugin-transform-modules-systemjs7.29.4 (for 7.x)
@babel/plugin-transform-modules-systemjs8.0.0-alpha.13 (for 8.x alpha)
@babel/preset-env7.29.5 (pulls in the patched plugin)

Upgrading can be done via npm install @babel/[email protected] or npm audit fix.

Ecosystem Response

Multiple high profile projects issued dependency bumps within days of the advisory:

ProjectActionDate
axiosBumped plugin to 7.29.4May 9, 2026
MetaMask SnapsDependency bump to 7.29.4May 9, 2026
Apache (mailing list PR)Bumped from 7.18.6 to 7.29.4May 9, 2026
OpenGeoscience/geojsBumped from 7.29.0 to 7.29.4Post advisory

Affected Systems and Versions

The following packages and version ranges are affected:

PackageAffected RangeFixed Version
@babel/plugin-transform-modules-systemjs>= 7.12.0 and < 7.29.47.29.4
@babel/plugin-transform-modules-systemjs>= 8.0.0-alpha.0 and < 8.0.0-alpha.138.0.0-alpha.13
@babel/preset-env (with modules: "systemjs")Same ranges (transitive)7.29.5

The vulnerability was introduced in version 7.12.0, which added support for ES2022 string name specifiers. This means the exposure window spans approximately four years of releases across the v7 line.

Vulnerable configurations: Any project is affected if it meets both of the following conditions:

  1. Uses @babel/plugin-transform-modules-systemjs directly, or uses @babel/preset-env with the modules: "systemjs" option.
  2. Compiles untrusted or third party source code through Babel.

Projects using CommonJS, AMD, or native ES module output are not affected by this specific vulnerability. Projects that compile only their own trusted source code are at significantly lower risk.

To identify affected configurations, audit Babel configuration files (.babelrc, babel.config.js, babel.config.json) for the modules: "systemjs" setting or direct references to @babel/plugin-transform-modules-systemjs.

Vendor Security History

CVE-2026-44728 is the second arbitrary code execution vulnerability discovered in Babel in recent years. The prior vulnerability, CVE-2023-45133, affected @babel/traverse, a core component of the Babel compilation pipeline, and was exploitable through the path.evaluate() function when processing crafted input.

CVE IDComponentCWEFixed Version
CVE-2023-45133@babel/traverseCWE-697, CWE-1847.23.2, 8.0.0-alpha.4
CVE-2026-44728@babel/plugin-transform-modules-systemjsCWE-94, CWE-8437.29.4, 8.0.0-alpha.13

A pattern emerges from these two vulnerabilities: both enable arbitrary code execution when Babel compiles specially crafted input, but they reside in different components and involve different root causes. CVE-2023-45133 affected the core traversal engine used by many plugins, while CVE-2026-44728 is isolated to the SystemJS module transform. The different CWE classifications (improper comparison and incomplete input filtering vs. code injection and type confusion) indicate distinct weakness mechanisms converging on the same outcome.

Babel is maintained by a small group of volunteers rather than a corporate entity with dedicated security staff. The original maintainer described working on Babel as a side project alongside his role as a JavaScript Engineer at Adobe. While the project responded promptly with fixes in both the v7 and v8 alpha channels for CVE-2026-44728, the four year exposure window highlights the challenge of proactively identifying such vulnerabilities in a volunteer maintained codebase. The Shenlong CVE Platform lists 4 total CVEs for the Babel product.

Organizations that depend on Babel should anticipate that further compilation time vulnerabilities may be discovered and should implement defense in depth around their build toolchains accordingly.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Jira
  • Linear
  • Slack
  • Security Compass
Security magnifying glass visualization