
TanStack npm 공급망 공격 지표(CVE-2026-45321 / GHSA-g7cv-rxg3-hmpx)를 탐지하는 셸 스크립트
개발자 머신, 저장소, CI 실행기에서 TanStack npm 공급망 공격의 지표를 탐지합니다. Bash 스크립트, Docker 이미지, GitHub Action — 동일한 엔진, 세 가지 진입점.
CVE-2026-45321 / GHSA-g7cv-rxg3-hmpx
2026년 5월 11일, 공격자는 GitHub Actions 캐시 중독을 통해 42개의 @tanstack/* 패키지에 걸쳐 84개의 악성 버전을 게시했습니다. 페이로드는 GitHub 토큰, npm 토큰, AWS/GCP/Azure 자격 증명을 탈취하고, 탈취된 토큰이 해지되는 즉시 ~/를 초기화하는 죽은 자의 스위치(dead-man's switch) 데몬을 설치합니다.
자신의 환경에 맞는 진입점을 선택하세요.
TAG=v1.0.0
curl -fsSLO https://github.com/fabriziosalmi/tanstack-compromise-checker/releases/download/$TAG/check.sh
curl -fsSLO https://github.com/fabriziosalmi/tanstack-compromise-checker/releases/download/$TAG/check.sh.sha256
sha256sum -c check.sh.sha256
bash check.sh --online
docker run --rm -v "$PWD":/scan \
ghcr.io/fabriziosalmi/tanstack-compromise-checker:v1.0.0 \
/scan true fail tanstack-findings.json '' GHSA-g7cv-rxg3-hmpx
이미지는 다중 아키텍처(linux/amd64, linux/arm64)이며 GitHub OIDC를 통해 서명된 빌드 출처가 함께 제공됩니다. 실행 전에 확인하세요:
gh attestation verify \
oci://ghcr.io/fabriziosalmi/tanstack-compromise-checker:v1.0.0 \
--repo fabriziosalmi/tanstack-compromise-checker
permissions: {}
jobs:
check:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: fabriziosalmi/tanstack-compromise-checker@v1
with:
scan-dir: .
online: 'true'
fail-on: fail
완전한 강화 워크플로는
.github/workflows/tanstack-check.yml에 있으며,
이 저장소가 자체적으로 실행하는 것과 동일한 워크플로입니다.
| # | 검사 | 설명 |
|---|---|---|
| 1 | 죽은 자의 스위치 | ~/.local/bin/gh-token-monitor.sh, LaunchAgents + LaunchDaemons (macOS), systemd 사용자 + 시스템 유닛 (Linux), 실행 중인 프로세스 |
| 2 | 지속성 | shell rc 파일, 사용자/시스템 crontab, /etc/cron.*, XDG 자동 시작, 전역 git core.hooksPath |
| 3 | 자격 증명 | 민감한 환경 변수, ~/.npmrc, ~/.yarnrc.yml, ~/.aws/credentials, ~/.config/gh/hosts.yml, ~/.netrc, ~/.docker/config.json, ~/.kube/config, .env* 파일 열거 |
| 4 | 네트워크 IOC | 수립된 아웃바운드 TCP 연결이 있는 실행 중인 node 프로세스 (휴리스틱, 수동 검토 필요) |
| 5 | 저장소 스캔 | package.json에 선언된 종속성 + 잠금 파일에 고정된 버전 (package-lock.json, yarn.lock, pnpm-lock.yaml) |
| 6 | 설치됨 | 알려진 악성 버전 목록에 대해 node_modules/@tanstack/*/package.json을 확인한 다음, --online 시 npm 레지스트리 time[<version>]에 대해 확인 |
| 7 | Actions 힌트 | .github/workflows/*에서 pull_request_target, 누락된 --ignore-scripts, actions/cache 사용 여부 스캔 |
| 8 | 미니 샤이훌루드 IOC | node_modules 내 페이로드 파일 (router_init.js, tanstack_runner.js), @tanstack/setup optionalDependency, AI 도구 설정 변조, 소스 내 C2 도메인, 공격자 커밋 작성자 + 브랜치 패턴, 몸값 표시 npm 토큰, 2차 웜 전파 패키지, 그리고 알 수 없는 페이로드 형태에 대한 정보 수준 휴리스틱 |
--online을 사용하면 스크립트가 런타임에 GHSA 권고를 가져와서
탐지 범위가 하드코딩된 버전 목록 대신 권고의 실시간 상태를 추적합니다.
영향을 받는 패밀리: @tanstack/router, @tanstack/react-router,
@tanstack/history, @tanstack/start, @tanstack/eslint-plugin-router 및 관련 패키지.
영향을 받지 않는 패밀리: @tanstack/react-query,
@tanstack/react-table, @tanstack/react-virtual, @tanstack/form,
@tanstack/store.
# 빠른 확인 (현재 사용자의 홈 디렉토리)
bash check.sh
# 온라인 모드: GHSA 권고 및 npm 레지스트리 게시 시간 조회
bash check.sh --online
# 특정 디렉토리 스캔
bash check.sh --scan-dir /path/to/your/repos
# CI/SIEM 통합을 위한 JSON 출력
bash check.sh --json > findings.json
# 영향을 받은 저장소에 대한 고정 명령어 제안 (dry-run)
bash check.sh --scan-dir /path/to/repos --fix
# 확장된 알려진 악성 목록 제공
bash check.sh --bad-versions-file ./extra-bad-versions.txt
| 플래그 | 설명 |
|---|---|
--scan-dir DIR | 스캔할 루트 디렉토리 (기본값: $HOME). 시스템 디렉토리(, /etc, /usr`)는 스캔을 거부합니다. |
--online | GHSA 권고 및 npm 레지스트리의 time[<version>]을 조회하여 손상 여부를 확인합니다. 기본값은 해제 — 옵트인. |
--ghsa GHSA-ID | --online에서 사용할 권고 ID를 재정의합니다 (기본값 GHSA-g7cv-rxg3-hmpx). |
--attack-window-start ISO, --attack-window-end ISO | --online에서 사용되는 게시 시간 창을 재정의합니다. |
--fix | 영향을 받은 저장소에 대한 권장 고정 명령어를 출력합니다 (기본값 dry-run). |
--apply | --fix와 함께: 실행에 옵트인합니다. 현재로서는 여전히 맹목적인 update를 거부합니다; 수동 고정이 필요합니다. |
--yes, -y | --apply에 대한 확인 프롬프트를 건너뜁니다. |
--json | 발견 사항을 JSON으로 stdout에 출력합니다 (--quiet를 암시). |
--bad-versions-file F | 알려진 악성 목록에 항목(pkg@version, 한 줄에 하나)을 추가합니다. |
--no-color | ANSI 색상을 비활성화합니다. |
--quiet, -q | 검사별 출력을 숨기고 요약만 출력합니다. |
-h, --help | 도움말을 표시합니다. |
| 코드 | 의미 |
|---|---|
| 0 | 깨끗함 — 스캔 범위 내 지표 없음 |
| 1 | 경고만 — 검토 권장 |
| 2 | 실패 — 손상 지표 존재 |
| 3 | 도구/사용법 오류 |
이 저장소는 탐지하는 동일한 공격 클래스로부터 자신을 방어합니다. 구체적으로:
uses:는 플로팅 태그가 아닌 40자 커밋 SHA에 고정됩니다.permissions: {}를 선언합니다; 작업은 필요한 권한만 부여합니다.actions/checkout은 persist-credentials: false로 호출되어 인증된 git 리모트가 $GITHUB_WORKSPACE에 남지 않습니다.step-security/harden-runner는 모든 워크플로의 첫 번째 단계이며 러너의 송신 트래픽을 모니터링합니다.main 브랜치에 푸시할 때마다 실행되며, 결과는 SARIF로 업로드됩니다.gh attestation으로 오프라인에서 확인할 수 있습니다.check.sh.sha256을 제공합니다.전체 정책은 SECURITY.md에 있습니다.
주의: 이 도구를 포함한 모든 보안 도구에 대해 curl | bash를 사용하지 마십시오. 항상 다운로드하고, 체크섬을 확인하고, 검사한 후 실행하십시오.
━━ 1 / 8 죽은 자의 스위치 아티팩트
✔ 죽은 자의 스위치 아티팩트 없음
━━ 2 / 8 지속성 벡터
✔ 지속성 벡터 감지되지 않음
━━ 3 / 8 토큰 / 자격 증명 노출
✔ 자격 증명 노출 감지되지 않음
━━ 4 / 8 네트워크 지표
✔ 현재 설정된 노드 TCP 연결 없음 (또는 관찰 가능한 것 없음)
━━ 5 / 8 저장소 스캔 — package.json + 잠금 파일
ℹ 12개의 package.json + 8개의 잠금 파일 발견
✔ 모든 package.json에 손상된 패밀리 선언 없음
✔ 모든 잠금 파일에서 손상된 고정 버전 없음
━━ 6 / 8 설치된 node_modules — 직접 버전 확인
✔ 설치된 node_modules에 손상된 패키지 없음
━━ 7 / 8 GitHub Actions 강화 힌트
✔ 위험한 GitHub Actions 패턴이 감지되지 않음 (스캔 범위 내)
━━ 8 / 8 미니 샤이훌루드 페이로드 + 보조 IOC
✔ 미니 샤이훌루드 페이로드 / 보조 IOC 감지되지 않음
━━ 요약
통과: 8 경고: 0 실패: 0
범위 : /Users/alice/dev
✔ 범위 내 손상 지표 없음: /Users/alice/dev
참고: /Users/alice/dev에 대한 깨끗한 결과는 시스템의 나머지 부분을 인증하지 않습니다.
데몬을 중지하기 전에 GitHub 토큰을 해지하지 마십시오 — 죽은 자의 스위치는 토큰이 해지되는 순간
rm -rf ~/를 실행합니다.
launchctl bootout gui/$UID ~/Library/LaunchAgents/com.user.gh-token-monitor.plistlaunchctl unload ~/Library/LaunchAgents/com.user.gh-token-monitor.plistsystemctl --user stop gh-token-monitor && systemctl --user disable gh-token-monitorrm -f ~/.local/bin/gh-token-monitor.sh~/.aws/credentials, ~/.kube/config, ~/.config/gh/hosts.yml, ~/.npmrc 검사.Bash 스크립트에 필요한 것:
bash 4+jq (권장) — package-lock.json 파싱python3 (선택적 대체) — jq가 없을 때 사용curl — --online에 필수lsof 또는 ss (선택적) — 네트워크 IOC 휴리스틱Docker 이미지는 위의 모든 것을 번들로 제공합니다. 이러한 도구를 설치할 수 없거나 설치하고 싶지 않은 시스템에서 사용하십시오.
이슈 및 풀 리퀘스트는 환영합니다. 이 저장소는 CODEOWNERS를 사용합니다: 모든 PR은 관리자 검토가 필요합니다.