# Безопасность обновления версии API для AI-агентов

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

Интеграции, которыми управляет человек, иногда переживают такую неоднозначность: человек замечает незнакомый экран или останавливается перед странным запросом. У автономного агента-программиста такой паузы нет. Если агент умеет составлять запросы по документации, изучать ошибки и пробовать другие варианты, любая новая доступная операция становится частью его практических полномочий.

## Номер версии не измеряет полномочия

Номер версии описывает обещание поставщика API по совместимости, но не изменение прав, которое ощущает агент. Считайте каждое обновление API проверкой полномочий, пока не сравните, что учетные данные могли делать до и после него.

Спецификация Semantic Versioning говорит, что MAJOR-версия меняется при несовместимом изменении публичного API. Это помогает разработчикам библиотек понять, могут ли сломаться вызывающие программы. Но из этого не следует, что MINOR-релиз не может добавить новый административный endpoint, расширить фильтр по умолчанию или начать принимать токен доступа с другой аудиторией. Все три изменения могут сохранить совместимость и одновременно расширить возможности агента.

Это различие важно, потому что команды часто задают неправильный вопрос: «Наш код по-прежнему запустится?» Вопрос, который защищает аккаунт: «Какие операции этот существующий агент теперь может успешно выполнять, с какими ресурсами и с какими учетными данными?»

У обновления API есть четыре отдельные области:

- Совместимость запросов: методы, пути, параметры и форматы полезной нагрузки.
- Доступ к ресурсам: аккаунты, проекты, репозитории, файлы или записи, к которым может обращаться запрос.
- Доступные действия: операции чтения, записи, удаления, развертывания, биллинга и управления удостоверениями.
- Принимаемые учетные данные: токены, ключи, подписи, аудитории и области действия, которые принимает поставщик.

Успешный тест совместимости запросов почти ничего не говорит о трех остальных областях. Поэтому изменение может пройти обычный регрессионный набор и все же открыть агенту новый путь к данным в продакшене.

Не считайте, что датированная версия API решает проблему. Поставщик может сохранить неизменной датированную поверхность API, одновременно изменив общий сервис аутентификации, добавив поля по запросу, которые обнаружит агент, или поменяв значения по умолчанию за пределами пути endpoint. Фиксация версии полезна. Считать ее границей полномочий без дополнительной проверки опасно.

## Составьте карту полномочий до и после обновления

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

Начинайте с реального трафика, а не с задуманной архитектуры. Агенты часто используют больше endpoint, чем предполагает исходная задача: вызовы обнаружения, повторные попытки после ошибок проверки, пагинацию, endpoint поиска, которые переводят имена в идентификаторы, и удобные API, предложенные в сообщениях об ошибках. Включайте эти вызовы, потому что они могут раскрыть идентификаторы ресурсов или открыть более широкий путь, чем запланированное действие.

Для каждого семейства запросов зафиксируйте следующие сведения:

| Поле | Что фиксировать |
|---|---|
| Операция | HTTP-метод и нормализованный путь, например `POST /v2/projects/{id}/deployments` |
| Граница ресурсов | Арендатор, проект, репозиторий, окружение или класс записей, к которым возможен доступ |
| Учетные данные | Класс токена или метка API-ключа, но не сам секрет |
| Условие авторизации | Область действия, роль, аудитория, пользовательское разрешение или правило на стороне сервера |
| Поведение по умолчанию | Что происходит, если не указаны необязательные фильтры, лимиты страниц и целевые поля |
| Ожидаемый отказ | Статус и ошибка, которые ожидаются для запрещенных ресурсов и действий |

Карта должна описывать границу ресурсов простыми словами. «Может вызвать API развертываний» слишком расплывчато. «Может создавать развертывания только в тестовом проекте» можно проверить. Если поставщик не раскрывает достаточно сведений, чтобы определить границу, используйте для агента отдельную тестовую учетную запись, пока не установите ее.

Затем составьте сравнение в два столбца. Выполните один и тот же набор запросов в старой и новой версиях с изолированным аккаунтом, в котором ресурсы намеренно разделены: как минимум один разрешенный проект, один запрещенный проект, одна неактивная запись и один аккаунт другого арендатора, если сервис поддерживает такую модель. Тестовые данные должны иметь заметные имена, чтобы случайные данные в ответах было легко обнаружить.

