fix(config): allow Read tool to open .env.example and .env.template #39

Merged
slaid098 merged 4 commits from fix/config/allow-env-example-read into main 2026-08-11 15:19:25 +03:00
Owner

Что сделано

  • .opencode/opencode.json permission.read: после "**/.env*": "deny" добавлены "**/.env.example": "allow" и "**/.env.template": "allow" (findLast — allow побеждает для точных имён placeholder-файлов).
  • tests/test_permissions.py: добавлены 2 теста — test_read_block_env_allow_rules (4 ассерта структуры read-блока: **/.env*=deny, **/.env.example=allow, **/.env.template=allow, **/id_ed25519.pub=allow — без регрессии прецедента) и test_env_example_has_no_real_secrets (regex safety net: 8 паттернов реальных секретов — OpenAI/GitHub/Slack/AWS/Google/JWT/long-hex — fail если найден).
  • .opencode/skills/configure-opencode/SKILL.md: исправлено описание read (документировало «массив glob-паттернов», но по схеме и факту — объект pattern → action с findLast).

3 логических коммита: config / test / docfix.

Почему

.env.example — placeholder-шаблон без реальных секретов, уже закоммичен в git и виден на Forgejo remote. Но **/.env* (deny) ловил .env.example через glob, и более позднего allow не было → Read tool не мог открыть безопасный файл. Прецедент **/id_ed25519.pub: allow (allow после deny через findLast) уже работает в конфиге — та же логика применяется для placeholder env-файлов. .env.template зарезервирован в .gitignore:36 — future-proofing той же сущности. Документация расходилась с реальным конфигом (массив vs объект).

Watch out

  • findLast semantics: **/.env* (deny) ДОЛЖЕН стоять ДО **/.env.example/**/.env.template (allow) — порядок в JSON сохранён (объекты сохраняют insertion order в json.load). Если переставить — deny снова победит.
  • .env (реальные секреты) остаётся denied — 4 правила (*.env, .env, **/.env, **/.env*) не тронуты. .env.example/.env.template не подпадают под первые три (те требуют, чтобы путь заканчивался на .env).
  • .env.production/.env.local/.env.staging остаются под **/.env* deny — для них явного allow нет.
  • check-permissions.py инспектирует только bash allow-правила, НЕ read-блок → CI не ломается.

Pending

—

Closes #38

Closes #38

## Что сделано - `.opencode/opencode.json` `permission.read`: после `"**/.env*": "deny"` добавлены `"**/.env.example": "allow"` и `"**/.env.template": "allow"` (findLast — allow побеждает для точных имён placeholder-файлов). - `tests/test_permissions.py`: добавлены 2 теста — `test_read_block_env_allow_rules` (4 ассерта структуры read-блока: `**/.env*`=deny, `**/.env.example`=allow, `**/.env.template`=allow, `**/id_ed25519.pub`=allow — без регрессии прецедента) и `test_env_example_has_no_real_secrets` (regex safety net: 8 паттернов реальных секретов — OpenAI/GitHub/Slack/AWS/Google/JWT/long-hex — fail если найден). - `.opencode/skills/configure-opencode/SKILL.md`: исправлено описание `read` (документировало «массив glob-паттернов», но по схеме и факту — объект `pattern → action` с findLast). 3 логических коммита: config / test / docfix. ## Почему `.env.example` — placeholder-шаблон без реальных секретов, уже закоммичен в git и виден на Forgejo remote. Но `**/.env*` (deny) ловил `.env.example` через glob, и более позднего allow не было → Read tool не мог открыть безопасный файл. Прецедент `**/id_ed25519.pub: allow` (allow после deny через findLast) уже работает в конфиге — та же логика применяется для placeholder env-файлов. `.env.template` зарезервирован в `.gitignore:36` — future-proofing той же сущности. Документация расходилась с реальным конфигом (массив vs объект). ## Watch out - **findLast semantics:** `**/.env*` (deny) ДОЛЖЕН стоять ДО `**/.env.example`/`**/.env.template` (allow) — порядок в JSON сохранён (объекты сохраняют insertion order в `json.load`). Если переставить — deny снова победит. - **`.env` (реальные секреты) остаётся denied** — 4 правила (`*.env`, `.env`, `**/.env`, `**/.env*`) не тронуты. `.env.example`/`.env.template` не подпадают под первые три (те требуют, чтобы путь заканчивался на `.env`). - `.env.production`/`.env.local`/`.env.staging` остаются под `**/.env*` deny — для них явного allow нет. - `check-permissions.py` инспектирует только `bash` allow-правила, НЕ read-блок → CI не ломается. ## Pending — Closes #38 Closes #38
docs(config): fix read permission description to object pattern-action
Some checks failed
CI (always) / bootstrap (pull_request) Successful in 6s
CI / bootstrap (pull_request) Successful in 10s
Permission Security Check / check (pull_request) Successful in 11s
CI / lint (pull_request) Failing after 31s
CI / complexity (pull_request) Successful in 30s
CI / typecheck (pull_request) Successful in 31s
CI / test (3.13) (pull_request) Successful in 1m40s
ae86d238cd
fix(ci): ruff format test_permissions env-allow tests
All checks were successful
CI (always) / bootstrap (pull_request) Successful in 4s
CI / bootstrap (pull_request) Successful in 10s
Permission Security Check / check (pull_request) Successful in 25s
CI / lint (pull_request) Successful in 14s
CI / typecheck (pull_request) Successful in 28s
CI / complexity (pull_request) Successful in 25s
CI / test (3.13) (pull_request) Successful in 2m17s
3bbc46ccee
Author
Owner

