chore(permissions): allow deletion ssh curl and fs operations #36
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?
Контекст
Зачем: второе расширение allow-list
permission.bashв.opencode/opencode.json. Первое (issue #34 / PR #35, merged в commit3ef28e9) добавило read-only команды (echo, cat, head, tail, grep, rg, find, ls, wc, pwd, which, date, test, sed -n, od, xxd, git rev-parse/remote/show/get-url/show/fetch/blame/ls-tree/ls-files, git -C * read-only, node/npm queries). Этого недостаточно — остаются ночные залипания.Контекст: анализ 56 ask-событий за 5 дней (6-10 августа) показал два главных stall-узла:
rm *(20 ask, 19 stall) иssh *(20 ask, 17 stall). Пользователь запускал overnight-задачу — она залипла наrm(удаление файлов). Также не покрыты первым расширением:git branch -D *(5 ask) иdocker exec *(2 ask).Решение пользователя: разрешить БОЛЬШЕ команд, включая удаление (
rm *,git rm *) и SSH (ssh *), потому что Docker-изоляция + git history делают большинство операций восстанавливаемыми. Пользователь явно принимает риск чтения агентом credentials через SSH (маленькие балансы, solo developer, агент нужен для деплоя). Carve-outssh * cat *— НЕ добавляем (user declined).Задача
Изменить блок
permission.bash(строки 174-381) в файле.opencode/opencode.json. Все изменения точечные — добавить/изменить паттерны в существующем JSON-массиве. Сохранить findLast-порядок: все deny/ask carve-outs идут ПОСЛЕ соответствующих allow правил.Bucket A — read-only (добавить в allow, рядом с существующими read-only паттернами ~строки 224-236):
Bucket A — docker exec carve-outs (deny/ask ПОСЛЕ
docker exec *: allow, findLast):Bucket B — мутирующие локальные (добавить в allow):
Bucket D — удаление (allow с carve-outs для секретов):
Bucket D — deny carve-outs для секретов (ПОСЛЕ
rm *: allow, findLast — защищают gitignored файлы которые НЕ recoverable via git):Bucket E — SSH (заменить catch-all):
Изменить существующее
"ssh *": "ask"(~строка 356) →"ssh *": "allow".Bucket E — SSH destructive carve-outs:
Эти правила УЖЕ есть в конфиге (~строки 332-354) как
askилиdeny— НЕ удалять, НЕ менять. Они должны остаться ПОСЛЕ новогоssh *: allowчтобы побеждать по findLast:ssh * rm *: askssh * rmdir *: askssh * mkfs*: denyssh * dd *: denyssh * fdisk*: denyssh * reboot*: askssh * shutdown*: askssh * systemctl restart*: askssh * systemctl stop*: askssh * apt *: askssh * yum *: askssh * mv *: askssh * chmod *: askssh * chown *: askssh * docker restart*: askssh * docker stop*: askssh * docker rm*: askssh * docker rmi*: askssh * docker update*: askssh * cat .env*: askssh * powershell -Command Set-*: askssh * powershell -Command New-*: askssh * powershell -Command Remove-*: askssh * powershell -Command Restart-*: askssh * powershell -Command Stop-*: askFix — убрать дубль
git commit *:Найти
"git commit *": "allow"(~строка 183) и УДАЛИТЬ его. Оставить"git commit *": "deny"(~строка 358). Причина: findLast — последнее правило побеждает, deny на 358 уже выигрывает, но дубль confusing.committool используется вместо bashgit commit.curl *carve-out (deny ПОСЛЕcurl *: allow, findLast):Существующее
"curl * -X DELETE *FORGEJO_URL*": "deny"(~строка 359) — ОСТАВИТЬ (уже есть, послеcurl *: allowпобеждает по findLast). Убедиться что новоеcurl *: allowдобавлено ВЫШЕ существующего deny.Per-agent: НЕ трогать
.opencode/agents/reviewer.md— НЕ менять (уже получил ruff/gh auth status в PR #35).opencode/agents/memory-syncer.md— НЕ менять (python3 bypass guard, read-only guardrail)Контракты
general/exploreбольше не ask наcurl *,rm *,git rm *,git branch -D *,docker exec *(read-only),ssh *(неинспекционные),cp *,mv *,touch *,ln *,tar *,zip *,unzip *,wget *,git switch *,git stash*,git tag,git config *,git reflog *,docker images*,docker inspect*,docker stats*.env,id_ed25519,*.pem,*secrets*) остаются deny дляrm— carve-outs послеrm *: allow(findLast)curl * -X DELETE *FORGEJO_URL*остаётся deny — существующее правило послеcurl *: allow(findLast)ssh * rm *,ssh * mkfs*,ssh * reboot*,ssh * docker restart*etc.) остаются ask/deny — существующие правила послеssh *: allow(findLast)docker exec * rm *: deny,docker exec * sh */docker exec * bash *: ask — carve-outs послеdocker exec *: allow(findLast)git push --force*,git push -f*,git push --delete *,git push origin --delete *— остаются ask (не трогаем)git reset *,git clean *— остаются deny (не трогаем)git commit *— остаётся deny (убираем дубль allow, оставляем deny)kill *,docker rm *,docker stop *,docker restart *— остаются ask (не трогаем)Инварианты
rm *: allow,curl *: allow,ssh *: allow,docker exec *: allow) в JSON-массивеpermission.bash— opencode findLast, последнее правило побеждаетssh * cat *: ask— user explicitly declined (agent may read remote creds for deploys, accepted risk)python3 *в memory-syncer (bypass risk)gh pr merge *(orchestrator uses merge-pr tool, not bash; gh CLI not installed)git push --force*/--deleteостаются ask (irreversible remote operations)git reset *остаётся deny (destructive)python3 -c "import json; json.load(open('.opencode/opencode.json'))"илиnode -e "JSON.parse(require('fs').readFileSync('.opencode/opencode.json','utf8'))")Граничные случаи
&&-chaining: opencode матчит ВСЮ строку.rm /tmp/x && git statusНЕ матчит ниrm *, ниgit status*— catch-all. Не лечится allow-list. Mitigation: дисциплина в промптах.rm .envзащищён carve-outrm .env*: deny. Ноrm .env.localилиrm /path/to/.env—rm .env*матчит только prefix.env*, НЕ матчит/path/to/.env. Секреты в нестандартных путях НЕ защищены. Acceptable risk (user confirmed).docker exec * rm *: deny защищает от удаления внутри контейнера. Ноdocker exec forgejo sh -c "rm /tmp/x"— матчитdocker exec * sh *: ask, НЕdocker exec * rm *: deny(findLast выберет ask). Acceptable.ssh * cat rclone.conf— БЕЗ carve-out, будет allow. User accepted (creds нужны для деплоя, балансы маленькие).git tag(create) — allow.git tag -d *(delete) — остаётся ask (существующее правило ~строка 366).Влияние на связанные компоненты
.opencode/opencode.json— основной файл изменений (блокpermission.bash, строки 174-381).opencode/agents/reviewer.md— НЕ меняется.opencode/agents/memory-syncer.md— НЕ меняетсяВне scope
ssh * cat *: ask(user declined — agent may read remote creds)python3 *в memory-syncer (bypass risk)gh pr merge *в allow (orchestrator tool, gh not installed)git push --force*/--deleteв allow (irreversible)git reset *в allow (destructive)Критерии приемки
.opencode/opencode.jsonдобавлены все Bucket A паттерны (curl, git reflog, docker images/inspect/stats/exec)docker exec * rm *: deny+docker exec * rmdir *: deny+docker exec * sh *: ask+docker exec * bash *: askидут ПОСЛЕdocker exec *: allow(findLast)rm *: allow+git rm *: allow+git branch -D *: allow+git -C * branch -D *: allowrm .env*: deny,rm *id_ed25519*: deny,rm *.pem*: deny,rm *secrets*: denyидут ПОСЛЕrm *: allow(findLast)ssh *изменён сaskнаallow(catch-all)ssh *: allowssh * cat *: askНЕ добавлен (user declined)git commit *убран (allowудалён,denyоставлен)curl * -X DELETE *FORGEJO_URL*: denyсохранён ПОСЛЕcurl *: allownode -e "JSON.parse(require('fs').readFileSync('.opencode/opencode.json','utf8'))"проходит без ошибок)git diffпоказывает только.opencode/opencode.json