---
id: CVE-2026-22871
aliases:
  - GHSA-xg9w-vg3g-6m68
  - PYSEC-2026-1430
title: >-
  GuardDog Path Traversal Vulnerability Leads to Arbitrary File Overwrite and
  RCE
summary: >-
  GuardDog Path Traversal Vulnerability Leads to Arbitrary File Overwrite and
  RCE
severity: high
vendor: guarddog
product: guarddog
ecosystem: pip
affected:
  - guarddog < 2.7.1
patched:
  - guarddog 2.7.1
published: '2026-01-13'
updated: '2026-07-07'
source: OSV
sourceUrl: 'https://osv.dev/vulnerability/GHSA-xg9w-vg3g-6m68'
references:
  - url: >-
      https://github.com/DataDog/guarddog/security/advisories/GHSA-xg9w-vg3g-6m68
  - url: 'https://nvd.nist.gov/vuln/detail/CVE-2026-22871'
  - url: >-
      https://github.com/DataDog/guarddog/commit/9aa6a725b2c71d537d3c18d1c15621395ebb879c
  - url: 'https://github.com/DataDog/guarddog'
tags:
  - osv
  - pip
epss: 0.01071
epssPercentile: 0.63457
ingestedAt: '2026-07-08T18:25:54.184Z'
---

## Overview

## Summary

A **path traversal vulnerability** exists in GuardDog's `safe_extract()` function that allows malicious PyPI packages to write arbitrary files outside the intended extraction directory, leading to **Arbitrary File Overwrite** and **Remote Code Execution** on systems running GuardDog.

**CWE:** CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)

## Details

### Vulnerable Code

**File:** `guarddog/utils/archives.py`

```python
elif zipfile.is_zipfile(source_archive):
    with zipfile.ZipFile(source_archive, "r") as zip:
        for file in zip.namelist():
            # Note: zip.extract cleans up any malicious file name
            # such as directory traversal attempts This is not the
            # case of zipfile.extractall
            zip.extract(file, path=os.path.join(target_directory, file))  # ❌ VULNERABLE
```

### Root Cause

The comment about `zip.extract()` fooled me at first :) then I noticed the `os.path.join()` call. 
The vulnerability stems from **incorrect usage of Python's `zipfile.ZipFile.extract()` API**:

- The `path` parameter should be the **target directory**, not a full file path
- `extract()` automatically appends the member name to the path
- By passing `os.path.join(target_directory, file)`, GuardDog causes the filename to be appended **twice**
- This breaks zipfile's built-in path traversal sanitization

### Attack Vector

1. Attacker creates malicious wheel with path traversal filenames
2. Uploads to PyPI or distributes directly
3. Package scan: `guarddog pypi scan malicious-pkg`
4. GuardDog downloads and extracts the package
5. Malicious files written to arbitrary locations
6. Code execution could be achieved

## Impact

Impact depends on how GuardDog is running and under which environment.

### Critical Scenarios

1. **Immediate Code Execution**
   - Write to `~/.bashrc` → executes on next shell
   - Write to `~/.profile` → executes on login

2. **Persistent Backdoors**
   - Write to `~/.ssh/authorized_keys` → SSH access
   - Write to `/etc/cron.d/malicious` → scheduled execution (if root)
   - Write to systemd user services → persistent execution

and more...

## Credits

**Reported by:** Charbel (dwbruijn)

## Affected packages

- `guarddog < 2.7.1`

## Remediation

Upgrade to a patched release:

- `guarddog 2.7.1`
