Mini Shai-Hulud uderza w openapi-react-query-codegen: 10 złośliwych wersji, npm provenance i dlaczego TanStack Query nie został zainfekowany Skip to content

Mini Shai-Hulud uderza w openapi-react-query-codegen: 10 złośliwych wersji, npm provenance i dlaczego TanStack Query nie został zainfekowany

Zweryfikowana analiza ataku z 28 sierpnia 2026 na @7nohe/openapi-react-query-codegen: 10 złośliwych wersji, GitHub Actions issue_comment, npm Trusted Publishing, valid provenance, binding.gyp, IOCs, remediacja i różnica względem TanStack Query.

Opublikowano Autor Czas czytania 17 min czytania

Zweryfikowana analiza ataku z 28 sierpnia 2026 na @7nohe/openapi-react-query-codegen: 10 złośliwych wersji, GitHub Actions issue_comment, npm Trusted Publishing, valid provenance, binding.gyp, IOCs, remediacja i różnica względem TanStack Query.

Na tej stronie
  1. 1TL;DR
  2. 2Co dokładnie wydarzyło się 28 sierpnia?
  3. 3Najważniejsza korekta: TanStack Query nie został skompromitowany
  4. 4Co robi openapi-react-query-codegen?
  5. 5Jak popularny był pakiet?
  6. 6Atak nie zaczął się od npm
  7. 7Jeden komentarz był triggerem release
  8. 8Checkout kodu z forka zmienił trigger w RCE
  9. 9id-token: write otworzył drogę do npm
  10. 10Nie trzeba było kraść hasła maintainera
  11. 11Wszystkie złośliwe wydania miały prawidłowe provenance
  12. 12Provenance nie jest skanerem malware
  13. 13Pierwszy trigger malware: binding.gyp
  14. 14Drugi trigger: jawny preinstall
  15. 15Dwa prerelease były złośliwe, ale inaczej
  16. 16Główny payload: 3FWCvzduYZg.js
  17. 17Co payload robił w runtime?
  18. 18Zakres potencjalnie dostępnych sekretów
  19. 19Dlaczego dev dependency jest równie groźny jak runtime dependency?
  20. 20npm install jest powierzchnią wykonania kodu
  21. 21Czy to na pewno TeamPCP?
  22. 22Dlaczego nazwa Mini Shai-Hulud wraca?
  23. 23Majowy TanStack attack był innym incydentem
  24. 24W maju TanStack Query także był czysty
  25. 25Majowy incydent pokazał realny downstream impact
  26. 26Czym sierpniowy atak różni się od majowego?
  27. 27Jak sprawdzić, czy projekt jest dotknięty?
  28. 28Najważniejsze IOCs
  29. 29Co zrobić, jeśli złośliwa wersja została zainstalowana?
  30. 30Jakie wersje są bezpieczne?
  31. 31Co naprawił maintainer?
  32. 32Jak zabezpieczyć GitHub Actions?
  33. 33Jak zabezpieczyć npm dependency install?
  34. 34Produkcyjna checklista po tym incydencie
  35. 35Werdykt POLPROG

28 sierpnia 2026 roku doszło do nowego ataku supply-chain na ekosystem npm. Tym razem zaatakowany został pakiet @7nohe/openapi-react-query-codegen, popularne narzędzie generujące kod TypeScript i hooki dla TanStack Query na podstawie specyfikacji OpenAPI.[1][2][3]

Atakujący nie musiał przejmować hasła maintainera do npm ani wykradać długowiecznego tokena publikacyjnego.

Wystarczyło wykorzystać błędnie skonfigurowany workflow GitHub Actions.

Repozytorium miało ścieżkę release uruchamianą zdarzeniem:

issue_comment

Jeżeli komentarz pod pull requestem miał dokładnie treść:

npm publish

workflow mógł przejść dalej bez sprawdzenia, czy osoba komentująca jest maintainerem, collaboratorem czy całkowicie zewnętrznym użytkownikiem GitHuba.[1][2]

Następnie pipeline:

sprawdzał kod z PR
→ wykonywał pnpm install
→ posiadał id-token: write
→ publikował przez npm Trusted Publishing

[1][2]

Rezultat był szczególnie niebezpieczny: 10 złośliwych wersji zostało opublikowanych z prawidłowym npm provenance.[1][3]

To kolejny incydent z 2026 roku pokazujący bardzo ważną prawdę:

Provenance może poprawnie potwierdzać, skąd pochodzi build, a jednocześnie ten build może być złośliwy.

Jeszcze ważniejsza korekta dotyczy samego TanStack Query.

