Модуль 7 · Репозиторий API

Справочник «Стандартизация HTTP API» · модуль 7 · Репозиторий API

Глава 7.2

7.2

Владельцы кода
и безопасность

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

Категория
Репозиторий
Уровень
Базовый
Связанные главы
7.1 Шаблон PR · 6.4 CI · 4.1 Problem Details
Зачем это знать
Чтобы контракт нельзя было изменить втихую, а об уязвимости сообщили вам, а не миру

§1CODEOWNERS: кто отвечает за контракт

Файл .github/CODEOWNERS сопоставляет пути в репозитории с людьми или группами. Тот, кто указан владельцем, автоматически получает запрос на просмотр изменения, если правки затронули его файлы.

.github/CODEOWNERS
# В учебном репозитории владелец один. В команде вместо имени пишут группу
# ревьюеров API, например @org/api-reviewers.

*                        @MaximBytecamp

# Публичный контракт и правила его проверки
/openapi/                @MaximBytecamp
/.spectral.yaml          @MaximBytecamp
/docs/API_STYLE_GUIDE.md @MaximBytecamp
/docs/VERSIONING.md      @MaximBytecamp
/docs/adr/               @MaximBytecamp

В связке с защитой ветки из главы 6.4 это даёт нужное свойство: изменение файла описания нельзя объединить без одобрения владельца контракта — это запрет на уровне настроек, а не договорённость.

§2Как читается файл

Строка = правилоСлева образец пути, справа — один или несколько владельцев через пробел.путь → владелец
Побеждает последнее совпадениеПорядок важен: общее правило ставят выше, частные — ниже. Иначе частное не сработает.порядок
Звёздочка* — владелец по умолчанию для всего репозитория. Страховка на случай, если файл не попал ни в одно частное правило.по умолчанию
Группа вместо человека@org/api-reviewers — запрос уйдёт группе, и ответить сможет любой её участник.в команде
Порядок правил в файле

Это самая частая причина, по которой CODEOWNERS не работает. Если строку * поставить в конец файла, она перекроет все частные правила, и владельцем всего окажется один человек. Проверяется это так: откройте изменение, затрагивающее /openapi/, и посмотрите, кого GitHub назначил ревьюером.

§3Что назначают владельцу API

В перечень путей включены файлы, изменение которых меняет обещания клиентам или правила их проверки.

ПутьЧто тамПочему нужен владелец
/openapi/Выгруженное описание контрактаЛюбая правка здесь видна клиентам
/.spectral.yamlПравила проверки описанияОслабление правила = тихое ослабление стандарта
/docs/API_STYLE_GUIDE.mdСам стандартМеняются договорённости всей команды
/docs/VERSIONING.mdПолитика совместимостиОпределяет, что считать поломкой
/docs/adr/Записи решенийРешение не пересматривают в одиночку

Обратите внимание на вторую строку. Файл с правилами линтера охраняется наравне с описанием: смягчив правило, можно провести изменение, которое до этого не проходило.

§4SECURITY.md: как сообщить об уязвимости

Второй файл отвечает на вопрос внешнего исследователя: куда сообщать о найденной уязвимости.

docs/SECURITY.md · выдержки
## Поддерживаемые версии

| Версия | Поддерживается |
|---|---|
| 1.1.x | да |
| 1.0.x | нет, обновитесь до 1.1 |

## Сообщить об уязвимости

Не публикуйте уязвимость в issue. Используйте приватное сообщение об уязвимости
на GitHub или напишите API Team: api-team@example.edu.

Укажите, насколько сможете:
- операцию и версию API;
- шаги воспроизведения;
- X-Request-Id из ответа;
- чем уязвимость опасна.

Мы ответим в течение трёх рабочих дней.
Таблица версийКакие редакции ещё чинят. Отвечает на вопрос «мне сообщать или сначала обновиться».что поддерживаем
Приватный каналСообщение об уязвимости видно только владельцам репозитория, а не всему интернету.куда писать
Что приложитьТот же X-Request-Id из главы 4.2: он приведёт разбирающегося прямо к нужной записи в журнале.что приложить
Срок ответаОбещание обратной связи. Без него нашедший не знает, дошло ли сообщение, и через неделю публикует находку.когда ответим

§5Почему не через обычный issue

Issue публичен с первой секунды. Сообщение об уязвимости в нём становится доступным всем, в том числе до выпуска исправления.

Публичный issue

Уязвимость известна всем одновременно: и вам, и тем, кто её применит. Времени на исправление нет.

Приватное сообщение

Видят только владельцы репозитория. Есть время выпустить исправление и лишь потом рассказать.

Чтобы путь не выбирался наугад, в проекте настроен файл .github/ISSUE_TEMPLATE/config.yml: он убирает возможность создать issue без формы и добавляет в список ссылку на приватное сообщение об уязвимости. Нашедший уязвимость видит нужный вариант сразу, до создания issue.

