EnglishРусский Map
Supply Chain Security

Конфликт мейнтейнеров в fsnotify и риски цепочки поставок

title
Конфликт мейнтейнеров в fsnotify и риски цепочки поставок
type
summary
summary
Конфликт прав доступа в fsnotify (321k зависимостей) со стороны выглядел как захват: что произошло на самом деле и почему неясное управление несёт риски.
tags
security, supply-chain, open-source, governance, go
created
2026-05-12
updated
2026-05-12
lang
ru
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 - источник материала