Конфликт мейнтейнеров в fsnotify и риски цепочки поставок
- title
- Конфликт мейнтейнеров в fsnotify и риски цепочки поставок
- type
- summary
- summary
- Конфликт прав доступа в fsnotify (321k зависимостей) со стороны выглядел как захват: что произошло на самом деле и почему неясное управление несёт риски.
- parent
- supply-chain-security
- tags
- security, supply-chain, open-source, governance, go
- sources
- fsnotify-maintainer-dispute
- created
- 2026-05-12
- updated
- 2026-05-12
- lang
- ru
- translation_of
- fsnotify-maintainer-dispute
- source_updated
- 2026-05-12
- translated
- 2026-09-01
- translator
- lllm/antigravity/gemini-3.7-flash-high
В начале мая 2026 года Ясухиро Мацумото (mattn) написал в X, что его удалили из организации fsnotify на GitHub. fsnotify - это Go-библиотека для кроссплатформенных уведомлений файловой системы, от которой зависит 321 тысяча проектов: она лежит глубоко в стеке под большинством CLI на Go, серверов разработки и инструментов инфраструктуры. Со стороны такое сочетание признаков - популярная зависимость, свежие релизы, внезапное удаление мейнтейнеров, удалённый публичный пост и неясность с тем, кто отвечает за выпуск релизов - выглядело очень похоже на ранние стадии атаки на цепочку поставок. В итоге Grafana завела публичный issue, Kubernetes открыл issue с вопросом "Жив проект или нет?" и начал обсуждать форки, а Socket выпустил разбор ситуации.
Нет никаких свидетельств того, что какой-либо релиз fsnotify был скомпрометирован. Самое примечательное в этой истории - то, что сама по себе непрозрачность управления вызвала реакцию как на угрозу цепочке поставок. В мире после xz-utils это правильная реакция, из-за которой ключевой переменной в работе становится не код, а вопрос "кто контролирует пайплайн релизов".
Что произошло на самом деле
Изначальный мейнтейнер проекта, Мартин Турнуа (arp242), годами щедро раздавал права на коммит всем, кто когда-то вносил исправления. Со временем этот доступ "перестал отражать реальное руководство проектом". Когда mattn решил возобновить поддержку библиотеки (релизов не было больше года, и автоматические сканеры начали помечать её как заброшенную - см. unmaintained-scanner-pressure), arp242 посчитал, что изменения вливаются слишком поспешно. При этом не учитывалось всё разнообразие платформ fsnotify (Windows, Linux, macOS, BSD, illumos) и не проводилось должного ревью, из-за чего возвращались нестыковки, на устранение которых ушли годы.
Решающим событием стало изменение файла FUNDING.yml. В самом начале своей работы mattn без обсуждения закоммитил обновление файла со спонсорами проекта прямо в ветку main. arp242 расценил это так: новый контрибьютор перенаправляет финансирование проекта на себя, ещё не наработав никакой истории ревью. Позже mattn признал, что этот коммит был ошибкой.
После этого arp242 отобрал права на запись у аккаунтов, которые считал неактивными историческими мейнтейнерами. В удалённом посте в X на японском языке mattn - воспользовавшись машинным переводом - описал ситуацию так, что на английском это выглядело, будто arp242 удалил и изначального автора проекта. В японском языке подлежащее часто опускается; переводчик подставил местоимение "мы", которого mattn не писал. Позже mattn исправил пост, пояснил, что утверждение об изначальном авторе было ошибочным, и извинился.
Версии 1.10.0 и 1.10.1 из свежих наработок mattn к тому моменту уже вышли (исправление в 1.10.1 закрывало баг в inotify, когда соседние watch'и с общим префиксом пути могли некорректно удаляться или переименовываться). После конфликта mattn создал отдельную реализацию gofsnotify/fsnotify, которую Kubernetes внёс в список "проектов для наблюдения".
Почему это выглядело как инцидент с цепочкой поставок
Главный вывод из этой истории касается взгляда внешнего наблюдателя. Сторонние пользователи не могут легко отличить реальный захват проекта от внутреннего конфликта, поскольку внешние признаки одинаковы:
- Неожиданный релиз.
- Изменение состава людей с правом на коммит.
- Противоречивые публичные заявления.
- Удаление исходного публичного поста.
- Появление нового форка или конкурирующей реализации.
Это типичный сбой по сценарию maintainer-governance-ambiguity: реальные решения внутри проекта принимаются кулуарно (и в рабочем порядке), но события вокруг прав доступа видны публично (и считываются как эскалация). Эталонным случаем остаётся инцидент с xz-utils - настоящая атака через социальную инженерию, которая почти год ничем не отличалась от ситуации "новый мейнтейнер работает активнее старого".
Себастьян ван Стейн (мейнтейнер Docker / Moby / containerd / runc) прямо сформулировал проблему в обсуждении Kubernetes: "Такие зависимости, как fsnotify, находятся достаточно 'низко' в стеке, чтобы о них 'забывали'. Атаки на цепочку поставок - это реальность, а dependabot позволяет проектам очень легко 'просто обновить зависимость', особо не задумываясь". Сочетание низкого уровня зависимости в стеке, средств автообновления и неясных полномочий мейнтейнеров как раз и формирует зону риска.
Давление автоматических сканеров
Второй важный сюжет в этой истории: в апреле 2026 года в issue (fsnotify#735) указали, что автоматические сканеры кода помечают проект как заброшенный, если в нём не было релизов 12 месяцев. fsnotify перешагнул этот порог 2026-04-04. Отчёты сканеров создали внешнее давление, требуя выпустить хоть какой-то релиз, что отчасти и подтолкнуло mattn взяться за работу. Это системная проблема современных инструментов безопасности: зрелые инфраструктурные библиотеки нередко вполне оправданно поддерживаются наплывами, но бинарные эвристики в духе "нет обновлений = всё плохо" создают шум. В результате проекты вынуждены либо выпускать пустые релизы, либо отдавать разработку "любому желающему". См. unmaintained-scanner-pressure.
Что нужно, чтобы подобные события можно было верифицировать
В исходной статье нет готовых рецептов, но системный вывод понятен. Для массово используемых проектов состав мейнтейнеров перестаёт быть внутренним делом ровно в тот момент, когда пользователи ниже по цепочке не могут понять, не был ли контроль над релизным пайплайном захвачен злоумышленниками. Практические меры защиты для любого проекта уровня fsnotify:
- Документировать роли мейнтейнеров - кто имеет право мержить, кто выпускает релизы, у кого остались "исторические права на коммит", а кто является "активным мейнтейнером". Достаточно файла
MAINTAINERS.mdс разделами для действующих и неактивных участников. - Сделать аудит изменений доступа прямо в репозитории - когда права на коммит выдаются или отзываются, оставлять след в виде, доступном для автоматического чтения (история CODEOWNERS, PR с правкой
MAINTAINERS.md, файл с решениями по проекту). - Отделить право выпуска релизов от прав на коммит - релизные теги должны подписываться известным ключом, а релизные workflow должны быть привязаны к конкретным мейнтейнерам. Доступ к коммитам не должен автоматически давать право собирать релизы.
- Не менять информацию о спонсорстве и финансировании тихими коммитами прямо в main - это не просто правка кода, это заявление о правах на проект. PR'ы с обсуждениями существуют не просто так.
Ни одна из этих мер не предотвратила бы сам конфликт. Но они сократили бы время, в течение которого внешние пользователи гадали, что происходит - захват проекта или внутренняя драма.
Связанные страницы
- supply-chain-security - безопасность цепочки поставок в целом
- maintainer-governance-ambiguity - базовая проблема непрозрачности управления
- unmaintained-scanner-pressure - эвристики автоматических сканеров как фактор давления на зрелые проекты
- xz-utils-incident - хрестоматийный случай, когда за неясным управлением скрывалась реальная атака
- fsnotify - страница самой библиотеки
- mattn, arp242 - ключевые участники событий
- socket-blog - источник материала