@tanstack/react-query nie został skompromitowany w tym incydencie. Zaatakowany został zewnętrzny generator kodu, który tworzy hooki i queryOptions dla TanStack Query.[6]

To nie jest oficjalny pakiet TanStack.

Stan informacji: 31 sierpnia 2026 roku.

TL;DR

PytanieZweryfikowana odpowiedź
Co zostało zaatakowane?@7nohe/openapi-react-query-codegen
Kiedy?28 sierpnia 2026
Ile złośliwych wersji?10
Ile stabilnych złośliwych wersji?8
Ile złośliwych prerelease?2
Czy @tanstack/react-query został zhakowany?Nie
Czy pakiet jest związany z TanStack Query?Tak, generuje dla niego kod i hooki
Główny wektorBłędny GitHub Actions issue_comment release workflow
Wymagane konto maintainera?Nie
Wymagany skradziony długowieczny npm token?Nie
Co dawało publish?id-token: write + npm Trusted Publishing
Czy złośliwe wydania miały provenance?Tak
Czy provenance gwarantuje bezpieczeństwo kodu?Nie
Główne triggery malwarebinding.gyp, a w części wersji także preinstall
Główny plik payloadu3FWCvzduYZg.js
Aktualny latest3.0.2
Czy 3.0.2 jest wskazywany jako czysty?Tak
Severity advisoryCritical, CVSS 9.6
CVEBrak znanego CVE w advisory na 31 sierpnia
KlasyfikacjaEmbedded Malicious Code / supply-chain compromise
Rodzina kampaniiBadacze łączą incydent z Mini Shai-Hulud / pochodnymi
Czy operator został jednoznacznie potwierdzony?Nie należy tego twierdzić z pełną pewnością
Najważniejsza reakcjaIzolacja hosta + rotacja wszystkich dostępnych credentiali z czystej maszyny

Co dokładnie wydarzyło się 28 sierpnia?

GitHub Security Advisory GHSA-9pvf-vcx3-x239 opisuje 10 złośliwych wersji @7nohe/openapi-react-query-codegen opublikowanych pomiędzy około 20:00 a 20:21 UTC.[1]

Osiem z nich to stabilne wersje:

0.5.4
0.5.5
1.6.3
1.6.4
2.2.1
2.2.2
3.0.3
3.0.4

oraz dwa prerelease:

0.0.0-365d4eb738d3146583431948d3ba6e27a32556be
0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab

[1][2]

Złośliwy latest wskazywał na 3.0.4 od około 20:19:29 UTC do około 22:51 UTC, kiedy tag przywrócono do czystego 3.0.2.[1]

Najważniejsza korekta: TanStack Query nie został skompromitowany

Nagłówki typu:

TanStack Query hacked

są w tym przypadku nieprecyzyjne.

Pakiet:

@7nohe/openapi-react-query-codegen

jest niezależnym projektem.

Jego zadaniem jest generowanie kodu dla:

@tanstack/react-query

na podstawie OpenAPI schema.[6]

To powiązanie technologiczne, nie własnościowe.

Aktualna strona npm @tanstack/react-query nadal wskazuje osobny oficjalny pakiet TanStack, a majowy postmortem TanStack również wyraźnie zaznaczał, że rodzina Query nie była objęta tamtym wcześniejszym kompromisem.[7][11]

Wniosek:

Incydent dotyczy narzędzia używanego z TanStack Query, nie samego TanStack Query.

Co robi openapi-react-query-codegen?

Pakiet generuje między innymi:

  • typed API clients,
  • query keys,
  • queryOptions,
  • infiniteQueryOptions,
  • useQuery,
  • useMutation,
  • useSuspenseQuery,
  • helpery prefetch i SSR.

[6]

Typowy workflow:

OpenAPI schema
      ↓
openapi-react-query-codegen
      ↓
TypeScript client
      ↓
TanStack Query hooks/options

To dev dependency.

I właśnie dlatego jest atrakcyjnym celem.

Dev dependency często uruchamia się:

  • na laptopie programisty,
  • w GitHub Actions,
  • w release pipeline,
  • w środowisku z tokenami,
  • obok kodu źródłowego,
  • w pobliżu credentiali cloud.

Nie musi trafić do browser bundle, aby wyrządzić szkody.

Jak popularny był pakiet?

31 sierpnia strona npm wskazywała około 155 tys. pobrań tygodniowo dla scoped package @7nohe/openapi-react-query-codegen.[6]

To metryka dynamiczna, więc nie należy traktować jej jako stałej wartości historycznej.

