chore(permissions): expand global allow-list with read-only commands #34

Closed
opened 2026-08-09 22:44:04 +03:00 by slaid098 · 0 comments
Owner

Контекст

Зачем: агенты general и explore не имеют per-agent permission.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):

"echo *": "allow"
"test -f *": "allow"
"test -d *": "allow"
"pwd": "allow"
"pwd *": "allow"
"date": "allow"
"date *": "allow"
"which *": "allow"
"bash -n *": "allow"
"wc*": "allow"
"wc *": "allow"
"grep *": "allow"
"rg *": "allow"
"find *": "allow"
"ls *": "allow"
"head*": "allow"
"tail*": "allow"
"head *": "allow"
"tail *": "allow"
"sed -n *": "allow"
"od *": "allow"
"xxd *": "allow"
"xxd": "allow"

Read-only git subcommands:

"git rev-parse *": "allow"
"git remote -v*": "allow"
"git remote show*": "allow"
"git remote get-url *": "allow"
"git show*": "allow"
"git fetch*": "allow"
"git blame*": "allow"
"git ls-tree*": "allow"
"git ls-files*": "allow"

Read-only git -C * subcommands (консистентно с существующими мутирующими git -C * правилами на строках 191-199):

"git -C * status*": "allow"
"git -C * diff*": "allow"
"git -C * log*": "allow"
"git -C * show*": "allow"
"git -C * branch*": "allow"
"git -C * blame*": "allow"
"git -C * fetch*": "allow"
"git -C * remote -v*": "allow"
"git -C * remote show*": "allow"
"git -C * ls-tree*": "allow"
"git -C * ls-files*": "allow"
"git -C * rev-parse *": "allow"

Read-only registry/version queries:

"node --version*": "allow"
"node -v*": "allow"
"npm view *": "allow"
"npm ls *": "allow"
"npm audit*": "allow"

cat * — С ОСОБЫМ УКАЗАНИЕМ О ПОРЯДКЕ (CRITICAL):

"cat *": "allow"

Обязательно поместить ДО существующих deny-правил cat .env* (строка 175), *cat */.env* (строка 176), cat *id_ed25519* (строка 252), *cat *ssh/id_ed25519* (строка 253). См. ## Инварианты — opencode findLast semantics.

2. Per-agent allow-list — .opencode/agents/reviewer.md (строки 9-75, frontmatter permission.bash)

Добавить ТОЛЬКО узкие read-only правила (Bucket B) — НЕ глобально:

"ruff check*": "allow"
"ruff format --check*": "allow"
"gh auth status*": "allow"

Причина не глобально: 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.

Инварианты

  • CRITICAL — порядок правил для 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).
  • НЕ добавлять bare 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 (см. memory technical/opencode-permission-ampersand-blocking.md), НЕ лечится allow-list. Mitigation: дисциплина в промптах агентов (не чейнить). Добавление Bucket A устраняет single-command asks (большинство), но chained команды всё равно будут ask.
  • cat файла с секретом через symlink/.. path: deny-правила используют wildcard cat .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 при отсутствии gh CLI: команда не существует → fail, не ask. Корректно (не блокирует pipeline).

Влияние на связанные компоненты

  • .opencode/opencode.json permission.bash (строки 174-334) — основной writer change: добавление ~45 паттернов + переупорядочивание для cat *.
  • .opencode/agents/reviewer.md permission.bash (строки 9-75) — добавление 3 паттернов (Bucket B).
  • .opencode/agents/general (строки 337-347 в opencode.json) — НЕ имеет per-agent permission.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.md known deterministic link opencode.json deny-rules ↔ *-status.ts native tools (строка 417-418 reviewer.md) — не нарушается: deny на direct python3 .../pipeline-status.py сохранён.

Вне scope

  • ❌ Перевод gh pr merge в allow (guardrail, не трогаем).
  • ❌ Добавление python3* в memory-syncer (bypass risk).
  • ❌ Добавление git push --force* в allow (деструктивно).
  • ❌ Глобальный Ask → 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-only git -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.
  • JSON валиден (опечаток нет, запятые корректны, python -m json.tool .opencode/opencode.json проходит без ошибок).
  • Существующие deny-правила для секретов и деструктивных команд сохранены (diff не удаляет строки 175-176, 200, 244-245, 252-253, 312, 327).
  • git diff показывает только изменения в .opencode/opencode.json и .opencode/agents/reviewer.md (2 файла).
