Skip to content

Исправления по ревью: CI на Linux, исход по цели, разведённые границы, новые классы - #1

Merged
shvma merged 2 commits into
mainfrom
review-fixes
Oct 30, 2026
Merged

shvma merged 2 commits into
mainfrom
review-fixes

Conversation

@shvma

@shvma shvma commented Oct 30, 2026

Copy link
Copy Markdown
Owner

Исправления по ревью 2026-10-30. CI на этой ветке зелёный: checks, srt (macos-14), srt (ubuntu-24.04), openshell (run 41027953167). История не переписывалась.

Общая проверка всего PR:

npm ci --ignore-scripts
python3 scripts/check.py && python3 scripts/test_results.py
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.md
(cd results/2026-10-30 && shasum -a 256 -c SHA256SUMS)

1. Красный CI на Linux

Причина (по артефактам run 40958209738): на Linux direct_tcp получал ECONNREFUSED. У SRT на Linux своё сетевое пространство имён; 127.0.0.1 внутри него — не loopback хоста (README SRT, network.allowLocalBinding: «Linux: ignored — the sandbox has its own network namespace, whose loopback is always usable and is not the host's»). Job openshell падал только из-за кода выхода SRT; сам OpenShell на Linux совпал с ожиданиями.
Сделано: сетевые исходы решает журнал приёмника. Если приёмник ничего не получил, а проба получила ECONNREFUSED/таймаут/EDESTADDRREQ, исход blocked засчитывается только при условии, что от control тот же приёмник получил данные. Ошибка разрешения имени остаётся ошибкой. errno сравниваются по именам: номера на Linux и macOS разные.
Проверка: статус CI этого PR; python3 scripts/test_results.py (тесты test_refused_connection_counts_only_if_control_was_delivered, test_name_resolution_error_is_not_protection).
Руками: открыть артефакт srt-ubuntu-24.04 и посмотреть detail у direct_tcp в hardened.

2. «Одна команда, код 0» у 2026-10-29

Сделано: новый прогон одной командой npm run matrix -- --with-openshell --output results/2026-10-30, код 0. benchmark.py сам пишет console.log (весь вывод, последняя строка — код выхода) и SHA256SUMS. В results/2026-10-29/README.md фраза заменена фактами (шлюз ~18:42Z, SRT 21:25:48Z, вывод исходной команды не сохранён); снапшот помечен архивом.
Проверка: cd results/2026-10-30 && shasum -a 256 -c SHA256SUMS; времена started_at в srt/raw.json и openshell/raw.json идут подряд.
Руками: перезапустить ту же команду в results/local-… и сравнить матрицы.

3. Разные границы под одинаковыми строками

Сделано: канарейка случайная и в argv не попадает (раньше её передавал run.py:206). Вынос — строка exfil_secret: проба шлёт только то, что прочитала сама. allowed_channel шлёт nonce пробы и показывает, что канал открыт. В README две отдельные таблицы (SRT — процесс на хосте; OpenShell — контейнер в VM) с колонкой «Что решает»; у OpenShell env помечен как выбор раннера, control — как docker run. Шапка каждой матрицы описывает границу, канарейки и control.
Проверка: канарейка (синтетика) записана в raw, но не в командах исполнений SRT: python3 -c "import json;r=json.load(open('results/2026-10-30/srt/raw.json'));print(any(r['canary'] in ' '.join(e['command']) for e in r['executions']))" → False.
Руками: прочитать раздел «Что сравнивать нельзя» в results/2026-10-30/README.md и решить, достаточно ли этого читателю.

4. Проверки на цели

Сделано:

  • OpenShell: состояние файлов проверяется docker cp из контейнера workload снаружи. Отчёт пробы о записи без файла → error; файл при заявленном отказе → allowed.
  • Мастер установки: исход решают файлы и журнал локального приёмника. Канарейка — синтетический файл со случайным значением в ~/.ssh (удаляется по trap), .env, curl и WebFetch на локальный приёмник, запись вне каталога. Если команды не выполнялись, исход «не проверена», а не «отказ».
  • validate() разделён: assess() возвращает ошибки стенда (код 1) и находки (код 2, раздел «Находки» в матрице). Упавший control — ошибка, а не находка.

Проверка: python3 scripts/test_results.py (18 тестов); bash -n claude-sandbox/install-wizard.sh; ShellCheck в CI.
Руками: мастер в этом PR не запускался — нужен Claude Code и sudo. Пройти шаг 5 и посмотреть results/install.log.

5. Пропущенные классы

Добавлено в стенд: dns_udp (DNS-пакет с nonce на локальный UDP-приёмник), unix_socket (синтетический сокет на хосте; в OpenShell не испытывался, вместо этого проверяется, что Docker socket не смонтирован), семь файлов, которые исполняют снаружи: .git/hooks/pre-commit, .claude/settings.json (hooks, statusLine, apiKeyHelper), .mcp.json, Makefile, package.json, .envrc, .pre-commit-config.yaml. В control SRT записанный хук исполнился при git commit вне песочницы.
Результат: standalone SRT запретил .git/hooks и .mcp.json (обязательный deny-лист) и разрешил остальные пять, включая .claude/settings.json (его, по документации, защищает сам Claude Code — не проверено).
Не покрыто, с причиной (README, «Что не покрыто»): хуки/MCP/apiKeyHelper в самом Claude Code и WebFetch (нужен Claude Code и модель; шаги подготовлены в мастере), DNS через системный резолвер (нужен свой авторитетный DNS), настоящий docker.sock (доступ к нему = доступ к хосту).
Руками: решить, достаточно ли синтетического unix socket или нужен прогон с allowUnixSockets на одноразовой машине.

6. Denylist в managed settings

Сделано (сверено с sandboxing и settings-reference 2026-10-30): ANTHROPIC_API_KEY в envVars; ~/.git-credentials, ~/.pypirc, ~/.azure, ~/.vault-token в credentials.files и permissions.deny; .env — sandbox.filesystem.denyRead: ["~/**/.env"] и Read(**/.env); WebFetch и WebSearch в permissions.deny; allowManagedHooksOnly: true, allowManagedMcpServersOnly: true, allowedMcpServers: [], enableAllProjectMcpServers: false. autoAllowBashIfSandboxed выключен (false): при true команды в песочнице без вопроса пишут файлы, которые потом исполняются снаружи. apiKeyHelper из репозитория отдельным managed-ключом не запрещается — записано как непокрытое.
Проверка: python3 scripts/check.py теперь сверяет значение каждого ключа с таблицей claude-sandbox/README.md (у списков — каждый элемент), а не упоминание имени.
Руками: это решения ученика: оставить ли autoAllowBashIfSandboxed: false и полный запрет WebFetch/WebSearch для пилота.

7. Рекомендация

Сделано: в RECOMMENDATION.md у каждого утверждения — чем подкреплено. Фоновый агент с управляемым шлюзом помечен «не проверено» (шлюз без аутентификации, --no-auto-providers). Вывод про Claude Code переформулирован как вывод про standalone SRT. Отказ записи в OpenShell не приписывается Landlock: прогона без Landlock не было. «Канал не закрыт» — в политиках стенда; filterRequest/tlsTerminate и правила запросов OpenShell не испытывались. Docker Desktop 4.72.0 / Engine 29.4.2 разведены. Удаление sandbox OpenShell подтверждается: раннер ждёт sandbox not found от шлюза и отсутствия контейнеров workload/supervisor в Docker (cleanup в openshell/raw.json).
Проверка: python3 -c "import json;print([c['stdout'][:70] for c in json.load(open('results/2026-10-30/openshell/raw.json'))['cleanup'] if c['command'][0]=='confirm-deleted'])".
Руками: перечитать рекомендацию — выводы в ней за учеником.

8. Хвосты

  • install-wizard.sh: убрано «Каркас взят из шаблона».
  • openshell/SEARCH.md: убраны «канал gh недоступен», «выводы агента», ADOPT/EXTEND, «Поправка к прежним текстам», «Mac автора»; у promptfoo — почему не использован.
  • results/install.log в .gitignore.
  • check.py: см. пункт 6.
  • Дополнительно: образы OpenShell закреплены по digest (pull пропускается, если локальный образ совпадает); список контейнеров стенда фильтруется по меткам OpenShell, чтобы в raw не попадали чужие контейнеры хоста; пути хоста заменяются при записи.

Не сделано

  • Прогон Claude Code и мастера установки — нужен CLI Claude Code, модель и sudo.
  • Фильтры содержимого, шлюз OpenShell с аутентификацией и providers, Linux arm64, WSL2.

🤖 Generated with Claude Code

shvma and others added 2 commits October 30, 2026 11:33
- канарейка случайная и не передаётся в argv; вынос считается только для
  значения, которое проба прочитала сама
- сеть: решает журнал приёмника; на Linux отказ соединения в отдельном
  сетевом пространстве имён засчитывается, только если control дошёл
- ФС: состояние цели проверяет раннер (на хосте, в OpenShell через docker cp)
- новые проверки: DNS/UDP, unix socket, файлы, которые исполняет компонент
  вне песочницы (git hook, .claude/settings.json, .mcp.json, Makefile,
  package.json, .envrc, pre-commit); git commit вне песочницы исполняет хук
- validate разделён на ошибки стенда (код 1) и находки (код 2)
- OpenShell: удаление sandbox подтверждается по шлюзу и Docker; образы
  закреплены по digest
- console.log и SHA256SUMS для каждого прогона, пути хоста заменяются
- managed settings: хуки и MCP только из managed, WebFetch/WebSearch,
  новые credential-файлы и ANTHROPIC_API_KEY, autoAllowBashIfSandboxed=false
- check.py сверяет значения ключей с таблицей README; мастер установки
  решает исход по файлам и журналу приёмника

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- results/2026-10-30: console.log, SHA256SUMS, матрицы пересчитываются из raw
- README: отдельные таблицы для SRT (процесс на хосте) и OpenShell
  (контейнер в VM), где проверено, что не покрыто и почему
- RECOMMENDATION: у каждого утверждения — прогон или «не проверено»;
  фильтры содержимого не испытывались; Landlock не изолирован прогоном
- 2026-10-29: фраза про одну команду с кодом 0 заменена фактами
  артефактов; Docker Engine, а не Docker Desktop
- openshell/SEARCH.md: хвосты формулировок

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shvma
shvma merged commit 7ba0f41 into main Oct 30, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant