{"containers":{"cna":{"affected":[{"cpes":["cpe:2.3:a:gleam-lang:gleam:*:*:*:*:*:*:*:*"],"defaultStatus":"unaffected","modules":["gleam_cli::dependencies","gleam_core::manifest","gleam_core::paths","gleam_core::build::project_compiler","gleam_cli::fs"],"packageName":"gleam","packageURL":"pkg:software-id/gleam.run/gleam","product":"Gleam","programFiles":["compiler-cli/src/dependencies.rs","compiler-core/src/manifest.rs","compiler-core/src/paths.rs","compiler-core/src/build/project_compiler.rs","compiler-cli/src/fs.rs"],"programRoutines":[{"name":"gleam_cli::dependencies::hex_manifest_package"},{"name":"gleam_core::manifest::ManifestPackage::application_name"},{"name":"gleam_core::paths::ProjectPaths::build_directory_for_package"},{"name":"gleam_core::build::project_compiler::ProjectCompiler::load_cache_or_compile_package"},{"name":"gleam_cli::fs::delete_directory"}],"vendor":"Gleam","versions":[{"lessThan":"1.19.2","status":"affected","version":"0.32.4","versionType":"semver"}]},{"collectionURL":"https://ghcr.io","cpes":["cpe:2.3:a:gleam-lang:gleam:*:*:*:*:*:*:*:*"],"defaultStatus":"unaffected","modules":["gleam_cli::dependencies","gleam_core::manifest","gleam_core::paths","gleam_core::build::project_compiler","gleam_cli::fs"],"packageName":"gleam-lang/gleam","packageURL":"pkg:oci/gleam?repository_url=ghcr.io%2Fgleam-lang","product":"Gleam","programFiles":["compiler-cli/src/dependencies.rs","compiler-core/src/manifest.rs","compiler-core/src/paths.rs","compiler-core/src/build/project_compiler.rs","compiler-cli/src/fs.rs"],"programRoutines":[{"name":"gleam_cli::dependencies::hex_manifest_package"},{"name":"gleam_core::manifest::ManifestPackage::application_name"},{"name":"gleam_core::paths::ProjectPaths::build_directory_for_package"},{"name":"gleam_core::build::project_compiler::ProjectCompiler::load_cache_or_compile_package"},{"name":"gleam_cli::fs::delete_directory"}],"vendor":"Gleam","versions":[{"lessThan":"v1.19.2-elixir","status":"affected","version":"v0.32.4-elixir","versionType":"other"},{"lessThan":"v1.19.2-erlang","status":"affected","version":"v0.32.4-erlang","versionType":"other"},{"lessThan":"v1.19.2-node","status":"affected","version":"v0.32.4-node","versionType":"other"},{"lessThan":"v1.19.2-node-slim","status":"affected","version":"v0.32.4-node-slim","versionType":"other"},{"lessThan":"v1.19.2-elixir-slim","status":"affected","version":"v0.32.4-elixir-slim","versionType":"other"},{"lessThan":"v1.19.2-erlang-slim","status":"affected","version":"v0.32.4-erlang-slim","versionType":"other"},{"lessThan":"v1.19.2-erlang-alpine","status":"affected","version":"v0.32.4-erlang-alpine","versionType":"other"},{"lessThan":"v1.19.2-elixir-alpine","status":"affected","version":"v0.32.4-elixir-alpine","versionType":"other"},{"lessThan":"v1.19.2-node-alpine","status":"affected","version":"v0.32.4-node-alpine","versionType":"other"},{"lessThan":"v1.19.2-scratch","status":"affected","version":"v0.32.4-scratch","versionType":"other"}]},{"collectionURL":"https://github.com","cpes":["cpe:2.3:a:gleam-lang:gleam:*:*:*:*:*:*:*:*"],"defaultStatus":"unaffected","modules":["gleam_cli::dependencies","gleam_core::manifest","gleam_core::paths","gleam_core::build::project_compiler","gleam_cli::fs"],"packageName":"gleam-lang/gleam","packageURL":"pkg:github/gleam-lang/gleam","product":"Gleam","programFiles":["compiler-cli/src/dependencies.rs","compiler-core/src/manifest.rs","compiler-core/src/paths.rs","compiler-core/src/build/project_compiler.rs","compiler-cli/src/fs.rs"],"programRoutines":[{"name":"gleam_cli::dependencies::hex_manifest_package"},{"name":"gleam_core::manifest::ManifestPackage::application_name"},{"name":"gleam_core::paths::ProjectPaths::build_directory_for_package"},{"name":"gleam_core::build::project_compiler::ProjectCompiler::load_cache_or_compile_package"},{"name":"gleam_cli::fs::delete_directory"}],"repo":"https://github.com/gleam-lang/gleam","vendor":"Gleam","versions":[{"changes":[{"at":"d15c981fac6e533d70ac8ee89a6ef4dbf139adc3","status":"unaffected"},{"at":"54bbb2b98af1505b6eed93c45b1936fc76412e5c","status":"unaffected"}],"lessThan":"*","status":"affected","version":"d13f27ee80aae7a521d2e9433c4deaf53f892574","versionType":"git"}]}],"configurations":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<p>Affects projects whose dependency tree, directly or transitively, contains a hex.pm package release whose OTP application name is a path, when the project is built with <code>gleam build</code>, <code>gleam check</code>, <code>gleam run</code>, <code>gleam test</code> or <code>gleam export</code>. <code>gleam deps download</code> alone doesn't reach the delete. Gleam only uses hex.pm, which rejects such application names when a release is published since 2026-10-12, so only releases published before then can have one.</p>"},{"base64":false,"type":"text/markdown","value":"Affects projects whose dependency tree, directly or transitively, contains a hex.pm package release whose OTP application name is a path, when the project is built with `gleam build`, `gleam check`, `gleam run`, `gleam test` or `gleam export`. `gleam deps download` alone doesn't reach the delete. Gleam only uses hex.pm, which rejects such application names when a release is published since 2026-10-12, so only releases published before then can have one."}],"value":"Affects projects whose dependency tree, directly or transitively, contains a hex.pm package release whose OTP application name is a path, when the project is built with gleam build, gleam check, gleam run, gleam test or gleam export. gleam deps download alone doesn't reach the delete. Gleam only uses hex.pm, which rejects such application names when a release is published since 2026-10-12, so only releases published before then can have one."}],"cpeApplicability":[{"nodes":[{"cpeMatch":[{"criteria":"cpe:2.3:a:gleam-lang:gleam:*:*:*:*:*:*:*:*","versionEndExcluding":"1.19.2","versionStartIncluding":"0.32.4","vulnerable":true}],"negate":false,"operator":"OR"}],"operator":"AND"}],"credits":[{"lang":"en","type":"finder","value":"Peter Ullrich"},{"lang":"en","type":"remediation developer","value":"Louis Pilfold"},{"lang":"en","type":"coordinator","value":"Eric Meadows-Jönsson"},{"lang":"en","type":"analyst","value":"Jonatan Männchen / EEF"}],"dateAssigned":"2026-10-07T22:06:26.000Z","descriptions":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<p>Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') vulnerability in gleam-lang gleam allows a malicious package publisher to delete a directory outside the project's build directory when a developer builds a project.</p>\n<p>A Hex package release's metadata carries an <code>app</code> field with the release's OTP application name. Gleam reads this field from the hex.pm release API when it downloads dependencies and uses it as the package's build directory name without validating it (<code>hex_manifest_package</code> in <code>compiler-cli/src/dependencies.rs</code>, <code>ProjectPaths::build_directory_for_package</code>). When a dependency fails to compile, Gleam deletes that directory with <code>remove_dir_all</code>. 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 <code>../../../../target</code> or an absolute path. When a project depends on such a release directly or transitively and the release fails to compile, <code>gleam build</code>, <code>gleam check</code>, <code>gleam run</code>, <code>gleam test</code> or <code>gleam export</code> deletes the directory the name resolves to. For a pure Gleam package no publisher code runs.</p>\n<p>This issue affects gleam: from 0.32.4 before 1.19.2.</p>"},{"base64":false,"type":"text/markdown","value":"Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') vulnerability in gleam-lang gleam allows a malicious package publisher to delete a directory outside the project's build directory when a developer builds a project.\n\nA Hex package release's metadata carries an `app` field with the release's OTP application name. Gleam reads this field from the hex.pm release API when it downloads dependencies and uses it as the package's build directory name without validating it (`hex_manifest_package` in `compiler-cli/src/dependencies.rs`, `ProjectPaths::build_directory_for_package`). When a dependency fails to compile, Gleam deletes that directory with `remove_dir_all`. 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` or an absolute path. When a project depends on such a release directly or transitively and the release fails to compile, `gleam build`, `gleam check`, `gleam run`, `gleam test` or `gleam export` deletes the directory the name resolves to. For a pure Gleam package no publisher code runs.\n\nThis issue affects gleam: from 0.32.4 before 1.19.2."}],"value":"Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') vulnerability in gleam-lang gleam allows a malicious package publisher to delete a directory outside the project's build directory when a developer builds a project.\n\nA Hex package release's metadata carries an app field with the release's OTP application name. Gleam reads this field from the hex.pm release API when it downloads dependencies and uses it as the package's build directory name without validating it (hex_manifest_package in compiler-cli/src/dependencies.rs, ProjectPaths::build_directory_for_package). When a dependency fails to compile, Gleam deletes that directory with remove_dir_all. 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 or an absolute path. When a project depends on such a release directly or transitively and the release fails to compile, gleam build, gleam check, gleam run, gleam test or gleam export deletes the directory the name resolves to. For a pure Gleam package no publisher code runs.\n\nThis issue affects gleam: from 0.32.4 before 1.19.2."}],"impacts":[{"capecId":"CAPEC-126","descriptions":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<p>A publisher of a package in a project's dependency tree can make a Gleam build delete a directory that the developer's account can write, outside the project's build directory or anywhere on the filesystem when the application name is an absolute path. For a pure Gleam package this happens without running any of the publisher's code. Developers and CI jobs that build projects with Gleam are affected; <code>gleam deps download</code> alone isn't.</p>"},{"base64":false,"type":"text/markdown","value":"A publisher of a package in a project's dependency tree can make a Gleam build delete a directory that the developer's account can write, outside the project's build directory or anywhere on the filesystem when the application name is an absolute path. For a pure Gleam package this happens without running any of the publisher's code. Developers and CI jobs that build projects with Gleam are affected; `gleam deps download` alone isn't."}],"value":"A publisher of a package in a project's dependency tree can make a Gleam build delete a directory that the developer's account can write, outside the project's build directory or anywhere on the filesystem when the application name is an absolute path. For a pure Gleam package this happens without running any of the publisher's code. Developers and CI jobs that build projects with Gleam are affected; gleam deps download alone isn't."}]}],"metrics":[{"cvssV4_0":{"Automatable":"NOT_DEFINED","Recovery":"NOT_DEFINED","Safety":"NOT_DEFINED","attackComplexity":"LOW","attackRequirements":"PRESENT","attackVector":"NETWORK","baseScore":5.9,"baseSeverity":"MEDIUM","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:N/VI:N/VA:H/SC:N/SI:N/SA:N","version":"4.0","vulnAvailabilityImpact":"HIGH","vulnConfidentialityImpact":"NONE","vulnIntegrityImpact":"NONE","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 ('Path Traversal')","lang":"en","type":"CWE"}]}],"providerMetadata":{"dateUpdated":"2026-10-12T18:23:41.507Z","orgId":"6b3ad84c-e1a6-4bf7-a703-f496b71e49db","shortName":"EEF"},"references":[{"name":"GitHub Advisory","tags":["related","vendor-advisory"],"url":"https://github.com/gleam-lang/gleam/security/advisories/GHSA-jhcr-c7qm-8589"},{"name":"EEF CNA record for CVE-2026-103061","tags":["related"],"url":"https://cna.erlef.org/cves/CVE-2026-103061.html"},{"name":"OSV record EEF-CVE-2026-103061","tags":["related"],"url":"https://osv.dev/vulnerability/EEF-CVE-2026-103061"},{"name":"Introducing commit d13f27e in gleam-lang/gleam","tags":["related"],"url":"https://github.com/gleam-lang/gleam/commit/d13f27ee80aae7a521d2e9433c4deaf53f892574"},{"name":"Fix commit d15c981 in gleam-lang/gleam","tags":["patch"],"url":"https://github.com/gleam-lang/gleam/commit/d15c981fac6e533d70ac8ee89a6ef4dbf139adc3"},{"name":"Fix commit 54bbb2b in gleam-lang/gleam","tags":["patch"],"url":"https://github.com/gleam-lang/gleam/commit/54bbb2b98af1505b6eed93c45b1936fc76412e5c"}],"solutions":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<p>Upgrade Gleam to 1.19.2 or later. Gleam then rejects a hex.pm release whose OTP application name, or the application name it gives a dependency, isn't a lowercase letter followed by lowercase letters, digits and <code>_</code>.</p>"},{"base64":false,"type":"text/markdown","value":"Upgrade Gleam to 1.19.2 or later. Gleam then rejects a hex.pm release whose OTP application name, or the application name it gives a dependency, isn't a lowercase letter followed by lowercase letters, digits and `_`."}],"value":"Upgrade Gleam to 1.19.2 or later. Gleam then rejects a hex.pm release whose OTP application name, or the application name it gives a dependency, isn't a lowercase letter followed by lowercase letters, digits and _."}],"source":{"discovery":"UNKNOWN"},"title":"Path traversal in Gleam via unvalidated package application names","x_proofOfConcept":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<p>Not reproduced end to end, because Gleam only uses hex.pm and has no setting for another repository. A unit test calling <code>ProjectPaths::build_directory_for_package</code> with the application names <code>../../../../canary</code> and an absolute path computed build directories outside the project, and <code>std::fs::remove_dir_all</code>, the call behind <code>delete_directory</code>, deleted a canary directory placed there. The chain from <code>meta.app</code> to the delete was established by reading the source.</p>"},{"base64":false,"type":"text/markdown","value":"Not reproduced end to end, because Gleam only uses hex.pm and has no setting for another repository. A unit test calling `ProjectPaths::build_directory_for_package` with the application names `../../../../canary` and an absolute path computed build directories outside the project, and `std::fs::remove_dir_all`, the call behind `delete_directory`, deleted a canary directory placed there. The chain from `meta.app` to the delete was established by reading the source."}],"value":"Not reproduced end to end, because Gleam only uses hex.pm and has no setting for another repository. A unit test calling ProjectPaths::build_directory_for_package with the application names ../../../../canary and an absolute path computed build directories outside the project, and std::fs::remove_dir_all, the call behind delete_directory, deleted a canary directory placed there. The chain from meta.app to the delete was established by reading the source."}],"x_technicalAnalysis":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<ol>\n<li><code>hex_manifest_package</code> (<code>compiler-cli/src/dependencies.rs</code>) sets the manifest package's <code>otp_app</code> to <code>meta.app</code> from <code>GET packages/&lt;name&gt;/releases/&lt;version&gt;</code> without validation. hex.pm doesn't restrict this field to a directory name. The signed registry record's dependency <code>app</code> label is decoded but never used as a path.</li>\n<li><code>ManifestPackage::application_name</code> (<code>compiler-core/src/manifest.rs</code>) returns <code>otp_app</code> when it is set.</li>\n<li><code>ProjectPaths::build_directory_for_package</code> (<code>compiler-core/src/paths.rs</code>) joins the application name onto the target's build directory. A <code>..</code> component escapes the build directory, and an absolute path replaces the whole path.</li>\n<li>When a dependency fails to compile, <code>ProjectCompiler::load_cache_or_compile_package</code> (<code>compiler-core/src/build/project_compiler.rs</code>) calls <code>delete_directory</code> on that path, which runs <code>std::fs::remove_dir_all</code> (<code>compiler-cli/src/fs.rs</code>).</li>\n<li>The manifest built by <code>download_dependencies</code> is passed to <code>build::main</code> in the same run, so the name reaches compilation without a <code>manifest.toml</code> round trip. <code>otp_app</code> is validated against <code>^[a-z][a-z0-9_]*$</code> only when <code>manifest.toml</code> is read from disk (commit 2dc0467, first released in v1.16.0), which rejects a stored malicious manifest on a later run but not the first run that produces it.</li>\n<li>Extraction uses the package name, which hex.pm validates, so the effect is a directory delete without an overwrite. For a package with <code>build_tools = [\"gleam\"]</code> no publisher code runs, so source that fails to compile or a malformed <code>gleam.toml</code> triggers the delete. For rebar3 and Mix dependencies, publisher code runs anyway.</li>\n</ol>"},{"base64":false,"type":"text/markdown","value":"1. `hex_manifest_package` (`compiler-cli/src/dependencies.rs`) sets the manifest package's `otp_app` to `meta.app` from `GET packages/<name>/releases/<version>` without validation. hex.pm doesn't restrict this field to a directory name. The signed registry record's dependency `app` label is decoded but never used as a path.\n2. `ManifestPackage::application_name` (`compiler-core/src/manifest.rs`) returns `otp_app` when it is set.\n3. `ProjectPaths::build_directory_for_package` (`compiler-core/src/paths.rs`) joins the application name onto the target's build directory. A `..` component escapes the build directory, and an absolute path replaces the whole path.\n4. When a dependency fails to compile, `ProjectCompiler::load_cache_or_compile_package` (`compiler-core/src/build/project_compiler.rs`) calls `delete_directory` on that path, which runs `std::fs::remove_dir_all` (`compiler-cli/src/fs.rs`).\n5. The manifest built by `download_dependencies` is passed to `build::main` in the same run, so the name reaches compilation without a `manifest.toml` round trip. `otp_app` is validated against `^[a-z][a-z0-9_]*$` only when `manifest.toml` is read from disk (commit 2dc0467, first released in v1.16.0), which rejects a stored malicious manifest on a later run but not the first run that produces it.\n6. Extraction uses the package name, which hex.pm validates, so the effect is a directory delete without an overwrite. For a package with `build_tools = [\"gleam\"]` no publisher code runs, so source that fails to compile or a malformed `gleam.toml` triggers the delete. For rebar3 and Mix dependencies, publisher code runs anyway."}],"value":"* hex_manifest_package (compiler-cli/src/dependencies.rs) sets the manifest package's otp_app to meta.app from GET packages/<name>/releases/<version> without validation. hex.pm doesn't restrict this field to a directory name. The signed registry record's dependency app label is decoded but never used as a path.\n* ManifestPackage::application_name (compiler-core/src/manifest.rs) returns otp_app when it is set.\n* ProjectPaths::build_directory_for_package (compiler-core/src/paths.rs) joins the application name onto the target's build directory. A .. component escapes the build directory, and an absolute path replaces the whole path.\n* When a dependency fails to compile, ProjectCompiler::load_cache_or_compile_package (compiler-core/src/build/project_compiler.rs) calls delete_directory on that path, which runs std::fs::remove_dir_all (compiler-cli/src/fs.rs).\n* The manifest built by download_dependencies is passed to build::main in the same run, so the name reaches compilation without a manifest.toml round trip. otp_app is validated against ^[a-z][a-z0-9_]*$ only when manifest.toml is read from disk (commit 2dc0467, first released in v1.16.0), which rejects a stored malicious manifest on a later run but not the first run that produces it.\n* Extraction uses the package name, which hex.pm validates, so the effect is a directory delete without an overwrite. For a package with build_tools = [\"gleam\"] no publisher code runs, so source that fails to compile or a malformed gleam.toml triggers the delete. For rebar3 and Mix dependencies, publisher code runs anyway."}]}},"cveMetadata":{"assignerOrgId":"6b3ad84c-e1a6-4bf7-a703-f496b71e49db","assignerShortName":"EEF","cveId":"CVE-2026-103061","datePublished":"2026-10-12T18:23:41.507Z","dateReserved":"2026-10-07T13:15:01.364Z","dateUpdated":"2026-10-12T18:23:41.507Z","state":"PUBLISHED"},"dataType":"CVE_RECORD","dataVersion":"5.2"}