CVE-2026-96612 (GCVE-0-2026-96612)
Vulnerability from cvelistv5 – Published: 2026-10-12 15:39 – Updated: 2026-10-12 15:39
VLAI
EPSS
VEX
Title
Path traversal in Hex via unvalidated dependency app names
Summary
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') vulnerability in hexpm hex allows a malicious package publisher to delete a directory outside the project's deps directory and extract package files into it when a developer fetches dependencies.
A package release's requirements carry an app label that names the dependency's OTP application. Hex takes this label from the repository's signed registry and uses it as the dependency's application name without validating it (Hex.RemoteConverger.deps/2, Hex.Mix.to_lock/1). Mix derives the dependency's checkout directory as deps/<app>, and Hex.SCM.update/1 runs File.rm_rf!/1 on that directory and unpacks the package into it. hex.pm only checks the label's length, so a publisher can set it to a value such as ../../target. When a project depends on such a release directly or transitively, mix deps.get or mix deps.update deletes the directory the label resolves to and writes the labelled package's files there. This happens before any dependency code is compiled.
This issue affects hex: from 0.6.0 before 2.5.2.
Severity
CWE
- CWE-22 - Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Assigner
References
6 references
Impacted products
{
"containers": {
"cna": {
"affected": [
{
"cpes": [
"cpe:2.3:a:hexpm:hex:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.Hex.RemoteConverger\u0027",
"\u0027Elixir.Hex.Mix\u0027",
"\u0027Elixir.Hex.SCM\u0027"
],
"packageName": "hex",
"packageURL": "pkg:otp/hex?repository_url=https:%2F%2Fgithub.com%2Fhexpm%2Fhex\u0026vcs_url=repository_url=git%2Bhttps:%2F%2Fgithub.com%2Fhexpm%2Fhex.git",
"product": "hex",
"programFiles": [
"lib/hex/remote_converger.ex",
"lib/hex/mix.ex",
"lib/hex/scm.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.Hex.RemoteConverger\u0027:deps/2"
},
{
"name": "\u0027Elixir.Hex.RemoteConverger\u0027:converge/2"
},
{
"name": "\u0027Elixir.Hex.Mix\u0027:to_lock/1"
},
{
"name": "\u0027Elixir.Hex.SCM\u0027:update/1"
},
{
"name": "\u0027Elixir.Hex.SCM\u0027:checkout/1"
}
],
"repo": "https://github.com/hexpm/hex",
"vendor": "hexpm",
"versions": [
{
"lessThan": "2.5.2",
"status": "affected",
"version": "0.6.0",
"versionType": "semver"
}
]
},
{
"collectionURL": "https://github.com",
"cpes": [
"cpe:2.3:a:hexpm:hex:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.Hex.RemoteConverger\u0027",
"\u0027Elixir.Hex.Mix\u0027",
"\u0027Elixir.Hex.SCM\u0027"
],
"packageName": "hexpm/hex",
"packageURL": "pkg:github/hexpm/hex",
"product": "hex",
"programFiles": [
"lib/hex/remote_converger.ex",
"lib/hex/mix.ex",
"lib/hex/scm.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.Hex.RemoteConverger\u0027:deps/2"
},
{
"name": "\u0027Elixir.Hex.RemoteConverger\u0027:converge/2"
},
{
"name": "\u0027Elixir.Hex.Mix\u0027:to_lock/1"
},
{
"name": "\u0027Elixir.Hex.SCM\u0027:update/1"
},
{
"name": "\u0027Elixir.Hex.SCM\u0027:checkout/1"
}
],
"repo": "https://github.com/hexpm/hex",
"vendor": "hexpm",
"versions": [
{
"changes": [
{
"at": "1b3e2ae8f0dd7273a2c2625b9e50b6c67c8568fb",
"status": "unaffected"
},
{
"at": "72279a8e2004b32b20d8767ff010c117193dd02c",
"status": "unaffected"
}
],
"lessThan": "*",
"status": "affected",
"version": "380f87054e1bc08ee13d2b5311bc01f98dad2fc6",
"versionType": "git"
}
]
}
],
"configurations": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eAffects projects whose dependency tree, directly or transitively, contains a package release whose registry metadata carries a crafted dependency \u003ccode\u003eapp\u003c/code\u003e label, when dependencies are fetched with \u003ccode\u003emix deps.get\u003c/code\u003e or \u003ccode\u003emix deps.update\u003c/code\u003e. Packages on hex.pm are affected while hex.pm accepts such labels; packages on self-hosted repositories are affected when the repository operator signs such metadata.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Affects projects whose dependency tree, directly or transitively, contains a package release whose registry metadata carries a crafted dependency `app` label, when dependencies are fetched with `mix deps.get` or `mix deps.update`. Packages on hex.pm are affected while hex.pm accepts such labels; packages on self-hosted repositories are affected when the repository operator signs such metadata."
}
],
"value": "Affects projects whose dependency tree, directly or transitively, contains a package release whose registry metadata carries a crafted dependency app label, when dependencies are fetched with mix deps.get or mix deps.update. Packages on hex.pm are affected while hex.pm accepts such labels; packages on self-hosted repositories are affected when the repository operator signs such metadata."
}
],
"cpeApplicability": [
{
"nodes": [
{
"cpeMatch": [
{
"criteria": "cpe:2.3:a:hexpm:hex:*:*:*:*:*:*:*:*",
"versionEndExcluding": "2.5.2",
"versionStartIncluding": "0.6.0",
"vulnerable": true
}
],
"negate": false,
"operator": "OR"
}
],
"operator": "AND"
}
],
"credits": [
{
"lang": "en",
"type": "finder",
"value": "Peter Ullrich"
},
{
"lang": "en",
"type": "remediation developer",
"value": "Eric Meadows-J\u00f6nsson"
},
{
"lang": "en",
"type": "analyst",
"value": "Jonatan M\u00e4nnchen / EEF"
}
],
"dateAssigned": "2026-10-07T22:03:18.000Z",
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eImproper Limitation of a Pathname to a Restricted Directory (\u0027Path Traversal\u0027) vulnerability in hexpm hex allows a malicious package publisher to delete a directory outside the project\u0027s \u003ccode\u003edeps\u003c/code\u003e directory and extract package files into it when a developer fetches dependencies.\u003c/p\u003e\n\u003cp\u003eA package release\u0027s requirements carry an \u003ccode\u003eapp\u003c/code\u003e label that names the dependency\u0027s OTP application. Hex takes this label from the repository\u0027s signed registry and uses it as the dependency\u0027s application name without validating it (\u003ccode\u003eHex.RemoteConverger.deps/2\u003c/code\u003e, \u003ccode\u003eHex.Mix.to_lock/1\u003c/code\u003e). Mix derives the dependency\u0027s checkout directory as \u003ccode\u003edeps/\u0026lt;app\u0026gt;\u003c/code\u003e, and \u003ccode\u003eHex.SCM.update/1\u003c/code\u003e runs \u003ccode\u003eFile.rm_rf!/1\u003c/code\u003e on that directory and unpacks the package into it. hex.pm only checks the label\u0027s length, so a publisher can set it to a value such as \u003ccode\u003e../../target\u003c/code\u003e. When a project depends on such a release directly or transitively, \u003ccode\u003emix deps.get\u003c/code\u003e or \u003ccode\u003emix deps.update\u003c/code\u003e deletes the directory the label resolves to and writes the labelled package\u0027s files there. This happens before any dependency code is compiled.\u003c/p\u003e\n\u003cp\u003eThis issue affects hex: from 0.6.0 before 2.5.2.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Improper Limitation of a Pathname to a Restricted Directory (\u0027Path Traversal\u0027) vulnerability in hexpm hex allows a malicious package publisher to delete a directory outside the project\u0027s `deps` directory and extract package files into it when a developer fetches dependencies.\n\nA package release\u0027s requirements carry an `app` label that names the dependency\u0027s OTP application. Hex takes this label from the repository\u0027s signed registry and uses it as the dependency\u0027s application name without validating it (`Hex.RemoteConverger.deps/2`, `Hex.Mix.to_lock/1`). Mix derives the dependency\u0027s checkout directory as `deps/\u003capp\u003e`, and `Hex.SCM.update/1` runs `File.rm_rf!/1` on that directory and unpacks the package into it. hex.pm only checks the label\u0027s length, so a publisher can set it to a value such as `../../target`. When a project depends on such a release directly or transitively, `mix deps.get` or `mix deps.update` deletes the directory the label resolves to and writes the labelled package\u0027s files there. This happens before any dependency code is compiled.\n\nThis issue affects hex: from 0.6.0 before 2.5.2."
}
],
"value": "Improper Limitation of a Pathname to a Restricted Directory (\u0027Path Traversal\u0027) vulnerability in hexpm hex allows a malicious package publisher to delete a directory outside the project\u0027s deps directory and extract package files into it when a developer fetches dependencies.\n\nA package release\u0027s requirements carry an app label that names the dependency\u0027s OTP application. Hex takes this label from the repository\u0027s signed registry and uses it as the dependency\u0027s application name without validating it (Hex.RemoteConverger.deps/2, Hex.Mix.to_lock/1). Mix derives the dependency\u0027s checkout directory as deps/\u003capp\u003e, and Hex.SCM.update/1 runs File.rm_rf!/1 on that directory and unpacks the package into it. hex.pm only checks the label\u0027s length, so a publisher can set it to a value such as ../../target. When a project depends on such a release directly or transitively, mix deps.get or mix deps.update deletes the directory the label resolves to and writes the labelled package\u0027s files there. This happens before any dependency code is compiled.\n\nThis issue affects hex: from 0.6.0 before 2.5.2."
}
],
"impacts": [
{
"capecId": "CAPEC-126",
"descriptions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eA publisher of any package in a project\u0027s dependency tree can make \u003ccode\u003emix deps.get\u003c/code\u003e delete a directory that the developer\u0027s account can write and that \u003ccode\u003e..\u003c/code\u003e segments relative to \u003ccode\u003edeps\u003c/code\u003e reach, such as the project directory or its parent, and fill it with the labelled package\u0027s files. Writing attacker-chosen files into a directory whose contents are later run or loaded can lead to code execution without the developer compiling the dependency. Developers who fetch dependencies to review them before compiling, CI jobs and dependency update bots that only run \u003ccode\u003emix deps.get\u003c/code\u003e or \u003ccode\u003emix deps.update\u003c/code\u003e are affected.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "A publisher of any package in a project\u0027s dependency tree can make `mix deps.get` delete a directory that the developer\u0027s account can write and that `..` segments relative to `deps` reach, such as the project directory or its parent, and fill it with the labelled package\u0027s files. Writing attacker-chosen files into a directory whose contents are later run or loaded can lead to code execution without the developer compiling the dependency. Developers who fetch dependencies to review them before compiling, CI jobs and dependency update bots that only run `mix deps.get` or `mix deps.update` are affected."
}
],
"value": "A publisher of any package in a project\u0027s dependency tree can make mix deps.get delete a directory that the developer\u0027s account can write and that .. segments relative to deps reach, such as the project directory or its parent, and fill it with the labelled package\u0027s files. Writing attacker-chosen files into a directory whose contents are later run or loaded can lead to code execution without the developer compiling the dependency. Developers who fetch dependencies to review them before compiling, CI jobs and dependency update bots that only run mix deps.get or mix deps.update are affected."
}
]
}
],
"metrics": [
{
"cvssV4_0": {
"Automatable": "NOT_DEFINED",
"Recovery": "NOT_DEFINED",
"Safety": "NOT_DEFINED",
"attackComplexity": "LOW",
"attackRequirements": "PRESENT",
"attackVector": "NETWORK",
"baseScore": 7.5,
"baseSeverity": "HIGH",
"privilegesRequired": "NONE",
"providerUrgency": "NOT_DEFINED",
"subAvailabilityImpact": "NONE",
"subConfidentialityImpact": "NONE",
"subIntegrityImpact": "NONE",
"userInteraction": "ACTIVE",
"valueDensity": "NOT_DEFINED",
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
"version": "4.0",
"vulnAvailabilityImpact": "HIGH",
"vulnConfidentialityImpact": "HIGH",
"vulnIntegrityImpact": "HIGH",
"vulnerabilityResponseEffort": "NOT_DEFINED"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "GENERAL"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-22",
"description": "CWE-22 Improper Limitation of a Pathname to a Restricted Directory (\u0027Path Traversal\u0027)",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-10-12T15:39:49.862Z",
"orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"shortName": "EEF"
},
"references": [
{
"name": "GitHub Advisory",
"tags": [
"related",
"vendor-advisory"
],
"url": "https://github.com/hexpm/hex/security/advisories/GHSA-2vjj-c5hr-6xc3"
},
{
"name": "EEF CNA record for CVE-2026-96612",
"tags": [
"related"
],
"url": "https://cna.erlef.org/cves/CVE-2026-96612.html"
},
{
"name": "OSV record EEF-CVE-2026-96612",
"tags": [
"related"
],
"url": "https://osv.dev/vulnerability/EEF-CVE-2026-96612"
},
{
"name": "Introducing commit 380f870 in hexpm/hex",
"tags": [
"related"
],
"url": "https://github.com/hexpm/hex/commit/380f87054e1bc08ee13d2b5311bc01f98dad2fc6"
},
{
"name": "Fix commit 1b3e2ae in hexpm/hex",
"tags": [
"patch"
],
"url": "https://github.com/hexpm/hex/commit/1b3e2ae8f0dd7273a2c2625b9e50b6c67c8568fb"
},
{
"name": "Fix commit 72279a8 in hexpm/hex",
"tags": [
"patch"
],
"url": "https://github.com/hexpm/hex/commit/72279a8e2004b32b20d8767ff010c117193dd02c"
}
],
"solutions": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eUpgrade Hex to 2.5.2 or later, for example with \u003ccode\u003emix local.hex\u003c/code\u003e. Hex then skips package releases that give a dependency an application name other than a letter followed by letters, digits, \u003ccode\u003e_\u003c/code\u003e and \u003ccode\u003e-\u003c/code\u003e, and warns about them. Resolution fails if no other release of the package fits the requirements.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Upgrade Hex to 2.5.2 or later, for example with `mix local.hex`. Hex then skips package releases that give a dependency an application name other than a letter followed by letters, digits, `_` and `-`, and warns about them. Resolution fails if no other release of the package fits the requirements."
}
],
"value": "Upgrade Hex to 2.5.2 or later, for example with mix local.hex. Hex then skips package releases that give a dependency an application name other than a letter followed by letters, digits, _ and -, and warns about them. Resolution fails if no other release of the package fits the requirements."
}
],
"source": {
"discovery": "UNKNOWN"
},
"title": "Path traversal in Hex via unvalidated dependency app names",
"workarounds": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003cp\u003eBefore running \u003ccode\u003emix deps.get\u003c/code\u003e on a \u003ccode\u003emix.lock\u003c/code\u003e from an untrusted change, check that every key in \u003ccode\u003emix.lock\u003c/code\u003e is a plain application name without \u003ccode\u003e/\u003c/code\u003e, \u003ccode\u003e\\\u003c/code\u003e or \u003ccode\u003e..\u003c/code\u003e. There\u0027s no workaround for new dependency resolution with \u003ccode\u003emix deps.update\u003c/code\u003e or \u003ccode\u003emix deps.get\u003c/code\u003e without a lock entry.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Before running `mix deps.get` on a `mix.lock` from an untrusted change, check that every key in `mix.lock` is a plain application name without `/`, `\\` or `..`. There\u0027s no workaround for new dependency resolution with `mix deps.update` or `mix deps.get` without a lock entry."
}
],
"value": "Before running mix deps.get on a mix.lock from an untrusted change, check that every key in mix.lock is a plain application name without /, \\ or ... There\u0027s no workaround for new dependency resolution with mix deps.update or mix deps.get without a lock entry."
}
],
"x_proofOfConcept": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003col\u003e\n\u003cli\u003ePublish package \u003ccode\u003ebeta\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003ePublish package \u003ccode\u003ealpha\u003c/code\u003e with a requirement on \u003ccode\u003ebeta\u003c/code\u003e whose \u003ccode\u003eapp\u003c/code\u003e is \u003ccode\u003e../../escape_marker\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eIn a project that depends on \u003ccode\u003ealpha\u003c/code\u003e, create \u003ccode\u003e../escape_marker/canary.txt\u003c/code\u003e next to the project directory.\u003c/li\u003e\n\u003cli\u003eRun \u003ccode\u003emix deps.get\u003c/code\u003e. Hex prints \u003ccode\u003e* Updating ../../escape_marker (Hex package)\u003c/code\u003e, deletes \u003ccode\u003ecanary.txt\u003c/code\u003e and writes \u003ccode\u003ebeta\u003c/code\u003e\u0027s files into \u003ccode\u003e../escape_marker\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "1. Publish package `beta`.\n2. Publish package `alpha` with a requirement on `beta` whose `app` is `../../escape_marker`.\n3. In a project that depends on `alpha`, create `../escape_marker/canary.txt` next to the project directory.\n4. Run `mix deps.get`. Hex prints `* Updating ../../escape_marker (Hex package)`, deletes `canary.txt` and writes `beta`\u0027s files into `../escape_marker`."
}
],
"value": "* Publish package beta.\n* Publish package alpha with a requirement on beta whose app is ../../escape_marker.\n* In a project that depends on alpha, create ../escape_marker/canary.txt next to the project directory.\n* Run mix deps.get. Hex prints * Updating ../../escape_marker (Hex package), deletes canary.txt and writes beta\u0027s files into ../escape_marker."
}
],
"x_technicalAnalysis": [
{
"lang": "en",
"supportingMedia": [
{
"base64": false,
"type": "text/html",
"value": "\u003col\u003e\n\u003cli\u003ehex.pm stores each requirement\u0027s \u003ccode\u003eapp\u003c/code\u003e field with only a length check (255 codepoints) and copies it into the signed package registry resource when it differs from the dependency\u0027s package name.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eHex.RemoteConverger.deps/2\u003c/code\u003e returns the dependency\u0027s children with \u003ccode\u003eString.to_atom/1\u003c/code\u003e of that label as the application name, and \u003ccode\u003eHex.Mix.to_lock/1\u003c/code\u003e writes it as the \u003ccode\u003emix.lock\u003c/code\u003e key.\u003c/li\u003e\n\u003cli\u003eMix computes the dependency destination as \u003ccode\u003ePath.join(Mix.Project.deps_path(), Atom.to_string(app))\u003c/code\u003e without validating the name.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eHex.SCM.update/1\u003c/code\u003e calls \u003ccode\u003eFile.rm_rf!(dest)\u003c/code\u003e and then \u003ccode\u003eHex.Tar.unpack!(path, dest)\u003c/code\u003e, so a label containing \u003ccode\u003e..\u003c/code\u003e segments deletes and repopulates a directory outside \u003ccode\u003edeps\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eMix doesn\u0027t evaluate a Hex dependency\u0027s \u003ccode\u003emix.exs\u003c/code\u003e during \u003ccode\u003emix deps.get\u003c/code\u003e, so this is reachable before any package code runs.\u003c/li\u003e\n\u003c/ol\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "1. hex.pm stores each requirement\u0027s `app` field with only a length check (255 codepoints) and copies it into the signed package registry resource when it differs from the dependency\u0027s package name.\n2. `Hex.RemoteConverger.deps/2` returns the dependency\u0027s children with `String.to_atom/1` of that label as the application name, and `Hex.Mix.to_lock/1` writes it as the `mix.lock` key.\n3. Mix computes the dependency destination as `Path.join(Mix.Project.deps_path(), Atom.to_string(app))` without validating the name.\n4. `Hex.SCM.update/1` calls `File.rm_rf!(dest)` and then `Hex.Tar.unpack!(path, dest)`, so a label containing `..` segments deletes and repopulates a directory outside `deps`.\n5. Mix doesn\u0027t evaluate a Hex dependency\u0027s `mix.exs` during `mix deps.get`, so this is reachable before any package code runs."
}
],
"value": "* hex.pm stores each requirement\u0027s app field with only a length check (255 codepoints) and copies it into the signed package registry resource when it differs from the dependency\u0027s package name.\n* Hex.RemoteConverger.deps/2 returns the dependency\u0027s children with String.to_atom/1 of that label as the application name, and Hex.Mix.to_lock/1 writes it as the mix.lock key.\n* Mix computes the dependency destination as Path.join(Mix.Project.deps_path(), Atom.to_string(app)) without validating the name.\n* Hex.SCM.update/1 calls File.rm_rf!(dest) and then Hex.Tar.unpack!(path, dest), so a label containing .. segments deletes and repopulates a directory outside deps.\n* Mix doesn\u0027t evaluate a Hex dependency\u0027s mix.exs during mix deps.get, so this is reachable before any package code runs."
}
]
}
},
"cveMetadata": {
"assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"assignerShortName": "EEF",
"cveId": "CVE-2026-96612",
"datePublished": "2026-10-12T15:39:49.862Z",
"dateReserved": "2026-10-01T09:00:02.103Z",
"dateUpdated": "2026-10-12T15:39:49.862Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2",
"vulnerability-lookup:meta": {
"epss": {
"cve": "CVE-2026-96612",
"date": "2026-10-13",
"epss": "0.00404",
"percentile": "0.32783"
},
"nvd": {
"cve": {
"affected": [
{
"affectedData": [
{
"cpes": [
"cpe:2.3:a:hexpm:hex:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.Hex.RemoteConverger\u0027",
"\u0027Elixir.Hex.Mix\u0027",
"\u0027Elixir.Hex.SCM\u0027"
],
"packageName": "hex",
"packageURL": "pkg:otp/hex?repository_url=https:%2F%2Fgithub.com%2Fhexpm%2Fhex\u0026vcs_url=repository_url=git%2Bhttps:%2F%2Fgithub.com%2Fhexpm%2Fhex.git",
"product": "hex",
"programFiles": [
"lib/hex/remote_converger.ex",
"lib/hex/mix.ex",
"lib/hex/scm.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.Hex.RemoteConverger\u0027:deps/2"
},
{
"name": "\u0027Elixir.Hex.RemoteConverger\u0027:converge/2"
},
{
"name": "\u0027Elixir.Hex.Mix\u0027:to_lock/1"
},
{
"name": "\u0027Elixir.Hex.SCM\u0027:update/1"
},
{
"name": "\u0027Elixir.Hex.SCM\u0027:checkout/1"
}
],
"repo": "https://github.com/hexpm/hex",
"vendor": "hexpm",
"versions": [
{
"lessThan": "2.5.2",
"status": "affected",
"version": "0.6.0",
"versionType": "semver"
}
]
},
{
"collectionURL": "https://github.com",
"cpes": [
"cpe:2.3:a:hexpm:hex:*:*:*:*:*:*:*:*"
],
"defaultStatus": "unaffected",
"modules": [
"\u0027Elixir.Hex.RemoteConverger\u0027",
"\u0027Elixir.Hex.Mix\u0027",
"\u0027Elixir.Hex.SCM\u0027"
],
"packageName": "hexpm/hex",
"packageURL": "pkg:github/hexpm/hex",
"product": "hex",
"programFiles": [
"lib/hex/remote_converger.ex",
"lib/hex/mix.ex",
"lib/hex/scm.ex"
],
"programRoutines": [
{
"name": "\u0027Elixir.Hex.RemoteConverger\u0027:deps/2"
},
{
"name": "\u0027Elixir.Hex.RemoteConverger\u0027:converge/2"
},
{
"name": "\u0027Elixir.Hex.Mix\u0027:to_lock/1"
},
{
"name": "\u0027Elixir.Hex.SCM\u0027:update/1"
},
{
"name": "\u0027Elixir.Hex.SCM\u0027:checkout/1"
}
],
"repo": "https://github.com/hexpm/hex",
"vendor": "hexpm",
"versions": [
{
"changes": [
{
"at": "1b3e2ae8f0dd7273a2c2625b9e50b6c67c8568fb",
"status": "unaffected"
},
{
"at": "72279a8e2004b32b20d8767ff010c117193dd02c",
"status": "unaffected"
}
],
"lessThan": "*",
"status": "affected",
"version": "380f87054e1bc08ee13d2b5311bc01f98dad2fc6",
"versionType": "git"
}
]
}
],
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db"
}
],
"cveTags": [],
"descriptions": [
{
"lang": "en",
"value": "Improper Limitation of a Pathname to a Restricted Directory (\u0027Path Traversal\u0027) vulnerability in hexpm hex allows a malicious package publisher to delete a directory outside the project\u0027s deps directory and extract package files into it when a developer fetches dependencies.\n\nA package release\u0027s requirements carry an app label that names the dependency\u0027s OTP application. Hex takes this label from the repository\u0027s signed registry and uses it as the dependency\u0027s application name without validating it (Hex.RemoteConverger.deps/2, Hex.Mix.to_lock/1). Mix derives the dependency\u0027s checkout directory as deps/\u003capp\u003e, and Hex.SCM.update/1 runs File.rm_rf!/1 on that directory and unpacks the package into it. hex.pm only checks the label\u0027s length, so a publisher can set it to a value such as ../../target. When a project depends on such a release directly or transitively, mix deps.get or mix deps.update deletes the directory the label resolves to and writes the labelled package\u0027s files there. This happens before any dependency code is compiled.\n\nThis issue affects hex: from 0.6.0 before 2.5.2."
}
],
"id": "CVE-2026-96612",
"lastModified": "2026-10-13T14:31:20.750",
"metrics": {
"cvssMetricV40": [
{
"cvssData": {
"Automatable": "NOT_DEFINED",
"Recovery": "NOT_DEFINED",
"Safety": "NOT_DEFINED",
"attackComplexity": "LOW",
"attackRequirements": "PRESENT",
"attackVector": "NETWORK",
"availabilityRequirement": "NOT_DEFINED",
"baseScore": 7.5,
"baseSeverity": "HIGH",
"confidentialityRequirement": "NOT_DEFINED",
"exploitMaturity": "NOT_DEFINED",
"integrityRequirement": "NOT_DEFINED",
"modifiedAttackComplexity": "NOT_DEFINED",
"modifiedAttackRequirements": "NOT_DEFINED",
"modifiedAttackVector": "NOT_DEFINED",
"modifiedPrivilegesRequired": "NOT_DEFINED",
"modifiedSubAvailabilityImpact": "NOT_DEFINED",
"modifiedSubConfidentialityImpact": "NOT_DEFINED",
"modifiedSubIntegrityImpact": "NOT_DEFINED",
"modifiedUserInteraction": "NOT_DEFINED",
"modifiedVulnAvailabilityImpact": "NOT_DEFINED",
"modifiedVulnConfidentialityImpact": "NOT_DEFINED",
"modifiedVulnIntegrityImpact": "NOT_DEFINED",
"privilegesRequired": "NONE",
"providerUrgency": "NOT_DEFINED",
"subAvailabilityImpact": "NONE",
"subConfidentialityImpact": "NONE",
"subIntegrityImpact": "NONE",
"userInteraction": "ACTIVE",
"valueDensity": "NOT_DEFINED",
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/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",
"version": "4.0",
"vulnAvailabilityImpact": "HIGH",
"vulnConfidentialityImpact": "HIGH",
"vulnIntegrityImpact": "HIGH",
"vulnerabilityResponseEffort": "NOT_DEFINED"
},
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"type": "Secondary"
}
]
},
"published": "2026-10-12T16:17:07.637",
"references": [
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://cna.erlef.org/cves/CVE-2026-96612.html"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/hexpm/hex/commit/1b3e2ae8f0dd7273a2c2625b9e50b6c67c8568fb"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/hexpm/hex/commit/380f87054e1bc08ee13d2b5311bc01f98dad2fc6"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/hexpm/hex/commit/72279a8e2004b32b20d8767ff010c117193dd02c"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/hexpm/hex/security/advisories/GHSA-2vjj-c5hr-6xc3"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://osv.dev/vulnerability/EEF-CVE-2026-96612"
}
],
"sourceIdentifier": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"vulnStatus": "Deferred",
"weaknesses": [
{
"description": [
{
"lang": "en",
"value": "CWE-22"
}
],
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"type": "Secondary"
}
]
}
},
"redhat_vex": {
"current_release_date": "2026-10-12T16:26:54+00:00",
"cve": "CVE-2026-96612",
"id": "CVE-2026-96612",
"initial_release_date": "2026-10-12T15:39:49.862000+00:00",
"product_status:known_not_affected": "1",
"source": "Red Hat CSAF VEX",
"status": "final",
"title": "hex: hex: Arbitrary code execution via path traversal in dependency application names",
"url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-96612.json",
"version": "3"
}
}
}
Loading…
Loading…
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Experimental. This forecast is provided for visualization only and may change without notice. Do not use it for operational decisions.
Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
Loading timeline…
Loading…
Loading…
The MITRE ATT&CK techniques below are AI-generated suggestions, inferred from the description of the
vulnerability by the CIRCL/vulnerability-attack-technique-classification-roberta-base
model, served locally by ML-Gateway.
They have not been verified by an analyst and are provided for guidance only.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Loading…
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.