Під час перевірки frontend залежностей у ICanUp я отримала результат, який виглядав достатньо серйозно, щоб захотіти виправити все однією командою.
npm audit показав:
9 vulnerabilities: 1 low, 1 moderate, 5 high і 2 critical.
npm одразу пропонує команди для автоматичного виправлення, але я свідомо не запустила npm audit fix --force.
Перед зміною дерева залежностей я хотіла зрозуміти, які саме пакети вразливі, звідки вони прийшли і що насправді змінить автоматичне виправлення.
9 vulnerabilities 1 low1 moderate5 high2 critical
Перший крок - зберегти фактичний audit report
Я почала не з автоматичного виправлення, а з повторюваної перевірки поточного dependency state.
Це важливо, тому що результат npm audit залежить від реального package-lock.json, а не просто від назв у package.json.
npm auditnpm audit --json > /tmp/npm-audit.jsonПід час роботи кількість результатів змінювалася разом зі станом дерева залежностей. В одному з наступних знімків було вже 7 вразливостей, а пізніше - 4.
Тому число знімків я не сприймаю як постійну характеристику проєкту. Це характеристика конкретного lockfile у конкретний момент.
Я не почала з npm audit fix --force
Опція --force приваблива тим, що обіцяє прибрати більше висновків автоматично.
Але вона може дозволити оновлення, які виходять за межі сумісних версій верхньорівневих залежностей.
Для продакшн проєкту це означає, що виправлення безпеки може одночасно стати framework або tooling migration.
# I did NOT start with this:npm audit fix --forceСпочатку потрібно зрозуміти direct і transitive dependencies
Audit report може показувати пакет, якого я ніколи не додавала в package.json.
Це не помилка. Багато vulnerabilities приходять через транзитивні залежності.
Тому наступне питання для кожного finding: хто саме підтягнув цей package?
npm ls \ shell-quote \ concurrently \ vite \ axios \ esbuild \ form-data \ immutablenpm explain shell-quote npm explain viteКритична shell-quote не була окремою прямою залежністю
Один із висновків стосувався shell-quote і мав критичну важкість.
Але shell-quote не був пакетом, який я свідомо встановлювала для коду програми. У дереві залежностей він приходив через concurrently@9.2.1.
Отже, правильне місце для рішення знаходиться вище в дереві залежностей, а не обов'язково в самому вразливому пакеті.
ICanUp | vconcurrently@9.2.1 | vshell-quote | vcritical advisoryVite теж був частиною audit report
Серед висновків був і vite з високою тяжкістю.
У Laravel/Vite проєкті це вже інший тип залежності: Vite є важливою частиною frontend набору інструментів, тому оновлення потрібно перевіряти не тільки через повторний audit, але й через реальний production build.
npm ls vite npm view vite versionAudit report містив не одну технологію
Під час аналізу я також бачила findings, пов'язані з Axios, esbuild, form-data та immutable.
Це ще одна причина не реагувати на весь report однією командою.
Різні packages можуть мати різні dependency paths, різний runtime context і різну ціну оновлення.
Package / area | Що я перевіряю |
|---|---|
| Який пакет підтягнув цю залежність і чи достатньо оновити батьківський пакет |
| Чи це пряма залежність для розробки і яке оновлення змінює дерево залежностей |
| Чи залишається збірка сумісною після оновлення Vite |
Axios / form-data | Ланцюжок залежностей і де цей код реально виконується |
esbuild | Чи використовується пакет лише під час збірки та яку його версію використовує інструментарій збірки |
immutable | Чи це пряма або транзитивна залежність і чи вона взагалі ще потрібна |
Рівень критичності й реальний ризик для production - не одне й те саме
Уразливість із критичним рівнем завжди потребує уваги, але сам рівень критичності ще не описує архітектуру конкретного проєкту.
У Laravel/Vite-застосунку частина Node-пакетів використовується лише під час розробки або збірки й узагалі не виконується як Node-код на production-сервері.
Інші пакети можуть потрапляти у браузерну збірку або використовуватися безпосередньо кодом застосунку.
Я не використовую це як привід ігнорувати вразливості залежностей для розробки. Це допомагає правильно визначити їхній пріоритет, реальний ризик і спосіб виправлення.
npm audit --omit=devПеред виправленням я перевіряю, що саме npm планує змінити
Між "нічого не робити" і npm audit fix --force є набагато безпечніший проміжний крок.
Спочатку можна подивитися, що npm здатний виправити без примусового major upgrade.
npm audit fix --dry-runПісля цього я можу окремо оцінити зміни у прямих залежностях і зрозуміти, чи потрібно вручну оновити конкретний пакет.
Звичайне та примусове виправлення - це різні рішення
Підхід | Що він означає для мене |
|---|---|
| Спробувати сумісні оновлення залежностей і потім перевірити зміни |
Manual package upgrade | Контрольовано оновити конкретну пряму залежність |
| Дозволити агресивніші зміни, які можуть вимагати переходу на нову основну версію та окремого тестування |
Leave finding temporarily | Лише якщо ризик зрозумілий, причина зафіксована і є чіткий план усунення |
Після кожного кроку я перевіряю зміни
Автоматична команда для виправлення вразливостей не звільняє мене від перевірки змін.
Навіть якщо переважно змінюється package-lock.json, я хочу бачити, які версії прямих і транзитивних залежностей реально оновилися.
git diff -- \ package.json \ package-lock.jsonnpm ls --depth=0Перевірку потрібно повторювати після кожної суттєвої зміни
Після безпечних оновлень я не вважаю задачу завершеною лише тому, що npm install виконався без помилок.
Я знову запускаю npm audit і порівнюю результат із попередньою перевіркою.
npm auditВ одному з пізніших знімків перевірки результат уже був іншим:
4 vulnerabilities 1 moderate3 highУ цьому результаті критичних вразливостей уже не було.
Для мене це набагато корисніший прогрес, ніж сліпо змусити npm переписати дерево залежностей лише заради того, щоб у звіті з’явився нуль.
Нуль вразливостей - не єдиний критерій завершення
Робота з безпекою не завжди завершується ідеальним результатом found 0 vulnerabilities за один commit.
Іноді вразливість залишається в транзитивній залежності, для якої ще немає сумісного виправлення у вищій залежності.
У такому разі я хочу знати три речі: де ця залежність використовується, чи може вона реально впливати на роботу застосунку у production і яке оновлення зрештою усуне вразливість.
Виправлення вразливостей має пройти збірку
Після змін у залежностях мені важливо перевірити не тільки результат npm audit.
Vite, esbuild та інші залежності 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 залишкові вразливості без критичних.
Для мене хороше виправлення вразливості - це не найкоротша команда. Це зміна, після якої я розумію, що саме оновилося, який ризик усунуто і чи продовжує застосунок працювати правильно.



