
Automated supply chain security monitor that polls PyPI and npm registries, diffs new releases against predecessors, and uses LLM analysis to detect malicious code changes with Slack alerting.
Automated monitoring of the top PyPI and npm packages for supply chain compromise. Polls both registries for new releases, diffs each release against its predecessor, and uses an LLM (via Cursor Agent CLI) to classify diffs as benign or malicious. Malicious findings trigger a Slack alert.
Both ecosystems are monitored by default. Use --no-pypi or --no-npm to disable one.
Each ecosystem runs its own polling thread but shares the analysis and alerting pipeline.
┌─── PyPI ──────────────────────┐ ┌─── npm ───────────────────────┐
│ │ │ │
│ changelog_since_serial() │ │ CouchDB _changes feed │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ ┌────────────┐ │ │ ┌────────────┐ │
│ │ All PyPI │─┐ │ │ │ All npm │─┐ │
│ │ events │ │ │ │ │ changes │ │ │
│ └────────────┘ ▼ │ │ └────────────┘ ▼ │
│ hugovk ──► Watchlist │ │ download-counts ─► Watchlist │
│ │ │ │ │ │
│ "new release" events only │ │ new versions since last epoch │
└───────────────┬───────────────┘ └───────────────┬───────────────┘
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ Download old + new│ │ Download old + new│
│ (sdist + wheel) │ │ (tarball) │
└───────────────────┘ └───────────────────┘
│ │
└─────────────────┬─────────────────┘
▼
┌───────────────┐
│ Unified diff │
│ report (.md) │
└───────┬───────┘
▼
┌───────────────┐ ◄── LLM analysis
│ Cursor Agent │ (read-only)
│ CLI (ask mode)│
└───────┬───────┘
│
verdict?
│
malicious │
▼
┌───────────────┐
│ Slack alert │
└───────────────┘
The LLM analysis is prompted to look for:
pip install -r requirements.txt (stdlib covers most of the tool; requests is used for Slack uploads)agent binary, not the IDEWindows (PowerShell):
irm 'https://cursor.com/install?win32=true' | iex
macOS / Linux:
curl https://cursor.com/install -fsS | bash
Verify with:
agent --version
You must be authenticated with Cursor (agent login or set CURSOR_API_KEY).
Place your Slack bot token in etc/slack.json:
{
"url": "https://hooks.slack.com/services/...",
"bot_token": "xoxb-...",
"channel": "C01XXXXXXXX"
}
The bot needs chat:write scope on the target channel. The channel field is the Slack channel ID where alerts are posted.
# One-shot: analyze releases from the last ~10 minutes
python monitor.py --once
# Continuous: monitor top 1000 packages (both ecosystems), poll every 5 min
python monitor.py --top 1000 --interval 300
# Production: monitor top 15000, alert to Slack
python monitor.py --top 15000 --interval 300 --slack
# npm only, top 5000
python monitor.py --no-pypi --npm-top 5000
# PyPI only
python monitor.py --no-npm
| File | Purpose |
|---|---|
monitor.py | Main orchestrator — poll PyPI + npm, diff, analyze, alert (parallel threads) |
pypi_monitor.py | Standalone PyPI changelog poller (used for exploration) |
package_diff.py | Download and diff two versions of any PyPI or npm package |
analyze_diff.py | Send a diff to Cursor Agent CLI, parse verdict |
top_pypi_packages.py | Fetch and list top N PyPI packages by download count |
slack.py | Slack API client (SendMessage, PostFile) |
etc/slack.json | Slack bot credentials |
last_serial.yaml | Persisted polling state (PyPI serial + npm sequence/epoch) |
logs/ | Daily log files (monitor_YYYYMMDD.log) |
python monitor.py [OPTIONS]
Options:
--top N Number of top packages to watch per ecosystem (default: 15000)
--interval SECS Poll interval in seconds (default: 300)
--once Single pass over recent events, then exit
--slack Enable Slack alerts for malicious findings
--model MODEL Override LLM model (default: composer-2-fast)
--debug Enable DEBUG logging (includes agent raw output)
PyPI options:
--no-pypi Disable PyPI monitoring
--serial N PyPI changelog serial to start from
npm options:
--no-npm Disable npm monitoring
--npm-top N Top N npm packages to watch (default: same as --top)
--npm-seq N npm replication sequence to start from
PyPI and npm each run in their own polling thread. Polling state (PyPI serial, npm sequence + epoch) is persisted to last_serial.yaml so the monitor resumes where it left off after a restart.
PyPI pipeline:
--interval seconds, calls changelog_since_serial() — a single API call that returns all events since the last check"new release" events matching the watchlistnpm pipeline:
replicate.npmjs.com--interval seconds, fetches the _changes feed for all registry changes since the last sequenceAll output is logged to both the console and logs/monitor_YYYYMMDD.log.
# Compare two versions from PyPI
python package_diff.py requests 2.31.0 2.32.0
# Compare two versions from npm
python package_diff.py --npm express 4.18.2 4.19.0