# Может ли идентичность хоста расширений IDE доказать, кто такой агент?

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

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

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

## Подпись хоста идентифицирует контейнер, а не его содержимое

Подпись кода подтверждает факты об исполняемом образе. Apple описывает подпись кода как способ установить происхождение и целостность программного обеспечения. Система может проверить, что код подписала доверенная сторона и что загруженный код по-прежнему соответствует подписанному материалу. Именно такие сведения нужны для решения, которое отвечает на вопрос: «Какой процесс приложения отправляет запрос?»

Но подпись не доказывает: «Какая функция расширения внутри этого процесса отправила запрос?» На этой границе у процесса одна исполняемая идентичность. Если редактор запускает хост расширений и тот загружает десять плагинов, ядро не создает десять отдельных идентичностей подписи кода для их JavaScript, байткода, обратных вызовов или вызовов API расширений.

У этого различия есть практическое следствие. Шлюз может обоснованно записать:

```text
caller executable: /Applications/Editor.app/.../extension-host
signing authority: Example Software Team ID ABC123
process id: 8421
parent process: Editor.app pid 8304
```

Но из одной подписи он не может вывести:

```text
extension: publisher.cloud-deploy
command: deployCurrentProject
prompt source: chat request 18
```

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

Люди часто называют и то и другое «идентичностью», а потом теряют различие при реализации. Не теряйте его. Подписавшая сторона говорит, кто создал контейнер. Она не говорит, какой пассажир потянулся к органам управления.

## Общие хосты расширений объединяют несколько источников полномочий

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

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

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

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

## Имя расширения становится сведением о происхождении только после привязки хостом

Идентификатор расширения, например `publisher.name`, дает полезный контекст, но шлюз не должен принимать строку, которую прислал вызывающий объект, за доказательство. Любой код, способный сформировать запрос, может записать `publisher.name` в заголовок, JSON-тело, аргумент команды или переменную окружения. Это лишь говорит, что он заявил.

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

Проверка проста. Спросите, откуда взялось каждое поле и кто мог его изменить.

| Поле | Что оно может подтвердить | Кто может подделать или изменить его |
|---|---|---|
| Подписывающая сторона хоста | Идентичность загруженного исполняемого файла хоста | Расширение обычно не может ее подделать |
| Идентификатор процесса и время запуска | Конкретный работающий экземпляр хоста | Его назначает операционная система |
| ID расширения в запросе | Заявленную идентичность расширения | Любой код, способный создать запрос, если хост не связывает это поле с запросом |
| Путь рабочей области | Заявленный хостом контекст | Хост или расширение могут указать его неверно |
| Решение о подтверждении | Пользователь одобрил показанный запрос | Интерфейс подтверждения должен связать его с действием |

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

## Подтверждение сеанса имеет полезную границу и жесткий предел

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

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

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

Используйте подтверждение сеанса для вопроса, на который оно может ответить: «Может ли этот подписанный процесс хоста пользоваться этой категорией доступа, пока он существует?» Не растягивайте его до вопроса: «Может ли это конкретное расширение выполнить это конкретное необратимое действие?»

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

Для чувствительных учетных данных запрашивайте подтверждение в момент формирования действия, а не только при первом появлении хоста. В запросе должно быть достаточно сведений для осмысленного решения: назначение, метод или SSH-цель, метка учетных данных и часть запроса, которая создает последствия.

Предположим, хост отправляет локальному шлюзу действий такой запрос:

```json
{
  "channel": "http",
  "credential": "production-deploy",
  "method": "POST",
  "url": "https://deploy.example.internal/releases",
  "body": {"service": "billing", "version": "a1b2c3d"}
}
```

Подходящая карточка подтверждения должна связать решение с точным методом, назначением, учетными данными и телом запроса либо со стабильным дайджестом тела. В ней не следует писать только: «Редактор хочет использовать production-deploy». Такая формулировка превращает подтверждение действия в чистый лист для любого вызова, который хост сможет выполнить до истечения срока решения.

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

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

## Узкие полномочия учетных данных уменьшают ущерб даже после правильного подтверждения

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

Разделяйте доступ по последствиям. Токен, который читает один репозиторий, не должен одновременно администрировать каждый проект. Учетные данные для развертывания не должны создавать пользователей или получать посторонние секреты. Для SSH используйте отдельную учетную запись или принудительную команду, если это поддерживает удаленная сторона, вместо общего shell-доступа для процесса, которому нужна одна задача обслуживания.

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

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

## Знакомая ошибка начинается с безобидного действия в палитре команд

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

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

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

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

## В журналах аудита нужна колонка доказательств, а не удобная история

Журналы вводят в заблуждение, когда объединяют проверенные факты и сообщенный самим хостом контекст в одно предложение. Фраза «Плагин X развернул сервис Y» выглядит точной, но может скрывать непроверенную метку плагина и неизвестную причинную цепочку. Сохраняйте исходное событие и указывайте источник каждого утверждения.

Удобная форма события разделяет эти утверждения:

```json
{
  "time": "2026-07-24T10:16:43Z",
  "caller": {
    "signing_authority": "Example Software Team ID ABC123",
    "pid": 8421,
    "started_at": "2026-07-24T09:58:03Z"
  },
  "host_reported_context": {
    "extension_id": "publisher.cloud-deploy",
    "workspace": "/work/payments"
  },
  "action": {
    "channel": "http",
    "method": "POST",
    "destination": "https://deploy.example.internal/releases",
    "credential": "production-deploy"
  },
  "authorization": {
    "session_approved": true,
    "per_use_approved": true
  }
}
```

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

Важна и защита от изменений. Локальный журнал, который скомпрометированный процесс может переписать, слабо подтверждает его собственное поведение. Sallyport строит представления сеансов и активности на основе зашифрованного аудита с цепочкой хешей, а `sp audit verify` может проверить цепочку офлайн без ключа хранилища. Это не решает атрибуцию расширений, но не позволяет позднее незаметно улучшить историю.

## Изолируйте операцию, если атрибуция должна быть точной

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

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

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

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

## Считайте формулировку запроса на подтверждение частью границы доступа

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

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

Начните с инвентаризации всех учетных данных, доступных хосту IDE. Для каждой записи укажите, какое разрешение на уровне хоста она может безопасно наследовать, для какой формы действия нужно новое решение и какие именно сведения сохраняет аудит. Если ответ на вопрос «какое расширение это сделало?» остается догадкой, не предоставляйте полномочия так, будто это установленный факт.