§6Что не должно утекать через API

Часть требований безопасности относится не к репозиторию, а к самим ответам сервиса. Ниже собрано то, что разбиралось в предыдущих модулях.

ОпасностьКак проявляетсяГде разбиралось
Трассировка в теле 500Клиент видит имена таблиц, путь к файлам, внутренние сервисыглава 4.1
Сообщение СУБД в ошибкеПо тексту видно, что такая запись существуетглава 4.1
401 вместо 403 и наоборотПодсказывает, существует ли учётная записьглава 4.2
Чужой X-Request-Id без проверкиВ журнал попадает произвольная строка клиентаглава 4.3
Слишком широкий CORSЛюбая страница может обращаться к API от имени пользователяглава 4.3
Отсутствие ограничения частотыПеребор паролей и выгрузка данных ничем не сдержаныглава 4.3

Все шесть пунктов проверяются тестами или настройкой, и все шесть входят в контракт, а не выносятся в отдельную работу по безопасности.

§7Dependabot: обновление зависимостей

Третий файл — .github/dependabot.yml. Он еженедельно проверяет версии библиотек и действий сборки и сам открывает изменения с обновлениями.

.github/dependabot.yml
version: 2
updates:
  - package-ecosystem: pip
    directory: "/"
    schedule:
      interval: weekly

  - package-ecosystem: github-actions
    directory: "/"
    schedule:
      interval: weekly
    groups:
      actions:
        patterns: ["*"]

Это связано с требованием из главы 6.4: версии всех инструментов закреплены, чтобы сборка не ломалась сама по себе. Закреплённая версия со временем устаревает, в том числе по части безопасности. Dependabot снимает это противоречие: версии остаются закреплёнными, а их обновление приходит отдельным изменением, которое проходит все проверки контракта.

Список открытых pull request от Dependabot в учебном репозитории
Обновления в учебном репозитории. Каждое обновление приходит отдельным изменением, и на нём запускается та же сборка: тесты, сверка файла, линтер. Если новая версия библиотеки ломает контракт, это видно до объединения.

Настройка groups для действий собирает их обновления в одно изменение вместо пяти — иначе мелкие правки приходят по нескольку в неделю и их перестают разбирать.

§8Как делают другие

Файл CODEOWNERS в репозитории OpenAPI Initiative
OpenAPI Initiative. Владельцами спецификации назначены группы, а не отдельные люди: документ переживёт уход любого участника. Для контракта, которым пользуются годами, это обязательное условие.
Файл SECURITY.md в репозитории Kubernetes с описанием приватного канала сообщения об уязвимости
Kubernetes. Тот же набор: поддерживаемые версии, приватный канал, срок ответа. Различаются масштаб и сроки, состав разделов — нет.

Сравнив три файла в учебном проекте с их же вариантами у Kubernetes, Azure и OpenAPI Initiative, можно заметить: чем крупнее проект, тем подробнее файлы, но состав разделов у них одинаковый. Поэтому их и берут за образец.

§9Частые ошибки

Строка * в конце CODEOWNERSПобеждает последнее совпадение, поэтому общее правило перекроет все частные.
Владелец — один человекОн уходит в отпуск, и изменения контракта встают. В команде назначают группу.
CODEOWNERS без защиты веткиЗапрос на просмотр придёт, но объединить можно и без ответа.
Нет SECURITY.mdНашедший уязвимость напишет в публичный issue — просто потому, что другого способа не нашёл.
Обещать срок ответа и не отвечатьХуже, чем не обещать: молчание подталкивает опубликовать находку.
Не читать обновления зависимостейКопятся десятками и перестают восприниматься; группировка и регулярный разбор обязательны.

§10Проверьте себя

  1. Что делает CODEOWNERS и почему без защиты ветки он почти бесполезен?
  2. Почему строка * должна стоять в начале файла, а не в конце?
  3. Почему в список охраняемых путей входит .spectral.yaml, а не только описание API?
  4. Назовите четыре части SECURITY.md и объясните, зачем нужен срок ответа.
  5. Чем плох публичный issue как канал сообщения об уязвимости?
  6. Как уживаются требование закреплять версии инструментов и необходимость их обновлять?
CODEOWNERSпуть → владелецпобеждает последнее правило* в начале файлав команде — группа
Охранять/openapi/.spectral.yamlAPI_STYLE_GUIDE.mdVERSIONING.md/docs/adr/
SECURITY.mdподдерживаемые версииприватный каналчто приложитьсрок ответа
Зависимостиверсии закрепленыDependabot обновляетобновление проходит CI

Справочник «Стандартизация HTTP API» · модуль 7 · глава 7.2Макаров Максим Николаевич

Кадр