При веб-разработке не забывайте фиксировать библиотеки, подгружаемые с публичных CDN
Небольшой дисклеймер.
Данная тема кстати касается не только подгрузки на сайте различных JS, CSS и пр. ресурсов с публичных CDN, но и вообще любого рода проектов, которые подгружают зависимости с удаленных источников (репозитории, CDN и пр). Т.е. это также касается работы с NPM, Composer, PIP и пр.
В статье я обсуждаю фиксацию зависимостей на проекте на примере CDN только потому, что эта проблема недавно коснулась меня.
Некоторое время назад встала задача внести небольшие доработки в сайт, который я разрабатывал еще в начале 2021г. Перед тем, как приступить к работе, я обратил внимание, что по какой-то причине, на сайте в некоторых местах "поехала" верстка, особенно в районе слайдеров. Т.к. на момент сдачи проекта, сайт был хорошо проверен и стабильно работал, меня такая проблема насторожила и я заинтересовался источником ее возникновения, ибо мало ли что могло с сайтом нехорошее произойти.
После непродолжительной отладки и копания в коде, на деле все оказалось достаточно просто. Суть в том, что для слайдера на сайте использовалась библиотека Swiper 6-ой версии (самой свежей на момент разработки сайта), которая подгружалась с публичного CDN, но без явного указания требуемой (т.е. 6-ой) версии и поэтому с CDN по умолчанию грузилась самая последняя доступная версия, что со временем и стало корнем проблемы.
Со временем, в последующих версиях Swiper, стали появляться изменения, которые нарушали обратную совместимость с прошлыми версиями. Например, в Swiper 7 поменяли CSS-класс по умолчанию для контейнера слайдера, с .swiper-container
на .swiper
, что привело к проблемам в верстке, где есть зависимость от старого именования классов, а также в скриптах, в которых используется поиск элемента по CSS-селектору. Помимо этого, была еще куча мелких проблем с совместимостью, но основная суть статьи все-таки не в этом.
Пример выше показывает то, что со временем, в любом программном коде могут появиться изменения, которые не будут обратно-совместимыми. Это впринципе нормальное явление для любого постоянно развивающегося проекта, но проблема в том, что не все следят за историей изменений в коде, чтобы знать о возможных проблемах при обновлении.
Семантическое версионирование (SemVer)
Чтобы хоть как-то подсказать пользователю о классификации грядущих изменений вроде бы как призвано cемантическое версионирование (SemVer), состоящее из нескольких разделенных точкой чисел (например 2.13.9), в которых первое число обычно представляет собой мажорную версию, изменение которой указывает на наличие кардинальных изменений в ПО, в т.ч. нарушающих обратную совместимость. Но даже изменение минорной версии, не даёт гарантию того, что что-нибудь не поломается в случаях:
- при использовании библиотек, могли использоваться корявые костыли для обхода какого-то бага, который мог быть исправлен в минорной версии программы. Соответственно источником проблем может стать сам костыль. А всегда ли вы и ваши коллеги помечают в коде костыли?
- Никто вам не гарантирует, что разработчики библиотеки всегда будут следовать правилам версионирования и в какой-то момент не внесут в минорную версию изменения, без обратной совместимости, ибо никто не может им этого запретить.
- Иногда некоторые исправления багов могут быть хуже самих багов и избавляясь от одних проблем, вы можете столкнуться с другими. Такие исправления могут нести временный характер до тех пор, пока не будет найдено более оптимальное решение.
Что можно посоветовать в таком случае, ведь если не обновлять библиотеки, то и исправления не получится получать, что тоже очень плохо. Для начала, все-таки стоит стараться фиксировать мажорные версии используемых библиотек (например 2.xxx.xxx) или в случае сомнений, минорные версии (например 2.13.xxx).
Ну а в случае вообще очень больших сомнений или высоких требований к надёжности, используйте e2e-тесты (Puppeteer, Cypress и пр.), которые могут выявить критичные отклонения и автоматизируйте их запуск перед каждым деплоем обновленной версии проекта.
Также с т.з. безопасности, я бы всё-таки добавил, что очень желательно обновлять на те версии, с которыми вы уже где-то работали и в которых вы более-менее уверены, поскольку в нынешних реалиях с обновлением может прийти черт знает какая нехорошая гадость (майнеры, нехорошие баннеры, редиректы пользователей на другие сайты и пр. зловредные штучки).
Атрибут integrity
Никто не дает гарантии, что CDN не взломают и не будет происходить какой-нибудь нехорошей подмены уже существующих файлов. Поэтому, помимо фиксации версии библиотек не забывайте всегда указывать у тегов <link>
и <script>
атрибут integrity
, как например тут:
<!-- Пример загрузки с CDN Swiper 6-ой версии -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/6.8.4/swiper-bundle.min.js" integrity="sha512-BABFxitBmYt44N6n1NIJkGOsNaVaCs/GpaJwDktrfkWIBFnMD6p5l9m+Kc/4SLJSJ4mYf+cstX98NYrsG/M9ag==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Браузер проверит совпадение хещ-суммы загружаемого файла с указанной в атрибуте integrity
и в случае их несовпадения, файл будет блокироваться.
Заключение
При разработке проектов, каждый может ошибаться и я в том числе. Не ошибается только тот, кто ничего не делает. В том проекте проблема со Swiper была не критичной, но вполне поучительной и явно на практике показала, что отсутствие фиксации зависимостей, подгружаемых с CDN, со временем так или иначе приводит к проблемам на сайте.
Ну а для себя я на всякий случай в чек-лист добавил обязательную проверку на приемлемую фиксацию зависимостей в проектах при ревью кода, чего и вам рекомендую.