Skip to main content

chaincanary

Stop supply-chain attacks before they execute. The only scanner with semantic .pth analysis.

Caught LiteLLM 1.82.8 as MALICIOUS at publish time — before any advisory existed. No cloud. No account. Nothing leaves your machine.

chaincanary demo

Try it now — zero install:

pipx run chaincanary check requests==2.28.0

Or scan the real LiteLLM attack (uses local mock wheel — no network):

git clone https://github.com/AetherCore-Dev/chaincanary && cd chaincanary
pip install -e .
chaincanary check litellm==1.82.8 --local tests/fixtures/litellm-1.82.8-py3-none-any.whl

Why developers use chaincanary:

  • Catches what others miss — semantic .pth classifier detects attacks that run on every Python startup, not just at install time
  • Zero config — pip install chaincanary && chaincanary check <package>, done
  • Nothing leaves your machine — pure offline static analysis, no cloud, no account, no proxy
  • CI-ready — GitHub Action blocks malicious packages on every push
  • Audit entire projects — scan all dependencies in requirements.txt or pyproject.toml in one command
  • Version diffing — see exactly what changed between releases
  • Works in China — no proxy needed, unlike socket.dev / Safety

PyPI version CI Python 3.9+ License: Apache 2.0


Install

pip install chaincanary

Or try without installing: pipx run chaincanary check <package>


Usage

Scan a single package

chaincanary check requests==2.28.0
chaincanary check litellm==1.82.8

# Scan a local .whl file (no network needed)
chaincanary check litellm==1.82.8 --local ./litellm-1.82.8-py3-none-any.whl

Audit your entire project

chaincanary audit requirements.txt
chaincanary audit pyproject.toml --fail-on HIGH_RISK

Compare two versions

chaincanary diff litellm 1.82.6 1.82.8

Safe install (scan before installing)

chaincanary install requests==2.32.0
chaincanary install litellm==1.82.8   # blocked — MALICIOUS

JSON output (for pipelines)

chaincanary check litellm==1.82.8 --json-output | jq '.verdict'
# "MALICIOUS"

SARIF output (for GitHub Code Scanning)

# Single package → SARIF
chaincanary check litellm==1.82.8 --local ./litellm-1.82.8-py3-none-any.whl --sarif-output > results.sarif

# Audit lockfile → SARIF
chaincanary audit requirements.txt --sarif-output > chaincanary.sarif

Upload to GitHub Security tab in your CI workflow:

- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: chaincanary.sarif

Offline mode (air-gapped / CI cache)

# Download wheels first (standard pip)
pip download -r requirements.txt -d ./wheels/

# Scan without any network calls
chaincanary audit requirements.txt --offline --wheel-dir ./wheels/

# Single package offline
chaincanary check mypackage==1.0.0 --offline --local ./mypackage-1.0.0-py3-none-any.whl

GitHub Action — drop-in CI protection

# .github/workflows/security.yml
name: Supply Chain Security
on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: AetherCore-Dev/chaincanary@v0.1.0
        with:
          requirements: requirements.txt
          fail-on: MALICIOUS   # or HIGH_RISK for stricter mode
          sarif-output: chaincanary.sarif  # optional: upload to GitHub Security tab
      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: chaincanary.sarif

Fails your build if any dependency matches a known attack pattern. Zero config.


Why chaincanary catches what others miss

A .pth file in Python's site-packages runs on every Python startup — not just at install time. Other scanners only check setup.py install hooks and miss this entirely.

chaincanary has a semantic .pth classifier with 4 categories:

.pth content Classification Finding
Empty Normal silent
/usr/local/lib/... Path-only silent
setuptools distutils shim Safe code LOW
subprocess.Popen(['curl', ...]) Dangerous CRITICAL

LiteLLM 1.82.8 was flagged MALICIOUS by chaincanary at publish time. Other tools missed it entirely, or flagged it only after manual rule updates.


What chaincanary detects