Code Review Summary

Точечный fix issue #38: 3 правки (config / test / docfix), 46 additions / 1 deletion. Логика findLast корректна, .env (секреты) остаётся denied, safety net тест предотвращает будущую утечку реальных секретов в placeholder-файл. CI green, риски низкие.

Positives

  • opencode.json:170-171 — findLast semantics корректны: **/.env* (deny) стоит ДО **/.env.example/**/.env.template (allow), порядок в JSON сохранён (объекты сохраняют insertion order в json.load). Прецедент **/id_ed25519.pub: allow после **/id_ed25519: deny (line 172-174) — аналогичный паттерн, соблюдён.
  • opencode.json:166-169 — 4 правила для реальных секретов (*.env, .env, **/.env, **/.env*) не тронуты. .env остаётся denied — security regression отсутствует.
  • test_permissions.py:172-183 — test_read_block_env_allow_rules покрывает 4 ассерта структуры read-блока: deny для **/.env*, allow для .env.example/.env.template, allow для id_ed25519.pub (регрессия прецедента). Стиль совпадает с существующими тестами (_load_config(), Google-style docstring, section comment с unicode box drawing).
  • test_permissions.py:186-208 — test_env_example_has_no_real_secrets — отличный safety net: 8 regex паттернов (OpenAI sk-, GitHub ghp_/gho_/ghs_/ghu_/ghr_ через gh[posur]_, GitHub fine-grained github_pat_, Slack xox, AWS AKIA, Google AIza, JWT eyJ…eyJ, long hex). Превентит будущую утечку реальных ключей в tracked placeholder-файл.
  • test_permissions.py:16 — import re добавлен в alphabetical order (после json, перед subprocess) — ruff sort корректен.
  • SKILL.md:115 — докфикс корректен: read теперь описан как объект pattern → action (как bash), с семантикой findLast. Прежняя формулировка («массив glob-паттернов») расходилась с реальным конфигом.
  • .gitignore:35-36 — !.env.example и !.env.template уже зарезервированы → .env.template (future-proofing) не создаст untracked file.
  • check-permissions.py — инспектирует только permission.bash (line 134: config.get("permission", {}).get("bash", {})), НЕ read-блок → CI не ломается. PR body это утверждает — подтверждено.
  • PR hygiene — title fix(config): … conventional, body содержит 4 heading'а (## Что сделано / ## Почему / ## Watch out / ## Pending) осмысленно, 3 логических коммита (config / test / docfix), только 3 целевых файла в diff.

Cross-file impact

PR меняет writer (opencode.json read-блок, SKILL.md докфикс). Readers:

  • check-permissions.py — читает только permission.bash, не read → не сломается.
  • Скилл configure-opencode — сам writer SKILL.md, не reader → не сломается.
  • test_permissions.py — reader opencode.json (через _load_config()), но тесты обновлены в этом же PR (paired update) → окно сломанного main закрыто.
    Missing paired update отсутствует.

