{"id":"EUVD-2026-91761","revision":null,"records":[{"revision":"23e3ae3bf978451c9ec14ac5d3dde1b9","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-104853","aliases":[],"title":"Nx: Path traversal in nx migrate package-migrations extraction","description":"Nx is a monorepo solution for TypeScript and polyglot codebases. From 13.10.0 until 22.7.10 and 23.2.1, Nx migration planning reads the nx-migrations.migrations value from a target package manifest without validating that it is a contained relative path. A hostile direct dependency or a package introduced through a trusted package's packageGroup can supply .. segments or an absolute path, causing nx migrate to join an escaping destination onto its temporary directory. The migration archive can then write attacker-controlled bytes outside the temporary directory, while opening the destination stream can truncate an existing writable file even when no archive entry matches. This occurs during migration planning before review of the migration list or use of --run-migrations; the vulnerable installed Nx copy is reached when the normal nx@latest handoff is bypassed with NX_USE_LOCAL, NX_MIGRATE_USE_LOCAL, NX_MIGRATE_CLI_VERSION, --run-id, or fallback after a temporary-install failure. This issue is fixed in versions 22.7.10 and 23.2.1.","state":0,"publishedAt":"2026-10-02T16:52:07.624+00:00","modifiedAt":"2026-10-02T17:41:54.417+00:00","affected":[{"ecosystem":"Vendor product","name":"nx","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"nrwl\",\n                    \"product\": \"nx\",\n                    \"versions\": [\n                        {\n                            \"version\": \">= 13.10.0, < 22.7.10\",\n                            \"status\": \"affected\"\n                        },\n                        {\n                            \"version\": \">= 23.0.0, < 23.2.1\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 4.0","vector":"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","score":5.8,"severity":"MEDIUM"}],"references":["https://github.com/nrwl/nx/security/advisories/GHSA-hrvq-x7jp-36xv","https://github.com/nrwl/nx/pull/36887","https://github.com/nrwl/nx/commit/38cd82a0c05e8212e538bdb87ccb198d6504e58f","https://github.com/nrwl/nx/commit/95474ab457e8e1dbdbefad29040c166105a8fb90","https://github.com/nrwl/nx/commit/de37c5ebd1852fac700d70726326236ac398af3f","https://github.com/nrwl/nx/releases/tag/22.7.10","https://github.com/nrwl/nx/releases/tag/23.2.1"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-104853","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-104853","identifiers":["CVE-2026-104853"]},"retained":false,"historicalAffected":[]},{"revision":"3c93faf470f647ff836862154fb3b054","observedAt":"2026-11-11T05:30:31.604+00:00","statement":{"source":"euvd","id":"EUVD-2026-91761","aliases":["CVE-2026-104853","GHSA-hrvq-x7jp-36xv"],"title":"Nx is a monorepo solution for TypeScript and polyglot codebases. From 13.10.0 until 22.7.10 and 23.2.1, Nx migration planning reads the nx-migrations.migrations value from a target package manifest without validating that it is a contained …","description":"Nx is a monorepo solution for TypeScript and polyglot codebases. From 13.10.0 until 22.7.10 and 23.2.1, Nx migration planning reads the nx-migrations.migrations value from a target package manifest without validating that it is a contained relative path. A hostile direct dependency or a package introduced through a trusted package's packageGroup can supply .. segments or an absolute path, causing nx migrate to join an escaping destination onto its temporary directory. The migration archive can then write attacker-controlled bytes outside the temporary directory, while opening the destination stream can truncate an existing writable file even when no archive entry matches. This occurs during migration planning before review of the migration list or use of --run-migrations; the vulnerable installed Nx copy is reached when the normal nx@latest handoff is bypassed with NX_USE_LOCAL, NX_MIGRATE_USE_LOCAL, NX_MIGRATE_CLI_VERSION, --run-id, or fallback after a temporary-install failure. This issue is fixed in versions 22.7.10 and 23.2.1.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 4.0","vector":"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","score":5.8,"severity":null}],"references":["https://github.com/nrwl/nx/security/advisories/GHSA-hrvq-x7jp-36xv","https://github.com/nrwl/nx/pull/36887","https://github.com/nrwl/nx/commit/38cd82a0c05e8212e538bdb87ccb198d6504e58f","https://github.com/nrwl/nx/commit/95474ab457e8e1dbdbefad29040c166105a8fb90","https://github.com/nrwl/nx/commit/de37c5ebd1852fac700d70726326236ac398af3f","https://github.com/nrwl/nx/releases/tag/22.7.10","https://github.com/nrwl/nx/releases/tag/23.2.1"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-91761","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Oct 2, 2026, 4:52:07 PM","dateUpdated":"Oct 2, 2026, 5:41:54 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-91761","identifiers":["EUVD-2026-91761","CVE-2026-104853","GHSA-hrvq-x7jp-36xv"]},"retained":false,"historicalAffected":[]},{"revision":"bf5ddae1cc504382b0c61a24c11bfd73","observedAt":"2026-10-25T04:17:29.458+00:00","statement":{"source":"nvd","id":"CVE-2026-104853","aliases":[],"title":"Nx is a monorepo solution for TypeScript and polyglot codebases. From 13.10.0 until 22.7.10 and 23.2.1, Nx migration planning reads the nx-migrations.migrations value from a target package manifest without validating that it is a contained …","description":"Nx is a monorepo solution for TypeScript and polyglot codebases. From 13.10.0 until 22.7.10 and 23.2.1, Nx migration planning reads the nx-migrations.migrations value from a target package manifest without validating that it is a contained relative path. A hostile direct dependency or a package introduced through a trusted package's packageGroup can supply .. segments or an absolute path, causing nx migrate to join an escaping destination onto its temporary directory. The migration archive can then write attacker-controlled bytes outside the temporary directory, while opening the destination stream can truncate an existing writable file even when no archive entry matches. This occurs during migration planning before review of the migration list or use of --run-migrations; the vulnerable installed Nx copy is reached when the normal nx@latest handoff is bypassed with NX_USE_LOCAL, NX_MIGRATE_USE_LOCAL, NX_MIGRATE_CLI_VERSION, --run-id, or fallback after a temporary-install failure. This issue is fixed in versions 22.7.10 and 23.2.1.","state":0,"publishedAt":"2026-10-02T17:17:03.92+00:00","modifiedAt":"2026-10-02T18:17:01.837+00:00","affected":[],"metrics":[{"system":"CVSS 4.0 · security-advisories@github.com · Secondary","vector":"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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","score":5.8,"severity":"MEDIUM"}],"references":["https://github.com/nrwl/nx/commit/38cd82a0c05e8212e538bdb87ccb198d6504e58f","https://github.com/nrwl/nx/commit/95474ab457e8e1dbdbefad29040c166105a8fb90","https://github.com/nrwl/nx/commit/de37c5ebd1852fac700d70726326236ac398af3f","https://github.com/nrwl/nx/pull/36887","https://github.com/nrwl/nx/releases/tag/22.7.10","https://github.com/nrwl/nx/releases/tag/23.2.1","https://github.com/nrwl/nx/security/advisories/GHSA-hrvq-x7jp-36xv"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-104853","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Awaiting Analysis","weaknesses":["CWE-22","CWE-73"],"productCriteria":[],"sourceDates":{"published":"2026-10-02T17:17:03.920","lastModified":"2026-10-02T18:17:01.837"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-104853","identifiers":["CVE-2026-104853"]},"retained":false,"historicalAffected":[]},{"revision":"28c08c5708f04041b6577633f45f5280","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-hrvq-x7jp-36xv","aliases":["CVE-2026-104853"],"title":"Nx: Path traversal in nx migrate package-migrations extraction","description":"## 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","state":0,"publishedAt":"2026-10-05T23:28:35+00:00","modifiedAt":"2026-10-05T23:45:08.231333+00:00","affected":[{"ecosystem":"npm","name":"nx","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"13.10.0"},{"kind":"fixed","version":"22.7.10"}]}],"versions":[],"rawVersionEvidence":null},{"ecosystem":"npm","name":"nx","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"23.0.0"},{"kind":"fixed","version":"23.2.1"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V4","vector":"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","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"MODERATE"}],"references":["https://github.com/nrwl/nx/security/advisories/GHSA-hrvq-x7jp-36xv","https://nvd.nist.gov/vuln/detail/CVE-2026-104853","https://github.com/nrwl/nx/pull/36887","https://github.com/nrwl/nx/commit/38cd82a0c05e8212e538bdb87ccb198d6504e58f","https://github.com/nrwl/nx/commit/95474ab457e8e1dbdbefad29040c166105a8fb90","https://github.com/nrwl/nx/commit/de37c5ebd1852fac700d70726326236ac398af3f","https://github.com/nrwl/nx","https://github.com/nrwl/nx/releases/tag/22.7.10","https://github.com/nrwl/nx/releases/tag/23.2.1"],"sourceUrl":"https://github.com/advisories/GHSA-hrvq-x7jp-36xv","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-hrvq-x7jp-36xv","identifiers":["GHSA-hrvq-x7jp-36xv","CVE-2026-104853"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-11T19:58:45.4661486+00:00","complete":true,"records":1779,"lastAttemptAt":"2026-11-11T19:58:45.4661486+00:00","lastSuccessAt":"2026-11-11T19:58:45.6262555+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:58:45.6262565+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-de3b77103708811b6494d2ff7c979037d73ae579742cd1649a5c765137ae5083-bc2564cf749a574ebe6216cd53b2772c98ccf01e107d9e465c7cb9637c59db40","complete":true,"records":424281,"lastAttemptAt":"2026-11-11T19:56:48.005675+00:00","lastSuccessAt":"2026-11-11T19:56:47.8167322+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:56:48.005675+00:00","snapshotPublishedAt":"2026-11-11T19:18:45+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-dc462e64383b443bb509cbf61bca2cb8","complete":false,"records":13759,"lastAttemptAt":"2026-11-11T20:09:03.8159657+00:00","lastSuccessAt":"2026-11-11T20:09:03.811+00:00","error":"167 identifier lookups failed; latest failures include AccessDenied (HTTP 403). Other identifiers were checked independently.","lastVerifiedAt":"2026-11-11T20:09:03.811+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13431,"expectedIdentifiers":13813,"failedIdentifiers":167},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-11T19:58:45.4661486+00:00","complete":true,"records":1097,"lastAttemptAt":"2026-11-11T19:58:45.4661486+00:00","lastSuccessAt":"2026-11-11T19:58:45.6274866+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:58:45.6274869+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-0feae4fee9a0467f9dde710515b6c624","complete":false,"records":13753,"lastAttemptAt":"2026-11-11T19:59:11.1509472+00:00","lastSuccessAt":"2026-11-11T19:59:11.147+00:00","error":"100 identifier lookups failed; latest failures include request timeout. Other identifiers were checked independently.","lastVerifiedAt":"2026-11-11T19:59:11.147+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13713,"expectedIdentifiers":13813,"failedIdentifiers":100},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-3562b6c78fd1bd0f0b55daf66a0926ae24f65e462df7f6d928caad458fc696bf","complete":true,"records":9947,"lastAttemptAt":"2026-11-11T19:58:45.3029621+00:00","lastSuccessAt":"2026-11-11T04:27:43.6877707+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:58:45.3029621+00:00","snapshotPublishedAt":"2026-11-11T03:32:46+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-456002455e0ab94bb92c95476d4e6d175c104cd594749982afd96c893728ecbc","complete":true,"records":2010,"lastAttemptAt":"2026-11-11T19:56:48.0811645+00:00","lastSuccessAt":"2026-11-11T02:36:58.1276999+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:56:48.0811645+00:00","snapshotPublishedAt":"2026-11-11T01:17:51+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-baa8362b4daf9e5f39d5563775a55935e7b442c0735d33274a921579ce4a0d66","complete":true,"records":3093,"lastAttemptAt":"2026-11-11T19:58:45.4258391+00:00","lastSuccessAt":"2026-11-11T13:00:02.5446881+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:58:45.4258391+00:00","snapshotPublishedAt":"2026-11-11T12:47:47+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-87231cdc7f9d4c655d3fcacbfa9f6f611f1797a1979bf49bac181b1f6e58d128","complete":true,"records":231686,"lastAttemptAt":"2026-11-11T19:58:45.1608584+00:00","lastSuccessAt":"2026-11-11T19:58:43.6336294+00:00","error":null,"lastVerifiedAt":"2026-11-11T19:58:45.1608584+00:00","snapshotPublishedAt":"2026-11-11T19:48:16+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}