Sentro — scan Python packages for malicious code, typosquatting & supply-chain attacks before install. Docs: sentro-docs.onrender.com
Project description
sentro
Scan Python packages for malicious code, typosquatting, and supply-chain attacks — before they ever install.
Built by Solvyx.dev
sentro install requests
╭──────────────────────── sentro scan ─────────────────────────╮
│ Package : requests 2.31.0 │
│ PyPI : verified │
│ Risk : SAFE (score 0/100) │
╰─────────────────────────────────────────────────────────────────╯
No issues found.
What it detects
- Malicious code —
eval()/exec()at module level,os.system(), socket connections to hardcoded IPs - Install hooks — dangerous calls in
setup.pythat run unconditionally at install time - Obfuscation —
exec(base64.b64decode(...))chains, high-entropy strings,marshal.loadspayloads - Typosquatting — names similar to popular packages (
reqeusts,numpy-dev), Unicode homoglyphs - Dependency confusion — package names that shadow Python stdlib modules
- Metadata signals — very new packages, suspiciously low download counts, missing author info
Each finding contributes to a risk score (0–100). The overall verdict is SAFE, WARNING, or DANGER.
Install
pip install sentro
Requires Python 3.11+.
Quick start
# Scan and install
sentro install requests
# Scan only — don't install
sentro install requests --no-install
# Block install if anything scores DANGER (for CI pipelines)
sentro install requests --strict
Full documentation — configuration reference, all CLI flags, CI integration guide, installer detection, and more:
sentro-docs.onrender.com
License
MIT — built and maintained by Solvyx.dev
Sentro Documentation
Scan Python packages for malicious code, typosquatting, and supply-chain attacks — before they ever install.
Sentro acts as a security-aware wrapper around pip, uv, poetry, and other Python package managers. It downloads the package from PyPI, statically analyzes the source code, and gives you a risk verdict (SAFE, WARNING, or DANGER) — all before a single byte of malicious code touches your environment.
Table of Contents
- Install
- Quick Start
- CLI Reference
- Configuration
- What Sentro Detects
- Reputation-Aware Scoring
- Output Formats
- CI Integration
- Troubleshooting False Positives
Install
pip install sentro
Requires Python 3.11+.
Quick Start
# Scan and install
sentro install requests
# Scan only — do not install
sentro install requests --no-install
# Block installation if DANGER is detected (great for CI)
sentro install requests --strict
# Detailed output with per-scanner summaries and metadata
sentro install requests --verbose
CLI Reference
sentro install <packages...>
The main command. Scans each package, prints a report, then forwards to the detected package manager (pip by default, or uv/poetry/etc. when available).
| Flag | Env Var | Description |
|---|---|---|
--strict |
SENTRO_STRICT=1 |
Exit with error and block installation if any package scores DANGER. |
--no-install |
— | Scan only; do not invoke the installer. |
--skip-scan |
— | Skip scanning entirely and pass straight to the installer. |
-v, --verbose |
SENTRO_VERBOSE=1 |
Show detailed findings, per-scanner summaries, package age, download counts, and progress messages. |
--output-format {text,json} |
SENTRO_OUTPUT_FORMAT |
Change report format. JSON supports verbose metadata. |
--installer {pip,uv,conda,mamba,poetry,pipenv,pdm,auto} |
SENTRO_INSTALLER |
Force a specific installer. Default is auto. |
-r requirements.txt |
— | Scan all packages listed in a requirements file. |
--config path.toml |
— | Load an explicit TOML config file. |
All unknown flags (e.g. --index-url, --constraint) are forwarded transparently to the underlying installer.
sentro detect-installer
Shows which package manager Sentro would use automatically.
$ sentro detect-installer
Detected installer: uv
Configuration
Sentro merges configuration from multiple sources (lowest to highest priority):
- Built-in defaults
~/.config/sentro/config.tomlpyproject.toml→[tool.sentro].sentro.tomlin the current directory- Explicit
--configpath SENTRO_*environment variables- CLI flags
Example .sentro.toml
[sentro]
strict = false
verbose = false
output_format = "text"
whitelist_packages = ["my-internal-lib", "another-private-pkg"]
scanners_disabled = ["metadata"]
pypi_timeout = 15
prefer_wheel = true
[sentro.thresholds]
warning = 30
danger = 70
Environment Variables
| Variable | Effect |
|---|---|
SENTRO_STRICT=1 |
Enable strict mode |
SENTRO_VERBOSE=1 |
Enable verbose output |
SENTRO_OUTPUT_FORMAT=json |
Set output format |
SENTRO_DANGER_THRESHOLD=70 |
Override danger threshold |
SENTRO_WARNING_THRESHOLD=30 |
Override warning threshold |
SENTRO_WHITELIST=pkg1,pkg2 |
Comma-separated whitelist |
What Sentro Detects
Sentro runs multiple specialized scanners against every package. Findings are aggregated into a single risk score (0–100).
1. Malicious Code (malicious_code)
Detects dynamic code execution, shell invocations, network anomalies, and real-world malware patterns.
| Pattern | Severity | Notes |
|---|---|---|
eval() / exec() at module level |
DANGER |
Runs unconditionally on import. |
eval(base64.b64decode(...)) / exec(zlib.decompress(...)) |
DANGER |
Classic obfuscation chain. |
os.system(...) |
DANGER or INFO |
Downgraded to INFO in CLI / viewer / launcher contexts. |
subprocess(..., shell=True) |
DANGER or WARNING |
Downgraded for known-safe commands (git log, xdg-open, clear, etc.). |
socket.connect(("1.2.3.4", port)) |
DANGER |
Hardcoded IPs are strong C2 indicators. |
socket.connect(("8.8.8.8", port)) |
INFO (score 0) |
Recognized as the common local-IP detection trick. |
requests.get("https://pastebin.com/...") |
WARNING |
Pastebin, Discord webhooks, Telegram, raw GitHub, and similar staging services. |
open("~/.bashrc", "a") |
DANGER |
Writing to sensitive files (persistence / credential theft). |
ctypes.CDLL("./payload.so") |
WARNING |
Loading native libraries — common payload hiding technique. |
getattr(__builtins__, "eval") |
DANGER |
Reflection evasion used to bypass simple static analysis. |
pip.main(["install", "evil"]) |
DANGER |
Programmatic pip installs (chain-loading malware). |
subprocess.run([sys.executable, "-m", "pip", "install", ...]) |
DANGER |
Same as above, via subprocess. |
importlib.import_module(variable) |
WARNING |
Dynamic imports outside try/except blocks. Skipped for normal compatibility shims. |
Smart exclusions:
# noseccomments suppress findings on that line (Bandit convention).- Name-shadowed builtins (e.g.
from mylib import eval) are ignored. eval(compile(..., "exec"))— the standard file-loading pattern — is scored asINFO(5) instead of double-flagged.compile()inside a function by itself is skipped entirely.__import__()at module level isINFO(0) because it is overwhelmingly used for lazy loading.
2. Obfuscation (obfuscation)
| Pattern | Severity |
|---|---|
exec(base64.b64decode(...)) chains |
DANGER |
| Large base64/hex string constants | WARNING |
| High-entropy strings (>6.2 bits/char) | WARNING — only when other suspicious findings already exist in the same file |
marshal.loads chains |
DANGER |
Test files are skipped for encoded-constant checks (they legitimately contain fixtures and key material).
3. Setup Hooks (setup_hooks)
Analyzes setup.py for dangerous install-time behavior.
| Pattern | Severity |
|---|---|
os.system() / exec() / eval() at module scope in setup.py |
DANGER |
cmdclass override |
WARNING |
Dynamic install_requires |
WARNING |
4. Typosquatting (typosquatting)
| Pattern | Severity |
|---|---|
| Non-ASCII / homoglyph characters in package name | DANGER |
| Name very similar to a top PyPI package | WARNING |
Popular package + suspicious suffix (-dev, -fix, 2, 3) |
WARNING |
5. Dependency Confusion (dependency_confusion)
| Pattern | Severity |
|---|---|
Package shadows a Python stdlib module (json, os, urllib) |
DANGER |
| Package not found on PyPI | DANGER |
6. Metadata (metadata)
Uses PyPI API data to surface reputation signals.
| Pattern | Severity |
|---|---|
| Published < 7 days ago | DANGER |
| Published < 30 days ago | WARNING |
| < 100 downloads last month | WARNING |
| Only one release ever | WARNING |
| No author, homepage, or description | WARNING |
Reputation-Aware Scoring
Not every eval() call is malicious. Sentro uses package reputation to avoid false-positive WARNING / DANGER on established, heavily-downloaded libraries.
If a package has no DANGER findings, its raw score is multiplied by a reputation discount:
| Downloads last month | Age | Discount | Example effect |
|---|---|---|---|
| > 50,000 | > 1 year | 0.25× | numpy, requests, pandas |
| > 10,000 | > 90 days | 0.5× | flask, django |
| > 1,000 | > 30 days | 0.75× | Mid-size utilities |
| ≤ 1,000 or ≤ 30 days | — | 1.0× (no discount) | Brand-new / niche packages |
Important: Discounts are ignored as soon as a single DANGER-severity finding exists (e.g. a decode-exec chain). Real malware always scores at full strength.
In --verbose mode you can see the exact discount applied:
Reputation discount : 25%
Output Formats
Text (default)
Clean, colorized terminal output. Use -v for extra columns and a per-scanner summary table.
sentro install requests -v
JSON
Machine-readable output ideal for CI dashboards.
sentro install requests --output-format json -v
Verbose JSON adds:
metadata—age_days,download_stats,reputation_discountscanner_summary— count of findings per scanner by severity
CI Integration
Use --strict in CI pipelines to fail the build when malicious code is detected:
# .github/workflows/security.yml
- name: Scan dependencies with Sentro
run: sentro install -r requirements.txt --strict
If any package scores DANGER, Sentro exits with code 1 and blocks installation.
For machine-readable CI logs, combine with JSON:
- run: sentro install -r requirements.txt --strict --output-format json -v
Troubleshooting False Positives
Sentro is designed to minimize false positives through contextual heuristics, but if you encounter one:
- Check
--verboseto see exactly which scanner and rule triggered. - Add
# nosecto the line in your own code if the finding is intentional (Bandit convention). - Whitelist internal packages in
.sentro.toml:whitelist_packages = ["my-private-lib"]
- Disable a specific scanner if it is noisy for your use case:
scanners_disabled = ["metadata"]
If a top PyPI package is still falsely flagged as DANGER, please open an issue — it is treated as a high-priority bug.
Changelog Highlights
Accuracy & False-Positive Reduction
- Name-shadowing detection — locally imported
eval/execnames are no longer confused with builtins. - Compile-exec deduplication —
exec(compile(..., "exec"))is recognized as the standard file-loading pattern. - Per-file finding caps — repeated identical patterns in large packages (e.g.
numpyf2py) are collapsed into a summary note. - Safe-command regex —
git log,xdg-open,clear,make,--versioninsubprocess(shell=True)are downgraded. - Safe-public-IP detection —
8.8.8.8,1.1.1.1, etc. used for local-IP discovery are scored asINFO(0). # nosecsupport — suppresses findings on manually reviewed lines.
New Real-World Attack Detections
- Sensitive file writes (
~/.bashrc,~/.ssh/authorized_keys, cron dirs, Windows startup) - Programmatic pip installations (
pip.main,subprocess pip install) ctypesnative library loadinggetattr(__builtins__, "eval")reflection evasion- Dynamic
importlib.import_module(outsidetry/except) - Hardcoded requests to suspicious staging URLs (Pastebin, Discord, Telegram, raw GitHub)
Reporting & UX
--verbose/-vflag for detailed findings, progress messages, scanner summaries, and metadata.- Enhanced text report with age, downloads, reputation discount, and per-scanner breakdown.
- Enhanced JSON report with
metadataandscanner_summaryin verbose mode. - Progress indicators during multi-package scans.
License: MIT — built and maintained by Solvyx.dev
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sentro-0.1.6.tar.gz.
File metadata
- Download URL: sentro-0.1.6.tar.gz
- Upload date:
- Size: 80.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65e13f403fdd471364092555d4156549877aa71b554e9895615eb7f3b82d0d6d
|
|
| MD5 |
9b66e17a81f95b31551f1114fe102a2f
|
|
| BLAKE2b-256 |
a5dbca4d470639d5fda3410b34e21d7f3eaddf20ad64fba1f9dc5b2feac130ab
|
File details
Details for the file sentro-0.1.6-py3-none-any.whl.
File metadata
- Download URL: sentro-0.1.6-py3-none-any.whl
- Upload date:
- Size: 47.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e0a4d43db118e58037b14b304f2db8893ba647ff95aa40663045814acb877b1
|
|
| MD5 |
87cb1fbd75f524e1f04431346c5c8436
|
|
| BLAKE2b-256 |
75322cd6ddf3a9fbc128736cea0137dad1d1b9dceee07ee9d27eb4573717fc1b
|