Suggestions (info, not blocking)

  • test_permissions.py:202 — regex (?<![A-Za-z0-9])[a-f0-9]{40,}(?![A-Za-z0-9]) (long hex token) может дать false positive на git SHA-1 (40 hex chars), если кто-то добавит комментарий с SHA в .env.example. Текущий файл чист, но паттерн широковат для будущего. Не blocking — при false positive тест просто укажет на конкретный match, и автор решит (whitelist SHA или сузить паттерн).

Verdict: APPROVE

## Code Review Summary Точечный fix issue #38: 3 правки (config / test / docfix), 46 additions / 1 deletion. Логика findLast корректна, `.env` (секреты) остаётся denied, safety net тест предотвращает будущую утечку реальных секретов в placeholder-файл. CI green, риски низкие. ### Positives - **opencode.json:170-171** — findLast semantics корректны: `**/.env*` (deny) стоит ДО `**/.env.example`/`**/.env.template` (allow), порядок в JSON сохранён (объекты сохраняют insertion order в `json.load`). Прецедент `**/id_ed25519.pub: allow` после `**/id_ed25519: deny` (line 172-174) — аналогичный паттерн, соблюдён. - **opencode.json:166-169** — 4 правила для реальных секретов (`*.env`, `.env`, `**/.env`, `**/.env*`) не тронуты. `.env` остаётся denied — security regression отсутствует. - **test_permissions.py:172-183** — `test_read_block_env_allow_rules` покрывает 4 ассерта структуры read-блока: deny для `**/.env*`, allow для `.env.example`/`.env.template`, allow для `id_ed25519.pub` (регрессия прецедента). Стиль совпадает с существующими тестами (`_load_config()`, Google-style docstring, section comment с unicode box drawing). - **test_permissions.py:186-208** — `test_env_example_has_no_real_secrets` — отличный safety net: 8 regex паттернов (OpenAI `sk-`, GitHub `ghp_`/`gho_`/`ghs_`/`ghu_`/`ghr_` через `gh[posur]_`, GitHub fine-grained `github_pat_`, Slack `xox`, AWS `AKIA`, Google `AIza`, JWT `eyJ…eyJ`, long hex). Превентит будущую утечку реальных ключей в tracked placeholder-файл. - **test_permissions.py:16** — `import re` добавлен в alphabetical order (после `json`, перед `subprocess`) — ruff sort корректен. - **SKILL.md:115** — докфикс корректен: `read` теперь описан как объект `pattern → action` (как `bash`), с семантикой findLast. Прежняя формулировка («массив glob-паттернов») расходилась с реальным конфигом. - **.gitignore:35-36** — `!.env.example` и `!.env.template` уже зарезервированы → `.env.template` (future-proofing) не создаст untracked file. - **check-permissions.py** — инспектирует только `permission.bash` (line 134: `config.get("permission", {}).get("bash", {})`), НЕ `read`-блок → CI не ломается. PR body это утверждает — подтверждено. - **PR hygiene** — title `fix(config): …` conventional, body содержит 4 heading'а (`## Что сделано` / `## Почему` / `## Watch out` / `## Pending`) осмысленно, 3 логических коммита (config / test / docfix), только 3 целевых файла в diff. ### Cross-file impact PR меняет writer (`opencode.json` read-блок, `SKILL.md` докфикс). Readers: - `check-permissions.py` — читает только `permission.bash`, не `read` → не сломается. - Скилл `configure-opencode` — сам writer SKILL.md, не reader → не сломается. - `test_permissions.py` — reader `opencode.json` (через `_load_config()`), но тесты обновлены в этом же PR (paired update) → окно сломанного main закрыто. Missing paired update отсутствует. ### Suggestions (info, not blocking) - **test_permissions.py:202** — regex `(?<![A-Za-z0-9])[a-f0-9]{40,}(?![A-Za-z0-9])` (long hex token) может дать false positive на git SHA-1 (40 hex chars), если кто-то добавит комментарий с SHA в `.env.example`. Текущий файл чист, но паттерн широковат для будущего. Не blocking — при false positive тест просто укажет на конкретный match, и автор решит (whitelist SHA или сузить паттерн). ### Verdict: APPROVE
slaid098 deleted branch fix/config/allow-env-example-read 2026-08-11 15:19:25 +03:00
Sign in to join this conversation.
No reviewers
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!39
No description provided.