
🐍 🔍 GuardDog is a CLI tool to Identify malicious PyPI and npm packages
GuardDog is a CLI tool that identifies malicious PyPI and npm packages, Go modules, Rust crates, RubyGems, GitHub actions, or VSCode extensions. It runs static analysis on package source code (through YARA rules) and analyzes package metadata to detect supply chain attacks.
What makes GuardDog different: Instead of just listing suspicious patterns, GuardDog correlates findings to identify actual risks based on attack chains. A package needs both the capability to perform an action (e.g., network access) and a threat indicator (e.g., suspicious domain) in the same file to be flagged as high risk.
It downloads and scans code from:

GuardDog uses a risk-based detection model that correlates code capabilities with threat indicators:
Traditional SAST tools flag every suspicious pattern independently, leading to alert fatigue. GuardDog understands that:
Packages receive a score from 0-10 based on four factors:
| Factor | Weight | Description |
|---|---|---|
| Severity | 30% | Highest severity finding (low/medium/high) |
| Attack Chain | 20% | Presence of complete attack stages (early → mid/late) |
| Specificity | 30% | How specific patterns are to malware vs legitimate code |
| Sophistication | 20% | Technique advancement level |
Score Labels:
Attack Chain Stages (based on MITRE ATT&CK):
The easiest way to run GuardDog is to use uvx:
uvx guarddog pypi scan requests
To install it locally:
uv tool install guarddog
# or
pip install guarddog
Or use the Docker image:
docker pull ghcr.io/datadog/guarddog
alias guarddog='docker run --rm ghcr.io/datadog/guarddog'
Note: On Windows, the only supported installation method is Docker.
# Scan the most recent version of the 'requests' package
guarddog pypi scan requests
# Scan a specific version of the 'requests' package
guarddog pypi scan requests --version 2.28.1
# Scan the 'request' package using 2 specific heuristics
guarddog pypi scan requests --rules exec-base64 --rules code-execution
# Scan the 'requests' package using all rules but one
guarddog pypi scan requests --exclude-rules exec-base64
# Scan a local package archive
guarddog pypi scan /tmp/triage.tar.gz
# Scan a local package directory
guarddog pypi scan /tmp/triage/
# Scan a package stored in S3 (a folder/prefix or a single archive object)
guarddog pypi scan s3://my-bucket/path/to/package/
guarddog pypi scan s3://my-bucket/path/to/package.tar.gz
# Scan every package referenced in a requirements.txt file of a local folder
guarddog pypi verify workspace/guarddog/requirements.txt
# Scan every package referenced in a requirements.txt file and output a sarif file - works only for verify
guarddog pypi verify --output-format=sarif workspace/guarddog/requirements.txt
# Output JSON to standard output - works for every command
guarddog pypi scan requests --output-format=json
# All the commands also work on npm, go, crates, rubygems
guarddog npm scan express
guarddog go scan github.com/DataDog/dd-trace-go
guarddog go verify /tmp/repo/go.mod
# Scan Rust crates
guarddog crates scan serde
guarddog crates verify /tmp/repo/Cargo.lock
# Scan RubyGems packages
guarddog rubygems scan rails
guarddog rubygems verify /tmp/repo/Gemfile.lock
# Additionally can support scanning GitHub actions that are implemented in JavaScript
guarddog github_action scan DataDog/synthetics-ci-github-action
guarddog github_action verify /tmp/repo/.github/workflows/main.yml
# Scan VSCode extensions from the marketplace
guarddog extension scan ms-python.python
# Scan a specific version of a VSCode extension
guarddog extension scan ms-python.python --version 2023.20.0
# Scan a local VSCode extension directory or VSIX archive
guarddog extension scan /tmp/my-extension/
# Run in debug mode
guarddog --log-level debug npm scan express
When scanning packages, GuardDog runs source code analysis inside a kernel-level sandbox (Linux via Landlock, macOS via Seatbelt, using nono). The sandbox blocks all network access and restricts filesystem operations to only the paths needed for analysis. This protects against malicious packages that attempt to execute code during archive extraction or scanning.
By default, the sandbox is required: if it's not available on the platform, the scan fails instead of running unprotected. To scan without it, you must explicitly pass --no-sandbox:
# Default: require the sandbox, exit with an error if it's unavailable
guarddog pypi scan requests
# Explicitly disable the sandbox
guarddog pypi scan requests --no-sandbox