Devlery
Blog/AI

Mistral SDK 감염, 신뢰된 CI가 악성 릴리스를 만들었다

Mini Shai-Hulud 공격은 Mistral AI SDK와 TanStack 패키지를 감염시키며 OIDC trusted publishing 시대의 새 공급망 위험을 드러냈습니다.

Mistral SDK 감염, 신뢰된 CI가 악성 릴리스를 만들었다
AI 요약
  • 무슨 일: Mini Shai-Hulud 공급망 공격이 Mistral AI SDK, TanStack, UiPath, Guardrails AI 등 개발자 패키지 생태계로 확산됐습니다.
    • Mistral은 mistralai==2.4.6과 여러 npm SDK 버전을 affected versions로 공지했고, PyPI 패키지는 Linux import 시 악성 payload를 실행했습니다.
  • 핵심 변화: 이번 사건은 장기 npm 토큰 탈취보다, GitHub Actions cache poisoning과 OIDC trusted publishing 경계가 결합된 CI 신뢰 실패에 가깝습니다.
  • 숫자: TanStack은 2026년 5월 11일 19:20~19:26 UTC 사이 42개 패키지에 84개 악성 버전이 게시됐다고 밝혔습니다.
  • 주의점: affected version을 설치한 개발자 머신과 CI runner는 package rollback만으로 충분하지 않고, 접근 가능한 secret rotation과 audit log 확인이 필요합니다.

2026년 5월 12일, Mistral이 조용하지만 꽤 무거운 보안 권고를 올렸습니다. 제목은 TanStack supply chain attack affecting Mistral AI SDK packages입니다. Mistral의 설명에 따르면 TanStack 보안 사고와 연결된 공급망 공격 때문에 Mistral의 npm 및 PyPI SDK 패키지 일부가 compromised versions로 게시됐습니다. Mistral은 자체 인프라가 침해됐다는 징후는 없고, affected developer device가 관여한 것으로 보인다고 밝혔습니다.

겉으로 보면 또 하나의 npm/PyPI 악성 패키지 사고처럼 보입니다. 하지만 이번 사건은 AI 개발자에게 더 큰 신호를 줍니다. Mistral AI SDK는 LLM 애플리케이션과 에이전트 런타임이 모델 API에 붙을 때 쓰는 기본 의존성입니다. TanStack은 프론트엔드와 풀스택 React 생태계의 핵심 도구입니다. UiPath와 Guardrails AI까지 함께 언급되면서, 공격 표면은 "웹 개발 패키지"나 "AI 패키지" 어느 한쪽에 머물지 않았습니다. AI 제품을 만드는 팀이 실제로 매일 설치하고, CI에서 실행하고, 코딩 에이전트가 자동으로 추가할 수 있는 의존성 체인이 공격 대상이 됐습니다.

더 중요한 점은 공격 방식입니다. TanStack의 공식 포스트모템은 이번 사건을 단순한 maintainer phishing이나 npm token 탈취로 설명하지 않습니다. 공격자는 pull_request_target 워크플로우, GitHub Actions cache poisoning, runner process memory에서의 OIDC token extraction을 결합했습니다. TanStack은 npm token이 도난당한 증거가 없고, npm publish workflow 자체가 직접 침해된 것도 아니라고 설명합니다. 그런데도 정상 CI 경로처럼 보이는 악성 릴리스가 만들어졌습니다.

이 차이가 핵심입니다. 지난 몇 년 동안 공급망 보안의 모범답안은 비교적 명확했습니다. 장기 토큰을 없애고, 2FA를 켜고, provenance를 남기고, OIDC trusted publishing으로 전환하라는 것이었습니다. TanStack도 그 방향을 따르고 있었습니다. 그런데 이번 공격은 그 방어 장치들을 우회한 것이 아니라, 그 장치들이 신뢰하는 workflow shape를 타고 들어왔습니다. "누가 npm token을 훔쳤는가"가 아니라 "왜 정상 release workflow가 공격자의 cache를 믿었는가"가 질문이 된 것입니다.

Mini Shai-Hulud 공격에서 GitHub Actions cache poisoning과 OIDC trusted publishing 경계가 연결되는 흐름

Mistral 권고가 말하는 실제 영향

Mistral의 공식 권고는 사고를 MAI-2026-002로 추적하고, 상태를 "Under investigation"으로 둡니다. 권고 게시일은 2026년 5월 12일입니다. 영향을 받은 npm 패키지는 세 갈래입니다. @mistralai/mistralai2.2.2, 2.2.3, 2.2.4, @mistralai/mistralai-azure1.7.1, 1.7.2, 1.7.3, @mistralai/mistralai-gcp1.7.1, 1.7.2, 1.7.3입니다. PyPI 쪽에서는 mistralai==2.4.6이 영향을 받았습니다.

Mistral은 compromised npm packages가 registry에서 제거됐고, May 11 22:45 UTC부터 May 12 01:53 UTC 사이에만 이용 가능했다고 설명합니다. PyPI release mistralai==2.4.6은 May 12 00:05 UTC around 업로드됐고, 현재 PyPI에서 quarantine 상태입니다. 짧은 노출 시간처럼 보일 수 있지만, 공급망 공격에서는 시간이 짧다고 영향이 작다고 단정할 수 없습니다. CI runner는 새 버전을 즉시 가져갈 수 있고, package cache, lockfile, container image, private mirror에 남은 사본은 registry에서 지워진 뒤에도 오래 살아남을 수 있습니다.

Mistral의 PyPI 영향 설명은 더 직접적입니다. compromised PyPI package는 import 시 악성 스크립트를 실행합니다. Linux에서 src/mistralai/client/__init__.py에 주입된 코드가 https://83.142.209.194/transformers.pyz/tmp/transformers.pyz로 내려받고, detached background process로 실행합니다. 이름이 transformers.pyz인 것도 의미심장합니다. 머신러닝 환경에서는 Hugging Face Transformers와 비슷하게 보이는 이름이 경계심을 낮출 수 있습니다.

Mistral은 확인 명령도 제시했습니다. Python 쪽에서는 pip show mistralai | grep -i ^version으로 설치 버전을 확인하고, requirements*.txt, pyproject.toml, uv.lock, poetry.lock, Pipfile, Pipfile.lock에서 mistralai2.4.6을 찾아야 합니다. Linux host에서는 /tmp/transformers.pyz, python /tmp/transformers.pyz 프로세스, MISTRAL_INIT=1 환경변수, 83.142.209.194로의 outbound connection을 IOC로 봅니다.

이 부분은 AI 팀에게 실무적으로 중요합니다. 많은 팀이 LLM SDK를 "애플리케이션 dependency"로만 생각합니다. 하지만 에이전트 런타임, evaluation harness, batch inference job, RAG ingestion pipeline, notebook, CI test job, demo server까지 같은 SDK를 가져다 쓸 수 있습니다. 어떤 환경에서 affected version을 설치했는지 모르면, "프로덕션 앱은 괜찮다"는 말만으로는 부족합니다. 개발자 노트북이나 CI runner가 cloud key, GitHub token, private package token, vector database credential을 가지고 있었다면 그쪽이 더 큰 문제일 수 있습니다.

TanStack에서 실제로 벌어진 일

