dbt-common: commonprefix() doesn't protect against path traversal
Description
dbt-common is the shared common utilities for dbt-core and adapter implementations use. Prior to versions 1.34.2 and 1.37.3, a path traversal vulnerability exists in dbt-common's safe_extract() function used when extracting tarball archives. The function uses os.path.commonprefix() to validate that extracted files remain within the intended destination directory. However, commonprefix() compares paths character-by-character rather than by path components, allowing a malicious tarball to write files to sibling directories with matching name prefixes. This issue has been patched in versions 1.34.2 and 1.37.3.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A path traversal vulnerability in dbt-common's safe_extract() allows a malicious tarball to write files to sibling directories, bypassing intended extraction path validation.
Vulnerability
Overview
The vulnerability resides in the CVE-2026-29790 is a path traversal flaw in the safe_extract() function of the dbt-common library, which provides shared utilities for dbt-core and its adapter implementations. The root cause is the use of os.path.commonprefix() to verify that files extracted from a tarball remain within the intended destination directory. Unlike os.path.commonpath(), which operates on path components, commonprefix() performs a character-by-character comparison [1], [2]. This subtle difference allows an attacker to craft a malicious tarball that includes file paths starting with a similar prefix as the target directory, thereby bypassing the intended path restriction.
Exploitation
Scenario
An attacker can exploit this vulnerability by providing a specially crafted tarball to a system that uses the vulnerable safe_extract() function. The attack does not require authentication or special privileges; an attacker only needs the ability to supply a tarball that is processed by the vulnerable function. The attack vector is local, as the extraction happens on the system where the tarball is processed [3]. Because the validation logic incorrectly relies on commonprefix(), the attacker can write files to sibling directories that share a common prefix with the intended extraction directory, effectively achieving a path traversal.
Impact
Successful exploitation allows an attacker to write arbitrary files to arbitrary locations on the file system, outside the intended extraction directory. This can lead to a range of severe consequences, including arbitrary code execution if the attacker can overwrite critical system files or configuration files used by the dbt ecosystem. The vulnerability can also be used to overwrite application code, leading to code injection or persistent backdoor [3].
Mitigation
The vulnerability has been patched in dbt-common versions 1.34.2 and 1.37.3. Users are strongly recommended to upgrade to these patched versions immediately [3]. There is no workaround mentioned in the advisories. While this vulnerability is not currently listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, the high potential impact warrants prompt patching.
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
dbt-commonPyPI | < 1.34.2 | 1.34.2 |
dbt-commonPyPI | >= 1.35.0, < 1.37.3 | 1.37.3 |
Affected products
1- dbt-labs/dbt-commonv5Range: < 1.37.3
Patches
1e547954a48baFix tar path traversal using commonpath instead of commonprefix (#353)
3 files changed · +35 −3
.changes/unreleased/Security-20260225-142955.yaml+7 −0 added@@ -0,0 +1,7 @@ +kind: Security +body: Replace os.path.commonprefix() with os.path.commonpath() to preventpath traversal + via sibling directories with matching prefixes. +time: 2026-02-25T14:29:55.922683-05:00 +custom: + Author: emmyoop + PR: "1"
dbt_common/clients/system.py+9 −3 modified@@ -620,14 +620,20 @@ def rename(from_path: str, to_path: str, force: bool = False) -> None: def safe_extract(tarball: tarfile.TarFile, path: str = ".") -> None: """ Fix for CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') - Solution copied from https://github.com/mindsdb/mindsdb/blob/main/mindsdb/utilities/fs.py + + Uses os.path.commonpath() instead of commonprefix() to prevent path traversal + via sibling directories with matching prefixes (CVE-2026-1703). """ def _is_within_directory(directory, target): abs_directory = os.path.abspath(directory) abs_target = os.path.abspath(target) - prefix = os.path.commonprefix([abs_directory, abs_target]) - return prefix == abs_directory + try: + prefix = os.path.commonpath([abs_directory, abs_target]) + return prefix == abs_directory + except ValueError: + # Captures the case of different drives on Windows so it fails safely + return False # for py >= 3.12 if hasattr(tarfile, "data_filter"):
tests/unit/test_system_client.py+19 −0 modified@@ -299,6 +299,25 @@ def test_untar_package_outside_directory(self) -> None: with self.assertRaises(tarfile.OutsideDestinationError): dbt_common.clients.system.untar_package(tar_file_full_path, self.tempdest) + def test_untar_package_sibling_path_traversal(self) -> None: + # A malicious tarball with a path that is a string prefix of the destination + # but not actually within it (e.g., "dest" vs "destevil") + with NamedTemporaryFile( + prefix="my-package.2", suffix=".tar.gz", dir=self.tempdir, delete=False + ) as named_tar_file: + tar_file_full_path = named_tar_file.name + with NamedTemporaryFile(prefix="a", suffix=".txt", dir=self.tempdir) as file_a: + file_a.write(b"malicious content") + # Create path that escapes to a sibling directory with matching prefix + # e.g., extracting to "/tmp/dest" but writing to "/tmp/destevil/file.txt" + sibling_path = "../" + os.path.basename(self.tempdest) + "evil/malicious.txt" + with tarfile.open(fileobj=named_tar_file, mode="w:gz") as tar: + tar.addfile(tarfile.TarInfo(sibling_path), open(file_a.name)) + + assert tarfile.is_tarfile(tar.name) + with self.assertRaises(tarfile.OutsideDestinationError): + dbt_common.clients.system.untar_package(tar_file_full_path, self.tempdest) + @pytest.mark.skipif( sys.version_info < (3, 12), reason="Tests Python 3.12+ tarfile filter='data' path"
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-6vgw-5pg2-w6jpghsaADVISORY
- github.com/advisories/GHSA-w75w-9qv4-j5xjghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-29790ghsaADVISORY
- docs.python.org/3/library/os.path.htmlghsaWEB
- github.com/dbt-labs/dbt-common/commit/e547954a48bac9394ef6eb98432e429dce9a7709ghsax_refsource_MISCWEB
- github.com/dbt-labs/dbt-common/security/advisories/GHSA-w75w-9qv4-j5xjghsax_refsource_CONFIRMWEB
- github.com/pypa/pip/pull/13777ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.