Не сравнивайте только коды статуса. Ответ `200` может скрывать важное различие: вдвое больше записей, новую ссылку `next_page`, пересекающую границу, дополнительное поле с учетными данными или идентификатор объекта, который позже позволит агенту вызвать привилегированный endpoint. Сравнивайте структуру ответа и идентификаторы, а затем проверяйте новые поля на предмет последующих полномочий.

## Изменившиеся значения по умолчанию создают незапрошенные пути доступа

Пропущенный параметр все равно участвует в решении об авторизации, если сервер сам определяет его смысл. Изменившиеся значения по умолчанию требуют такой же проверки, как новый endpoint записи.

Типичный случай начинается с безобидного запроса списка. Версия первая требует `project_id` и возвращает только активные записи. Версия вторая разрешает запрос без `project_id`, а поставщик трактует пропуск как «все проекты, видимые этому токену». Исходный код агента не изменился, если он и раньше не указывал необязательное поле. Но доступные ему данные расширились.

К такому же результату приводят и другие значения по умолчанию:

- Endpoint списка начинает включать архивные, удаленные или унаследованные объекты.
- Пагинация переходит от небольшого фиксированного набора результатов к обходу курсором со ссылкой `next`.
- Endpoint создания выбирает рабочее пространство вызывающей стороны по умолчанию вместо отказа при отсутствии идентификатора рабочего пространства.
- Endpoint обновления принимает пропущенные поля как «сохранить текущее значение», а не требует явной версии для конкурентного изменения.
- Endpoint поиска начинает индексировать содержимое подключенных сервисов.

Поставщики называют такие изменения улучшениями, потому что они уменьшают объем работы на стороне клиента. Для агента меньшая работа клиента часто означает меньше препятствий перед действием, направленным на более широкий набор ресурсов.

Проверяйте значения по умолчанию намеренно неполными запросами. Для каждого необязательного параметра отправьте запрос без параметра, с пустым значением, если API это допускает, и с явно указанным безопасным значением. Сравните набор целей и ошибку сервера. Агент, который сам формирует запросы, естественным образом будет пропускать поля, особенно если увидит пример документации без них.

Не пытайтесь ограничить это текстом промпта вроде «используй только проект A». Инструкции в промпте влияют на выбор запроса, но API решает, может ли запрос затронуть проект B. Границу проекта нужно закрепить в учетных данных, устройстве endpoint или шлюзе, который проверяет запрос до его выхода с машины.

## Новые endpoint расширяют широкие учетные данные

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

Команды часто исключают новые endpoint из проверки, называя их «новой функциональностью». Такой подход работает, когда человек получает новую кнопку в интерфейсе, а администратор отдельно выдает доступ. Он не работает, когда bearer-токен с широкой областью действия автоматически проходит на новом маршруте.

Предположим, у агента есть токен с описанием `projects:write`. В первой версии токен позволяет создавать и изменять метаданные проекта. Во второй появляется `POST /projects/{id}/exports`, создающий выгрузку для скачивания и использующий ту же область действия. Строка области действия не изменилась, но изменился эффект от обладания токеном. Агент может обнаружить endpoint через схему API, сгенерированный клиент, подсказку в ошибке или обычную документацию.

Классифицируйте новые endpoint по эффекту, а не по HTTP-методу. Endpoint `GET` может раскрывать исходный код, секреты, историю аудита, персональные данные или подписанные URL для скачивания. Endpoint `POST` может создавать необратимые расходы или запускать внешние процессы. Маршрут `DELETE` иногда менее опасен, чем `GET`, который раскрывает учетные данные для использования в другом месте.

Проверяйте каждый новый маршрут по четырем вопросам:

1. Проходит ли существующая учетная запись агента аутентификацию?
2. Какие существующие области действия, роли или классы API-ключей разрешают этот маршрут?
3. Может ли его результат дать идентификаторы, URL или токены для другой операции?
4. Может ли агент добраться до него через клиентскую библиотеку, документ обнаружения или предоставленную документацию?

