CVE-2026-103061 (GCVE-0-2026-103061)
Vulnerability from cvelistv5 – Published: 2026-10-12 18:23 – Updated: 2026-10-12 18:23
VLAI
EPSS
VEX
Title
Path traversal in Gleam via unvalidated package application names
Summary
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.
A 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.
This issue affects gleam: from 0.32.4 before 1.19.2.
Severity
CWE
- CWE-22 - Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Assigner
References
6 references
Impacted products
3 products
| Vendor | Product | Version | CPE status | |
|---|---|---|---|---|
| Gleam | Gleam |
Affected:
0.32.4 , < 1.19.2
(semver)
cpe:2.3:a:gleam-lang:gleam:*:*:*:*:*:*:*:* |
||
| Gleam | Gleam |
Affected:
v0.32.4-elixir , < v1.19.2-elixir
(other)
Affected: v0.32.4-erlang , < v1.19.2-erlang (other) Affected: v0.32.4-node , < v1.19.2-node (other) Affected: v0.32.4-node-slim , < v1.19.2-node-slim (other) Affected: v0.32.4-elixir-slim , < v1.19.2-elixir-slim (other) Affected: v0.32.4-erlang-slim , < v1.19.2-erlang-slim (other) Affected: v0.32.4-erlang-alpine , < v1.19.2-erlang-alpine (other) Affected: v0.32.4-elixir-alpine , < v1.19.2-elixir-alpine (other) Affected: v0.32.4-node-alpine , < v1.19.2-node-alpine (other) Affected: v0.32.4-scratch , < v1.19.2-scratch (other) cpe:2.3:a:gleam-lang:gleam:*:*:*:*:*:*:*:* |
||
| Gleam | Gleam |
Affected:
d13f27ee80aae7a521d2e9433c4deaf53f892574 , < *
(git)
cpe:2.3:a:gleam-lang:gleam:*:*:*:*:*:*:*:* |
{
"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": "\u003cp\u003eAffects 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 \u003ccode\u003egleam build\u003c/code\u003e, \u003ccode\u003egleam check\u003c/code\u003e, \u003ccode\u003egleam run\u003c/code\u003e, \u003ccode\u003egleam test\u003c/code\u003e or \u003ccode\u003egleam export\u003c/code\u003e. \u003ccode\u003egleam deps download\u003c/code\u003e alone doesn\u0027t 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.\u003c/p\u003e"
},
{
"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\u0027t 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\u0027t 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\u00f6nsson"
},
{
"lang": "en",
"type": "analyst",
"value": "Jonatan M\u00e4nnchen / EEF"
}
],
"dateAssigned": "2026-10-07T22:06:26.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 gleam-lang gleam allows a malicious package publisher to delete a directory outside the project\u0027s build directory when a developer builds a project.\u003c/p\u003e\n\u003cp\u003eA Hex package release\u0027s metadata carries an \u003ccode\u003eapp\u003c/code\u003e field with the release\u0027s OTP application name. Gleam reads this field from the hex.pm release API when it downloads dependencies and uses it as the package\u0027s build directory name without validating it (\u003ccode\u003ehex_manifest_package\u003c/code\u003e in \u003ccode\u003ecompiler-cli/src/dependencies.rs\u003c/code\u003e, \u003ccode\u003eProjectPaths::build_directory_for_package\u003c/code\u003e). When a dependency fails to compile, Gleam deletes that directory with \u003ccode\u003eremove_dir_all\u003c/code\u003e. hex.pm didn\u0027t restrict the field to a directory name before 2026-10-12, so a publisher could set it to a value such as \u003ccode\u003e../../../../target\u003c/code\u003e or an absolute path. When a project depends on such a release directly or transitively and the release fails to compile, \u003ccode\u003egleam build\u003c/code\u003e, \u003ccode\u003egleam check\u003c/code\u003e, \u003ccode\u003egleam run\u003c/code\u003e, \u003ccode\u003egleam test\u003c/code\u003e or \u003ccode\u003egleam export\u003c/code\u003e deletes the directory the name resolves to. For a pure Gleam package no publisher code runs.\u003c/p\u003e\n\u003cp\u003eThis issue affects gleam: from 0.32.4 before 1.19.2.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "Improper Limitation of a Pathname to a Restricted Directory (\u0027Path Traversal\u0027) vulnerability in gleam-lang gleam allows a malicious package publisher to delete a directory outside the project\u0027s build directory when a developer builds a project.\n\nA Hex package release\u0027s metadata carries an `app` field with the release\u0027s OTP application name. Gleam reads this field from the hex.pm release API when it downloads dependencies and uses it as the package\u0027s 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\u0027t 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 (\u0027Path Traversal\u0027) vulnerability in gleam-lang gleam allows a malicious package publisher to delete a directory outside the project\u0027s build directory when a developer builds a project.\n\nA Hex package release\u0027s metadata carries an app field with the release\u0027s OTP application name. Gleam reads this field from the hex.pm release API when it downloads dependencies and uses it as the package\u0027s 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\u0027t 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": "\u003cp\u003eA publisher of a package in a project\u0027s dependency tree can make a Gleam build delete a directory that the developer\u0027s account can write, outside the project\u0027s 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\u0027s code. Developers and CI jobs that build projects with Gleam are affected; \u003ccode\u003egleam deps download\u003c/code\u003e alone isn\u0027t.\u003c/p\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "A publisher of a package in a project\u0027s dependency tree can make a Gleam build delete a directory that the developer\u0027s account can write, outside the project\u0027s 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\u0027s code. Developers and CI jobs that build projects with Gleam are affected; `gleam deps download` alone isn\u0027t."
}
],
"value": "A publisher of a package in a project\u0027s dependency tree can make a Gleam build delete a directory that the developer\u0027s account can write, outside the project\u0027s 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\u0027s code. Developers and CI jobs that build projects with Gleam are affected; gleam deps download alone isn\u0027t."
}
]
}
],
"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 (\u0027Path Traversal\u0027)",
"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": "\u003cp\u003eUpgrade 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\u0027t a lowercase letter followed by lowercase letters, digits and \u003ccode\u003e_\u003c/code\u003e.\u003c/p\u003e"
},
{
"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\u0027t 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\u0027t 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": "\u003cp\u003eNot reproduced end to end, because Gleam only uses hex.pm and has no setting for another repository. A unit test calling \u003ccode\u003eProjectPaths::build_directory_for_package\u003c/code\u003e with the application names \u003ccode\u003e../../../../canary\u003c/code\u003e and an absolute path computed build directories outside the project, and \u003ccode\u003estd::fs::remove_dir_all\u003c/code\u003e, the call behind \u003ccode\u003edelete_directory\u003c/code\u003e, deleted a canary directory placed there. The chain from \u003ccode\u003emeta.app\u003c/code\u003e to the delete was established by reading the source.\u003c/p\u003e"
},
{
"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": "\u003col\u003e\n\u003cli\u003e\u003ccode\u003ehex_manifest_package\u003c/code\u003e (\u003ccode\u003ecompiler-cli/src/dependencies.rs\u003c/code\u003e) sets the manifest package\u0027s \u003ccode\u003eotp_app\u003c/code\u003e to \u003ccode\u003emeta.app\u003c/code\u003e from \u003ccode\u003eGET packages/\u0026lt;name\u0026gt;/releases/\u0026lt;version\u0026gt;\u003c/code\u003e without validation. hex.pm doesn\u0027t restrict this field to a directory name. The signed registry record\u0027s dependency \u003ccode\u003eapp\u003c/code\u003e label is decoded but never used as a path.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eManifestPackage::application_name\u003c/code\u003e (\u003ccode\u003ecompiler-core/src/manifest.rs\u003c/code\u003e) returns \u003ccode\u003eotp_app\u003c/code\u003e when it is set.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eProjectPaths::build_directory_for_package\u003c/code\u003e (\u003ccode\u003ecompiler-core/src/paths.rs\u003c/code\u003e) joins the application name onto the target\u0027s build directory. A \u003ccode\u003e..\u003c/code\u003e component escapes the build directory, and an absolute path replaces the whole path.\u003c/li\u003e\n\u003cli\u003eWhen a dependency fails to compile, \u003ccode\u003eProjectCompiler::load_cache_or_compile_package\u003c/code\u003e (\u003ccode\u003ecompiler-core/src/build/project_compiler.rs\u003c/code\u003e) calls \u003ccode\u003edelete_directory\u003c/code\u003e on that path, which runs \u003ccode\u003estd::fs::remove_dir_all\u003c/code\u003e (\u003ccode\u003ecompiler-cli/src/fs.rs\u003c/code\u003e).\u003c/li\u003e\n\u003cli\u003eThe manifest built by \u003ccode\u003edownload_dependencies\u003c/code\u003e is passed to \u003ccode\u003ebuild::main\u003c/code\u003e in the same run, so the name reaches compilation without a \u003ccode\u003emanifest.toml\u003c/code\u003e round trip. \u003ccode\u003eotp_app\u003c/code\u003e is validated against \u003ccode\u003e^[a-z][a-z0-9_]*$\u003c/code\u003e only when \u003ccode\u003emanifest.toml\u003c/code\u003e 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.\u003c/li\u003e\n\u003cli\u003eExtraction uses the package name, which hex.pm validates, so the effect is a directory delete without an overwrite. For a package with \u003ccode\u003ebuild_tools = [\"gleam\"]\u003c/code\u003e no publisher code runs, so source that fails to compile or a malformed \u003ccode\u003egleam.toml\u003c/code\u003e triggers the delete. For rebar3 and Mix dependencies, publisher code runs anyway.\u003c/li\u003e\n\u003c/ol\u003e"
},
{
"base64": false,
"type": "text/markdown",
"value": "1. `hex_manifest_package` (`compiler-cli/src/dependencies.rs`) sets the manifest package\u0027s `otp_app` to `meta.app` from `GET packages/\u003cname\u003e/releases/\u003cversion\u003e` without validation. hex.pm doesn\u0027t restrict this field to a directory name. The signed registry record\u0027s 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\u0027s 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\u0027s otp_app to meta.app from GET packages/\u003cname\u003e/releases/\u003cversion\u003e without validation. hex.pm doesn\u0027t restrict this field to a directory name. The signed registry record\u0027s 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\u0027s 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",
"vulnerability-lookup:meta": {
"epss": {
"cve": "CVE-2026-103061",
"date": "2026-10-13",
"epss": "0.00404",
"percentile": "0.32783"
},
"nvd": {
"cve": {
"affected": [
{
"affectedData": [
{
"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"
}
]
}
],
"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 gleam-lang gleam allows a malicious package publisher to delete a directory outside the project\u0027s build directory when a developer builds a project.\n\nA Hex package release\u0027s metadata carries an app field with the release\u0027s OTP application name. Gleam reads this field from the hex.pm release API when it downloads dependencies and uses it as the package\u0027s 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\u0027t 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."
}
],
"id": "CVE-2026-103061",
"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": 5.9,
"baseSeverity": "MEDIUM",
"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:N/VI:N/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": "NONE",
"vulnIntegrityImpact": "NONE",
"vulnerabilityResponseEffort": "NOT_DEFINED"
},
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"type": "Secondary"
}
]
},
"published": "2026-10-12T19:16:36.387",
"references": [
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://cna.erlef.org/cves/CVE-2026-103061.html"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/gleam-lang/gleam/commit/54bbb2b98af1505b6eed93c45b1936fc76412e5c"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/gleam-lang/gleam/commit/d13f27ee80aae7a521d2e9433c4deaf53f892574"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/gleam-lang/gleam/commit/d15c981fac6e533d70ac8ee89a6ef4dbf139adc3"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://github.com/gleam-lang/gleam/security/advisories/GHSA-jhcr-c7qm-8589"
},
{
"source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
"url": "https://osv.dev/vulnerability/EEF-CVE-2026-103061"
}
],
"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": {
"aggregate_severity": "Important",
"current_release_date": "2026-10-13T08:37:51+00:00",
"cve": "CVE-2026-103061",
"id": "CVE-2026-103061",
"initial_release_date": "2026-10-12T18:23:41.507000+00:00",
"product_status:known_affected": "3",
"product_status:known_not_affected": "15",
"source": "Red Hat CSAF VEX",
"status": "final",
"title": "gleam: gleam: Arbitrary directory deletion via path traversal in package metadata",
"url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-103061.json",
"version": "3"
}
}
}
Loading…
Loading…
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.
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.
Loading…