Skip to content

Risk Levels

pipguard assigns every scanned package one of five risk levels based on patterns found during static AST analysis.

CRITICAL

Action: Block immediately. Exit 1. No confirmation prompt.

Trigger Why it's CRITICAL
.pth file containing executable Python code .pth files are executed automatically at Python interpreter startup — before any user code runs. Any executable content is unambiguously malicious.
eval(base64.b64decode(...)) in any file Classic obfuscated payload pattern. Legitimate packages never need this.
Network calls (urllib, httpx, requests, socket) in setup.py or install hooks Build-time network calls have no legitimate use case. The March 2026 litellm attack used this to exfiltrate data.
Shell/subprocess execution in install hooks (os.system, os.popen, shell execution via subprocess) Install-time command execution is a high-confidence attacker primitive and is blocked by default.

CRITICAL is never reduced

The --allow flag and the seed allowlist do not reduce CRITICAL findings. Only --force bypasses CRITICAL — use with extreme caution.

HIGH

Action: Block. Exit 1. No confirmation prompt.

Trigger Scope Why
Non-ASCII character in package name Name check (pre-scan) Possible homoglyph / typosquatting attack (e.g. bоto3 with Cyrillic о instead of Latin o).
Reads credential paths (~/.ssh, ~/.aws, ~/.kube, ~/.gnupg) Install hooks only A package reading your SSH keys during pip install is an attack, not a feature.
Direct subprocess execution (non-shell) Install hooks only Elevated risk in install-time context; surfaced as HIGH for manual review.
Binary IOC credential markers (/.ssh/id_rsa, /.aws/credentials) Binary extension scan Heuristic binary content includes hard-coded credential path indicators.

HIGH in runtime code

If the same patterns appear in runtime code (not install hooks), the finding is downgraded to MEDIUM. boto3 legitimately reads ~/.aws at runtime — that's fine. It reading ~/.aws in setup.py is not.

MEDIUM

Action: Warn and prompt for confirmation. (--yes skips prompt and proceeds.)

Trigger Notes
Binary-only wheel (no Python source) Wheel contains only .so / .pyd / .dylib files — AST scan cannot verify contents. Confirmation gate fires; use --yes to proceed.
Network calls in runtime .py files Common in legitimate packages; shown for transparency
Sensitive env var access (*TOKEN*, *KEY*, *SECRET*, *PASSWORD*, *CREDENTIAL*) Flagged in runtime code
Large source file over 1MB Scanner continues, but emits confidence-reduction warning for manual review
Binary IOC string indicators (https://, /bin/sh, socket) Heuristic binary scan detected suspicious runtime/exfil primitives

LOW

Action: Warn. Confirmation prompt fires (skippable with --yes).

Trigger Notes
Compiled binary extension in mixed wheel .so / .pyd / .dylib file present alongside Python source — AST scanner is blind to any payload in compiled code
importlib.import_module(variable) Dynamic imports can load arbitrary code
__import__(variable) Same concern as above

By default, LOW findings are shown as package-level counts in the summary output. Use --verbose to expand file-level LOW details.

CLEAN

Action: Included in summary totals; install proceeds.

No patterns matching CRITICAL, HIGH, MEDIUM, or LOW were found. By default, CLEAN packages are counted but not listed individually. Use --verbose to print every CLEAN package line.