История left-pad, самого нелепого апокалипсиса в истории open-source. Заодно про то, почему миф о надёжности открытого кода не выдерживает проверки фактами, и про деталь, которую из этой истории обычно выбрасывают.

Что должно было случиться

В 2016 году весь мир писал на JavaScript, и почти всё, что тогда писали, так или иначе проходило через Babel: React, Vue, Ember, webpack, редактор Atom. Babel работал на Node.js, Node.js тянул пакеты из NPM, а NPM к тому моменту стал крупнейшим репозиторием пакетов в истории. Обычная матрёшка, в которую никто не заглядывает, пока она собирается.

Где-то в глубине этой матрёшки лежал пакет left-pad. Он дополнял строку слева до нужной длины. Берёт строку, длину, символ-заполнитель. Не передал символ, получишь пробелы; передал ноль, получишь 00005. Одиннадцать строк, если не считать пустые. Написан на коленке, выложен в марте 2014-го.

Напрямую его почти никто не подключал. Он приезжал транзитивно, вместе с чем-то другим, и к марту 2016-го набрал больше пятнадцати миллионов скачиваний. Подавляющее большинство скачивающих про его существование не знали вообще.

Это, в общем, нормальная жизнь в JS-экосистеме. Нужна функция, ставишь пакет. Нужны одиннадцать строк, ставишь пакет на одиннадцать строк. Так было устроено всё, и до 22 марта 2016 года никого это особо не смущало.

Что произошло

Программиста звали Азер Кочулу (Azer Koçulu), жил в Окленде. Среди его пакетов был один под названием kik, небольшой генератор шаблонов для новых проектов.

11 марта 2016-го ему написала компания Kik Interactive. Написали довольно вежливо: у нас зарегистрирована марка, ваш пакет с таким именем нас смущает, давайте обсудим компенсацию.

Азер отказался. Запросил тридцать тысяч долларов «за то, что приходится расставаться со своим проектом ради кучки корпоративных придурков», и дальше в переписке была ещё одна фраза, которую в приличном изложении обычно заменяют многоточием. Её лучше прочитать в оригинале, чем пересказывать.

Kik пошла в NPM. 18 марта Айзек Шлютер, гендиректор компании, написал обеим сторонам, что имя kik передаётся Kik Interactive вручную. Пакет Азера переименовали, его согласия никто не спрашивал.

Азер ответил, что не хочет больше иметь дела с платформой, и спросил, как ему быстро убрать оттуда всё остальное.

Вот тут начинается часть, которую в пересказах обычно теряют. Скрипт для массового удаления ему прислали из NPM. Шлютер дал команду, которая снимала с публикации все 273 пакета разом. Двадцать второго числа Азер её выполнил, а через час написал на Medium пост под заголовком «I've Just Liberated My Modules».

Среди этих 273 был left-pad.

Что случилось дальше

Babel перестал устанавливаться. Цепочка там была такая: babel-core зависел от пакета line-numbers авторства Саймона Лиделла, а line-numbers требовал left-pad версии 0.0.3, прибитой гвоздями.

Следом посыпалось всё остальное: React Native, Ember, webpack, Atom. Тысячи сборок в CI ложились на npm install с одинаковой строкой, которую половина фронтенда того года до сих пор помнит наизусть:

npm ERR! 404 'left-pad' is not in the npm registry.

Дальше было несколько часов, которые лучше всего описываются словом «паника». Люди писали, что у них упал прод, что не выходит задеплоить, что вообще происходит. Reddit и Stack Overflow стояли на ушах.

Сообщество попробовало починиться само, и это отдельный сюжет. Разработчик Кэмерон Уэстленд оперативно опубликовал свой left-pad, версия 1.0.0. Не помогло почти никому: line-numbers просил именно 0.0.3, а такой версии в реестре больше не было. Нужен был тот же номер, а не замена посвежее.

Выяснилось, что один человек с 273 пакетами держал заметную часть JavaScript-экосистемы. Причём держал не потому, что писал что-то выдающееся. Его код был мелкий, скучный, подключённый везде. Как гвоздь в стене.

Чем всё закончилось

NPM вернула left-pad 0.0.3 в реестр принудительно. Решение принимала Лори Восс, тогдашний технический директор: автор удалил пакет добровольно, а компания это удаление откатила.

До того дня NPM никогда не отменяла удаление против воли автора. Выбор стоял между уважением к праву человека на свой код и тем, чтобы половина интернета продолжала собираться. Выбрали второе. Я, честно говоря, не знаю, как тут можно было поступить иначе, и подозреваю, что никак.

Была и вторая проблема, о которой вспоминают реже. Освободившиеся имена его пакетов начали разбирать посторонние, заливая туда неизвестно что. Кроме дыры в сборках получилось ещё и удобное окно для подмены, то есть ровно то, что мы сегодня называем атакой на цепочку поставок, а тогда никак не называли.

Правила после этого меняли дважды, и путаница в пересказах идёт как раз отсюда. Сначала, в 2016-м, запретили снимать с публикации пакет, от которого кто-то зависит. Нынешняя редакция появилась только в январе 2020 года. Удалить свободно можно в первые 72 часа после публикации, и то при условии, что зависимых нет. Позже нужно выполнить три условия сразу: ни одного зависимого пакета в публичном реестре, меньше трёхсот скачиваний за неделю, единственный владелец. Именно скачиваний. «Триста зависимостей», которые кочуют по статьям, — это чья-то ошибка десятилетней давности, разошедшаяся копипастой.