Attack vector Location Severity
.pth file with network/subprocess site-packages/*.pth CRITICAL
Network call during pip install setup.py HIGH
Shell command during pip install setup.py HIGH
exec(base64.decode(...)) obfuscation anywhere HIGH
DNS exfiltration via socket.getaddrinfo __init__.py HIGH
Network call on every import __init__.py MEDIUM
SSH / AWS credential access anywhere HIGH
Path traversal in wheel zip .whl structure CRITICAL
Known malicious SHA256 hash .whl file CRITICAL
Typosquatting (Levenshtein distance) package name MEDIUM

Comparison

chaincanary pip-audit Trivy socket.dev Safety
.pth semantic analysis 4-category classifier -- -- no static classifier --
Detects LiteLLM 1.82.8 at publish time offline, no rules needed -- -- only after manual rule update --
No account / cloud needed yes yes yes no (GitHub App) no (account)
Nothing leaves your machine yes yes yes no (uploads metadata) yes
Offline capable yes partial yes no no
China mainland access (no proxy) yes yes yes no (403) no (403)
Open source yes yes yes no (SaaS) partial

chaincanary is not a CVE scanner — use it alongside pip-audit for vulnerability advisory coverage.


The LiteLLM attack — what happened

March 24, 2026: TeamPCP hijacked LiteLLM on PyPI

Threat actor TeamPCP hijacked the LiteLLM maintainer's PyPI account and published two malicious versions:

Version Attack vector Trigger
1.82.7 Payload injected into litellm/proxy/proxy_server.py import litellm.proxy
1.82.8 Hidden .pth file (litellm_init.pth, 34 KB) Every Python startup — no import needed

The .pth attack in 1.82.8:

# litellm_init.pth — executes on every Python startup, silently, forever
import os, subprocess, sys
subprocess.Popen([sys.executable, "-c", "import base64; exec(base64.b64decode('...'))"])

The payload collects SSH keys, env vars, AWS/GCP/K8s credentials, crypto wallets, CI secrets — encrypts with AES-256 + RSA-4096 and exfiltrates to https://models.litellm.cloud/ (a fake domain registered the day before).

This runs every time you start Python — not just during pip install. LiteLLM was downloaded ~95 million times per month. chaincanary flagged both versions MALICIOUS at publish time — without any advisory, rule update, or cloud lookup.


How it works

chaincanary check <package>
        |
        v
Download wheel (no install, no execute)
        |
        v
Static analysis:
  - Zip safety (path traversal, zip bomb)
  - .pth semantic classifier (4 categories)
  - AST analysis of setup.py / install hooks
  - Obfuscation detection (base64, eval, exec)
  - __init__.py delayed-trigger scan
  - DNS exfiltration patterns
  - SHA256 malicious hash database
  - Typosquatting distance check
        |
        v
Score 0-10 -> Verdict: SAFE / LOW_RISK / HIGH_RISK / MALICIOUS
        |
        v
Block or proceed

No sandboxing. No Docker. No kernel modules. Pure Python static analysis in seconds.


Add chaincanary badge to your project

Show that your project is scanned for supply-chain attacks:

[![chaincanary](https://img.shields.io/badge/scanned%20by-chaincanary-blue)](https://github.com/AetherCore-Dev/chaincanary)

chaincanary


Known Limitations

chaincanary is a static behavioral scanner, not a magic bullet:

Gap Mitigation
No C extension analysis (.so/.pyd) Sandbox mode in v0.3
No CVE database Use alongside pip-audit
No dynamic sandbox Static signals only (for now)
Multi-stage payloads (download at runtime) Runtime monitoring in v0.3

Roadmap

Version Theme Status
v0.1 Core engine: .pth classifier, audit, diff, GitHub Action shipped
v0.2 SARIF output, offline mode, hash feed, pre-commit hook in progress
v0.3 Lightweight sandbox, package reputation, npm/cargo later

Full plan: ROADMAP.md


Contributing

git clone https://github.com/AetherCore-Dev/chaincanary
cd chaincanary
pip install -e ".[dev]"
pytest tests/

PRs welcome — especially:

  • New malicious hash signatures
  • Detection rules for new attack patterns
  • False positive reports

License

Apache 2.0. See LICENSE.


Built after the LiteLLM supply chain attack, March 2026.

Metadata

Release files for chaincanary 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for chaincanary 0.2.0
File Size Uploaded
chaincanary-0.2.0.tar.gz 75.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for chaincanary 0.2.0
File Interpreter ABI Platform
chaincanary-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 128.7 kB

Release files / chaincanary-0.2.0.tar.gz

Download URL chaincanary-0.2.0.tar.gz
Size 75.0 kB
Tags Source
SHA-256 checksum
How to use checksums
b93e9230b7742e8ed5fffc83f9425eb853891ba8990f0bc3335133c0f56a2968
BLAKE2b-256 checksum
How to use checksums
97cf15f3b127b46d0fe4e2b0843e6153a30cc13c0ae1d85fd4282bbe6c3c2566
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 27, 2026.

Transparency log

Release files / chaincanary-0.2.0-py3-none-any.whl

Download URL chaincanary-0.2.0-py3-none-any.whl
Size 53.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a8264b5a29034d926d6d8c6ae2a571742024c994be1497e24011605062cd11b2
BLAKE2b-256 checksum
How to use checksums
c3f81fd9a3ec77be4d8145f25c87fc34e0bc758e96952569b8626be27db72940
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page