{"affected":[{"ranges":[{"events":[{"introduced":"b4e665d46be8a5f81e8cbbe4c3a67058a19cf133"},{"fixed":"eed2bdf85320ae9e244629511c03f5f3f8c5d0b5"},{"fixed":"1b6fddfeadd98ed26a143f60356efade0494e2ae"}],"repo":"https://github.com/hexpm/hex","type":"GIT"}]}],"aliases":["GHSA-pqhp-5x9x-69h7","CVE-2026-105668"],"credits":[{"name":"Peter Ullrich","type":"FINDER"},{"name":"Eric Meadows-Jönsson","type":"REMEDIATION_DEVELOPER"},{"name":"Eric Meadows-Jönsson","type":"COORDINATOR"},{"name":"Jonatan Männchen / EEF","type":"ANALYST"}],"database_specific":{"capec_ids":["CAPEC-538"],"cpe_ids":["cpe:2.3:a:hexpm:hex:*:*:*:*:*:*:*:*"],"cwe_ids":["CWE-693"]},"details":"## Summary\n\nProtection Mechanism Failure vulnerability in hexpm hex allows the owner of a package, or an attacker who has taken over the owner's account, to get a newly published release past the release-age cooldown.\n\nThe cooldown (`cooldown` config, `HEX_COOLDOWN`) withholds versions younger than the configured age during dependency resolution. When the release of a package locked in `mix.lock` is retired or has a security advisory, `Hex.RemoteConverger.build_cooldown_bypass/3` and `Hex.RemoteConverger.unsafe_lock_bypass/1` lift the cooldown for every version of that package and of every package the locked release depends on, so that `mix deps.update` can move off the unsafe release. Retiring a release needs the same package owner permission as publishing one. An attacker who publishes a malicious release can retire the currently locked release, and the next `mix deps.update` that unlocks the package selects the new release regardless of its age. `Hex.Registry.Cooldown.versions/2` checks the bypass by package name alone, so the bypass also covers packages with the same name in other repositories. Cooldown configured through an organization dependency policy is not affected.\n\nThis issue affects hex: from 2.5.0 before 2.5.2.\n\n## Details\n\n1. `Hex.RemoteConverger.build_cooldown_bypass/3` adds `unsafe_lock_bypass(old_lock)` to the set of packages exempt from the cooldown.\n2. `unsafe_lock_bypass/1` adds a package's name, and the names of its locked children, whenever `Registry.retired/3` or `Registry.advisories/3` reports the locked version as unsafe.\n3. `Hex.Registry.Cooldown.versions/2` returns every version unfiltered for any package name in that set, so freshly published versions become eligible as soon as the package is unlocked.\n4. hex.pm lets the package owner retire any of their releases, so the owner controls the trigger. `Hex.Policy.Filter` applies policy cooldowns without consulting the bypass set.\n\n## Proof of concept\n\n1. Lock `foo 1.0.0` in a project with `HEX_COOLDOWN=7d`.\n2. Publish `foo 1.0.1`; `mix deps.update foo` reports it as filtered by cooldown and keeps 1.0.0.\n3. Retire `foo 1.0.0`.\n4. Run `mix deps.update foo` again; it upgrades to the 1-hour-old 1.0.1.\n\n## Impact\n\nThe cooldown exists to keep a compromised release out of projects until it can be detected and retired. An attacker who controls a package owner account can defeat it by also retiring the currently locked release, so a malicious release reaches projects that rely on the local cooldown as soon as they run `mix deps.update`, including automated dependency update jobs. The malicious release's code then runs when the dependency is compiled.\n\n## Workarounds\n\nEnforce the cooldown through an organization dependency policy instead of the local `cooldown` setting, or run `mix hex.audit` before `mix deps.update` and don't update a package whose locked release was retired until its replacement has been reviewed.\n\n## Configurations\n\nAffects projects that set the `cooldown` config or `HEX_COOLDOWN` and update dependencies with `mix deps.update`, or otherwise unlock the package during resolution. Cooldowns enforced through an organization dependency policy aren't affected.\n\n## Solutions\n\nUpgrade Hex to 2.5.2 or later, for example with `mix local.hex`. A retired locked release then no longer lifts the release-age cooldown for its package. A locked release with a security advisory still lifts it for its package and the direct dependencies of the locked release, only in the repository the package is locked from.","id":"EEF-CVE-2026-105668","modified":"2026-10-12T15:31:59.210310Z","published":"2026-10-12T15:31:58.926Z","references":[{"type":"ADVISORY","url":"https://github.com/hexpm/hex/security/advisories/GHSA-pqhp-5x9x-69h7"},{"type":"WEB","url":"https://cna.erlef.org/cves/CVE-2026-105668.html"},{"type":"WEB","url":"https://github.com/hexpm/hex/commit/b4e665d46be8a5f81e8cbbe4c3a67058a19cf133"},{"type":"FIX","url":"https://github.com/hexpm/hex/commit/eed2bdf85320ae9e244629511c03f5f3f8c5d0b5"},{"type":"FIX","url":"https://github.com/hexpm/hex/commit/1b6fddfeadd98ed26a143f60356efade0494e2ae"}],"related":[],"schema_version":"1.7.3","severity":[{"score":"CVSS:4.0/AV:N/AC:L/AT:P/PR:H/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","type":"CVSS_V4"}],"summary":"Hex release-age cooldown is lifted package-wide when the locked release is retired","upstream":[]}