Сам Азер из индустрии на время вышел, уехал путешествовать и поменял приоритеты; сейчас занимается бизнесом и маркетингом наравне с кодом. Совсем он никуда не пропадал. Спустя восемь лет он впервые подробно рассказал свою версию событий, и главный акцент там как раз на том, что рубильник ему выдали в NPM.

left-pad жив до сих пор, его ведёт другой человек, и его по-прежнему тянут тысячи проектов.

Почему это не «просто инцидент»

Через неделю после событий вышла куча статей, и почти все сводились к одним и тем же моралям. Open-source хрупок. Не надо зависеть от мелких пакетов. Азер поступил непрофессионально. По-моему, мимо все три.

Хрупкость тут — свойство не системы, а способа, которым мы ей пользуемся. Мы сами построили мир, где npm install тянет сотни мегабайт ради одной функции, сами решили, что не писать одиннадцать строк самому — это норма и экономия, а потом удивляемся, что одиннадцать чужих строк роняют прод.

С непрофессионализмом получается ещё интереснее. Красную кнопку Азер не искал, её ему принесли, и принесла её ровно та компания, которая за четыре дня до этого забрала у него имя пакета. Если в системе есть механизм, способный вынести четверть тысячи пакетов одним вызовом, и вы вручаете его человеку в разгар конфликта, то вопрос уже совсем не к человеку.

У самой NPM тоже не было наготове ничего. Ни политики на такой случай, ни процедуры отката, ни внятного плана, что говорить пользователям. Тушили руками, сработало скорее по везению.

И это, разумеется, не единичный случай. В 2018-м в популярный event-stream заехал вредоносный код, через обычную передачу прав новому мейнтейнеру, который вызвался помогать. В 2022-м автор colors и faker сломал свои пакеты сам, в знак протеста. Каждый раз страдают миллионы, каждый раз реакция в режиме пожара, и каждый раз выясняется, что архитектурно система от одного человека не защищена никак.

Что это говорит про open-source

Есть удобный тезис: открытый код надёжен, потому что его смотрят тысячи глаз. При первом же серьёзном разборе он рассыпается.

Начать с того, что код никто не смотрит. Одиннадцать строк left-pad не аудировал за два года ни один человек, и это объяснимо. Смотреть скучно, одиннадцать строк кажутся очевидными, работающий код проверять вроде бы незачем. Самое смешное всплыло потом, когда все наконец в него заглянули. Реализация была так себе: цикл, посимвольная конкатенация, никакого padStart (его в стандарте тогда ещё не было). Пятнадцать миллионов скачиваний у кода, в который не заглядывали.

Открытость вообще означает «можно проверить», и только. Между «можно» и «кто-то действительно проверил» лежит пропасть, и все проблемы открытого кода живут именно там.

Отдельная история — то, что критичные проекты слишком часто держатся на одном человеке. Тут лучше не пересказывать ощущения, а взять посчитанное. Работа Дюкена, Мена и Монперрюса (arXiv:2004.03347, 2020) разбирала 50 тысяч самых зависимых пакетов npm: у 18% из них был единственный контрибьютор, то есть единственный человек, когда-либо трогавший код. Linux Foundation вместе с гарвардской LISH с 2015 года ведут Census, перепись открытых компонентов, которые реально стоят в коммерческих продуктах; третий выпуск вышел в декабре 2024-го и отдельно отмечает, что большая доля критичных пакетов принадлежит индивидуальным аккаунтам разработчиков, а не организациям.

За последствия при этом не отвечает никто. left-pad ломает Babel, кто оплачивает простой? Мейнтейнер отдаёт права случайному человеку, тот добавляет малварь, с кого спрос? Пока всё работает, вопросов не возникает. Когда перестаёт, вопросы появляются, а отвечать на них некому.

И экономика не сходится. Компании зарабатывают на открытом коде, но за него не платят. NPM жила на приватных реестрах и платных организациях, в 2020-м её купил GitHub, теперь это часть Microsoft, а мейнтейнеры как были волонтёрами, так и остались. Babel сегодня — инфраструктура для индустрии на миллиарды, и пишут его в основном бесплатно.

Что с этим делать

Посчитайте свои зависимости транзитивно, а не по package.json. Это одна команда, npm ls --all --parseable | wc -l, и результат обычно неприятно удивляет. Посмотрите сами, прежде чем верить чьим-то оценкам из статей, включая эту.

Зафиксируйте версии лок-файлом. Обновление зависимостей — это изменение кода, а не гигиеническая процедура. В 2016-м лок-файлов в нынешнем виде просто не существовало, отчего всё и разошлось так широко.

И если функция укладывается в десять строк, напишите её сами. Кстати, line-numbers в итоге починили именно так: выкинули left-pad и взяли padStart из lodash.

Комментарии (2)


  1. shasoftZ
    11.09.2026 08:30

     Азер поступил непрофессионально.

    При этом не он начал всё это а NPM отобрав у него пакет


  1. rusl002
    11.09.2026 08:30

    Посыл статьи весьма странный. То что все не думают о защите от того, что кто-то вдруг решит убрать свой код, не является проблемой open source. Это всего лишь значит, что люди не думают о возможных последствиях и ничего больше.