Ważniejszy jest charakter użycia.

Code generator jest instalowany w środowisku developerskim, czyli dokładnie tam, gdzie można znaleźć:

GitHub auth
npm credentials
SSH keys
cloud credentials
CI secrets
source code

Atak nie zaczął się od npm

Najważniejszy błąd znajdował się w GitHub Actions.

Release workflow nasłuchiwał:

on:
  issue_comment:
    types: [created]

i akceptował określony komentarz na pull requeście.[1]

Sam issue_comment nie jest automatycznie podatnością.

Problem powstał z kombinacji:

zewnętrzny komentarz
+
brak kontroli uprawnień autora
+
checkout kodu z PR
+
wykonanie kodu
+
id-token: write
+
publish

Każdy element osobno może być poprawny.

Łańcuch razem stworzył ścieżkę od anonimowego użytkownika GitHuba do publikacji w npm.

Jeden komentarz był triggerem release

Warunek workflow sprawdzał przede wszystkim, czy:

  • komentarz dotyczył pull requesta,
  • treść komentarza była równa npm publish.

[1][2]

Brakowało kluczowego gate:

czy commenter należy do zaufanej grupy?

Na przykład:

OWNER
MEMBER
COLLABORATOR

Atakujący mógł więc przygotować kod w forku, otworzyć PR i uruchomić release commentem.

To nie jest problem samego issue_comment.

To problem braku authorization check przed wejściem w privileged workflow.

Checkout kodu z forka zmienił trigger w RCE

Samo uruchomienie workflow jeszcze nie daje złośliwemu PR możliwości wykonania kodu.

Kolejny krok był decydujący.

Pipeline checkoutował kod pull requesta, a następnie wykonywał:

pnpm install

[2]

Od tej chwili attacker-controlled repository content przestał być tylko danymi.

Stał się kodem wykonywanym wewnątrz release job.

To fundamentalna granica bezpieczeństwa:

untrusted code as data

vs

untrusted code executed in privileged context

GitHub od dawna ostrzega przed uruchamianiem kodu z niezaufanych PR w workflowach posiadających podwyższone uprawnienia.[9]

id-token: write otworzył drogę do npm

Workflow posiadał:

permissions:
  id-token: write

[1][2]

To nie jest samo w sobie złe.

Jest potrzebne do OIDC-based Trusted Publishing.

npm Trusted Publishing pozwala CI opublikować pakiet bez przechowywania długowiecznego tokena npm.[10]

Zwykle jest to poprawa bezpieczeństwa.

W tym przypadku problem brzmiał:

kto może doprowadzić do uruchomienia uprzywilejowanego workflow?

Jeżeli niezaufany kod trafi do joba posiadającego prawo mintowania OIDC identity, Trusted Publishing zaczyna ufać legalnemu workflow wykonującemu nielegalny kod.

Nie trzeba było kraść hasła maintainera

StepSecurity podkreśla, że attacker nie potrzebował:

  • hasła maintainera npm,
  • long-lived npm publish tokena.

[2]

To ważna zmiana względem klasycznego account takeover.

Stary model zagrożenia:

ukradnij token
→ npm publish

Nowy model:

wpłyń na trusted workflow
→ workflow sam dostaje prawidłową tożsamość
→ publish

Przesunięcie granicy z konta maintainera do pipeline’u ma ogromne znaczenie dla DevSecOps.

Wszystkie złośliwe wydania miały prawidłowe provenance

Według GitHub advisory i analiz Socket publikacje przeszły przez prawdziwy workflow oraz npm Trusted Publishing, dlatego złośliwe wydania miały prawidłowe provenance/SLSA provenance.[1][3]

To nie znaczy, że provenance zawiodło kryptograficznie.

Wręcz przeciwnie.

Provenance prawidłowo mówiło:

ten artifact został opublikowany
przez ten workflow
z tego repozytorium

Problem:

workflow uruchamiał kod kontrolowany przez atakującego

Provenance nie jest skanerem malware

To jeden z najważniejszych wniosków z całego incydentu.

Provenance odpowiada na pytania typu:

  • skąd pochodzi build,
  • jaki workflow go zbudował,
  • jakie repo jest źródłem,
  • czy chain of custody jest zgodny.

Nie odpowiada automatycznie:

czy source code jest bezpieczny?
czy PR był autoryzowany?
czy workflow ma logiczną podatność?
czy artifact zawiera malware?

Dlatego:

valid provenance
≠
benign package

npm sam opisuje Trusted Publishing i automatyczną provenance jako mechanizmy potwierdzające pochodzenie, a nie jako pełny malware verdict.[10]

Pierwszy trigger malware: binding.gyp

Część złośliwych wydań nie miała klasycznego:

"preinstall": "..."

[1]

Zamiast tego do package root dodano:

binding.gyp

Node/npm może automatycznie uruchomić node-gyp rebuild, gdy package wygląda jak native addon.

Złośliwy binding.gyp wykorzystywał evaluator Pythona w GYP do dotarcia do os.system() i uruchomienia JavaScript payloadu.[1][5]

To szczególnie ważne dla skanerów.

Reguła:

szukaj wyłącznie preinstall/postinstall

nie wystarcza.

Drugi trigger: jawny preinstall

W kolejnej grupie stabilnych wydań pojawił się także:

"preinstall": "node 3FWCvzduYZg.js"

[1][2]

Dotyczyło to:

0.5.5
1.6.4
2.2.2
3.0.4

Pierwsze wersje:

0.5.4
1.6.3
2.2.1
3.0.3

wykorzystywały binding.gyp bez jawnego install scriptu.[1]

To świadome zróżnicowanie triggerów utrudnia prostą detekcję opartą na jednym IOC.

Dwa prerelease były złośliwe, ale inaczej

Dwa 0.0.0-* prerelease miały złośliwy preinstall, lecz przez ustawienie:

"files": ["dist"]

payload files nie znalazły się w tarballu.[1]

To oznacza, że nie należy traktować wszystkich 10 artifactów jako bitowo identycznych lub wykonujących identyczną ścieżkę.

Wszystkie są klasyfikowane jako złośliwe publikacje.

Ale osiem stabilnych wersji ma pełniejszy, potwierdzony zestaw triggerów i payloadów.

Główny payload: 3FWCvzduYZg.js

Stabilne złośliwe tarballe zawierały plik:

3FWCvzduYZg.js

o rozmiarze około 4,4–6,4 MB zależnie od wydania.[1]

Wersja 3.0.4 miała payload o rozmiarze ponad 6 MB i jawny preinstall.[2]

Duży, silnie obfuscated JavaScript w małym codegen package jest sam w sobie sygnałem anomalii.

StepSecurity zwraca uwagę, że 0.5.4 urósł z około 41 KB w 0.5.3 do ponad 5,6 MB.[2]

Nagły package-size delta jest bardzo przydatnym sygnałem supply-chain.

Co payload robił w runtime?

StepSecurity uruchomił złośliwe wersje na izolowanych GitHub-hosted runners.[2]

Dla 3.0.4 obserwowano między innymi:

Node uruchamia payload
→ curl pobiera Bun
→ Bun startuje z /tmp/trinnyyyy-*
→ wykonywane jest gh auth token
→ odpytywany jest Git credential manager
→ sprawdzane są ssh / scp
→ enumerowane są procesy
→ uruchamiany jest tymczasowy updater.py

[2]

Obserwowano także kontakt z GitHub API i próby dotarcia do Google Cloud metadata hostname.[2]

To jest credential-focused malware, nie zwykły cryptominer.

Zakres potencjalnie dostępnych sekretów

Jeżeli malware działa jako proces użytkownika lub CI runnera, jego realny zakres zależy od środowiska.

Może potencjalnie zobaczyć to, do czego proces ma dostęp:

GitHub auth
npm credentials
SSH keys
cloud env vars
CI/CD secrets
source repositories
deployment credentials

Nie oznacza to, że każdy z tych sekretów został skradziony na każdym hoście.

Prawidłowe sformułowanie brzmi:

Jeżeli złośliwa wersja została wykonana, wszystkie credentials dostępne dla procesu należy uznać za potencjalnie narażone.

Dlaczego dev dependency jest równie groźny jak runtime dependency?

Frontend developer może pomyśleć:

to tylko codegen
nie trafia do produkcyjnego bundle

To błędny model bezpieczeństwa.

Malware potrzebuje wykonać się podczas:

npm install
pnpm install
yarn install

Nie potrzebuje trafić do kodu klienta.

Dev dependency może być nawet cenniejszym celem, bo działa na maszynach:

  • z SSH,
  • z gh,
  • z cloud CLI,
  • z .npmrc,
  • z repo,
  • z CI tokens.

npm install jest powierzchnią wykonania kodu

Package managers od dawna wspierają lifecycle scripts.

Przykłady:

preinstall
install
postinstall
prepare

Dodatkowo native addon flows mogą uruchamiać narzędzia takie jak node-gyp.

Dlatego installation jest w praktyce:

dependency resolution
+
download
+
potential code execution

