CVE-2026-104853: Nx Path Traversal Vulnerability

MODERATE
Advisory Identifier: GHSA-hrvq-x7jp-36xv
CWE-73 CWE-22
Published
Advisory Summary

CVE-2026-104853 is a MODERATE path traversal vulnerability (CWE-73: External Control of File Name or Path) affecting Nx libraries in the npm ecosystem. The issue involves path traversal in nx migrate package-migrations extraction. Remediation is available by upgrading affected packages to version 22.7.10, 23.2.1.

CVE-2026-104853 at a Glance

CVE Identifier CVE-2026-104853
GHSA Identifier GHSA-hrvq-x7jp-36xv
Severity MODERATE
CVSS v3.1 Base Score N/A
CWE Classification CWE-73: External Control of File Name or Path
Affected Product Nx
Package Ecosystem npm
Published Date
Last Updated

How to Fix CVE-2026-104853

To remediate this vulnerability, upgrade all affected Nx dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

nx fixed: 22.7.10
npm install nx@22.7.10
nx fixed: 23.2.1
npm install nx@23.2.1
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls nx

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-104853, inspect your project dependencies using npm:

npm ls nx

Analyze the terminal output to locate any version of Nx in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 22.7.10), your application is affected.

Note: The npm ls nx command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-73: External Control of File Name or Path, where the product allows user input to control or influence paths or file names that are used in filesystem operations. In Nx, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary `nx migrate` reads each target package's `nx-migrations.migrations` value from its manifest and extracts the referenced file to a path built by joining that value onto a temporary directory. The value is never validated, so a package whose `migrations` field contains `..` segments (or an absolute path) steers the extraction to write outside the temporary directory. A hostile package — or any package pulled in transitively through a trusted package's `packageGroup` — can write attacker-controlled content, or truncate an existing file, anywhere the running user can write. This happens during migration *planning*, before the user reviews the migration list and before `--run-migrations`, so it does not require the user to approve or execute anything. **Most workspaces need no action.** By default `nx migrate` does not run the nx installed in your workspace — it installs `nx@latest` into a temporary directory and performs the upgrade planning, including this extraction, with that copy. Now that a patched nx is the latest release, a default `nx migrate` run is unaffected whatever version the workspace has installed. The installed version only runs, and is only then exposed, when that hand-off is bypassed — see Remediation. ## Severity Exploitable when the victim runs `nx migrate` against a package the attacker controls, directly or through a trusted package's `packageGroup`. The primary impact is a file write with attacker-controlled content and no path confinement; overwriting an auto-loaded file (a shell rc, a git hook, a CI script) escalates that write to code execution. There is no known evidence of exploitation in the wild. ## Affected & Patched Versions | Package | Vulnerable | Patched | | --- | --- | --- | | `nx` | `>= 13.10.0, < 22.7.10`; `>= 23.0.0, < 23.2.1` | `22.7.10`, `23.2.1` | Every version in the ranges above is affected. The lower bound is 13.10.0, the first release where `nx migrate` extracted a package's migrations file from its tarball; earlier versions resolved migrations without that extraction. > [!IMPORTANT] > `nx migrate` normally fetches and runs `nx@latest` rather than the nx installed in your workspace. The ranges above therefore say where the vulnerable code ships, not who is exposed — it only runs when that hand-off is bypassed. ## Remediation **If you run `nx migrate` normally, there is nothing to do.** It resolves and runs the latest nx, which is patched, so your workspace's own nx version does not matter for this flaw. Upgrade only if you bypass that hand-off and run the workspace's nx instead — that is, if you set `NX_USE_LOCAL` or `NX_MIGRATE_USE_LOCAL`, pin `NX_MIGRATE_CLI_VERSION` to an affected version, resume an existing run with `--run-id`, or run where the temporary install fails and `nx migrate` falls back to the local nx. In those cases upgrade to **22.7.10** (22.x line) or **23.2.1** (23.x line) or later: ``` nx migrate 23.2.1 ``` The fix is a drop-in — no configuration changes are required, and no legitimate `migrations` value is affected (real packages reference `./migrations.json` or another path within their own directory, all of which remain valid). Either way, do not run `nx migrate` against packages, or `packageGroup` members, that you do not trust. ## Details While planning an upgrade, `nx migrate` extracts each target package's migrations file to a destination built by joining the package's own `nx-migrations.migrations` value onto a temporary directory. That value is read from the manifest without validation, and it is handled asymmetrically: the name Nx matches against the archive entries is normalized (so its `..` segments collapse), while the destination path it writes to is a raw join that keeps the `..` segments and resolves outside the temporary directory. Because the attacker controls the tarball, they name their entry to equal the normalized form; the match then succeeds and the bytes are written to the un-normalized, escaping destination. The normalization is not a defence — it only dictates what the attacker must name their entry. The same value also seeds the directory used for prompt-file extraction, which has the same shape, so both writes are steerable from the one field. Two distinct primitives fall out of this: - **Truncation** — the destination write stream is opened, and truncates, before any tar entry is inspected. So a package that points `migrations` at an existing file empties that file even when no tar entry matches — no crafted archive required. - **Controlled write** — when a tar entry's name matches, its bytes are written to the escaping destination. The extractor performs no path containment of its own. ## Credits - **Arkadiusz Marta** (RE:SOURCE) — Reporter

