Skip to content

fix(security): harden @general agent permission model and add git-commit gating validator#241

Merged
kyaulabs-bot merged 3 commits into
developfrom
fix/kyau-db55-general-permission-hardening
Jul 23, 2026
Merged

fix(security): harden @general agent permission model and add git-commit gating validator#241
kyaulabs-bot merged 3 commits into
developfrom
fix/kyau-db55-general-permission-hardening

Conversation

@kyaulabs-bot

@kyaulabs-bot kyaulabs-bot commented Jul 23, 2026

Copy link
Copy Markdown
Member

📋 Summary

The general inline primary agent — the most-invoked default agent — shipped with only lsp: allow and inherited the top-level permissive permission.bash (which denied only git push*). This meant it could git add, git stage, and git commit with no approval gate, bypassing the permission model that build/design already enforce. This PR closes that gap and extends the harness validator so the drift class cannot recur.

Three changes: (1) record the decision in ADR-0006, (2) add the same bash gate to general that build/design carry, (3) extend validate-harness.sh to flag any inline agent that can git commit without an explicit gate.

📦 Changes by Phase

Phase A — ADR amendment

  • Modified: adr/0006-readonly-agent-permission-contract.md
    — Appended 2026-07-22 (issue #202) amendment explaining why the read-only contract did not already cover general (no description → fell through every guard) and the mirror-build/design decision.

Phase B — Config fix + regression test

  • Modified: opencode.jsonc (lines 130–137)
    — Replaced general's bare {"lsp": "allow"} permission with a full bash block mirroring build/design: "*": "allow", git add*/stage*/commit*: ask, git push*: deny.
  • New: tests/Unit/Harness/GeneralAgentPermissionTest.php
    — Pest regression test asserting all 4 git-verdict keys exist with correct values.
  • Tests: 1 test (9 assertions), Red→Green validated.

Phase C — Validator extension

  • Modified: .github/scripts/inline-agent-permissions.js
    — Switched from tab-delimited to pipe-delimited output to prevent IFS tab-collapsing of empty fields. Added 5th column (git_commit verdict) and 6th column (has_permission boolean). Updated header comment.
  • Modified: .github/scripts/validate-harness.sh
    — Updated the pre-existing read for the inline read-only check from 4→6 fields. Added a new inline-agent git-commit gating check that flags any agent whose bash is not a full deny but that lacks an explicit git commit* gate (ask/deny). Skips .md-backed agents (their real permissions live in frontmatter) and built-in agents with no project-level permission block.
  • Modified: tests/Shell/validate-harness_test.sh
    — Added 3 Shell tests (ungated → flagged, ask-gated → not flagged, bash:deny → skipped).
  • Tests: 3 new tests (44 total, all pass).

📜 ADRs

ADR Action
0006 Amended (issue #202 entry) — records the @general git-mutation gate decision and the validator extension for the inherited-default drift class

✅ Verification

Gate Result
Harness tests (tests/Unit/Harness) 138 passed (667 assertions)
Shell tests (tests/Shell/validate-harness_test.sh) 44 passed, 0 failed
Real-repo validator 3 inline agents checked, 0 violations
Pest regression test 1 passed (9 assertions)
php-cs-fixer 0 violations (54 files checked)
stylelint N/A (no SCSS changed)
ESLint N/A (no JS sources changed)
Coverage gate PASS (all changed files are config/scripts — correctly skipped)
gitleaks 0 leaks
@code-review (4 axes) 0 blocking, 6 non-blocking observations — all informational/suggested
@spec-review 10/10 acceptance criteria covered

🏗️ Architect Conditions (if applicable)

N/A — the change was scoped and low-complexity; the root cause was fully documented in the issue. @debug's investigation loop had nothing to discover. Proceeded straight to plan + @tdd.

📝 Commits (3 total)

Atomic, conventional-commits-formatted, signed. All reference Refs: #202.

SHA Type Subject
dc2d779 docs(adr) Amend ADR-0006 for @general git-mutation gate (#202)
a4b4e2d fix(security) Gate @general git mutations like build/design (#202)
b6f9fc1 fix(security) Flag ungated inline-agent git commit in validator (#202)

🧪 Test Plan

  • php vendor/bin/pest tests/Unit/Harness/GeneralAgentPermissionTest.php — Run the regression test
  • php -d pcov.enabled=1 vendor/bin/pest tests/Unit/Harness — Run the full harness suite
  • bash tests/Shell/validate-harness_test.sh — Run the Shell validator tests
  • bash .github/scripts/validate-harness.sh — Run the real-repo validator
  • grep -A10 '"general":' opencode.jsonc — Verify the general block in opencode.jsonc. Should have: bash > git add*/stage*/commit*: ask, git push*: deny

kyau added 3 commits July 22, 2026 19:37
Records why the read-only contract did not cover the inline primary agent
@general: it has no description, so it inherited the top-level permissive
permission.bash (only git push* denied) and could git add/stage/commit with
no gate. The fix mirrors build/design (git add*/stage*/commit*: ask + git
push*: deny); general stays general-purpose, not read-only. The validator is
extended to flag inline agents that can commit without an explicit gate.

Refs: #202
Authored-by: glm-5.2
Tested-by: deepseek-v4-pro
Signed-off-by: kyau <git@kyaulabs.com>
@general shipped only lsp: allow and inherited the top-level
permission.bash (only git push* denied), so the most-invoked default agent
could git add/stage/commit with no gate. Add the same bash block as
build/design: "*" allow plus git add*/stage*/commit*: ask and git push*:
deny. general stays general-purpose (not read-only); the safety hook of
ADR-0023/0036 already covers destructive non-git commands harness-wide.

Refs: #202
Authored-by: glm-5.2
Tested-by: deepseek-v4-pro
Signed-off-by: kyau <git@kyaulabs.com>
Extends validate-harness.sh (ADR-0006 Decision #4) with a check that flags
any inline agent whose bash is not a full deny but that lacks an explicit
git commit* gate (ask/deny). inline-agent-permissions.js now emits a sixth
column (git_commit verdict) and uses pipe delimiter to prevent IFS tab-
collapsing of empty fields. Agents backed by .md files or without a project-
level permission block are skipped. Prevents recurrence of the @general drift
class (issue #202): an inline agent that inherits the top-level permissive
default and can commit with no gate.

Fixes: #202
Authored-by: glm-5.2
Tested-by: deepseek-v4-pro
Signed-off-by: kyau <git@kyaulabs.com>

@kyau kyau left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️ Approved by: @kyau

@kyaulabs-bot
kyaulabs-bot merged commit 4dc39f2 into develop Jul 23, 2026
6 checks passed
@kyaulabs-bot
kyaulabs-bot deleted the fix/kyau-db55-general-permission-hardening branch July 23, 2026 03:39
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.

2 participants