## Контекст Зачем: агенты `general` и `explore` не имеют per-agent `permission.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):** ``` "echo *": "allow" "test -f *": "allow" "test -d *": "allow" "pwd": "allow" "pwd *": "allow" "date": "allow" "date *": "allow" "which *": "allow" "bash -n *": "allow" "wc*": "allow" "wc *": "allow" "grep *": "allow" "rg *": "allow" "find *": "allow" "ls *": "allow" "head*": "allow" "tail*": "allow" "head *": "allow" "tail *": "allow" "sed -n *": "allow" "od *": "allow" "xxd *": "allow" "xxd": "allow" ``` **Read-only git subcommands:** ``` "git rev-parse *": "allow" "git remote -v*": "allow" "git remote show*": "allow" "git remote get-url *": "allow" "git show*": "allow" "git fetch*": "allow" "git blame*": "allow" "git ls-tree*": "allow" "git ls-files*": "allow" ``` **Read-only `git -C *` subcommands** (консистентно с существующими мутирующими `git -C *` правилами на строках 191-199): ``` "git -C * status*": "allow" "git -C * diff*": "allow" "git -C * log*": "allow" "git -C * show*": "allow" "git -C * branch*": "allow" "git -C * blame*": "allow" "git -C * fetch*": "allow" "git -C * remote -v*": "allow" "git -C * remote show*": "allow" "git -C * ls-tree*": "allow" "git -C * ls-files*": "allow" "git -C * rev-parse *": "allow" ``` **Read-only registry/version queries:** ``` "node --version*": "allow" "node -v*": "allow" "npm view *": "allow" "npm ls *": "allow" "npm audit*": "allow" ``` **`cat *` — С ОСОБЫМ УКАЗАНИЕМ О ПОРЯДКЕ (CRITICAL):** ``` "cat *": "allow" ``` Обязательно поместить ДО существующих deny-правил `cat .env*` (строка 175), `*cat */.env*` (строка 176), `cat *id_ed25519*` (строка 252), `*cat *ssh/id_ed25519*` (строка 253). См. `## Инварианты` — opencode `findLast` semantics. ### 2. Per-agent allow-list — `.opencode/agents/reviewer.md` (строки 9-75, frontmatter `permission.bash`) Добавить ТОЛЬКО узкие read-only правила (Bucket B) — НЕ глобально: ``` "ruff check*": "allow" "ruff format --check*": "allow" "gh auth status*": "allow" ``` Причина не глобально: 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. ## Инварианты - **CRITICAL — порядок правил для `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`). - НЕ добавлять bare `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 (см. memory `technical/opencode-permission-ampersand-blocking.md`), НЕ лечится allow-list. Mitigation: дисциплина в промптах агентов (не чейнить). Добавление Bucket A устраняет single-command asks (большинство), но chained команды всё равно будут ask. - `cat` файла с секретом через symlink/`..` path: deny-правила используют wildcard `cat .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` при отсутствии `gh` CLI: команда не существует → fail, не ask. Корректно (не блокирует pipeline). ## Влияние на связанные компоненты - `.opencode/opencode.json` `permission.bash` (строки 174-334) — основной writer change: добавление ~45 паттернов + переупорядочивание для `cat *`. - `.opencode/agents/reviewer.md` `permission.bash` (строки 9-75) — добавление 3 паттернов (Bucket B). - `.opencode/agents/general` (строки 337-347 в `opencode.json`) — НЕ имеет per-agent `permission.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.md` known deterministic link `opencode.json deny-rules ↔ *-status.ts native tools` (строка 417-418 reviewer.md) — не нарушается: deny на direct `python3 .../pipeline-status.py` сохранён. ## Вне scope - ❌ Перевод `gh pr merge` в allow (guardrail, не трогаем). - ❌ Добавление `python3*` в memory-syncer (bypass risk). - ❌ Добавление `git push --force*` в allow (деструктивно). - ❌ Глобальный Ask → 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-only `git -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`. - [ ] JSON валиден (опечаток нет, запятые корректны, `python -m json.tool .opencode/opencode.json` проходит без ошибок). - [ ] Существующие deny-правила для секретов и деструктивных команд сохранены (diff не удаляет строки 175-176, 200, 244-245, 252-253, 312, 327). - [ ] `git diff` показывает только изменения в `.opencode/opencode.json` и `.opencode/agents/reviewer.md` (2 файла).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
slaid098/opencode-config#34
No description provided.