Nie jest to wyłącznie „kopiowanie bibliotek do node_modules”.

To jeden z powodów, dla których supply-chain security musi działać przed lub w trakcie instalacji, a nie dopiero na finalnym artifact.

Czy to na pewno TeamPCP?

Badacze JFrog, Socket i inni łączą malware z rodziną Mini Shai-Hulud i jej późniejszymi wariantami.[3][4]

JFrog zwraca jednak uwagę na ważną niepewność dotyczącą operatora.

Podobieństwo kodu, technik i kampanijnych markerów może wskazywać na:

  • kontynuację tej samej operacji,
  • wykorzystanie pozostałego dostępu,
  • reuse publicznie dostępnego malware,
  • copycat.

[4]

Dlatego nie będziemy pisać:

na 100% zrobił to konkretny operator

jeżeli publiczne dowody nie rozstrzygają tego jednoznacznie.

Dlaczego nazwa Mini Shai-Hulud wraca?

Mini Shai-Hulud to rodzina/kampania supply-chain skupiona na:

  • credential theft,
  • CI/CD,
  • npm,
  • GitHub,
  • automatycznej propagacji.

W maju 2026 badacze obserwowali kompromisy obejmujące TanStack, Mistral, UiPath i kolejne pakiety.[12]

W sierpniu podobne lub pochodne techniki nadal pojawiają się w nowych kompromisach.

Najważniejsze nie jest logo kampanii.

Najważniejszy jest powtarzający się model:

wejdź przez pipeline
→ ukradnij/uprawnij publish
→ opublikuj zaufany artifact
→ wykonaj malware na kolejnych developer machines
→ szukaj następnych credentiali

Majowy TanStack attack był innym incydentem

11 maja 2026 kompromis dotyczył repozytorium TanStack Router/Start.[11]

Atakujący połączył:

  1. pull_request_target Pwn Request,
  2. GitHub Actions cache poisoning,
  3. odczyt OIDC tokena z pamięci runnera.

Efektem było:

42 pakiety
84 złośliwe wersje

[11]

To nie jest ten sam techniczny root cause co 28 sierpnia.

W maju TanStack Query także był czysty

Oficjalny TanStack postmortem po audycie stwierdził:

  • affected: Router/Start repo,
  • unaffected: Query, DB, Store, AI, Table, Form, Virtual i inne rodziny.

[11]

To ważne, bo w 2026 wystąpiły co najmniej dwa incydenty, które można powierzchownie opisać jako:

TanStack supply-chain attack

ale w żadnym z nich nie należy automatycznie stwierdzać:

@tanstack/react-query był zainfekowany

W sierpniu zaatakowano zewnętrzny codegen.

W maju zaatakowano Router/Start monorepo.

Majowy incydent pokazał realny downstream impact

OpenAI potwierdziło, że dwie maszyny pracowników zostały dotknięte majowym atakiem TanStack/Mini Shai-Hulud.[13]

Firma podała między innymi:

  • credential-focused activity,
  • ograniczoną eksfiltrację materiałów credentialowych z części repozytoriów,
  • brak dowodów na dostęp do user data,
  • brak dowodów na kompromis produkcyjnych systemów lub IP,
  • rotację credentiali i certyfikatów podpisujących jako środek ostrożności.

[13]

To pokazuje, dlaczego kilka minut ekspozycji złośliwego package może być istotne nawet dla bardzo dojrzałych organizacji.

Czym sierpniowy atak różni się od majowego?

Maj

fork PR
→ pull_request_target
→ poisoned cache
→ trusted release restores cache
→ OIDC token extracted from runner memory
→ publish

[11]

Sierpień

fork PR
→ public issue_comment "npm publish"
→ privileged workflow checks out PR
→ pnpm install executes attacker code
→ workflow already has id-token: write
→ Trusted Publishing

[1][2]

Sierpniowa ścieżka jest prostsza.

Nie wymaga skomplikowanego cache poisoning.

To właśnie powinno niepokoić najbardziej.

Jak sprawdzić, czy projekt jest dotknięty?

Najpierw sprawdź lockfile i SBOM.

Szukaj dokładnie wersji:

0.5.4
0.5.5
1.6.3
1.6.4
2.2.1
2.2.2
3.0.3
3.0.4
0.0.0-365d4eb738d3146583431948d3ba6e27a32556be
0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab

[1]

Sprawdź:

package-lock.json
pnpm-lock.yaml
yarn.lock
bun.lock
SBOM
CI install logs
dependency cache

Nie wystarczy sprawdzić obecnego package.json.