이번 사건의 기술적 뼈대는 TanStack 포스트모템이 가장 잘 보여줍니다. TanStack에 따르면 2026년 5월 11일 19:20~19:26 UTC 사이 공격자는 42개 @tanstack/* npm 패키지에 84개 악성 버전을 게시했습니다. 두 개 버전씩 약 6분 간격으로 올라갔습니다. TanStack은 @tanstack/query*, table*, form*, virtual*, store, start meta-package 등은 clean family로 확인했다고 덧붙였습니다.

공격 체인은 며칠 전부터 준비됐습니다. 공격자는 TanStack/router의 fork를 만들고, 이름을 configuration으로 바꿔 fork 탐색에서 덜 눈에 띄게 했습니다. 이후 악성 commit을 만들고, WIP: simplify history build라는 PR을 열었습니다. 중요한 지점은 pull_request_target입니다. 이 이벤트는 fork가 아니라 base repository context에서 실행됩니다. TanStack의 특정 workflow는 fork PR의 merge ref를 checkout하고 build를 실행했습니다. 그 과정에서 공격자 코드가 실행됐고, GitHub Actions cache에 오염된 pnpm store가 저장됐습니다.

공격자는 PR을 다시 깨끗한 상태로 force-push하고 닫았습니다. 겉으로 보이는 PR은 0-file no-op처럼 사라졌지만, cache poison은 남았습니다. 이후 전혀 다른 정상 PR이 main에 merge되자 release workflow가 실행됐고, release workflow는 오염된 cache를 복원했습니다. 그 결과 malware가 release workflow 안에서 실행됐고, workflow의 id-token: write 권한을 통해 OIDC token을 mint한 뒤 npm registry에 직접 POST했습니다. TanStack은 이 publish가 release workflow의 정의된 Publish Packages step에서 나온 것이 아니라, tests failed 상태의 run 중 malware가 별도로 수행한 publish라고 설명합니다.

이 공격이 불편한 이유는 "좋은 보안 기능"과 "나쁜 workflow shape"가 동시에 존재했기 때문입니다. TanStack은 장기 npm publish token이 없었습니다. npm publish는 GitHub OIDC trusted-publisher integration으로 묶여 있었습니다. publish credential은 release time에 만들어지고 거의 즉시 만료됩니다. 일반적인 npm token theft 공격에 강한 구조입니다. 하지만 release workflow가 오염된 cache를 읽고, 그 workflow 안에서 실행된 malware가 짧은 publish token을 바로 훔쳐 쓰면 이야기가 달라집니다.

TanStack의 follow-up 글은 이 지점을 솔직히 인정합니다. npm provenance, SLSA, OIDC, 2FA가 모두 의도대로 작동했지만, workflow shape 자체가 hole이었다는 것입니다. pull_request_target에서 fork code를 checkout하고 실행하는 패턴은 GitHub Security Lab이 오래전부터 "Pwn Request"로 경고한 known-bad pattern입니다. 이번 사건은 그 오래된 경고가 최신 trusted publishing 환경에서도 여전히 유효하다는 것을 보여줍니다.

Mini Shai-Hulud가 AI 생태계 사건인 이유

이 사건을 TanStack과 Mistral의 개별 보안 사고로만 읽으면 절반만 보는 것입니다. Mistral AI SDK는 AI 제품이 모델 provider에 붙는 접점입니다. Guardrails AI는 LLM 출력 검증과 정책 enforcement 계층입니다. UiPath는 enterprise automation과 agentic orchestration 흐름에 연결됩니다. 즉 이번 wave는 "AI 모델을 직접 공격했다"가 아니라, AI 소프트웨어가 의존하는 developer supply chain을 공격했습니다.

AI 개발 환경은 공급망 공격에 특히 민감합니다. 첫째, secret density가 높습니다. 일반 웹 앱 개발 환경에도 GitHub token과 cloud credential이 있지만, AI 개발 환경에는 모델 API key, vector DB token, observability key, dataset storage credential, private model endpoint credential이 추가됩니다. 둘째, 설치와 실행이 잦습니다. 에이전트 프레임워크, evaluation tool, SDK, model connector가 빠르게 바뀌고, notebook과 CI가 새 dependency를 즉시 시험합니다. 셋째, 코딩 에이전트가 package install을 대신 수행하는 일이 늘고 있습니다. 사람이 모든 install script와 diff를 읽지 않는 환경에서, 에이전트의 자동화는 생산성과 공격 표면을 동시에 키웁니다.

Mistral PyPI payload가 import-time execution이었다는 점도 AI 환경에서는 더 위험합니다. 많은 Python job은 module import만으로 SDK 초기화를 확인합니다. "실제 API 호출은 안 했으니 괜찮다"가 통하지 않을 수 있습니다. 테스트가 from mistralai import Mistral만 수행해도 import path가 실행됐을 수 있기 때문입니다. CI에서 unit test가 돌며 affected version을 import했다면, 그 runner가 볼 수 있던 environment variables와 metadata service credential을 의심해야 합니다.

TanStack 쪽 payload는 install lifecycle을 타고 실행됐습니다. TanStack 포스트모템은 npm, pnpm, yarn install이 affected version을 설치하면 malicious optionalDependencies entry가 orphan payload commit을 fetch하고 prepare lifecycle script를 실행한다고 설명합니다. 이후 약 2.3MB obfuscated router_init.js가 credential harvesting, exfiltration, self-propagation을 수행합니다. 이 구조는 개발자 워크스테이션과 CI runner를 모두 노립니다. AI 팀이 프론트엔드 dashboard, admin UI, agent control plane을 React로 만든다면 TanStack 의존성은 충분히 현실적인 경로입니다.

"정상 출처"의 의미가 흔들립니다

공급망 보안에서 provenance는 매우 중요합니다. 하지만 이번 사건은 provenance가 무엇을 보장하고 무엇을 보장하지 않는지 분명히 보여줍니다. provenance는 "이 artifact가 어떤 workflow에서 나왔는가"를 설명할 수 있습니다. 하지만 그 workflow가 실행 중 오염된 cache를 복원했고, 그 안에서 malware가 token을 mint했다면, provenance는 오히려 공격자에게 더 그럴듯한 포장을 제공할 수 있습니다.

이것은 provenance가 쓸모없다는 뜻이 아닙니다. TanStack도 OIDC 기반 publish 덕분에 어떤 workflow run에서 publish가 발생했는지 빠르게 추적할 수 있었다고 설명합니다. 장기 token이 없었기 때문에 공격자가 나중에 같은 credential을 재사용하는 것도 막았습니다. 즉 trusted publishing은 피해를 줄이고 조사 속도를 높였습니다. 다만 그것만으로 release workflow 안에서 벌어지는 모든 실행을 신뢰할 수는 없습니다.

보안팀 입장에서는 신뢰 경계를 다시 그려야 합니다. fork PR이 base repo context에서 실행될 때 어떤 권한을 갖는가. cache는 어떤 branch scope로 저장되고 복원되는가. release workflow가 build/test cache를 그대로 믿어도 되는가. install script는 CI에서 언제 실행되는가. OIDC token을 mint할 수 있는 job은 어떤 step 이후에만 접근 가능한가. 이런 질문들이 이제 package maintainer만의 문제가 아니라 AI 제품팀의 dependency 운영 문제가 됩니다.

TanStack은 즉시 여러 조치를 취했습니다. release pipeline에서 pnpm cache를 disable했고, affected workflows의 GitHub Actions cache를 제거했습니다. org의 모든 action을 commit SHA에 pinning했고, npm과 GitHub에서 non-SMS 2FA를 강제했습니다. pull_request_target 사용을 제거했고, 필요하면 sandboxed pull_request job의 artifact를 workflow_run으로 처리하는 GitHub 권장 패턴을 쓰겠다고 밝혔습니다. 또한 pnpm 11로 올려 ecosystem install-cooldown behavior를 적용했습니다. 이 조치들은 근본 원인을 모두 해결한다기보다 비슷한 공격의 blast radius를 줄이는 방향입니다.

개발팀은 무엇을 확인해야 하나

이번 사건에 직접 노출됐을 가능성이 있는 팀은 우선 affected version을 찾아야 합니다. Mistral을 쓴다면 Python lockfile과 package cache에서 mistralai==2.4.6을 확인하고, npm lockfile에서 @mistralai/mistralai, @mistralai/mistralai-azure, @mistralai/mistralai-gcp의 affected versions를 찾아야 합니다. TanStack을 쓴다면 공식 GitHub Security Advisory와 TanStack tracking issue의 affected version list를 기준으로 lockfile, build cache, container image, private registry mirror를 확인해야 합니다.

그 다음은 단순 삭제가 아닙니다. Mistral 권고도 명확히 말합니다. affected package version 사용을 즉시 중단하고, 해당 package가 설치된 시스템을 clean하며, 그 시스템에서 접근 가능했던 모든 secret을 rotate하고, cloud audit log를 확인해야 합니다. C2 indicator로는 api[.]masscan[.]cloud, filev2[.]getsession[.]org, git-tanstack[.]com, seed1[.]getsession[.]org, 83[.]142[.]209[.]194 등이 제시됐습니다. TanStack도 affected version을 설치한 host는 potentially compromised로 보라고 권고합니다.

AI 개발 조직이라면 여기에 몇 가지를 더해야 합니다. 모델 API key를 일반 backend secret과 같은 중요도로 다뤄야 합니다. LLM SDK가 compromised 된 환경에서는 OpenAI, Anthropic, Google, Mistral, Cohere, vector DB, tracing vendor key가 모두 노출될 수 있습니다. 코딩 에이전트나 evaluation runner가 package install을 수행하는 경우, 그 runner의 secret scope를 최소화해야 합니다. "테스트를 돌려야 하니 production과 같은 env를 준다"는 관행은 공급망 공격에서 가장 큰 blast radius를 만듭니다.

또한 package age policy가 현실적인 방어가 될 수 있습니다. TanStack follow-up은 pnpm 11의 install-cooldown behavior를 언급합니다. 새로 올라온 버전을 일정 시간 설치하지 않는 정책은 zero-day malicious package를 완벽히 막지는 못하지만, 이번처럼 외부 연구자가 20분 안에 탐지한 사건에서는 시간을 벌어줍니다. CI에서 dependency update bot이 자동 merge하고 바로 deploy하는 흐름이라면, critical package에는 최소 대기시간과 별도 검증을 두는 편이 합리적입니다.

코딩 에이전트 시대의 supply chain baseline

이번 사건의 장기적 의미는 코딩 에이전트 운영과 연결됩니다. AI 에이전트는 사람보다 빠르게 package를 설치하고, 에러를 해결하기 위해 대체 dependency를 찾고, 테스트 실패를 고치려고 lockfile을 바꿉니다. 이 능력은 유용하지만, supply chain policy가 약하면 에이전트가 공격자의 배포 속도에 맞춰 악성 버전을 더 빨리 끌어올 수도 있습니다.

따라서 에이전트에게 필요한 것은 더 똑똑한 모델만이 아닙니다. package firewall, install script 제한, lockfile diff policy, allowed registry, secret scoping, network egress control, cache isolation, provenance verification, workflow static analysis가 같이 필요합니다. 특히 GitHub Actions workflow는 애플리케이션 코드만큼 리뷰돼야 합니다. pull_request_target, unpinned actions, shared cache, broad id-token: write, release job의 install lifecycle은 이제 high-risk surface입니다.

Mistral과 TanStack 사건은 역설적으로 공급망 보안이 앞으로 어디로 가야 하는지 꽤 선명하게 보여줍니다. 장기 token을 없애는 것은 맞습니다. provenance를 남기는 것도 맞습니다. 2FA를 강제하는 것도 맞습니다. 하지만 그것들은 출발점입니다. 신뢰된 CI가 무엇을 실행하는지, 어떤 cache를 읽는지, 언제 publish 권한을 얻는지, 실패한 workflow에서도 token을 mint할 수 있는지까지 봐야 합니다.

Mini Shai-Hulud의 이번 wave는 "AI SDK가 감염됐다"는 사건으로 끝나지 않습니다. AI 개발 생태계가 프레임워크, SDK, CI, 에이전트, 클라우드 credential을 하나의 자동화된 작업 표면으로 묶고 있다는 증거입니다. 그 표면에서 한 dependency install은 단순한 설치가 아닙니다. 때로는 조직의 모델 키, GitHub 권한, 클라우드 권한, 배포 파이프라인 전체를 여는 실행입니다.

그래서 이번 사건의 교훈은 냉정합니다. AI 개발 속도가 빨라질수록 dependency trust는 더 느리고 더 보수적으로 관리해야 합니다. 자동화가 많아질수록 secret은 더 작게 쪼개야 합니다. trusted publishing을 도입할수록 trusted workflow 자체를 더 강하게 의심해야 합니다. 공급망 공격자는 이제 "토큰을 훔치는" 단계에서 "토큰을 만들어내는 시스템을 조종하는" 단계로 이동하고 있습니다. AI 팀이 봐야 할 진짜 변화는 바로 그 지점입니다.