Для ноутбука с Claude Code — встроенная песочница с managed settings, сначала в пилоте. Для автономных задач и других агентов — отдельная среда; OpenShell рассматриваем как кандидата. Для CI — одноразовый runner с ограниченными доступами. Обоснование и что в нём не проверено: рекомендация.
Статус на 2026-10-30: есть исполняемый стенд и сырые результаты прогона. Проверены SRT 0.0.81 (движок песочницы Claude Code, запущенный отдельно, без Claude Code) и OpenShell 0.1.6 с Docker driver. Claude Code, применение managed settings, встроенные инструменты, IDE и prompt injection не проверены. Оснований для массового развёртывания пока нет.
| Вариант | macOS arm64 | Linux x86_64 |
|---|---|---|
| SRT | локальный прогон 2026-10-30; CI macos-14 |
CI ubuntu-24.04 |
| OpenShell, Docker driver | локальный прогон 2026-10-30 (Docker Desktop, Linux VM arm64) | CI ubuntu-24.04 |
Linux arm64, WSL2 и другие compute drivers OpenShell не проверялись. CI запускает все три job'а на каждый push; ветка с этими исправлениями прошла их зелёными.
Один probe, синтетические файлы и переменные, локальные приёмники. Настоящие ключи пользователя не читаются и не передаются. Две таблицы ниже не сравниваются построчно: у вариантов разные границы и разные control. Почему — в разделе «Что сравнивать нельзя» README прогона.
Control — тот же процесс на хосте без песочницы. Канарейки: файл в домашнем каталоге хоста и переменная окружения. Baseline разрешает запись в рабочий каталог и один адрес приёмника; hardened добавляет запрет чтения файла и удаление переменной; offline — hardened без сети.
| Проверка | control | baseline | hardened | offline | Что решает |
|---|---|---|---|---|---|
| Запись в рабочий каталог | разрешена | разрешена | разрешена | разрешена | allowWrite |
Чтение файла-канарейки в $HOME |
разрешено | разрешено | отказ | отказ | denyRead; без него SRT файл не защищает |
| Переменная-канарейка | доступна | доступна | удалена | удалена | credentials.envVars |
| Запись вне рабочего каталога | разрешена | отказ | отказ | отказ | SRT |
| HTTP на запрещённый порт | доходит | отказ 403 | отказ 403 | отказ 403 | прокси SRT |
| Прямой TCP мимо прокси | доходит | не доходит | не доходит | не доходит | macOS: EPERM; Linux: своё сетевое пространство имён, ECONNREFUSED |
| DNS-запрос по UDP на локальный резолвер | доходит | не доходит | не доходит | не доходит | macOS: EPERM; Linux: таймаут |
| Unix socket на хосте (синтетический приёмник) | доходит | отказ | отказ | отказ | SRT по умолчанию закрывает unix sockets: macOS — Seatbelt, Linux — seccomp |
| Разрешённый канал (nonce пробы) | доходит | доходит | доходит | отказ | allowedDomains |
| Вынос прочитанного секрета по разрешённому каналу | доходит | доходит | нечего выносить | нечего выносить | только запрет чтения выше по цепочке |
.git/hooks/pre-commit |
записан, git commit его исполнил |
отказ | отказ | отказ | обязательный запрет SRT |
.mcp.json |
записан | отказ | отказ | отказ | обязательный запрет SRT |
.claude/settings.json (hooks, statusLine, apiKeyHelper) |
записан | записан | записан | записан | standalone SRT его не защищает; Claude Code, по документации, защищает сам — не проверено |
Makefile, package.json, .envrc, .pre-commit-config.yaml |
записаны | записаны | записаны | записаны | обычные файлы проекта; песочница их не отличает |
Control — docker run того же образа, то есть уже контейнерная изоляция. Канарейка-файл записана в образ при сборке; домашнего каталога хоста в workload нет. Baseline разрешает чтение каталога с канарейкой и получает переменную через --env; hardened не делает ни того, ни другого. Landlock — hard_requirement; сеть — один TCP-адрес приёмника для python3.12.
| Проверка | control (docker run) |
baseline | hardened | Что решает |
|---|---|---|---|---|
| Запись в рабочий каталог | разрешена | разрешена | разрешена | read_write |
| Чтение файла-канарейки из образа | разрешено | разрешено | отказ | filesystem_policy |
| Переменная-канарейка | доступна | доступна | отсутствует | раннер: передал --env или нет |
| Запись вне рабочего каталога | разрешена | отказ | отказ | filesystem_policy |
| HTTP на запрещённый порт | доходит | отказ | отказ | network_policies |
| Прямой TCP | доходит | отказ | отказ | network_policies |
| DNS-запрос по UDP | доходит | не доходит (EDESTADDRREQ) |
не доходит | OpenShell; механизм стенд не различает |
| Unix socket хоста | не испытывалось | не испытывалось | не испытывалось | Docker socket в workload не смонтирован (проверка docker inspect) |
| Разрешённый канал (nonce пробы) | доходит | доходит | доходит | network_policies |
| Вынос прочитанного секрета по разрешённому каналу | доходит | доходит | нечего выносить | только запрет чтения выше по цепочке |
.git/hooks, .mcp.json, .claude/settings.json, Makefile, package.json, .envrc, .pre-commit-config.yaml |
записаны | записаны | записаны | файлы остаются в контейнере; на хосте их никто не исполняет, пока рабочий каталог не синхронизируют наружу |
Это политики стенда, а не сравнение заводских настроек. Разрешённый канал в обоих вариантах открыт по замыслу: стенд проверяет, что allowlist адресов не ограничивает содержимое. Фильтры содержимого (filterRequest и tlsTerminate у SRT, правила запросов в network_policies OpenShell) не испытывались.
- Канарейка случайная на каждый прогон и в командную строку пробы не попадает; вынос засчитывается, только если приёмник получил значение, которое проба прочитала сама.
- Сетевые строки решает журнал приёмника. «Не доходит» без явного отказа засчитывается, только если от control тот же приёмник получил данные; ошибка DNS-резолвинга остаётся ошибкой стенда.
- Файловые строки решает состояние цели: файл проверяет раннер на хосте или
docker cpснаружи контейнера. - Исход, не совпавший с ожиданием политики, записывается в матрицу как находка (код 2), ошибка стенда — код 1. Ожидания и их источники —
expectations()в run.py и Mandatory Deny Paths SRT.
Нужны Python 3.9+, Node.js 18+, npm и git. Для SRT на Linux также нужны bubblewrap, socat, ripgrep и доступные user namespaces; на macOS используется Seatbelt. Требования upstream SRT.
npm ci --ignore-scripts
npm run matrixОба варианта одной командой:
npm run matrix -- --with-openshellДля OpenShell нужен работающий Docker; на Mac — Docker Desktop с включённым host networking для supervisor и приёмника. Стенд скачивает CLI 0.1.6 в .tools, сверяет SHA256 с опубликованным списком, сверяет digest образов 0.1.6 с закреплёнными в benchmark.py, запускает временный шлюз на 127.0.0.1:18080 и собирает образ со случайной канарейкой. Существующий контейнер с именем стенда не заменяется.
Шлюз получает Docker socket; workload — нет. Это лабораторный шлюз без аутентификации пользователей и с --no-auto-providers, не конфигурация общего сервера. Workload, приёмник, control-контейнер и шлюз удаляются после проверки, в том числе при ошибке; удаление sandbox подтверждается по шлюзу и Docker. Скачанные образы, npm-зависимости и .tools остаются для повторных прогонов. Системные настройки Claude не меняются.
Каждый прогон пишет console.log и SHA256SUMS. Результаты по умолчанию идут в игнорируемый каталог results/local-…; для именованного прогона — --output results/<имя>. Пересчёт сохранённых матриц без запуска песочницы, дифф должен быть пустым:
python3 scripts/run.py --render results/2026-10-30/srt/raw.json | diff - results/2026-10-30/srt/matrix.md
python3 scripts/run.py --render results/2026-10-30/openshell/raw.json | diff - results/2026-10-30/openshell/matrix.mdallowed означает, что действие удалось, включая утечку, а не «тест пройден».
python3 scripts/check.py проверяет конфигурацию (значение каждого ключа managed-settings.json должно совпасть с таблицей в claude-sandbox/README.md), shell, ссылки, даты и соответствие сохранённых матриц сырым данным. python3 scripts/test_results.py проверяет, что ошибки, отсутствие наблюдений и упавший control не превращаются в защиту, а неожиданный исход становится находкой.
| Пробел | Почему не покрыто | Как получить свидетельство |
|---|---|---|
| Claude Code под managed settings | нет установленного Claude Code и прогона с моделью | версия, effective config, отказ старта без sandbox, игнорирование ослабляющих настроек репозитория; детерминированные канарейки — в мастере установки |
Хуки, локальные MCP, apiKeyHelper, statusLine из репозитория |
Claude Code запускает их вне песочницы («run with your full access», Sandboxing); без Claude Code стенд проверяет только запись этих файлов | репозиторий с .claude/settings.json и .mcp.json, журнал того, что Claude Code запустил, при allowManagedHooksOnly и пустом allowedMcpServers |
| Вынос через WebFetch | встроенный инструмент, не подчиняется сети песочницы («allowedDomains doesn't limit WebFetch»); нужен Claude Code |
шаг с локальным приёмником в мастере установки; правило WebFetch в managed settings |
| DNS через системный резолвер | нужен авторитетный DNS-сервер под нашим контролем в интернете; стенд проверяет только DNS-пакет на локальный UDP-приёмник | свой домен и сервер, журнал запросов |
Настоящий /var/run/docker.sock |
доступ к нему из песочницы равен доступу к хосту; стенд проверяет синтетический unix socket в SRT и отсутствие монтирования в OpenShell | отдельный прогон с allowUnixSockets, только на одноразовой машине |
| Инъекция через файл правил | нужен агент с моделью | версия агента и модели, фиксированный репозиторий с инструкцией, журнал действий и приёмник канарейки |
| Фильтры содержимого | в политиках стенда не включались | SRT с tlsTerminate и filterRequest, OpenShell с правилами запросов |
| OpenShell без network rules | в этом релизе ошибка DNS, не явный отказ политики | повтор на новой версии |
| Шлюз OpenShell с аутентификацией и providers | лабораторный шлюз без аутентификации, --no-auto-providers |
общий шлюз с аутентификацией, provider с коротким токеном, аудит |
| Цена для разработчика | не измерялась | установка пакетов, git, тесты, dev server, число подтверждений и исключений в реальной IDE |
| Кластер и CI с реальным агентом | локальный Docker driver не доказывает поведение Kubernetes/Podman/VM | отдельный прогон |
- Конфигурация Claude Code и мастер установки — подготовлены, интеграционный прогон не выполнен.
- Разведка Claude и OpenShell — источники и гипотезы, не результаты стенда.
- Рекомендация — решение и границы его обоснованности.
- Прогон 2026-10-29 — архив прежней методики.
Разведку, конфигурацию и код стенда готовили ИИ-агенты по постановке автора. Сырые журналы отражают исполнение команд, но не заменяют независимую проверку рабочего процесса команды.