0% прочитано

npm audit у реальному Laravel/Vite проєкті: чому я не запустила npm audit fix --force

У Laravel/Vite проєкті ICanUp npm audit показав 9 vulnerabilities, серед них 2 critical і 5 high. Найпростіше було б запустити npm audit fix --force, але я цього не зробила. Спочатку я розібрала dependency tree, відокремила прямі залежності від транзитивних, перевірила dev і production context, а вже потім застосовувала безпечні оновлення та повторно запускала audit і build.

14 вересня 2026 р. 10 хв читанняDevOps

Під час перевірки frontend залежностей у ICanUp я отримала результат, який виглядав достатньо серйозно, щоб захотіти виправити все однією командою.

npm audit показав:

9 vulnerabilities: 1 low, 1 moderate, 5 high і 2 critical.

npm одразу пропонує команди для автоматичного виправлення, але я свідомо не запустила npm audit fix --force.

Перед зміною дерева залежностей я хотіла зрозуміти, які саме пакети вразливі, звідки вони прийшли і що насправді змінить автоматичне виправлення.

Початковий результат npm audit
9 vulnerabilities 1 low1 moderate5 high2 critical
Ступінь тяжкості у npm audit є важливим сигналом, але вона не відповідає автоматично на питання, наскільки конкретна вразливість доступна для експлуатації саме у цьому застосунку. Спочатку потрібно побачити path залежності і контекст виконання.
npm audit in a Laravel and Vite project showing critical and high vulnerabilities before dependency analysis
Замість npm audit fix --force я спочатку розклала звіт про прямі залежності, транзитивні залежності, контекст виконання і безпечні шляхи оновлення.

Перший крок - зберегти фактичний audit report

Я почала не з автоматичного виправлення, а з повторюваної перевірки поточного dependency state.

Це важливо, тому що результат npm audit залежить від реального package-lock.json, а не просто від назв у package.json.

BASH - Запустити аудит безпеки
npm audit
BASH - Зберегти машинозчитуваний знімок аудиту
npm audit --json > /tmp/npm-audit.json

Під час роботи кількість результатів змінювалася разом зі станом дерева залежностей. В одному з наступних знімків було вже 7 вразливостей, а пізніше - 4.

Тому число знімків я не сприймаю як постійну характеристику проєкту. Це характеристика конкретного lockfile у конкретний момент.

Я не почала з npm audit fix --force

Опція --force приваблива тим, що обіцяє прибрати більше висновків автоматично.

Але вона може дозволити оновлення, які виходять за межі сумісних версій верхньорівневих залежностей.

Для продакшн проєкту це означає, що виправлення безпеки може одночасно стати framework або tooling migration.

BASH - Команда, яку я не запускала одразу
# I did NOT start with this:npm audit fix --force
Моя мета не була отримати "0 vulnerabilities" будь-якою ціною. Мета - прибрати реальний ризик, не зламавши build, runtime або development workflow неконтрольованим major upgrade.

Спочатку потрібно зрозуміти direct і transitive dependencies

Audit report може показувати пакет, якого я ніколи не додавала в package.json.

Це не помилка. Багато vulnerabilities приходять через транзитивні залежності.

Тому наступне питання для кожного finding: хто саме підтягнув цей package?

BASH - Подивитися dependency paths для висновків
npm ls \  shell-quote \  concurrently \  vite \  axios \  esbuild \  form-data \  immutable
BASH - Пояснити, чому пакет є в dependency tree
npm explain shell-quote npm explain vite

Критична shell-quote не була окремою прямою залежністю

Один із висновків стосувався shell-quote і мав критичну важкість.

Але shell-quote не був пакетом, який я свідомо встановлювала для коду програми. У дереві залежностей він приходив через concurrently@9.2.1.

Отже, правильне місце для рішення знаходиться вище в дереві залежностей, а не обов'язково в самому вразливому пакеті.

Спрощений dependency path
ICanUp  |  vconcurrently@9.2.1  |  vshell-quote  |  vcritical advisory

Vite теж був частиною audit report

Серед висновків був і vite з високою тяжкістю.

У Laravel/Vite проєкті це вже інший тип залежності: Vite є важливою частиною frontend набору інструментів, тому оновлення потрібно перевіряти не тільки через повторний audit, але й через реальний production build.

BASH - Перевірити встановлений Vite і доступну версію
npm ls vite npm view vite version

Audit report містив не одну технологію

Під час аналізу я також бачила findings, пов'язані з Axios, esbuild, form-data та immutable.

Це ще одна причина не реагувати на весь report однією командою.

Різні packages можуть мати різні dependency paths, різний runtime context і різну ціну оновлення.

Package / area

Що я перевіряю

shell-quote

