Mar 5, 2026·5 min read·39 visits
dbt-common incorrectly uses string prefix matching to validate file paths during tarball extraction. Attackers can bypass this check using crafted paths (e.g., `/target_evil` vs `/target`) to write files outside the intended directory. Fixed in versions 1.34.2 and 1.37.3.
A path traversal vulnerability exists in the `dbt-common` Python package due to insecure usage of `os.path.commonprefix` during archive extraction. This flaw allows malicious tarballs to bypass directory confinement checks and write files to sibling directories of the intended destination. The vulnerability affects versions prior to 1.34.2 and versions between 1.35.0 and 1.37.3. It arises from a logic error where string prefix matching is used instead of path component comparison, effectively neutralizing the sandbox check intended to prevent arbitrary file writes.
The dbt-common package, a foundational library for the dbt (Data Build Tool) ecosystem, contains a path traversal vulnerability within its safe_extract() utility. This function is designed to unpack tarball archives securely by ensuring that all extracted files reside strictly within a specified target directory. However, the implementation of this boundary check was flawed due to the misuse of the Python standard library function os.path.commonprefix.
This vulnerability is technically indistinguishable from CVE-2026-1703, a similar flaw identified in the pip package manager. Both vulnerabilities stem from the misconception that commonprefix operates on path components (directories), whereas it actually operates on raw strings. This distinction allows an attacker to construct a path that matches the characters of the target directory's name but extends into a sibling directory, thereby bypassing the confinement check.
The impact is categorized as a partial integrity violation. An attacker who can convince a user to install a malicious dbt package or process a crafted tarball can write files to arbitrary locations on the disk, provided those locations share a string prefix with the intended destination or if the attacker utilizes specific traversal techniques relative to that prefix. This can lead to the overwriting of configuration files or the planting of malicious scripts in adjacent directories.
The root cause of this vulnerability lies in the _is_within_directory helper function located in dbt_common/clients/system.py. The function attempts to verify that a file path falls under a parent directory by comparing the common prefix of the two absolute paths. The vulnerable logic is implemented as follows:
prefix = os.path.commonprefix([abs_directory, abs_target])
return prefix == abs_directoryThe Python documentation for os.path.commonprefix explicitly states that it returns the "longest string prefix," acting on a character-by-character basis. It does not respect directory separators. Consequently, if the intended destination is /usr/local/dbt/packages and the malicious path is /usr/local/dbt/packages_evil/exploit.sh, the common prefix is calculated as /usr/local/dbt/packages.
Since the calculated prefix matches the authorized abs_directory, the function returns True, erroneously confirming that the file is safe to extract. The operating system, however, treats packages and packages_evil as two distinct, sibling directories. This discrepancy between the validator's string-based logic and the filesystem's path-based logic creates the traversal primitive.
The remediation involves replacing os.path.commonprefix with os.path.commonpath. Introduced in Python 3.5, commonpath correctly handles filesystem paths by returning the longest common sub-path, respecting directory separators. If paths are on different drives or do not share a common root, it raises a ValueError.
Below is the comparison of the vulnerable and patched code in dbt_common/clients/system.py:
Vulnerable Implementation:
def _is_within_directory(directory, target):
abs_directory = os.path.abspath(directory)
abs_target = os.path.abspath(target)
# FLAW: Character-wise comparison ignores directory boundaries
prefix = os.path.commonprefix([abs_directory, abs_target])
return prefix == abs_directoryPatched Implementation (Commit e547954):
def _is_within_directory(directory, target):
abs_directory = os.path.abspath(directory)
abs_target = os.path.abspath(target)
try:
# FIX: Component-wise comparison respects directory boundaries
prefix = os.path.commonpath([abs_directory, abs_target])
return prefix == abs_directory
except ValueError:
# Handles cases like different drives on Windows
return FalseIn the patched version, comparing /tmp/pkg and /tmp/pkgevil using commonpath returns /tmp. Since /tmp does not equal /tmp/pkg, the check fails (False), and the extraction is aborted, preventing the traversal.
To exploit this vulnerability, an attacker must craft a malicious tarball (.tar.gz) containing file entries with manipulated paths. The exploit relies on the fact that dbt packages are often installed via URLs or Git repositories, which dbt-common processes.
The attack flow is as follows:
.../dbt_packages/target, the attacker names the file inside the archive ../target_suffix/malicious_file. When resolved absolutely, if the prefix matches, the check passes.dbt deps or installs the package. The untar_package function invokes _is_within_directory, which returns True for the malicious path.The accompanying Proof-of-Concept (PoC) from the unit tests demonstrates this explicitly:
# Creating a malicious path that is a sibling with a shared prefix
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))When this archive is extracted against self.tempdest, the file is written to self.tempdest + "evil", successfully escaping the sandbox.
The impact of this vulnerability is classified as Medium (CVSS 4.0 Score: 4.0). While it allows for arbitrary file writes, several mitigating factors limit the severity compared to a critical Remote Code Execution (RCE) flaw.
CVSS Vector: CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:A/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N
Despite the "Medium" rating, in automated CI/CD environments where dbt runs might occur with elevated privileges or where the filesystem is shared across pipeline stages, this could facilitate lateral movement or persistence.
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:A/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
dbt-common dbt Labs | < 1.34.2 | 1.34.2 |
dbt-common dbt Labs | >= 1.35.0, < 1.37.3 | 1.37.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| CWE Name | Improper Limitation of a Pathname to a Restricted Directory |
| CVSS Score | 4.0 (Medium) |
| CVSS Vector | CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:A/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N |
| Exploit Maturity | Proof of Concept (PoC) |
| Patch Status | Available (1.34.2, 1.37.3) |
The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
A critical Server-Side Request Forgery (SSRF) bypass vulnerability exists in CodeWhale before version 0.8.64 (and version 0.8.41 in the 0.8.x branch) due to a Time-of-Check to Time-of-Use (TOCTOU) bug in its DNS pre-flight validation mechanism. By returning a temporary resolution failure during validation and subsequently resolving to restricted IPs during HTTP execution, attackers can bypass security rules.
An argument injection vulnerability in CodeWhale (CVE-2026-75912 / GHSA-c6mw-8xh8-gpq6) allows unauthenticated remote attackers to execute arbitrary option commands on the git binary. By passing malicious command-line flags inside git_blame and git_show tool helper functions, an attacker can bypass typical access controls to read arbitrary local system files via the underlying git process.
SurrealDB prior to version 3.2.0 is vulnerable to an authorization bypass where authenticated users can invoke custom API endpoints belonging to other tenants. This cross-tenant data access occurs because the system fails to validate authorization scope boundaries against request-supplied namespace and database identifiers before executing scripts with elevated definer's rights.
An incorrect authorization vulnerability (CWE-863) in SurrealDB allows authenticated, low-privileged users to execute unauthorized state-modifying queries. This occurs because the database disabled permissions during evaluation of custom PERMISSIONS WHERE predicates to prevent infinite recursion.
SiYuan before v3.7.4 fails to enforce publish-access filters on five filetree path-resolution endpoints, allowing unauthenticated attackers to reconstruct private directory layouts and map document structures.
Prior to version v3.7.4, the SiYuan personal knowledge management system contained a critical logical authorization vulnerability within its database view rendering component. The flaws allowed unauthenticated remote attackers to bypass publish-access filters on databases, exposing sensitive Relation and Rollup cell contents belonging to private or password-protected repositories.