chore(permissions): expand global allow-list with read-only commands #34
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Контекст
Зачем: агенты
generalиexploreне имеют per-agentpermission.bashконфигурации и работают только на глобальном allow-list (.opencode/opencode.json, блокpermission.bash, строки 174-334). Глобальный allow-list содержит git/docker/uv/npm/python/ssh паттерны, но НЕ содержит базовых read-only утилит inspection (echo,cat,head,tail,rg,grep,find,ls,wc,pwd,which,date,test,sed -n,od,xxd) и read-only git subcommands (git rev-parse,git remote -v/show/get-url,git show,git fetch,git blame,git ls-tree,git ls-files). Каждый такой вызов попадает в catch-all → ask → подтверждение пользователем.Контекст: исторический анализ логов (187k строк, 2026-06-10 → 2026-07-20) зафиксировал ~128 ask-событий, большинство — routine read-only команды. Цель: промотировать безопасные read-only команды в глобальный allow-list для снижения prompt fatigue, сохранив gating деструктивных команд (
gh pr merge,git push --force,git reset,git rm,git commit,python3в memory-syncer).Задача
1. Глобальный allow-list —
.opencode/opencode.json, блокpermission.bash(строки 174-334)Добавить следующие паттерны (Bucket A). Размещение — логическими группами, с учётом инварианта порядка для
cat *(см.## Инварианты).Read-only утилиты (не мутируют FS/state):
Read-only git subcommands:
Read-only
git -C *subcommands (консистентно с существующими мутирующимиgit -C *правилами на строках 191-199):Read-only registry/version queries:
cat *— С ОСОБЫМ УКАЗАНИЕМ О ПОРЯДКЕ (CRITICAL):Обязательно поместить ДО существующих deny-правил
cat .env*(строка 175),*cat */.env*(строка 176),cat *id_ed25519*(строка 252),*cat *ssh/id_ed25519*(строка 253). См.## Инварианты— opencodefindLastsemantics.2. Per-agent allow-list —
.opencode/agents/reviewer.md(строки 9-75, frontmatterpermission.bash)Добавить ТОЛЬКО узкие read-only правила (Bucket B) — НЕ глобально:
Причина не глобально: bare
ruff format(без--check) переписывает файлы — только read-only формы безопасны;gh *глобально = slippery slope кgh pr merge(который MUST stay gated).3. НЕ трогать (Bucket C — оставить gated)
gh pr merge *— без правила (defaults to ask) — reviewer protocol violation guardrail (13 исторических попыток)git reset *,git -C * reset *— уже deny (строки 200, 244)git rm *— нет правила (defaults to ask) — деструктивноpython3 *вmemory-syncer.md— НЕ добавлять (bypass risk,subprocess.runобходит bash permission)git commit *— уже deny (строка 312 переопределяет ранний allow на строке 182)git push --force*,git push -f*,git branch -D *,git tag -d *— уже ask (строки 231-234, 315-320)rm *,kill *,docker rm *,docker stop *— уже ask (строки 237-249)git clone*— уже deny (строка 327)Контракты
generalиexploreбольше не ask на single-command вызовыecho,cat,head,tail,rg,grep,find,ls,wc,pwd,which,date,test -f,sed -n,od,xxd,git rev-parse,git remote -v/show/get-url,git show,git fetch,git blame,git ls-tree,git ls-files,node --version,npm view/ls/audit.reviewerбольше не ask/deny наruff check,ruff format --check,gh auth status.cat .env,cat ~/.ssh/id_ed25519) остаются deny — порядок правил сохранён (deny ПОСЛЕcat *: allow).gh pr merge *остаётся ask (нет правила) — guardrail против reviewer protocol violations.git reset,git rm,git push --force,git commit,rm,kill) остаются gated.Инварианты
cat *: deny-правила для секретов (cat .env*,*cat */.env*,cat *id_ed25519*,*cat *ssh/id_ed25519*) должны идти ПОСЛЕ"cat *": "allow"в JSON-объектеpermission.bash. opencode permission matching используетfindLast(последнее совпадающее правило побеждает). Если"cat *": "allow"стоит ПОСЛЕ"cat .env*": "deny", то allow переопределит deny → секреты утекут. Правильный порядок: сначала"cat *": "allow", ЗАТЕМ deny-правила для секретов.gh pr merge*ни в глобальный, ни в per-agent allow-list (reviewer protocol violation guardrail).python3*вmemory-syncer.md(bypass risk черезsubprocess.run).ruff format*(мутирует файлы) — толькоruff format --check*.git pull*вreviewer.md(reviewer read-only, 4 исторических deny корректны).git checkout*вmemory-syncer.md(read-only guardrail).Граничные случаи
&&-chaining caveat: opencode матчит ВСЮ строку команды как один unit.echo "---" && git statusНЕ матчит ниecho *, ниgit status*— попадает в catch-all → ask. Это ограничение opencode (см. memorytechnical/opencode-permission-ampersand-blocking.md), НЕ лечится allow-list. Mitigation: дисциплина в промптах агентов (не чейнить). Добавление Bucket A устраняет single-command asks (большинство), но chained команды всё равно будут ask.catфайла с секретом через symlink/..path: deny-правила используют wildcardcat .env*/cat *id_ed25519*— покрывают прямые пути. Path traversal (cat ../../.env) не покрываетсяcat *: allow+deny, но это известное ограничение wildcard matching — вне scope этого issue.ruff format(без--check) случайно вызванное reviewer'ом: не в allow-list → catch-all"*": denyблокирует. Корректно.gh auth statusпри отсутствииghCLI: команда не существует → fail, не ask. Корректно (не блокирует pipeline).Влияние на связанные компоненты
.opencode/opencode.jsonpermission.bash(строки 174-334) — основной writer change: добавление ~45 паттернов + переупорядочивание дляcat *..opencode/agents/reviewer.mdpermission.bash(строки 9-75) — добавление 3 паттернов (Bucket B)..opencode/agents/general(строки 337-347 вopencode.json) — НЕ имеет per-agentpermission.bash, наследует глобальный → получит все Bucket A автоматически. Поведение: меньше ask-промптов. Положительное влияние.exploreагент (built-in, без per-agent config) — наследует глобальный → получит Bucket A. Положительное влияние (read-only inspection — основная работа explore)..opencode/agents/memory-syncer.md(строки 9-31) — НЕ трогается. Уже имеет свой rich allow-list. Глобальные Bucket A не переопределяют per-agent (per-agent имеет приоритет). Корректно.pipeline-status.py/spec-status.py/project-status.py— deny-правила (строки 215-229) сохраняются, НЕ трогаются. Глобальное добавлениеpython*НЕ производится (уже естьpython*: allowна строке 214 — это существующее поведение, не меняется).reviewer.mdknown deterministic linkopencode.json deny-rules ↔ *-status.ts native tools(строка 417-418 reviewer.md) — не нарушается: deny на directpython3 .../pipeline-status.pyсохранён.Вне scope
gh pr mergeв allow (guardrail, не трогаем).python3*в memory-syncer (bypass risk).git push --force*в allow (деструктивно).stepsлимитов (отдельная задача).external_directoryправила (ужеallowглобально, строка 163).&&-chaining ask (ограничение opencode, mitigation — промпт-дисциплина).Критерии приемки
.opencode/opencode.jsonблокpermission.bash(строки 174-334) добавлены все Bucket A паттерны: read-only утилиты (24) + read-only git (9) + read-onlygit -C *(12) + registry queries (5) +cat *."cat *": "allow"помещён ДО deny-правилcat .env*(строка 175),*cat */.env*(строка 176),cat *id_ed25519*(строка 252),*cat *ssh/id_ed25519*(строка 253) — findLast ordering invariant..opencode/agents/reviewer.md(строки 9-75) добавленыruff check*,ruff format --check*,gh auth status*(read-only, per-agent).gh pr merge*НЕ добавлен ни глобально, ни per-agent (остаётся ask/deny).python3*НЕ добавлен вmemory-syncer.md.python -m json.tool .opencode/opencode.jsonпроходит без ошибок).git diffпоказывает только изменения в.opencode/opencode.jsonи.opencode/agents/reviewer.md(2 файла).