Який пакет підтягнув цю залежність і чи достатньо оновити батьківський пакет

concurrently

Чи це пряма залежність для розробки і яке оновлення змінює дерево залежностей

vite

Чи залишається збірка сумісною після оновлення Vite

Axios / form-data

Ланцюжок залежностей і де цей код реально виконується

esbuild

Чи використовується пакет лише під час збірки та яку його версію використовує інструментарій збірки

immutable

Чи це пряма або транзитивна залежність і чи вона взагалі ще потрібна

Рівень критичності й реальний ризик для production - не одне й те саме

Уразливість із критичним рівнем завжди потребує уваги, але сам рівень критичності ще не описує архітектуру конкретного проєкту.

У Laravel/Vite-застосунку частина Node-пакетів використовується лише під час розробки або збірки й узагалі не виконується як Node-код на production-сервері.

Інші пакети можуть потрапляти у браузерну збірку або використовуватися безпосередньо кодом застосунку.

Я не використовую це як привід ігнорувати вразливості залежностей для розробки. Це допомагає правильно визначити їхній пріоритет, реальний ризик і спосіб виправлення.

BASH - Окремо подивитися non-dev dependency exposure
npm audit --omit=dev
npm audit --omit=dev не замінює повну перевірку. Він допомагає окремо оцінити залежності, що використовуються поза середовищем розробки, але вразливості інструментів збірки та залежностей для розробки все одно потрібно перевірити.

Перед виправленням я перевіряю, що саме npm планує змінити

Між "нічого не робити" і npm audit fix --force є набагато безпечніший проміжний крок.

Спочатку можна подивитися, що npm здатний виправити без примусового major upgrade.

BASH - Перевірити заплановані безпечні зміни
npm audit fix --dry-run

Після цього я можу окремо оцінити зміни у прямих залежностях і зрозуміти, чи потрібно вручну оновити конкретний пакет.

Звичайне та примусове виправлення - це різні рішення

Підхід

Що він означає для мене

npm audit fix

Спробувати сумісні оновлення залежностей і потім перевірити зміни

Manual package upgrade

Контрольовано оновити конкретну пряму залежність

npm audit fix --force

Дозволити агресивніші зміни, які можуть вимагати переходу на нову основну версію та окремого тестування

Leave finding temporarily

Лише якщо ризик зрозумілий, причина зафіксована і є чіткий план усунення

Після кожного кроку я перевіряю зміни

Автоматична команда для виправлення вразливостей не звільняє мене від перевірки змін.

Навіть якщо переважно змінюється package-lock.json, я хочу бачити, які версії прямих і транзитивних залежностей реально оновилися.

BASH - Перевірити зміни залежностей
git diff -- \  package.json \  package-lock.json
BASH - Перевірити прямі залежності після змін
npm ls --depth=0

Перевірку потрібно повторювати після кожної суттєвої зміни

Після безпечних оновлень я не вважаю задачу завершеною лише тому, що npm install виконався без помилок.

Я знову запускаю npm audit і порівнюю результат із попередньою перевіркою.

BASH - Повторно запустити npm audit після змін залежностей
npm audit

В одному з пізніших знімків перевірки результат уже був іншим:

Результат повторної перевірки
4 vulnerabilities 1 moderate3 high

У цьому результаті критичних вразливостей уже не було.

Для мене це набагато корисніший прогрес, ніж сліпо змусити npm переписати дерево залежностей лише заради того, щоб у звіті з’явився нуль.

Нуль вразливостей - не єдиний критерій завершення

Робота з безпекою не завжди завершується ідеальним результатом found 0 vulnerabilities за один commit.

Іноді вразливість залишається в транзитивній залежності, для якої ще немає сумісного виправлення у вищій залежності.

У такому разі я хочу знати три речі: де ця залежність використовується, чи може вона реально впливати на роботу застосунку у production і яке оновлення зрештою усуне вразливість.

Залишкова вразливість без пояснення - поганий результат. Якщо ж я перевірила її ланцюжок залежностей, розумію реальний ризик і маю план оновлення, це вже керований технічний борг.

Виправлення вразливостей має пройти збірку

Після змін у залежностях мені важливо перевірити не тільки результат npm audit.

Vite, esbuild та інші залежності frontend-частини безпосередньо впливають на збірку застосунку.

BASH - Перебудувати production frontend
npm run build

Якщо після оновлення залежностей результат npm audit став кращим, але production-збірка більше не проходить, роботу ще не завершено.

Потім я перевіряю сам застосунок, а не лише npm

  • Production-збірка завершується без помилок.

  • Основні тести frontend-частини проходять.

  • Публічні сторінки завантажуються коректно.

  • Адмінка працює.

  • Зібрані Vite-файли коректно підключені.

  • SSR, якщо він використовує ту саму frontend-збірку, продовжує запускатися.

  • У консолі браузера немає нових помилок виконання.

  • Повторний npm audit показує очікуваний результат.

Чому npm audit fix --force міг перетворити виправлення на окрему задачу

Якщо для усунення вразливості потрібно перейти на несумісну основну версію прямої залежності, це вже не просто виправлення безпеки.

Це окреме оновлення, для якого потрібно переглянути опис змін, перевірити конфігурацію та виконати регресійне тестування.

Я не хочу приховувати таке велике оновлення всередині однієї автоматичної команди для виправлення вразливостей.

Різні способи усунення вразливостей
Security finding      |      vDoes a compatible fix exist?   /               \ yes                no  |                  |  v                  vsafe update       evaluate major upgrade  |                  |  v                  vaudit + build     migration + regression tests

Що я не роблю після тривожного результату npm audit

  • Не запускаю npm audit fix --force автоматично, не розібравшись у причинах.

  • Не оцінюю ризик лише за загальною кількістю вразливостей.

  • Не вважаю кожну транзитивну залежність прямою залежністю застосунку.

  • Не ігнорую критичну вразливість лише тому, що пакет використовується тільки під час розробки.

  • Не оновлюю всі пакети одночасно без перевірки змін.

  • Не вважаю успішний npm install достатньою перевіркою після оновлення.

  • Не прагну отримати нуль вразливостей ціною неконтрольованого переходу на нові основні версії залежностей.

  • Не залишаю невиправлену вразливість із високим або критичним рівнем без пояснення та плану дій.

Мій порядок перевірки після npm audit

  • Зафіксувати повний результат npm audit.

  • Зберегти JSON-знімок, якщо потрібно порівнювати стан до і після змін.

  • Визначити прямі та транзитивні ланцюжки залежностей.

  • Окремо оцінити використання залежностей у production та під час розробки.

  • Для вразливостей із високим і критичним рівнем перевірити опис проблеми та версії, яких вона стосується.

  • Переглянути результат npm audit fix --dry-run перед застосуванням змін.

  • Застосовувати сумісні оновлення окремими контрольованими кроками.

  • Перевіряти зміни у package.json і package-lock.json.

  • Повторно запускати npm audit після змін.

  • Запускати production-збірку та регресійні тести.

  • Для невиправлених вразливостей зафіксувати ланцюжок залежностей, реальний ризик і план подальшого оновлення.

Цей випадок змінив моє ставлення до автоматичних виправлень вразливостей

Раніше було легко сприймати npm audit як команду, яка має завершитися зеленим статусом.

На практиці npm audit - це початок перевірки й пошуку причини, а не готове рішення.

Особливо добре це видно у Laravel/Vite-проєкті, де дерево Node-залежностей одночасно містить пакети для браузера, інструменти збірки та залежності для розробки.

npm audit показує, де шукати проблему. Але він не може вирішити замість мене, чи безпечний для проєкту перехід на несумісну основну версію.

Що я залишила собі на майбутнє

  • Зберігати результат npm audit перед змінами у залежностях.

  • Вразливості з критичним рівнем перевіряти першими, але завжди разом із ланцюжком залежностей.

  • Розрізняти прямі та транзитивні залежності.

  • Окремо оцінювати ризики під час збірки, розробки та роботи застосунку у production.

  • Спочатку застосовувати сумісні виправлення, а перехід на нову основну версію розглядати окремо.

  • Перед змінами дивитися npm audit fix --dry-run, а після змін - git diff.

  • Після оновлення залежностей обов'язково повторювати npm audit.

  • Не вважати виправлення вразливостей завершеним, доки production-збірка не проходить успішно.

  • Не прагнути отримати нуль вразливостей ціною неконтрольованого великого оновлення.

Висновок

Початкова перевірка у <span style="color: rgb(249, 115, 22);">ICanUp</span> показала 9 вразливостей, серед них 2 критичні та 5 із високим рівнем. Це точно не було сигналом, що проблему можна ігнорувати.

Але це також не означало, що потрібно негайно запускати npm audit fix --force.

Я перевірила ланцюжки залежностей, знайшла критичну вразливість у shell-quote, який підтягувався через concurrently, окремо перевірила Vite та інші проблемні пакети, а потім застосовувала контрольовані оновлення. Після кожного етапу я повторювала npm audit і перевіряла production-збірку.

Пізніше перевірка показувала вже 4 залишкові вразливості без критичних.

Для мене хороше виправлення вразливості - це не найкоротша команда. Це зміна, після якої я розумію, що саме оновилося, який ризик усунуто і чи продовжує застосунок працювати правильно.