CVE-2026-104853 Affected Nx Versions

2 component(s)
Fixed Versions:
22.7.1023.2.1
Product / Package Ecosystem Affected Versions Fixed Version
nx
npm
≥ 13.10.0
< 22.7.10
22.7.10
nx
npm
≥ 23.0.0
< 23.2.1
23.2.1

References

Frequently Asked Questions

What is CVE-2026-104853?
CVE-2026-104853 is a security vulnerability in Nx involving Path traversal in nx migrate package-migrations extraction under CWE-73: External Control of File Name or Path.
Which Nx versions are affected?
Affected versions include releases prior to 22.7.10, 23.2.1 across affected packages: nx.
What version fixes CVE-2026-104853?
Upgrading to 22.7.10, 23.2.1 (or the latest patched release for your respective branch) resolves the vulnerability.
Is nx affected?
Yes, nx is affected in releases prior to 22.7.10, 23.2.1. Upgrade your dependency to 22.7.10, 23.2.1 or later.
What CWE is associated with CVE-2026-104853?
CVE-2026-104853 is classified under CWE-73: External Control of File Name or Path (The product allows user input to control or influence paths or file names that are used in filesystem operations.).
How can I check npm dependencies for CVE-2026-104853?
Run the dependency tree command: 'npm ls nx'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-hrvq-x7jp-36xv",
  "modified" : "2026-10-05T23:28:35Z",
  "published" : "2026-10-05T23:28:35Z",
  "aliases" : [ "CVE-2026-104853" ],
  "summary" : "Nx: Path traversal in nx migrate package-migrations extraction",
  "details" : "## Summary\n\n`nx migrate` reads each target package's `nx-migrations.migrations` value from its manifest and extracts the referenced file to a path built by joining that value onto a temporary directory. The value is never validated, so a package whose `migrations` field contains `..` segments (or an absolute path) steers the extraction to write outside the temporary directory. A hostile package — or any package pulled in transitively through a trusted package's `packageGroup` — can write attacker-controlled content, or truncate an existing file, anywhere the running user can write. This happens during migration *planning*, before the user reviews the migration list and before `--run-migrations`, so it does not require the user to approve or execute anything.\n\n**Most workspaces need no action.** By default `nx migrate` does not run the nx installed in your workspace — it installs `nx@latest` into a temporary directory and performs the upgrade planning, including this extraction, with that copy. Now that a patched nx is the latest release, a default `nx migrate` run is unaffected whatever version the workspace has installed. The installed version only runs, and is only then exposed, when that hand-off is bypassed — see Remediation.\n\n## Severity\n\nExploitable when the victim runs `nx migrate` against a package the attacker controls, directly or through a trusted package's `packageGroup`. The primary impact is a file write with attacker-controlled content and no path confinement; overwriting an auto-loaded file (a shell rc, a git hook, a CI script) escalates that write to code execution. There is no known evidence of exploitation in the wild.\n\n## Affected & Patched Versions\n\n| Package | Vulnerable | Patched |\n| --- | --- | --- |\n| `nx` | `>= 13.10.0, < 22.7.10`; `>= 23.0.0, < 23.2.1` | `22.7.10`, `23.2.1` |\n\nEvery version in the ranges above is affected. The lower bound is 13.10.0, the first release where `nx migrate` extracted a package's migrations file from its tarball; earlier versions resolved migrations without that extraction.\n\n> [!IMPORTANT]\n> `nx migrate` normally fetches and runs `nx@latest` rather than the nx installed in your workspace. The ranges above therefore say where the vulnerable code ships, not who is exposed — it only runs when that hand-off is bypassed.\n\n## Remediation\n\n**If you run `nx migrate` normally, there is nothing to do.** It resolves and runs the latest nx, which is patched, so your workspace's own nx version does not matter for this flaw.\n\nUpgrade only if you bypass that hand-off and run the workspace's nx instead — that is, if you set `NX_USE_LOCAL` or `NX_MIGRATE_USE_LOCAL`, pin `NX_MIGRATE_CLI_VERSION` to an affected version, resume an existing run with `--run-id`, or run where the temporary install fails and `nx migrate` falls back to the local nx. In those cases upgrade to **22.7.10** (22.x line) or **23.2.1** (23.x line) or later:\n\n```\nnx migrate 23.2.1\n```\n\nThe fix is a drop-in — no configuration changes are required, and no legitimate `migrations` value is affected (real packages reference `./migrations.json` or another path within their own directory, all of which remain valid). Either way, do not run `nx migrate` against packages, or `packageGroup` members, that you do not trust.\n\n## Details\n\nWhile planning an upgrade, `nx migrate` extracts each target package's migrations file to a destination built by joining the package's own `nx-migrations.migrations` value onto a temporary directory. That value is read from the manifest without validation, and it is handled asymmetrically: the name Nx matches against the archive entries is normalized (so its `..` segments collapse), while the destination path it writes to is a raw join that keeps the `..` segments and resolves outside the temporary directory. Because the attacker controls the tarball, they name their entry to equal the normalized form; the match then succeeds and the bytes are written to the un-normalized, escaping destination. The normalization is not a defence — it only dictates what the attacker must name their entry.\n\nThe same value also seeds the directory used for prompt-file extraction, which has the same shape, so both writes are steerable from the one field.\n\nTwo distinct primitives fall out of this:\n\n- **Truncation** — the destination write stream is opened, and truncates, before any tar entry is inspected. So a package that points `migrations` at an existing file empties that file even when no tar entry matches — no crafted archive required.\n- **Controlled write** — when a tar entry's name matches, its bytes are written to the escaping destination. The extractor performs no path containment of its own.\n\n## Credits\n\n- **Arkadiusz Marta** (RE:SOURCE) — Reporter",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:P/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "nx"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "13.10.0"
      }, {
        "fixed" : "22.7.10"
      } ]
    } ]
  }, {
    "package" : {
      "ecosystem" : "npm",
      "name" : "nx"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "23.0.0"
      }, {
        "fixed" : "23.2.1"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/nrwl/nx/security/advisories/GHSA-hrvq-x7jp-36xv"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-104853"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nrwl/nx/pull/36887"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nrwl/nx/commit/38cd82a0c05e8212e538bdb87ccb198d6504e58f"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nrwl/nx/commit/95474ab457e8e1dbdbefad29040c166105a8fb90"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nrwl/nx/commit/de37c5ebd1852fac700d70726326236ac398af3f"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/nrwl/nx"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nrwl/nx/releases/tag/22.7.10"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nrwl/nx/releases/tag/23.2.1"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-22", "CWE-73" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-10-05T23:28:35Z",
    "nvd_published_at" : "2026-10-02T17:17:03Z"
  }
}