
PyPIとnpmレジストリをポーリングし、新しいリリースをその前のバージョンと比較し、LLM分析を使用して悪意のあるコード変更を検出し、Slackアラートを送信する自動化されたサプライチェーンセキュリティモニター。
上位のPyPIおよびnpmパッケージのサプライチェーン侵害を自動監視します。両方のレジストリをポーリングして新しいリリースを取得し、各リリースを以前のバージョンと差分比較し、LLM(Cursor Agent CLI経由)を使用して差分を良性または悪意に分類します。悪意のある結果はSlackアラートをトリガーします。
両エコシステムはデフォルトで監視されます。一方を無効にするには--no-pypiまたは--no-npmを使用します。
各エコシステムは独自のポーリングスレッドで実行されますが、分析とアラートのパイプラインを共有します。
┌─── 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 │
└───────────────┘
LLM分析は以下を探すように指示されています。
pip install -r requirements.txtでインストールします(標準ライブラリがツールの大半をカバーしており、requestsはSlackアップロードに使用されます)agentバイナリ(IDEではありません)Windows (PowerShell):
irm 'https://cursor.com/install?win32=true' | iex
macOS / Linux:
curl https://cursor.com/install -fsS | bash
Verify with:
agent --version
Cursorで認証されている必要があります(agent loginまたはCURSOR_API_KEYの設定)。
Slackボットトークンをetc/slack.jsonに配置します:
{
"url": "https://hooks.slack.com/services/...",
"bot_token": "xoxb-...",
"channel": "C01XXXXXXXX"
}
ボットにはターゲットチャンネルに対するchat:writeスコープが必要です。channelフィールドはアラートが投稿されるSlackチャンネルIDです。
# 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
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とnpmはそれぞれ独自のポーリングスレッドで実行されます。ポーリング状態(PyPIシリアル、npmシーケンス+エポック)はlast_serial.yamlに永続化されるため、再起動後もモニターは中断したところから再開します。
PyPIパイプライン:
--interval秒ごとにchangelog_since_serial()を呼び出します — 前回のチェック以降のすべてのイベントを返す単一のAPI呼び出しです"new release"イベントをフィルタリングしますnpmパイプライン:
replicate.npmjs.comから現在のCouchDBレプリケーションシーケンスを読み取ります--interval秒ごとに、最後のシーケンス以降のすべてのレジストリ変更の_changesフィードを取得しますすべての出力はコンソールと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
# Save to file
python package_diff.py telnyx 2.0.0 2.1.0 -o telnyx_diff.md
# Compare local archives
python package_diff.py --local old.tar.gz new.tar.gz -n mypackage
ダウンロードはpipやnpmではなく、レジストリAPI(PyPI JSON API / npmレジストリ)を介して直接行われます。つまり:
# Analyze a diff file
python analyze_diff.py telnyx_diff.md
# JSON output
python analyze_diff.py telnyx_diff.md --json
# Use a specific model
python analyze_diff.py telnyx_diff.md --model claude-4-opus
Cursor Agent CLIを--mode ask(読み取り専用)で--trust付きで実行します。エージェントは差分ファイルを読み取り、構造化された判定を返します。
終了コード:0 = 良性、1 = 悪意、2 = 不明/エラー。
# See what's being released right now (last ~10 min)
python pypi_monitor.py --once --top 15000
# Continuous monitoring (console output only, no analysis)
python pypi_monitor.py --top 1000 --interval 120
完全な分析パイプラインを実行せずにPyPIのリリース速度を調査したり、変更ログAPIをデバッグしたりするのに便利です。
# Print top 1000 packages
python top_pypi_packages.py
# Use as a library
from top_pypi_packages import fetch_top_packages
packages = fetch_top_packages(top_n=500)
# [{"project": "boto3", "download_count": 1577565199}, ...]
モニターはエコシステムごとにポーリング間隔あたり1回のAPI呼び出し(PyPI変更ログ / npm _changes)に加えて、新しいリリースあたり2~3回の呼び出し(バージョン履歴+ダウンロード)を行います。これは非常に軽量です。
モニターが悪意のあるリリースを検出すると、Slackに投稿します:
PyPI:
🚨 Supply Chain Alert: telnyx 4.87.2
Rank: #5,481 of top PyPI packages
Verdict: MALICIOUS
PyPI: https://pypi.org/project/telnyx/4.87.2/
Analysis summary (truncated):
The changes to src/telnyx/_client.py implement obfuscated
download-decrypt-execute behavior and module-import side effects.
A _d() function decodes base64 strings, a massive _p blob contains
an exfiltration script that downloads a .wav file from
http://83.142.209.203:8080/ringtone.wav and extracts a hidden
payload via steganography...
npm:
🚨 Supply Chain Alert: axios 0.30.4
Rank: #42 of top npm packages
Verdict: MALICIOUS
npm: https://www.npmjs.com/package/axios/v/0.30.4
Analysis summary (truncated):
1. **Non-standard dependency** — The `dependencies` block includes `plain-crypto-js`. Published axios only depends on `follow-redirects`, `form-data`, and `proxy-from-env`. A fourth package whose name looks like a **`crypto-js`–style typosquat** is a classic sign of a tampered or fake package, not a normal axios release.
agent CLIに依存します。askモードで実行されますが、OSレベルのサンドボックスはありません。ログはstdoutとlogs/monitor_YYYYMMDD.logの両方に書き込まれます。新しいファイルは毎日作成されます。両方のエコシステムは同じファイルにログを記録し、npm行には[npm]プレフィックスが付きます。例:
2026-03-27 12:01:15 [INFO] Fetching top 15,000 packages from hugovk dataset...
2026-03-27 12:01:16 [INFO] Watchlist loaded: 15,000 packages (dataset updated 2026-03-01 07:34:08)
2026-03-27 12:01:16 [INFO] Fetching top 15,000 npm packages from download-counts dataset...
2026-03-27 12:01:18 [INFO] npm watchlist loaded: 15,000 packages (download-counts 1.0.52)
2026-03-27 12:01:19 [INFO] [pypi] Starting serial: 35,542,068 (from last_serial.yaml) — polling every 300s
2026-03-27 12:01:19 [INFO] [npm] Starting seq: 42,817,503 (from last_serial.yaml) — polling every 300s
2026-03-27 12:06:18 [INFO] [pypi] 2 new watchlist releases detected (serial 35,542,068 -> 35,542,190)
2026-03-27 12:06:18 [INFO] [pypi] Processing fast-array-utils 1.4 (rank #8,231)...
2026-03-27 12:06:18 [INFO] [pypi] Diffing fast-array-utils 1.3 -> 1.4
2026-03-27 12:06:50 [INFO] [pypi] Analyzing diff for fast-array-utils...
2026-03-27 12:07:35 [INFO] [pypi] Verdict for fast-array-utils 1.4: BENIGN
2026-03-27 12:06:20 [INFO] [npm] 1 new watchlist releases detected (seq -> 42,817,612)
2026-03-27 12:06:20 [INFO] [npm] Processing axios 0.30.4 (rank #42)...
2026-03-27 12:06:21 [INFO] [npm] Diffing axios 0.30.3 -> 0.30.4
2026-03-27 12:07:01 [INFO] [npm] Analyzing diff for axios...
2026-03-27 12:07:45 [INFO] [npm] Verdict for axios 0.30.4: MALICIOUS
このプロジェクトはMITライセンスの下でライセンスされています。サードパーティのデータソースと通知はNOTICE.txtにまとめられています。
貢献は歓迎します — CONTRIBUTING.mdをご覧ください。このリポジトリはContributor Covenantに従います。セキュリティ問題は公開issueではなく、SECURITY.mdを通じて報告してください。
質問や議論:Elastic community Slack。
| ファイル | 目的 |
|---|
monitor.py | メインオーケストレーター — PyPI + npmをポーリング、差分、分析、アラート(並列スレッド) |
pypi_monitor.py | スタンドアロンPyPI変更ログポーラー(探索用) |
package_diff.py | 任意のPyPIまたはnpmパッケージの2つのバージョンをダウンロードして差分比較 |
analyze_diff.py | 差分をCursor Agent CLIに送信し、判定を解析 |
top_pypi_packages.py | ダウンロード数で上位N個のPyPIパッケージを取得してリスト表示 |
slack.py | Slack APIクライアント(SendMessage, PostFile) |
etc/slack.json | Slackボットの認証情報 |
last_serial.yaml | 永続化されたポーリング状態(PyPIシリアル + npmシーケンス/エポック) |
logs/ | 日次ログファイル(monitor_YYYYMMDD.log) |
| ソース | 内容 | レート制限 |
|---|
| hugovk/top-pypi-packages | 30日間のダウンロード数による上位15,000のPyPIパッケージ(月次JSON) | なし(静的ファイル) |
PyPI XML-RPC changelog_since_serial() | リアルタイムPyPIイベントストリーム | 非推奨だが機能する;ポーリングあたり1回の呼び出しで問題ない |
| PyPI JSON API | パッケージメタデータ、バージョン履歴、ダウンロードURL | 寛大;控えめに使用(リリースあたり1回の呼び出し) |
| download-counts (nice-registry) | 全npmパッケージの月間ダウンロード数(counts.json) | なし(npm tarball) |
npm CouchDB replication _changes feed | リアルタイムのnpmレジストリ変更ストリーム | 公開;ページネーション付き読み取り |
| npm registry API | パッケージ文書、tarballダウンロード | 寛大;控えめに使用 |