Know every vulnerabilitybefore it knows you.
DevGuard continuously monitors your dependencies and alerts you when CVEs like this one affect your stack — with real-time threat intelligence built for developers.
EEF-CVE-2026-103060
Summary
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') vulnerability in erlang rebar3 allows a malicious package publisher to delete a directory outside the project's _build directory and extract package files into it when a developer fetches dependencies.
A package release's dependencies in a Hex registry carry an app field that names the dependency's OTP application. rebar3 uses this field as the dependency's name without validating it (rebar_packages:parse_deps/1) and builds the dependency's directory from that name (rebar_app_utils:dep_to_app/7). rebar_fetch:download_source_online/2 deletes that directory with rm -rf and moves the fetched package into it. hex.pm didn't restrict the field to a directory name before 2026-10-12, so a publisher could set it to a value such as ../../../../target. When a project depends on such a release directly or transitively, rebar3 get-deps deletes the directory the name resolves to and writes the dependency's files there. This happens before the dependency's own rebar.config.script is evaluated, and get-deps compiles nothing.
This issue affects rebar3: from 3.7.0-rc1 before 3.27.2.
Details
- hex.pm's registry record lists each release's dependencies as maps with
package,requirement,optional,appandrepository. Theappvalue comes from the publisher's package metadata and hex.pm doesn't restrict it to a directory name. The record is signed with the repository's key after the publisher has chosen the value, so the signature doesn't constrain it. r3_hex_registry:decode_package/3decodes each dependency, includingapp.rebar_packages:parse_deps/1sets the dependency's name to theappvalue, falling back to the package name:{maps:get(app, D, Name), {pkg, Name, Constraint, undefined, undefined}}. The name is stored in#package.dependenciesand passed throughrebar_app_utils:update_source/3andrebar_prv_install_deps:handle_dep/6.rebar_app_utils:dep_to_app/7builds the fetch directory withfilename:join([DepsDir, Name])and no check.rebar_fetch:download_source_online/2runsrebar_file_utils:rm_rf(filename:absname(FetchDir))and thenrebar_file_utils:mv(TmpDir, filename:absname(FetchDir)).filename:absname/1doesn't collapse..segments, and on Unixrm_rf/1runsrm -rfthrough the shell withrebar_utils:escape_chars/1, which doesn't escape/or., so the..segments resolve outside the deps directory.- The delete and overwrite happen for the dependency named by
appbefore its ownrebar.config.scriptis consulted (later inrebar_fetch:download_source/2), andget-depscompiles nothing. rebar3 already runs publisher code duringget-depsby evaluating a fetched package'srebar.config.scriptand installing a dependency's plugins; theappname adds a delete and overwrite outside_buildfor a dependency named by its parent package.
Proof of concept
Reproduced with rebar3 3.27.0 on Erlang/OTP 29 against a local Hex repository, with origin and signature verification left on:
- Build a signed local Hex repository serving
parent1.0.0, whose dependency is#{package => <<"evildep">>, requirement => <<"1.0.0">>, optional => false, app => <<"../../../../CANARY_OUTSIDE">>}, andevildep1.0.0. - Create a project with
{deps, [{parent, "1.0.0"}]}that uses the local repository, and a fileCANARY_OUTSIDE/precious.txtone level above the project root. - Run
rebar3 get-deps. It printsFetching parent v1.0.0and thenFetching ../../../../CANARY_OUTSIDE v1.0.0. CANARY_OUTSIDE/precious.txtis gone andCANARY_OUTSIDE/holdsevildep's extracted files (hex_metadata.config,src/evildep.app.src). No scripts, plugins or hooks are in either package.
Impact
A publisher of a package in a project's dependency tree can make rebar3 get-deps delete a directory outside the project's _build directory that the developer's account can write, such as the project directory or its parent, and replace its contents with a dependency package's files. Replacing a directory whose contents are later run or loaded, such as a project directory whose rebar.config.script the next rebar3 command there evaluates, can lead to code execution. It works when the package is a transitive dependency and needs no compilation. Developers, CI jobs and dependency update bots that fetch dependencies with rebar3 are affected.
Configurations
Affects projects whose dependency tree, directly or transitively, contains a package release whose registry metadata gives a dependency a crafted app name, when dependencies are fetched with rebar3 get-deps or another command that fetches dependencies. hex.pm rejects such names when a release is published since 2026-10-12, and every requirement app name in hex.pm's public registry on 2026-10-06 was a letter followed by letters, digits, _ and -. Packages on other Hex repositories are affected when the repository operator signs such metadata.
Solutions
Upgrade rebar3 to 3.27.2 or later, for example with rebar3 local upgrade. rebar3 then fails with an error when a dependency's name, including the app name a package release gives a dependency, isn't a letter followed by letters, digits, _ and -.
Upload your own SBOM in CycloneDX 1.6 or higher (JSON) directly here to check your vulnerabilities.
Drag and drop some file here, or click to select
The vulnerability can be exploited over the network without needing physical access. It is easy for an attacker to exploit this vulnerability. An attacker does not need any special privileges or access rights.
Exploitation attempts have been detected. Elevated vigilance and prompt remediation are advised.
Probability that this vulnerability will be exploited in the wild within the next 30 days.
We did not find any exploit available. Neither in GitHub repositories nor in the Exploit-Database.
- CVE-2026-103060Alias
- EUVD-2026-97510Alias
Browse More
Continuously monitor your dependencies and get alerted when vulnerabilities like this one affect your stack.
Checkout DevGuard