Postmortem: TanStack NPM supply-chain compromise
TL;DR Highlight
2026년 5월 11일 TanStack의 42개 npm 패키지가 GitHub Actions cache poisoning과 OIDC 토큰 탈취를 조합한 공격으로 악성 버전이 배포됐으며, 공격 벡터와 대응 과정을 상세히 분석한 글이다.
Who Should Read
GitHub Actions로 CI/CD 파이프라인을 운영하거나 npm 패키지를 배포하는 개발자 및 DevOps 엔지니어. 특히 오픈소스 프로젝트에서 외부 PR을 받는 메인테이너라면 반드시 읽어야 한다.
Core Mechanics
- 공격은 2026-05-11 19:20~19:26 UTC, 단 6분 만에 42개 @tanstack/* 패키지의 84개 악성 버전을 npm에 배포하는 데 성공했다. 패키지당 2개 버전이 약 6분 간격으로 올라왔다.
- 공격의 핵심은 세 가지 기법의 조합이다: pull_request_target의 'Pwn Request' 패턴(포크 PR에서 base 저장소 권한으로 워크플로우 실행), GitHub Actions cache poisoning(포크↔base 신뢰 경계를 넘는 캐시 오염), 그리고 OIDC 토큰의 런타임 메모리 탈취.
- 공격자는 먼저 2026-05-10에 TanStack/router 포크를 만들어 'zblgg/configuration'으로 이름을 바꿔 포크 목록 검색을 회피했다. 악성 커밋에는 '[skip ci]' 메시지를 붙여 포크 측 CI 실행을 막았다.
- bundle-size.yml과 labeler.yml이 pull_request_target 트리거를 사용했기 때문에, 첫 기여자 승인 없이 자동 실행됐다. 이 워크플로우가 악성 커밋(vite_setup.mjs, ~30,000줄 번들 페이로드)을 실행하면서 캐시가 오염됐다.
- 오염된 캐시 항목(1.1GB)은 refs/heads/main 범위로 저장됐고, release.yml이 main에 push될 때 그 캐시를 그대로 복원해 사용한다. 공격자는 다음 릴리즈 때 실행될 캐시 키를 미리 맞춰 심어뒀다.
- 배포된 악성 패키지를 npm install/pnpm install/yarn install로 설치하면, optionalDependencies의 prepare 라이프사이클 스크립트로 ~2.3MB의 난독화된 router_init.js가 실행된다.
- 악성 스크립트는 AWS IMDS/Secrets Manager, GCP 메타데이터, Kubernetes 서비스 어카운트 토큰, Vault 토큰, ~/.npmrc, GitHub 토큰, SSH 개인 키 등을 수집하고, Session/Oxen 메신저 파일 업로드 네트워크(filev2.getsession.org 등)로 유출한다. 종단간 암호화라 IP/도메인 차단 외 네트워크 레벨 방어가 어렵다.
- 악성 스크립트는 자기 전파 기능도 있다. 피해자가 npm에서 유지 관리하는 다른 패키지를 registry.npmjs.org 검색 API로 조회한 뒤, 같은 방식으로 재감염시켜 퍼뜨린다. @mistralai/mistralai 패키지도 이 웜으로 인해 감염됐다가 npm에서 내려졌다.
Evidence
- 토큰 폐기 시 주의사항으로, 악성 페이로드가 ~/.local/bin/gh-token-monitor.sh를 systemd user service(Linux) 또는 LaunchAgent(macOS)로 설치해 60초마다 GitHub API로 토큰 유효성을 polling하며, 토큰이 폐기되면(HTTP 40x 응답) rm -rf ~/ 명령을 실행하는 데드맨 스위치가 심어진다는 경고가 공유됐다.
- Trusted Publishing(CI에서 장기 토큰 없이 OIDC로 npm 배포하는 방식)이 보안의 전부가 아니라는 의견이 제기됐다. 로컬 배포 시 2FA가 있던 것과 달리, CI 파이프라인 배포는 두 번째 인증 요소가 없어서 CI가 뚫리면 바로 배포가 가능하다는 구조적 문제다. GitHub 신뢰 모델 외부에 2FA를 추가한 staged publishing이 필요하다는 의견이 나왔다.
- postinstall/prepare 같은 라이프사이클 스크립트가 근본적인 위험이라는 지적이 많았다. bun은 기본적으로 라이프사이클 스크립트를 비활성화하기 때문에 이 공격에 면역이라는 점도 언급됐다. '2026년에도 의존성에서 임의 코드를 기본 실행하는 건 명백한 실수'라는 강한 비판이 있었다.
- GitHub Actions 캐시가 포크 PR과 base 저장소 사이에 공유된다는 점이 두 번째로 대규모 공급망 공격의 원인이 됐다는 지적이 있었다. 릴리즈 파이프라인은 캐시 없이 항상 새로 빌드해야 한다는 교훈이 공유됐고, 'global mutable cache를 브랜치 간에 공유하면서 호출자가 키를 지정하는 구조 자체가 업계 전체의 실패'라는 비판도 있었다.
- npm의 '의존자가 있으면 unpublish 불가' 정책 때문에 대부분의 패키지를 즉시 내릴 수 없어 npm 보안팀에 서버 측 tarball 삭제를 요청해야 했고, 이 과정에서 수 시간 동안 악성 tarball이 설치 가능한 상태로 남아 있었다. 이에 대해 '그냥 deprecated 처리라도 해라'는 반응이 나왔다.
How to Apply
- 오픈소스 프로젝트에서 외부 PR을 받는 경우, pull_request_target 트리거를 사용하는 워크플로우가 있는지 즉시 점검하라. pull_request_target은 포크 PR에서도 base 저장소 시크릿과 캐시에 접근할 수 있으므로, 신뢰할 수 없는 코드를 checkout해서 실행하는 구조가 되면 이번 공격과 동일한 취약점이 생긴다.
- 릴리즈/배포 워크플로우(release.yml 등)는 PR 워크플로우와 캐시를 공유하지 않도록 별도의 캐시 키를 사용하거나, 배포 시 캐시를 사용하지 않고 항상 새로 빌드하도록 설정하라. 이번 공격은 PR 워크플로우가 오염시킨 캐시를 배포 워크플로우가 그대로 복원하는 구조를 노렸다.
- 2026-05-11에 @tanstack/router, @tanstack/router-core 등 영향받은 패키지를 설치한 CI 환경이나 개발 머신이 있다면, 해당 호스트에서 접근 가능한 AWS, GCP, Kubernetes, Vault, GitHub, npm, SSH 크리덴셜을 전부 교체(rotate)해야 한다. 단, 토큰 폐기 전에 데드맨 스위치 스크립트(~/.local/bin/gh-token-monitor.sh, systemd 서비스 또는 LaunchAgent)가 설치됐는지 먼저 확인하라.
- npm 패키지를 사용하는 프로젝트에서 보안을 강화하려면, bun의 기본 설정처럼 라이프사이클 스크립트(postinstall, prepare 등)를 기본 비활성화하거나 허용 목록(allowlist)으로 관리하는 방식을 도입하는 것을 고려하라. pnpm은 --ignore-scripts 옵션을 지원하며, 이를 CI 환경에 적용하면 이런 류의 공급망 공격의 실행 경로를 차단할 수 있다.
Terminology
Related Papers
What happened after 2k people tried to hack my AI assistant
실제로 6,000개 이상의 이메일로 AI 에이전트에 prompt injection 공격을 시도한 공개 실험 결과로, Claude Opus 4.6이 비밀 파일 유출을 한 번도 허용하지 않았지만 실험 설계의 현실성에 대한 논란이 뜨거웠다.
When Does Combining Language Models Help? A Co-Failure Ceiling on Routing, Voting, and Mixture-of-Agents Across 67 Frontier Models
여러 LLM을 조합해도 '모든 모델이 동시에 틀리는 비율(β)'이 성능 상한선이며, 업계가 쓰는 pairwise 상관계수(ρ)는 이 상한선을 예측하지 못한다.
Beyond Function Calling: Benchmarking Tool-Using Agents under Tool-Environment Unreliability
실제 환경처럼 API가 망가지거나 결과가 이상할 때 LLM 에이전트가 얼마나 잘 버티는지 측정하는 벤치마크 ToolBench-X 공개.
Nearly Half of LG Smart TV Apps Contain Residential Proxy SDKs
6,038개의 LG·Samsung 스마트 TV 앱을 스캔했더니 2,058개에서 사용자의 IP를 몰래 팔아 트래픽을 중계하는 Residential Proxy SDK가 발견됐다. TV는 컴퓨터처럼 감시받지 않아서 프록시 호스트로 거의 이상적인 환경이다.
Prompt Injection as Role Confusion
LLM이 시스템 프롬프트, 사용자 입력, 툴 출력을 구분하지 못하는 구조적 결함이 prompt injection의 근본 원인이라는 ICML 2026 논문으로, 현재 LLM 보안 아키텍처의 한계를 명확히 분석한다.
GPT-5.5 hallucinates 3x more than MIT-licensed GLM-5.2
모델 크기가 커질수록 성능이 좋아진다는 통념에 반해, 오픈소스 753B 모델 GLM-5.2가 추정 1~2T 규모의 GPT-5.5보다 환각 비율이 3배 낮다는 벤치마크 결과가 나왔다. 단순히 파라미터 수와 벤치마크 점수만으로 모델을 선택하면 실제 업무에서 낭패를 볼 수 있다는 경고다.