fix(tier2): top-level permission allowlist - sandbox paths now enforced
Regression: a Tier 2 session was denied access to
C:\\projects\\manual_slop_tier2\\scripts\\run_tests_batched.py
with 'Allowed base directories are: gencpp, manual_slop'. The
tier2-autonomous agent had a correct permission.read allowlist, but
the top-level permission block (inherited from the main repo's
opencode.json via 'git clone') had no read/write keys, and OpenCode
uses the top-level for the default agent path. The agent's
permission.read was merged but apparently not enforced for the
default-agent access check.
Fix:
1. Add a top-level 'permission' block to
conductor/tier2/opencode.json.fragment with:
- permission.edit: 'deny' (default agents locked down)
- permission.read: deny *, allow sandbox clone + app-data dirs
- permission.write: same
- permission.bash: deny *, allowlist of read-only git commands +
uv run python scripts/{run_tests_batched.py,tier2/*} + basic
shell commands. git push/checkout/restore/reset remain denied.
2. Update setup_tier2_clone.ps1 to also patch the top-level
'permission' block (was only merging the tier2-autonomous agent
block). The script preserves the user's mcp, model, instructions,
watcher, and plugin settings from the inherited opencode.json.
3. Update test_tier2_slash_command_spec.py:
- Rename test_command_fetches_origin_main -> ..._master (we
changed the slash command on 2026-06-17).
- Add test_config_fragment_has_top_level_permission to assert
the new top-level permission block has the right deny-all +
allowlist shape.
The tier2-autonomous agent's permission block is unchanged; it
overrides the top-level for that agent's tool calls.
This commit is contained in:
@@ -61,13 +61,24 @@ if ($PSCmdlet.ShouldProcess("Bootstrap Tier 2 clone at $Tier2ClonePath")) {
|
||||
Copy-Item -Force "$MainRepoPath\conductor\tier2\agents\tier2-autonomous.md" "$Tier2ClonePath\.opencode\agents\tier2-autonomous.md"
|
||||
Copy-Item -Force "$MainRepoPath\conductor\tier2\commands\tier-2-auto-execute.md" "$Tier2ClonePath\.opencode\commands\tier-2-auto-execute.md"
|
||||
|
||||
# Merge opencode.json.fragment into the clone's opencode.json
|
||||
# Merge opencode.json.fragment into the clone's opencode.json.
|
||||
# The clone inherits a copy of the main repo's opencode.json (via
|
||||
# `git clone`), which has top-level `permission.edit: ask` and
|
||||
# `permission.bash: ask`. Those would be unsafe in the sandbox: the
|
||||
# build/plan default agents could read/write anywhere on disk, and
|
||||
# there is no file-system allowlist at the top level. We replace
|
||||
# the top-level `permission` with the hardened sandbox version
|
||||
# (deny-all + allowlist for the sandbox dirs + the tier2-autonomous
|
||||
# agent's permission block). The agent's `permission` overrides the
|
||||
# top-level for that agent's tool calls.
|
||||
$cloneConfig = "$Tier2ClonePath\opencode.json"
|
||||
$fragment = Get-Content "$MainRepoPath\conductor\tier2\opencode.json.fragment" -Raw | ConvertFrom-Json
|
||||
if (Test-Path $cloneConfig) {
|
||||
$existing = Get-Content $cloneConfig -Raw | ConvertFrom-Json
|
||||
if (-not $existing.agent) { $existing | Add-Member -MemberType NoteProperty -Name agent -Value ([PSCustomObject]@{}) }
|
||||
$existing.agent | Add-Member -MemberType NoteProperty -Name "tier2-autonomous" -Value $fragment.agent."tier2-autonomous" -Force
|
||||
if (-not $existing.permission) { $existing | Add-Member -MemberType NoteProperty -Name permission -Value ([PSCustomObject]@{}) }
|
||||
$existing.permission = $fragment.permission
|
||||
$existing | Add-Member -MemberType NoteProperty -Name default_agent -Value "tier2-autonomous" -Force
|
||||
$existing | ConvertTo-Json -Depth 10 | Set-Content $cloneConfig
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user