CVE-2026-82393 w pnpm: złośliwy tarball zapisuje pliki poza node_modules
Nazwa pakietu z path traversal pozwalała instalacji pnpm nadpisać dowolne pliki, nawet przy --ignore-scripts. Poprawki są w 10.34.5 i 11.11.0.
- AUTOR
- Karol Rapacz / CEO Breachroad · OSCP · PNPT
- PUBLIKACJA
- 1 września 2026
- CZAS CZYTANIA
- 19 min czytania
- TEMAT
- Łańcuch dostaw
Dzisiejszy rekord CVE-2026-82393 opisuje lukę w pnpm, przez którą złośliwa zależność dostarczona jako tarball mogła zapisywać i nadpisywać pliki poza node_modules. Źródłem ścieżki była nazwa z package.json, a nie nazwa pliku archiwum. Odpowiednio zbudowana nazwa pakietu scoped przechodziła walidację i trafiała do surowych operacji path.join. Instalacja mogła w efekcie zmienić plik startowy powłoki, hook Git albo kod innej zależności, prowadząc do wykonania kodu w kontekście dewelopera lub runnera CI.
Problem występował nawet przy pnpm install --ignore-scripts. To najważniejsza lekcja z advisory: wyłączenie lifecycle scripts redukuje jedną klasę ryzyka, lecz menedżer pakietów nadal rozpakowuje i przenosi pliki. Gdy sama ścieżka docelowa jest kontrolowana przez niezaufane metadane, nie potrzeba postinstall, aby naruszyć system.
Podatne są wydania starsze niż 10.34.5 oraz linia 11 od 11.0.0 do 11.10.x. Poprawki znalazły się w 10.34.5 i 11.11.0. Advisory pnpm opublikowano 2 sierpnia, natomiast NVD zarejestrowało CVE 31 sierpnia o 22:17 UTC, czyli 1 września czasu warszawskiego. Dzisiejszą nowością jest więc wejście jednoznacznego rekordu do feedów zarządzania podatnościami, a nie dopiero powstanie patcha.
Jak nazwa pakietu stała się ścieżką zapisu
Manifest npm zawiera pole name. Dla pakietu scoped legalna nazwa ma formę @scope/package. pnpm odrzucał slash w nazwach unscoped, lecz walidacja nie zamykała analogicznej możliwości po prefiksie scope. Sekwencje katalogu nadrzędnego mogły przez to przetrwać etap rozwiązywania pakietu.
Później isolated linker budował miejsce importu na podstawie rozwiązanej nazwy. Advisory wskazuje trzy ścieżki kodu: resolvePeers.ts, główny resolver zależności i lockfileToDepGraph.ts. Używały one surowego połączenia katalogu modułów z nazwą. Jeśli ta druga część nie jest zwykłym segmentem, wynik po normalizacji może wyjść poza wirtualny store oraz poza projekt.
Zasada bezpiecznego łączenia ścieżek brzmi: nie wystarczy, że pierwszy argument wskazuje bezpieczny katalog. Trzeba potraktować drugi argument jako niezaufany, rozwiązać wynik kanonicznie i ponownie dowieść zawierania w dozwolonym korzeniu. pnpm miał już helper safeJoinModulesDir stosowany w innych ścieżkach, ale vulnerable sink nie korzystał z tej samej ochrony.
Atakujący kontrolujący tarball mógł wybrać nie tylko katalog, ale też nazwy i treść plików w paczce. Zapis nowego pliku narusza integralność. Nadpisanie istniejącego pliku zwiększa wpływ, bo może przejąć mechanizm, który ofiara i tak uruchomi.
Dlaczego —ignore-scripts nie zatrzymał ataku
--ignore-scripts zabrania wykonywania skryptów z sekcji takich jak preinstall, install i postinstall. Jest ważną kontrolą w CI oraz podczas analizy nieznanej zależności. Nie zmienia jednak instalacji w operację tylko do odczytu. pnpm musi pobrać artefakt, zweryfikować jego metadane, rozpakować pliki, zbudować store i połączyć zależności.
CVE znajduje się właśnie w tej fazie. Złośliwa paczka nie prosiła pnpm o wykonanie jawnego skryptu. Sprawiała, że legalna operacja kopiowania trafiała do nielegalnego celu. Jeśli celem jest .git/hooks/pre-commit, kod może uruchomić się przy późniejszym commicie. Jeśli to plik powłoki, wykonanie nastąpi przy nowej sesji. Jeśli nadpisany zostanie plik zależności, kod wejdzie do procesu podczas kolejnego testu, buildu lub uruchomienia aplikacji.
To opóźnienie utrudnia analizę. Log instalacji może zakończyć się bez klasycznego komunikatu postinstall, a wykonanie nastąpi w innym procesie i czasie. Zespół może błędnie przypisać zdarzenie Gitowi, shellowi albo test runnerowi, mimo że pierwotną zmianę wprowadził instalator pakietów.
Jaki jest realny model zagrożenia
CVSS 3.1 wynosi 7,5 (High), z wymaganą interakcją użytkownika i wysoką złożonością. Ofiara musi zainstalować zależność od kontrolowanego tarballa albo przetworzyć wrogą zmianę lockfile/manifestu, która kieruje resolver do takiego artefaktu. Nie jest to bezpośredni atak HTTP na każdą aplikację używającą pnpm.
Najbardziej realistyczne wejścia to:
- pull request zmieniający
package.json, workspace lub lockfile; - zależność pobierana z URL, prywatnego rejestru albo artefaktu buildu;
- przejęte konto maintainera publikujące zmodyfikowany pakiet;
- automatyczny bot aktualizacyjny, którego propozycja przechodzi bez inspekcji metadanych;
- pipeline instalujący zależności z szerokimi prawami do workspace i katalogu domowego.
Lockfile pomaga w powtarzalności, lecz nie jest automatycznie zaufany. Jest instrukcją dla menedżera pakietów: zawiera lokalizacje, identyfikatory i graf zależności. Review, podpisany commit i polityka zmian są równie ważne jak hash treści paczki.
Kogo dotyczy i jak sprawdzić wersję
Sprawdź pnpm uruchamiany lokalnie, w obrazie developerskim, w runnerze CI oraz przez Corepack. Pole packageManager w package.json może przypinać inną wersję niż globalny binarny pnpm. Obraz kontenera i cache narzędzi również mogą zachować starsze wydanie po aktualizacji deklaracji.
Za bezpieczne względem tego CVE uznaje się 10.34.5 lub późniejsze 10.x oraz 11.11.0 lub późniejsze 11.x. Jeżeli organizacja korzysta z wcześniejszej głównej linii, powinna porównać wsparcie producenta i przejść na utrzymywane wydanie zamiast zakładać, że brak wymienienia oznacza bezpieczeństwo.
Po aktualizacji wyczyść tylko cache, którego pochodzenia nie można potwierdzić, zgodnie z procedurą organizacji. Sam cache nie uruchamia ataku, lecz przebudowa z zaufanego lockfile i zweryfikowanych artefaktów daje czystszy dowód niż ponowne użycie nieznanego stanu z czasu ekspozycji.
Priorytetowe działania obronne
- Przypnij i wdroż pnpm 10.34.5, 11.11.0 lub późniejszą zgodną wersję we wszystkich runnerach.
- Sprawdź zmiany w manifestach i lockfile od czasu ostatniego znanego bezpiecznego builda, zwłaszcza zależności tarball/URL i nietypowe nazwy scoped.
- Buduj w efemerycznym kontenerze bez zapisu do katalogu domowego hosta, socketu Dockera i poświadczeń produkcyjnych.
- Używaj
--ignore-scriptstam, gdzie ma sens, ale traktuj je jako warstwę, nie sandbox. - Ogranicz egress runnera do zatwierdzonych rejestrów i weryfikuj integralność pobieranych artefaktów.
- Promuj gotowy, podpisany artefakt między etapami zamiast ponownie instalować zależności w środowisku produkcyjnym.
W monorepo warto uruchomić jeden kontrolowany build po aktualizacji i porównać listę plików zmienionych poza katalogami roboczymi. Narzędzia EDR lub auditd mogą wykazać, czy proces pnpm zapisywał do .git/hooks, profilu powłoki, katalogów innych repozytoriów albo systemowych lokalizacji tymczasowych.
Detekcja i reakcja na podejrzaną instalację
Przeszukaj historię CI pod kątem zależności z URL oraz zmian nazw pakietów scoped. W telemetryce procesu szukaj zapisów wykonanych przez Node/pnpm poza workspace i skonfigurowanym store. Porównaj hashe hooków Git, plików startowych powłoki, wrapperów narzędzi i kodu zainstalowanych paczek z zaufanym baseline’em.
Jeżeli podejrzany build miał dostęp do sekretów, zakładaj możliwość ich odczytu po późniejszym wykonaniu nadpisanego kodu. Odtwórz runner z czystego obrazu, unieważnij tokeny dostępne w tym jobie i sprawdź artefakty wytworzone po zdarzeniu. Kompromitacja pipeline’u może przenieść się dalej przez obrazy, paczki i pliki release, nawet gdy sam runner był efemeryczny.
Bezpieczny test regresji
Nie testuj poprawki na katalogu domowym dewelopera. W izolowanym katalogu tymczasowym utwórz workspace i sąsiadujący plik-wabik, a następnie użyj nieszkodliwego lokalnego tarballa zawierającego manifest z niepoprawną nazwą. Poprawiona wersja powinna odrzucić paczkę przed zapisem, a canary pozostać niezmieniony. Test powinien działać również z --ignore-scripts i dla obu linkerów używanych przez organizację.
Weryfikuj efekt, nie tylko kod wyjścia. Nieudana instalacja, która wcześniej zdążyła zmienić plik, nie jest zachowaniem fail-safe. Monitor systemu plików powinien potwierdzić brak prób zapisu poza wyznaczonym korzeniem.
Fakty źródłowe i wnioski Breachroad
Zakres wersji, path traversal w nazwie manifestu, miejsca niebezpiecznych joinów, działanie przy --ignore-scripts i wersje naprawcze pochodzą z advisory pnpm, commitów oraz NVD. Model zagrożeń CI, plan huntingu i zalecenia izolacji są wnioskami obronnymi Breachroad. Źródła nie stwierdzają aktywnej eksploatacji w konkretnych organizacjach.
Źródła pierwotne
- pnpm Advisory: CVE-2026-82393
- pnpm 10.34.5
- pnpm 11.11.0
- Commit naprawczy linii 10
- Commit naprawczy linii 11
- NVD: CVE-2026-82393
Bezpieczny łańcuch dostaw wymaga rozumienia tego, co instalator robi przed uruchomieniem skryptów. Na szkoleniach z cyberbezpieczeństwa uczymy zespoły analizować granice CI/CD i zaufanie do artefaktów. Kontrolowany pentest web i API może zostać rozszerzony o przegląd procesu build, runnerów i ścieżek publikacji.


