Osiem dziur w zależnościach, których nie zainstalowałeś. Audyt npm w praktyce
npm audit pokazuje liczbę, ale decyzja należy do Ciebie. Jak czytać wynik, kiedy podpowiadana naprawa cofa projekt o kilkanaście wydań, jak wymusić bezpieczną wersję zależności przechodniej i dlaczego lockfile potrafi przejść lokalnie, a wywalić wdrożenie.

W skrócie
Typowa strona na Node ma w katalogu node_modules kilkaset pakietów, z czego samodzielnie wybrałeś może dwadzieścia. Reszta przyszła jako zależności Twoich zależności i to właśnie tam najczęściej siedzą podatności. Polecenie npm audit policzy je w dwie sekundy, ale liczba to dopiero początek rozmowy. Trzy rzeczy potrafią zamienić rutynową aktualizację w stracone popołudnie. Po pierwsze, podpowiadana przez npm „naprawa" bywa cofnięciem pakietu o kilkanaście wydań wstecz. Po drugie, część poprawek istnieje wyłącznie w nowej wersji głównej, więc trzeba sprawdzić, co się przy okazji zepsuje. Po trzecie, plik package-lock.json wygenerowany na Twoim komputerze potrafi przejść lokalnie i wywalić budowanie na serwerze, bo brakuje w nim wariantów dla innego systemu. Niżej opisuję każdą z tych pułapek na konkretnym przykładzie i podaję polecenia, którymi się je rozbraja.
Jedno polecenie na początek
npm auditWynik to lista zgłoszeń z czterostopniową skalą wagi: low, moderate, high, critical. Przy każdym pakiecie zobaczysz zakres podatnych wersji i informację, czy poprawka jest dostępna.
Sama liczba niewiele mówi. Dwa zgłoszenia o wadze krytycznej w pakiecie, który odpala się wyłącznie podczas budowania projektu, są mniej pilne niż jedno średnie w bibliotece obsługującej formularz kontaktowy. Dlatego zanim cokolwiek zaktualizujesz, warto zobaczyć pełny obraz:
npm audit --json | python3 -c "
import json,sys
d=json.load(sys.stdin)
for name,v in d['vulnerabilities'].items():
fix = v['fixAvailable']
print(v['severity'].upper(), name, 'zakres:', v['range'], 'poprawka:', fix)
"Interesują Cię trzy rzeczy przy każdym wpisie. Czy pakiet jest Twoją bezpośrednią zależnością, czy przyszedł z czyjąś. Czy jego kod wykonuje się na produkcji, czy tylko w czasie budowania. I czy poprawka wymaga skoku w obrębie tej samej wersji głównej, czy wyżej.
Co znaczy „nie zainstalowałem tego pakietu"
Najczęstsze nieporozumienie brzmi tak: skoro nie dodałem tej biblioteki, to nie mój problem. Niestety, to Twój problem, bo to Twój serwer wykonuje jej kod.
Sprawdź, kto ją ciągnie:
npm ls nazwa-pakietuDostaniesz drzewko pokazujące drogę od Twojego package.json do podatnej biblioteki. To kluczowa informacja, bo determinuje sposób naprawy. Jeśli pakiet jest Twoją bezpośrednią zależnością, po prostu podbijasz wersję. Jeśli przyszedł z czyjąś, masz trzy wyjścia: zaktualizować pakiet nadrzędny, wymusić bezpieczną wersję wpisem overrides, albo pogodzić się z ryzykiem i zapisać, dlaczego.
Warto przy tym oddzielić podatności osiągalne na produkcji od tych, które dotyczą wyłącznie procesu budowania. Dziura w narzędziu przetwarzającym Twoje własne pliki źródłowe w momencie kompilacji ma inny ciężar niż dziura w kodzie obsługującym ruch z internetu. Napastnik nie poda Ci złośliwych danych do kompilatora, jeśli nie ma dostępu do repozytorium. To nie jest powód, żeby jej nie łatać, tylko powód, żeby wiedzieć, co robić najpierw.
Pułapka pierwsza. „Naprawa", która cofa projekt
Zacznij od wariantu bezpiecznego:
npm audit fixTo polecenie aktualizuje tylko w granicach zakresów zapisanych w package.json, więc nie zmieni wersji głównych. Zwykle zdejmuje większość zgłoszeń.
Dla reszty npm podpowie npm audit fix --force i w tym miejscu trzeba przystanąć. Ta flaga robi wszystko, co uzna za konieczne, łącznie z cofaniem pakietów. Spotykany scenariusz wygląda tak: wtyczka w najnowszym wydaniu przypina sztywno starą wersję swojej zależności, w której ktoś znalazł dziurę. Autor wtyczki nie wypuścił jeszcze poprawki, więc npm proponuje jedyne rozwiązanie, jakie zna, czyli zejście na wydanie sprzed dwóch lat, kiedy ta zależność była inna.
Zanim uruchomisz --force, sprawdź, co konkretnie zrobi:
npm audit fix --force --dry-runJeśli w wyniku zobaczysz numer wersji niższy niż obecny, odpuść. Cofnięcie o kilkanaście wydań oznacza utratę wszystkich poprawek błędów z tego okresu i zwykle rozjazd z resztą projektu. Lek bywa gorszy od choroby.
Gdy u źródła nie ma poprawki
Zostaje wymuszenie bezpiecznej wersji. W package.json dopisujesz sekcję overrides:
{
"overrides": {
"nazwa-zaleznosci-przechodniej": "^7.1.6"
}
}Potem npm install i npm audit powinien zgłosić zero. To działa od npm w wersji 8.3 i rozwiązuje dokładnie ten przypadek: pakiet nadrzędny dostaje nowszą wersję swojej zależności, niż sam deklaruje.
I tu najważniejsze zdanie całego tekstu. Wymuszenie wersji to obejście cudzej deklaracji zgodności, więc sam wpis niczego nie dowodzi. Autor przypiął starą wersję z jakiegoś powodu i może nim być zmiana API. Musisz sprawdzić, czy po podmianie wszystko nadal działa, i zrobić to pomiarem, a nie na oko.
Najprostszy wiarygodny test to porównanie wyniku budowania przed zmianą i po niej. Zapisz sumę kontrolną wygenerowanych plików przed wprowadzeniem wpisu:
npm run build
cat .next/static/chunks/*.css | md5Potem dodaj overrides, przebuduj projekt i porównaj ponownie. Jeśli suma jest identyczna, podmiana nie zmieniła niczego w wyniku i możesz spać spokojnie. Jeśli się różni, zobacz czym:
cat przed/*.css | tr '}' '\n' | sort > a.txt
cat po/*.css | tr '}' '\n' | sort > b.txt
diff a.txt b.txtZero różnic to twardy dowód. Opinia, że „chyba wygląda tak samo", nim nie jest.
Wpis overrides traktuj jako tymczasowy. Gdy autor pakietu nadrzędnego zaktualizuje swoją zależność, wpis trzeba zdjąć, bo inaczej zacznie blokować nowsze wersje. Zanotuj to gdzieś, gdzie ktoś zajrzy, bo sam package.json nie przyjmuje komentarzy.
Pułapka druga. Poprawka tylko w nowej wersji głównej
Czasem jedyne wydanie bez dziury ma wyższy numer główny. To znaczy, że autor miał prawo zepsuć zgodność, ale wcale nie znaczy, że zepsuł ją akurat w miejscu, którego używasz.
Trzy sprawdzenia, w tej kolejności, zajmują kwadrans.
Najpierw wymagania środowiska:
npm view nazwa-pakietu@10.2.1 enginesJeśli pakiet żąda nowszego Node niż masz na produkcji, aktualizacja pakietu właśnie zamieniła się w aktualizację serwera i trzeba to zaplanować inaczej.
Potem lista zmian łamiących. Szukaj pliku CHANGELOG.md w repozytorium autora i wpisu dla wersji z kropką zerową, czyli 10.0.0, a nie dla najnowszej łatki. Zmiany łamiące opisuje się tam, nie w wydaniu 10.2.1.
Na końcu własna powierzchnia użycia. Wypisz, czego naprawdę wołasz z tej biblioteki:
grep -rn "nazwa-pakietu" --include="*.ts" --include="*.js" ./src ./lib ./appJeżeli używasz dwóch funkcji, a zmiany łamiące dotyczą zupełnie innych, ryzyko jest małe. Bywa, że cała lista zmian łamiących sprowadza się do podniesienia minimalnej wersji Node, którą i tak masz. Wtedy skok major jest formalnością, a zwlekanie z nim oznacza tylko dłuższe siedzenie na dziurawej wersji.
Pułapka trzecia. Lockfile, który przechodzi u Ciebie i pada na serwerze
Ta jest najbardziej zdradliwa, bo lokalnie wszystko wygląda dobrze.
Część pakietów ma zależności opcjonalne zależne od platformy, czyli osobne paczki z gotowym kodem binarnym dla każdego systemu i procesora. Biblioteki do obróbki obrazów są tu klasycznym przykładem. Gdy uruchamiasz npm install na macOS, npm zapisuje w package-lock.json to, co jest potrzebne na macOS. Twój serwer buduje obraz na Linuksie, często na Alpine z biblioteką musl, i poleceniem:
npm ciPolecenie npm ci jest celowo rygorystyczne. Nie dogaduje zależności, tylko instaluje dokładnie to, co jest w pliku blokady, a gdy czegoś brakuje, przerywa z błędem:
npm error `npm ci` can only install packages when your package.json and
npm error package-lock.json are in sync.
npm error Missing: @nazwa/runtime@1.11.3 from lock fileEfekt bywa mylący. Budowanie na serwerze pada, poprzednia wersja aplikacji działa dalej, więc strona wygląda normalnie i nikt nie zauważa, że wdrożenie nie weszło. Można tak przegapić łatkę na poważną dziurę.
Naprawa jest prosta. Usuwasz plik blokady razem z katalogiem pakietów i generujesz go od nowa:
rm -rf node_modules package-lock.json
npm installWeryfikacja jest ważniejsza od naprawy i to ona najczęściej zostaje pominięta. Lokalne npm ci niczego nie dowodzi, bo przechodzi na Twoim systemie. Sprawdź w tym środowisku, w którym naprawdę buduje serwer:
docker run --rm --platform linux/amd64 \
-v "$(pwd)/package.json:/t/package.json:ro" \
-v "$(pwd)/package-lock.json:/t/package-lock.json:ro" \
-w /t node:24-alpine npm ciTrzydzieści sekund, a odpowiada na jedyne pytanie, które się liczy: czy to się zbuduje tam, gdzie ma się zbudować. Jeśli Twoje wdrożenie korzysta z obrazu kontenera, jeszcze lepszym testem jest zbudowanie go lokalnie dokładnie tym samym plikiem Dockerfile i uruchomienie.
Pakiety to nie wszystko. Interpreter też ma wydania bezpieczeństwa
Łatwo wpaść w rutynę pilnowania npm audit i przegapić, że sam Node dostaje poprawki. Wydanie z 21 października 2026 zamykało cztery dziury o wysokiej wadze, w tym zapis poza buforem w dekoderze Brotli prowadzący do zdalnego wykonania kodu oraz trzy obejścia weryfikacji nazwy hosta przy połączeniach TLS i DNS. Dotyczyły wszystkich wspieranych linii.
Sprawdzenie, na czym stoisz:
node -vAktualizacja przy zarządzaniu wersjami przez nvm, z przeniesieniem pakietów globalnych:
nvm install 24.22.1 --reinstall-packages-from=24.12.0
nvm alias default 24.22.1Po aktualizacji warto posprzątać, bo stare wersje zostają na dysku i każda zajmuje kilkaset megabajtów:
nvm ls
nvm uninstall 24.12.0Jedna uwaga o produkcji. Jeśli Twój obraz zaczyna się od wiersza FROM node:24-alpine, czyli od tagu bez numeru łatki, to każde kolejne budowanie pobiera najnowsze wydanie tej linii i łatka wchodzi sama. Jeśli masz tam wersję przypiętą co do kropki, trzeba ją podbić ręcznie i nikt o tym nie przypomni.
Checklista
Do powtórzenia raz w miesiącu, albo po każdym komunikacie o poważnej podatności.
npm auditi spojrzenie na wagi, nie tylko na liczbę.npm ls nazwa-pakietuprzy każdym zgłoszeniu, żeby wiedzieć, kto je ciągnie.npm audit fixbez flagi wymuszającej jako pierwszy krok.- Przy propozycji z
--forcesprawdzenie, czy nie proponuje cofnięcia wersji. - Dla zależności przechodnich bez poprawki u źródła wpis
overridesi porównanie wyniku budowania przed i po. - Przy skoku na nową wersję główną sprawdzenie wymagań środowiska, listy zmian łamiących i własnej powierzchni użycia.
npm ciw obrazie identycznym z produkcyjnym, zanim cokolwiek trafi na serwer.node -vi porównanie z aktualnym wydaniem wspieranej linii.
Nie chcesz tego pilnować samodzielnie
To praca, która nie daje nic widocznego, dopóki czegoś się nie przegapi. Nie przybywa od niej funkcji ani szybkości, a zajmuje kilka godzin miesięcznie i wymaga czytania komunikatów o podatnościach w kilku ekosystemach naraz.
W ramach opieki nad stroną wydania bezpieczeństwa wgrywamy poza zwykłym cyklem przeglądów, po sprawdzeniu, czy nie zmieniają zachowania aplikacji, a kopia zapasowa na osobnym serwerze daje punkt powrotu. Jeśli prowadzisz projekt, w którym nikt nie ma przypisanego obowiązku patrzenia na te komunikaty, napisz do nas albo wyceń opiekę.
Dziura w bibliotece, której nazwy nigdy nie wpisałeś do swojego projektu, działa dokładnie tak samo jak każda inna. Różnica polega tylko na tym, że nikt Ci o niej nie powie, dopóki sam nie zapytasz.

O autorze
Mirosław Parcz
Programista i administrator serwerów
Programista i administrator serwerów z 16+ letnim doświadczeniem. Specjalizacja: WordPress/WooCommerce, aplikacje webowe, hosting, wydajność, bezpieczeństwo, integracje AI.
Spodobało się? Podaj dalej.
Powiązane artykuły

WordPress 7.1.2 łata krytyczną lukę. Atak bez logowania, podatne wersje od 4.7
CVE-2026-87902 to krytyczna luka w rdzeniu WordPressa, którą można wykorzystać bez konta i która w sprzyjających warunkach daje zdalne wykonanie kodu. Kogo dotyczy, jak w dwie minuty sprawdzić własną stronę i jak zaktualizować bez niespodzianek.

PHP 8.2 traci wsparcie 31 grudnia. Jaka wersja PHP dla WordPressa na 2027
Koniec wsparcia PHP 8.2 wypada 31 grudnia 2026, a PHP 8.1 jest bez łatek od grudnia 2025. Tabela wersji PHP z datami, ryzyka dla strony na starym PHP, rekomendacja której wersji użyć i procedura bezpiecznej zmiany bez wywalenia strony.

AI Act od 2 sierpnia 2026. Co musisz zmienić na stronie WordPress, jeśli używasz sztucznej inteligencji
Drugiego sierpnia 2026 wchodzą w życie nowe obowiązki dotyczące przejrzystości sztucznej inteligencji. Dotyczą każdej polskiej firmy z czatem AI na stronie (Tidio, LiveChat, Smartsupp), obrazami z Midjourney lub artykułami pisanymi z pomocą ChatGPT. Sprawdź czy Cię dotyczą, jakie są kary do 15 milionów euro i jak wdrożyć rozwiązanie w WordPress w trzech wariantach: gotową wtyczką, w polskim czacie albo własnym kodem.
Potrzebujesz pomocy?
mDiv może zająć się tym za Ciebie. Bezpiecznie, z backupem i bez przestojów.
Napisz do mnie