
커뮤니티를 위한 공용 패키지 스캐너
간단하면서도 강력한, Docker 우선 npm 공급망 스캐너입니다. 하나의 compose 파일로 다음을 실행합니다:
컨테이너 전용 에디션입니다. 이 프로젝트는 EC2, SQS, RDS를 사용하여 확장 가능하게 구축할 수 있으며, 대부분의 설정이 이미 도구 세트에 포함되어 있습니다.
scan.yml을 통해 설정 가능한 규칙 (허용 목록, 임계값, YARA)scan_runs) 바로 사용 가능~/.aws 사용)docker-compose.yml – 서비스: db, enumerator, fetcher, analyzer, dashboard, init-dbenumerator/ – NDJSON 큐를 구축하는 Node 워커fetcher/ – tarball을 다운로드하는 Node 워커 (+ S3 업로드 가능)analyzer/ – Python 정적 분석기 (+ 선택적 YARA 인라인)dashboard/ – Streamlit 앱 (포트 8501)infra/migrations.sql – 핵심 DB 스키마 (packages, versions, findings, scores, indexes)infra/20251106_scan_runs.sql – 스캔 기록 테이블scan.yml – 분석 설정 (규칙, 점수, 허용 목록, YARA)scripts/run_pipeline.sh – enumerate → fetch → analyze 실행scripts/init_db.sh – DB 스키마 초기화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에서 미리 빌드된 컨테이너를 가져와 실행:
# 설정 파일 및 스크립트를 위해 리포지토리 클론
git clone https://github.com/MHaggis/Package-Inferno.git
cd Package-Inferno
# 미리 빌드된 이미지로 실행
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
# http://localhost:8501 열기
결과는 ./out/findings/*.findings.json 및 DB가 활성화된 경우 findings 테이블에 저장됩니다.
PackageInferno는 목표에 따라 여러 스캔 전략을 지원합니다:
| 모드 | 사용 사례 | 속도 | 범위 | 명령 |
|---|---|---|---|---|
| 특정 시드 | 알려진 패키지 테스트/조사 | 가장 빠름 | 대상 지정 | SEEDS="pkg1,pkg2" |
| 소규모 배치 | 설정 검증, 샘플 스캔 | 빠름 | 10-100개 패키지 | MAX_CHUNKS=2 CHUNK_LIMIT=10 |
| 전체 레지스트리 | 포괄적 공급망 감사 | 수시간~수일 | 2M+ 패키지 | MAX_CHUNKS=0 CHUNK_LIMIT=100 |
| 변경 피드 | 새 릴리스 모니터링 (기본 포함) | 실시간 | 최근 업데이트 | 내장 |
분석하려는 특정 패키지를 대상으로 설정:
# 시드로 한 번에 실행
export SEEDS="lodash,express,axios"
./scripts/run_pipeline.sh
# 또는 파일에서 읽기
echo -e "react\nvue\nangular" > packages.txt
export SEEDS_FILE=packages.txt
./scripts/run_pipeline.sh
초기 테스트 방법: SEEDS="is-odd,is-even" 사용하여 빠르게 검증.
_all_docs)npm 레지스트리에서 페이지네이션으로 패키지 스캔:
# 이전 실행 결과 정리
rm -rf downloads/* out/*
# 페이지 2개, 페이지당 10개 패키지 (총 20개)
export MAX_CHUNKS=2 # 페이지 수
export CHUNK_LIMIT=10 # 페이지당 패키지 수
unset SEEDS # 중요: 시드 모드 비활성화
# 각 단계를 개별적으로 실행하여 가시성 확보
docker compose run --rm enumerator # 패키지 발견 및 큐잉
docker compose run --rm fetcher # tarball 다운로드
docker compose run --rm analyzer # 위협 스캔
출력 예시:
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 = 무제한
export CHUNK_LIMIT=100 # 효율성을 위해 큰 배치
./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 # 자동 재개
새로 스캔하려면:
rm -f out/enumerator_state.json
./scripts/run_pipeline.sh
22개 패키지의 2페이지 스캔 결과, PackageInferno가 탐지한 내용:
-- 점수별 상위 의심 패키지
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;
-- 결과:
name | score | label | findings
-----------------------+-------+------------+----------
rendition | 606 | malicious | 153
vs-deploy | 454 | malicious | 119
--123hoodmane-pyodide | 213 | malicious | 46
rendition이 왜 의심스러운가?
url_outside_allowlist - 허용되지 않은 도메인suspicious_pattern - 셸/eval 패턴advanced_obfuscation - 16진수 인코딩, XOR, 문자열 배열big_base64_blob - 대형 Base64 페이로드url_in_code - 내장 URLscan.yml에서 설정된 점수 시스템은 이러한 결과를 집계하여 위험 점수와 레이블(clean, suspicious, malicious)을 생성합니다.
docker compose up -d dashboard 실행 후 http://localhost:8501 열기
기능:
직접 SQL로 맞춤 분석:
# 데이터베이스 연결
docker exec -it pi-postgres psql -U piuser -d packageinferno
유용한 쿼리:
-- 자격 증명 도용 시도가 있는 패키지
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;
-- 발견된 모든 C2/웹훅 대상
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';
-- 타입스쿼팅 시도
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;
-- 네이티브 바이너리가 있는 패키지
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';
결과는 ./out/findings/ 아래에 구조화된 JSON으로도 저장됩니다:
# 특정 패키지 결과 보기
cat out/findings/[email protected] | jq .
# 심각도별 결과 개수
jq -r '.findings[].severity' out/findings/*.findings.json | sort | uniq -c
# 발견된 모든 C2 URL 추출
jq -r '.findings[] | select(.rule=="c2_webhook") | .details.full_urls[]' out/findings/*.findings.json