Floating range mógł historycznie rozwiązać się do złośliwej wersji, a potem wrócić do czystego latest.

Najważniejsze IOCs

Publiczne IOCs obejmują:[1][2][5]

3FWCvzduYZg.js
binding.gyp
is_it_this_simple.js
nu.js
/tmp/trinnyyyy-*/bun
/tmp/*/updater.py

Dodatkowo warto szukać:

  • nagłego downloadu Bun podczas install,
  • wywołań gh auth token,
  • nietypowego git credential-manager,
  • outbound traffic do GitHub release assets podczas instalacji,
  • nieoczekiwanego node-gyp w package, który nie potrzebuje native addon.

IOC nie powinien być jedyną metodą detekcji.

Payload może zmienić nazwę.

Behavioral detection jest ważniejsza.

Co zrobić, jeśli złośliwa wersja została zainstalowana?

Oficjalne advisory i StepSecurity rekomendują traktowanie hosta jako potencjalnie skompromitowanego.[1][2]

Minimalny response:

1. zatrzymaj buildy
2. odizoluj host
3. użyj osobnej czystej maszyny
4. rotuj wszystkie dostępne sekrety
5. unieważnij aktywne sesje
6. sprawdź GitHub/npm/cloud audit logs
7. usuń dependency caches
8. odrzuć artifacts z zainfekowanego runnera
9. odbuduj środowisko z czystego źródła

Nie rotuj tylko npm tokena.

Jeżeli proces widział więcej, zakres rotacji musi być szerszy.

Jakie wersje są bezpieczne?

GitHub advisory wskazuje 3.0.2 jako patched/known-good i stwierdza, że żadna wersja opublikowana przed 28 sierpnia nie była objęta tym incydentem.[1]

StepSecurity wskazuje czyste poprzedniki dla poszczególnych linii:

0.5.3
1.6.2
2.2.0
3.0.2

[2]

31 sierpnia npm ponownie wskazuje:

latest = 3.0.2

[6]

Jeśli nie masz powodu utrzymywać starszej linii, oficjalne advisory prowadzi do 3.0.2.

Co naprawił maintainer?

Według GitHub advisory upstream zastosował między innymi:[1]

  • usunięcie issue_comment trigger,
  • publikację po tag push,
  • revocation npm trusted publisher,
  • rotację long-lived tokens,
  • przywrócenie latest do 3.0.2,
  • usunięcie pre dist-tag,
  • deprecację wszystkich 10 złośliwych wersji,
  • zgłoszenie removal do npm security.

To prawidłowy kierunek:

public interaction

nie powinno być bezpośrednim gatewayem do:

production publish identity

Snyk również klasyfikuje kompromitację jako Critical i wskazuje CVSS 9.6, co potwierdza wagę wykonania złośliwego kodu podczas instalacji.[8] Warto też odnieść się do zmian wprowadzonych przez TanStack po majowym incydencie, w tym dalszego hardeningu workflowów, cache i release pipeline.[14]

Jak zabezpieczyć GitHub Actions?

Nigdy nie wykonuj kodu z niezaufanego PR w privileged job

GitHub zaleca oddzielać:

untrusted build

od:

trusted action

[9]

Sprawdzaj actor authorization

Dla comment-driven automation potrzebny jest jawny gate.

Minimalizuj permissions

Jeśli job nie publikuje:

id-token: write

nie powinno istnieć.

Rozdziel build od publish

Najbezpieczniej:

untrusted PR CI
↓
artifact jako dane
↓
review / trusted trigger
↓
release

Pin GitHub Actions do commit SHA

Tag akcji również jest częścią supply chain.

Jak zabezpieczyć npm dependency install?

Nie ma jednego magicznego przełącznika.

Praktyczny zestaw:

  • lockfiles,
  • frozen lockfile installs,
  • package release cooldown/minimum age,
  • monitoring nowych publishów,
  • analiza package-size delta,
  • lifecycle-script policy,
  • sandboxowane CI,
  • outbound network policy,
  • SCA + malicious-package detection,
  • SBOM,
  • minimalne credentials na runnerze.

npm Trusted Publishing nadal ma sens.[10]

Ale:

Trusted Publishing

musi być połączone z:

Trusted Workflow Design

Produkcyjna checklista po tym incydencie

Dependencies

  • Używamy lockfile.
  • CI odrzuca nieoczekiwane zmiany lockfile.
  • Mamy minimum release age/cooldown.
  • Nowa wersja package nie wpada automatycznie do produkcji.
  • Monitorujemy nagłe package-size changes.
  • Sprawdzamy install scripts.
  • Sprawdzamy binding.gyp w nietypowych paczkach.
  • Skanujemy malicious packages, nie tylko CVE.

GitHub Actions

  • Nie wykonujemy fork code w privileged context.
  • issue_comment ma authorization gate.
  • pull_request_target jest używany tylko, gdy naprawdę potrzebny.
  • GITHUB_TOKEN ma minimalne permissions.
  • id-token: write ma tylko publish job.
  • Publish wymaga trusted trigger.
  • Build i publish są rozdzielone.
  • Actions są przypięte do SHA.
  • Comment-driven automation ma test negatywny dla użytkownika bez uprawnień.
  • Release workflow nie checkoutuje kodu z niezaufanego forka.

npm

  • Trusted Publisher jest przypisany do minimalnej liczby workflows.
  • Nie trzymamy zbędnych long-lived publish tokens.
  • Publikacje są monitorowane w czasie rzeczywistym.
  • Alerts obejmują nowe wersje poza planowanym release.
  • Provenance jest weryfikowane, ale nie traktowane jako malware verdict.
  • W przypadku incydentu potrafimy natychmiast deprecate/revoke.

Runnery i stacje developerów

  • CI runner jest ephemeral.
  • Workspace nie zawiera niepotrzebnych sekretów.
  • Cloud credentials są short-lived.
  • SSH keys mają minimalny scope.
  • Outbound traffic jest monitorowany.
  • Dependency install nie ma otwartego dostępu do całej infrastruktury.
  • Developer endpoints mają EDR/runtime monitoring.
  • Potrafimy szybko odizolować host.

Incident response

  • Znamy ownera dependency incident response.
  • Potrafimy przeszukać lockfiles historycznie.
  • Potrafimy wyczyścić CI caches.
  • Potrafimy unieważnić sessions.
  • Mamy mapę credentiali dostępnych z runnerów.
  • Rotacja odbywa się z clean machine.
  • Sprawdzamy nieautoryzowane npm publishes.
  • Sprawdzamy GitHub audit logs po rotacji.

Werdykt POLPROG

Incydent @7nohe/openapi-react-query-codegen jest jednym z najlepszych przykładów tego, jak zmienia się software supply-chain security w 2026 roku.

Atakujący nie musiał:

zhakować npm
ukraść hasła maintainera
przejąć 2FA
ukraść klasycznego publish tokena

Wystarczyło, że release workflow pozwalał niezaufanemu użytkownikowi doprowadzić do wykonania jego kodu w jobie posiadającym prawa do Trusted Publishing.

To ogromna różnica.

Klasyczna ochrona konta nadal jest potrzebna.

Ale współczesna granica bezpieczeństwa przesuwa się do:

CI/CD graph
workflow triggers
cache
artifacts
OIDC
release authorization
dependency execution

Drugi ważny wniosek dotyczy provenance.

Valid provenance jest cenne.

W tym incydencie jednak prawidłowo potwierdzało ono pochodzenie złośliwego builda.

To nie wada kryptografii.

To konsekwencja błędnego zaufania do workflow.

Trzeci wniosek dotyczy nazewnictwa.

TanStack Query nie został w tym incydencie skompromitowany.

Zaatakowany pakiet generuje kod dla TanStack Query, ale jest niezależnym projektem.

Takie rozróżnienie ma znaczenie, bo w security nie można przenosić winy lub zakresu incydentu na cały ekosystem tylko dlatego, że biblioteka pojawia się w nazwie lub dependency graph.

Na 31 sierpnia:

malicious versions: znane
advisory: opublikowane
latest: przywrócone do 3.0.2
upstream workflow: zmieniony

[1][6]

Ale najważniejsze pytanie dla każdego zespołu JavaScript nie brzmi:

Czy używaliśmy tego konkretnego pakietu?

Tylko:

Czy nasz własny release pipeline ma podobną ścieżkę, w której niezaufany input może dotrzeć do joba z prawem publikacji?

Jeżeli odpowiedź nie jest natychmiast oczywista, warto zrobić audyt GitHub Actions teraz.

Mini Shai-Hulud npm supply chain TanStack Query openapi-react-query-codegen GitHub Actions Trusted Publishing OIDC npm provenance DevSecOps

Najczęściej zadawane pytania

Czy TanStack Query został zhakowany?

Nie. @tanstack/react-query nie jest pakietem objętym tym incydentem.

Co zostało zhakowane?

@7nohe/openapi-react-query-codegen, zewnętrzny generator kodu dla TanStack Query.

Ile było złośliwych wersji?

10.

Kiedy je opublikowano?

28 sierpnia 2026, główna fala między około 20:00 a 20:21 UTC.

Które wersje są złośliwe?

0.5.4, 0.5.5, 1.6.3, 1.6.4, 2.2.1, 2.2.2, 3.0.3, 3.0.4 i dwa wskazane w advisory prerelease 0.0.0-*.

Jaki jest aktualny bezpieczny latest?

3.0.2 według GitHub advisory i aktualnej strony npm z 31 sierpnia.

Czy atakujący ukradł npm token maintainera?

Nie był potrzebny. Wykorzystano workflow z npm Trusted Publishing i OIDC.

Co było błędem?

Publiczny issue_comment mógł uruchomić release bez sprawdzenia roli autora komentarza. Workflow checkoutował kod PR i wykonywał go w uprzywilejowanym jobie.

Czy złośliwe wersje miały npm provenance?

Tak.

Jak to możliwe?

Były publikowane przez prawdziwy, zaufany workflow. Provenance potwierdzało origin, nie bezpieczeństwo source code.

Czy binding.gyp może wykonać kod podczas npm install?

Tak. W tym incydencie był użyty jako ukryty execution path przez node-gyp.

Czy wszystkie wersje miały preinstall?

Nie. Część stabilnych wersji używała wyłącznie binding.gyp, a późniejsze miały również jawny preinstall.

Co robić po instalacji złośliwej wersji?

Odizolować host, zatrzymać buildy, rotować dostępne credentials z osobnej czystej maszyny, przejrzeć audit logs i odbudować środowisko.

Czy wystarczy zmienić npm token?

Nie. Narażone mogły być także GitHub, SSH, cloud i inne credentials dostępne dla procesu.

Czy Mini Shai-Hulud to potwierdzony operator?

To przede wszystkim nazwa rodziny/kampanii używana przez badaczy. Publiczne dowody nie powinny być upraszczane do pewnego przypisania każdej nowej fali jednemu operatorowi.

Czy npm Trusted Publishing jest zepsute?

Nie w takim sensie. Problemem był workflow, któremu Trusted Publishing prawidłowo ufało. OIDC nie zastępuje bezpiecznej architektury CI/CD.

Źródła i przypisy

  1. GitHub Security Advisory, GHSA-9pvf-vcx3-x239 — Malicious versions of @7nohe/openapi-react-query-codegen published via a compromised release workflow, 28 sierpnia 2026, dostęp: 31 sierpnia 2026.123456789101112131415161718192021222324
  2. StepSecurity, @7nohe/openapi-react-query-codegen Compromised Through an Exposed npm Publishing Workflow, 28 sierpnia 2026.123456789101112131415161718
  3. Socket, OpenAPI React Query Codegen Compromised in Mini Shai-Hulud npm Supply Chain Attack, 28 sierpnia 2026.1234
  4. JFrog Security Research, Shai-Hulud Trinitite Hits @7nohe/openapi-react-query-codegen, 30 sierpnia 2026.12
  5. OSV, MAL-2026-15494 — malicious code in @7nohe/openapi-react-query-codegen, 28 sierpnia 2026.12
  6. npm, @7nohe/openapi-react-query-codegen, stan sprawdzony 31 sierpnia 2026.123456
  7. npm, @tanstack/react-query, stan sprawdzony 31 sierpnia 2026.
  8. Snyk, Embedded Malicious Code affecting @7nohe/openapi-react-query-codegen, 28 sierpnia 2026.
  9. GitHub Docs, Securely using pull_request_target, dostęp: 31 sierpnia 2026.12
  10. npm Docs, Trusted publishing for npm packages, dostęp: 31 sierpnia 2026.123
  11. TanStack, Postmortem: TanStack npm supply-chain compromise, 11 maja 2026, aktualizacja 15 maja 2026.12345
  12. Aikido Security, Mini Shai-Hulud Is Back: npm Worm Hits over 160 Packages, including Mistral and Tanstack, 12 maja 2026.
  13. OpenAI, Our response to the TanStack npm supply chain attack, 13 maja 2026, później aktualizowane.12
  14. TanStack, Hardening TanStack After the npm Compromise, 12 maja 2026, aktualizacja 15 maja 2026.

Czy ten artykuł był pomocny?

Nowe artykuły na e-mail

Jeden krótki e-mail przy każdym nowym artykule. Bez spamu, wypisujesz się jednym kliknięciem.

Wykorzystujemy e-mail wyłącznie do wysyłki nowych artykułów. Bez udostępniania stronom trzecim.

Wróć do bazy wiedzy