Skip to Content
ДизайнПолитика разрешений для shell-сессий

Политика разрешений для shell-сессий

Проблема

POST /session/:id/shell выполняет shell-команду напрямую через демон, без вызова инструмента LLM или стандартного потока посредничества в разрешениях агента. До этого изменения эндпоинт был нестрогой мутацией и был доступен по токену демона вместе с id сессии, либо через loopback-интерфейс разработчика по умолчанию без токена.

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

Цели

  • Отключить прямой shell сессии по умолчанию.
  • Требовать явного включения оператором с помощью qwen serve --enable-session-shell.
  • Требовать настройки bearer-токена перед тем, как включение вступит в силу.
  • Требовать client id, зарегистрированный в адресуемой сессии.
  • Применять ту же политику в REST-маршруте, HTTP-диспетчере ACP и sink-е выполнения bridge.
  • Оставить стандартные подтверждения shell-инструментов агента и посредничество в разрешениях без изменений.

Нецели

  • Не маршрутизировать прямой shell через PermissionMediator.
  • Не изменять отправку промптов, постановку промптов в очередь или поведение ожидающих промптов SDK.
  • Не добавлять специфичный для shell rate limiter.
  • Не добавлять алиас переменной окружения для флага включения.

Проектирование

runQwenServe один раз разрешает и обрезает bearer-токен. После этого вычисляется одно итоговое булево значение:

sessionShellCommandEnabled = opts.enableSessionShell === true && token !== undefined;

Это значение передается в bridge, REST-приложение и ACP-диспетчер. Встроенные вызывающие стороны, которые напрямую вызывают createServeApp, вычисляют наличие токена с помощью проверки на непустую строку, чтобы token: '' вел себя как отсутствие токена как для строгого контроля мутаций, так и для анонсирования возможностей shell.

REST-маршрут использует mutate({ strict: true }). В демоне без токена на loopback-интерфейсе строгий контроль возвращает 401 token_required до запуска обработчика. Когда токен настроен, обработчик отклоняет отключенный shell с ошибкой session_shell_disabled, затем требует X-Qwen-Client-Id, затем проверяет тело команды и, наконец, делегирует выполнение bridge.

ACP-диспетчер сохраняет возможность диспетчеризации _qwen/session/shell для старых клиентов, но не анонсирует её в списке _qwen.methods при инициализации, если эффективная политика не включена. Отключенные ACP-вызовы возвращают стабильную JSON-RPC ошибку session_shell_disabled без логирования команды или вызова bridge. Включенные вызовы по-прежнему требуют, чтобы соединение владело сессией и должны использовать client id привязки к сессии, установленный bridge-ом.

Bridge применяет финальную проверку защиты в глубину в executeShellCommand(): отключено, отсутствует client id, неизвестная сессия, затем непривязанный client id. Только после прохождения этих проверок он публикует shell-события, выполняет команду или записывает историю shell.

Контракт ошибок

REST:

  • нет токена: 401, code: token_required
  • отключено: 403, code/errorKind: session_shell_disabled
  • отсутствует client id: 403, code/errorKind: client_id_required
  • некорректный или непривязанный client id: существующая 400 invalid_client_id
  • неизвестная сессия: существующий маппинг 404 SessionNotFoundError

ACP:

  • отключено: RPC.INVALID_REQUEST, data.errorKind: session_shell_disabled
  • отсутствует client id привязки к сессии: RPC.INVALID_REQUEST, data.errorKind: client_id_required
  • сессия без владельца и невалидный client id сохраняют существующие маппинги JSON-RPC

Совместимость

DaemonSessionClient.shellCommand() продолжает работать, когда демон явно включен и аутентифицирован, поскольку клиент сессии несет в себе привязанный к сессии client id. Базовый вызов DaemonClient.shellCommand(sessionId, command) должен передавать opts.clientId, иначе он получит client_id_required.

Покрытие тестами

Реализация покрыта целевыми тестами bridge, REST, транспорта ACP, запуска serve и парсера команд. Наиболее ценные проверки — это поведение с отключенным состоянием по умолчанию, строгий контроль без токена, анонсирование возможностей, фильтрация методов инициализации ACP, контроль sink-а bridge и распространение привязанного к сессии client id.

Last updated on