Последний вопрос помогает заметить распространенный плохой совет: «Мы не будем рассказывать агенту о новом endpoint». Ограничение кажется практичным, потому что агенты большую часть времени следуют рабочему контексту. Но это не средство контроля. Агент может изучить схемы, вывести стандартные пути или получить новую задачу с другими инструкциями. Сервер должен отклонять неодобренную операцию, даже если клиент знает ее точный URL.

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

## Изменения аутентификации меняют полномочия

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

OAuth 2.0 определяет токены доступа как учетные данные, представляющие разрешение на авторизацию, а RFC 9700, документ OAuth 2.0 Security Best Current Practice, требует точного сопоставления URI перенаправления и описывает защиту от повторного использования токенов и механизмы привязки токена к отправителю. Практический вывод шире OAuth: формат токена сам по себе не определяет его получателя, отправителя или область действия. Сервер ресурсов должен проверять эти свойства в каждом принятом запросе.

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

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

Полезная запись может выглядеть так:

```text
credential: build-agent-sandbox
request: POST /v3/projects/prod-42/deployments
expected: 403 forbidden
old version: 403 {"error":"insufficient_scope"}
new version: 201 {"id":"dep_...","environment":"production"}
review result: block upgrade and revoke credential
```

Важно проверять тело ответа. Изменение `403` на `404` может быть намеренным скрытием информации. Изменение `403` на `201` означает рост полномочий, даже если в журнале изменений это названо улучшением совместимости.

Проверьте также поведение заголовков. Пользовательские заголовки могут выбирать версию API, организацию или пользователя, от имени которого выполняется действие. Если новая версия воспринимает отсутствие заголовка как организацию по умолчанию, повторная попытка агента после ошибки формата заголовка может отправить запрос не туда. Фиксируйте в тестах точные заголовки, скрывая секреты, и отдельно проверяйте их отсутствие.

## Поведение агента превращает небольшие различия в полноценные процессы

Агент может объединить обычные по отдельности вызовы в результат, который разработчик API никогда не рассматривал как единое полномочие. Проверка версии должна учитывать такие цепочки.

Новое поле в списке может раскрыть идентификатор репозитория. Этот идентификатор передается в endpoint скачивания. Ответ скачивания содержит подписанный URL. URL открывает артефакт, в конфигурации которого указан endpoint другого сервиса. Каждый вызов по отдельности может быть разрешен. Вся последовательность способна превысить задачу, полученную агентом.

Поэтому проверки авторизации для отдельных endpoint необходимы, но недостаточны. Добавьте тесты процессов для действий, которые агент должен выполнять, и соседних действий, которые нужно исключить. Отслеживайте переход идентификаторов между вызовами: идентификаторы, курсоры пагинации, расположения, предварительно подписанные URL, идентификаторы задач и сообщения об ошибках, раскрывающие существующие имена ресурсов.

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

- Прочитать нужную задачу и изменить разрешенные поля.
- Получить отказ при попытке использовать идентификатор задачи из другого репозитория.
- Получить отказ при попытке изменить настройки репозитория или webhook.
- Получить отказ при переходе по ссылке к выгрузке, списку участников или маршруту управления токенами.

Путь отказа важен не меньше успешного пути. Агент воспринимает ошибки как информацию. Подробный отказ, в котором назван альтернативный endpoint, может упростить поиск нежелательного маршрута. Для людей-разработчиков такой компромисс может быть приемлем, но перед подключением интеграции к автономному процессу нужно понимать, что он существует.

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

## Сравнение возможностей выявляет то, что пропускают обычные тесты

Сравнение возможностей, или capability diff, - это повторяемый тест, который проверяет, какие запросы может выполнить учетная запись, а не то, получает ли приложение по-прежнему ожидаемые данные. Держите его достаточно компактным, чтобы запускать для каждого кандидата на версию.

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

```sh
curl -sS -D headers.txt -o body.json \
  -H "Authorization: Bearer $TEST_TOKEN" \
  -H "X-API-Version: 2025-01-01" \
  "https://api.example.test/v1/projects?limit=2"

printf 'status: ' && head -n 1 headers.txt
printf 'headers:\n' && grep -Ei '^(link|location|x-request-id|www-authenticate):' headers.txt
printf 'identifiers:\n' && jq -r '.. | objects | (.id? // empty)' body.json | sort -u
```

Запустите набор один раз для каждой версии, затем сравните статус, выбранные заголовки и нормализованные идентификаторы. Не сравнивайте вслепую весь JSON. Временные метки, идентификаторы запросов и порядок элементов создают шум, из-за которого проверяющие начинают игнорировать различия. Сначала нормализуйте эти поля, но сохраните ссылки пагинации, идентификаторы ресурсов, названия ролей и любые поля, которые могут направить следующий запрос.

В набор должны входить успешные запросы, ожидаемые отказы, запросы с пропущенными необязательными параметрами, первая страница и последующий запрос пагинации. Добавьте по одному запросу для каждого нового документированного маршрута, связанного с существующей областью действия агента. Цель не в том, чтобы перечислить весь API поставщика. Нужно охватить каждую операцию, которую агент реалистично может обнаружить или объединить с другими.

Простой файл результатов делает решения проверяемыми:

```json
{
  "case": "forbidden-production-deploy",
  "credential": "build-agent-sandbox",
  "request": "POST /v3/projects/prod-42/deployments",
  "expected_status": 403,
  "observed_status": 403,
  "observed_resource_ids": [],
  "version": "2025-01-01"
}
```

Для каждого различия требуйте явного решения проверяющего. «Ожидаемо, потому что поставщик это изменил» не считается решением. Проверяющий должен указать, остается ли новое поведение в пределах одобренных полномочий агента и где именно эти полномочия обеспечиваются.

## Журналы показывают, что произошло, но не что должно было произойти

Журналы запросов помогают расследовать обновление, но не заменяют предварительную проверку полномочий. Они отвечают на разные вопросы.

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

Записывайте селектор версии, нормализованную операцию, границу цели, метку учетных данных, решение, статус и идентификатор корреляции. Не записывайте bearer-токены, исходные заголовки авторизации, полные тела запросов и поля ответов, содержащие секреты. Журнал безопасности, в котором хранится защищаемое им credential, лишь перемещает место утечки.

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

Sallyport хранит журнал Sessions и журнал Activity, построенные из одного зашифрованного аудита с цепочкой хешей. Оператор может проверить и запуск агента, и каждое HTTP- или SSH-действие. Офлайн-проверка `sp audit verify` подтверждает цепочку без доступа к хранилищу, что полезно, когда проверка обновления превращается в расследование инцидента.

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

## Подтверждение должно относиться к процессу, а не к расплывчатой задаче

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

Связывайте авторизацию сессии с полномочиями подписи кода запрашивающего процесса, если операционная система их предоставляет. Это помогает предотвратить распространенную подмену: доверенный агент запускает сессию, а затем недоверенный помощник или скопированный бинарный файл пытается использовать тот же путь к учетным данным. Идентичность процесса не доказывает разумность каждого будущего запроса, но дает оператору конкретный объект для подтверждения или отзыва.

Запрашивайте подтверждение каждого вызова только для учетных данных, эффект которых трудно ограничить, например для развертывания в продакшене, администрирования аккаунта или экспорта данных. Если просить человека подтверждать каждое безобидное чтение, он привыкнет автоматически нажимать кнопки. Усталость от подтверждений - ошибка дизайна, а не недостаток пользователя.

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

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

## Превратите проверку полномочий в контроль выпуска

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

Добавьте проверку в ту же запись об изменении, где фиксируются обновления зависимостей и сгенерированного клиента. Запишите старые и новые селекторы API, примечания к выпуску поставщика, результат сравнения возможностей, проверенные классы учетных данных и имя человека, который принял каждое намеренное различие. Это обычная работа, поэтому ее и пропускают до появления первой странной записи аудита.

Не ждите новой MAJOR-версии API. Запускайте проверку, когда поставщик меняет версию API, сервис аутентификации, настройки OAuth-приложения, сгенерированный SDK, схему обнаружения, заголовок по умолчанию или определение области действия. Изменение за пределами URL тоже может изменить решение удаленного сервера.

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