Skip to content

feat(opencode): add command and URL allowlists - #3789

Open
Sylvester Kaczmarek (sylvesterkaczmarek) wants to merge 9 commits into
microsoft:mainfrom
sylvesterkaczmarek:feat/3666-opencode-policy-allowlists
Open

feat(opencode): add command and URL allowlists#3789
Sylvester Kaczmarek (sylvesterkaczmarek) wants to merge 9 commits into
microsoft:mainfrom
sylvesterkaczmarek:feat/3666-opencode-policy-allowlists

Conversation

@sylvesterkaczmarek

@sylvesterkaczmarek Sylvester Kaczmarek (sylvesterkaczmarek) commented Aug 20, 2026

Copy link
Copy Markdown

Summary

Add opt-in positive command and URL/domain allowlists to the OpenCode governance policy surface, closing #3666.

Problem

OpenCode currently supports negative controls through blockedToolCalls[].commandPatterns and directResourcePolicies.urlRules, but cannot express policies such as “only these commands” or “only these network destinations” with an explicit default-deny posture.

Changes

  • add toolPolicies.allowedCommandPatterns and commandDefaultEffect
  • add directResourcePolicies.allowedDomains, allowedUrlPatterns, and urlDefaultEffect
  • support exact hosts, *.example.com subdomains, and optional explicit ports
  • preserve deny precedence so a positive match can never override an existing deny
  • reject malformed domains, invalid regexes, unsupported default effects, and stateful g/y regex flags
  • normalize outer command whitespace and CRLF line endings before matching
  • route invalid allowlist configuration through the existing denyOnPolicyError behavior
  • wire the OpenCode plugin, public ./policy export, and bundled MCP server through the OpenCode-specific allowlist layer
  • add an opt-in example and focused regression tests

Security and compatibility

The positive allowlist controls are opt-in: both new default effects remain allow unless explicitly changed to deny. Separately, this PR tightens existing URL-rule handling: surfaced HTTP(S) forms such as https:host are canonicalized before existing urlRules are evaluated, and ambiguous HTTP(S) authorities containing backslashes are denied even when urlDefaultEffect is allow.

The positive layer can only add a deny. PolicyEngine already resolves backend decisions with deny taking precedence over review and allow.

For URLs, every HTTP(S) value surfaced as a tool argument is checked, so an allowed primary URL does not authorize a separate unapproved redirect-target argument. Redirects hidden inside an HTTP client remain outside the plugin hook boundary.

No runtime dependency is added.

Testing

Added agent-governance-opencode/test/allowlist-policy.test.mjs and wired it into the package's existing npm run check / npm test glob.

Closes #3666

Signed-off-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Aug 20, 2026
Signed-off-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com>
@github-actions github-actions Bot added the size/XL Extra large PR (500+ lines) label Aug 20, 2026
@github-actions

Copy link
Copy Markdown

PR Review Summary

Check Status Details
🔍 Code Review ⚠️ Missing No current-run comment
🛡️ Security Scan ⚠️ Missing No current-run comment
🔄 Breaking Changes ⚠️ Missing No current-run comment
📝 Docs Sync ⚠️ Missing No current-run comment
🧪 Test Coverage ⚠️ Missing No current-run comment

Verdict: ⚠️ AI review incomplete; ready for human review

AI review comments are untrusted advisory output. The summary reports workflow-generated completion status only, not model-authored pass/fail claims.

@github-actions

Copy link
Copy Markdown

🟡 Contributor Check: MEDIUM

Check Result
Profile MEDIUM
Credential LOW
Overall MEDIUM

Automated check by AGT Contributor Check.

@github-actions github-actions Bot added the needs-review:MEDIUM Contributor check flagged MEDIUM risk label Aug 20, 2026
Signed-off-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com>
Comment thread agent-governance-opencode/config/allowlist-policy.example.json
Comment thread agent-governance-opencode/lib/opencode-policy.mjs Outdated
Comment thread agent-governance-opencode/lib/opencode-policy.mjs
Comment thread agent-governance-opencode/lib/opencode-policy.mjs Outdated
Comment thread agent-governance-opencode/lib/opencode-policy.mjs Outdated
Comment thread agent-governance-opencode/README.md
Signed-off-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com>
Signed-off-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com>
Signed-off-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com>
Signed-off-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com>
Signed-off-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com>
@sylvesterkaczmarek

Copy link
Copy Markdown
Author

MohammadHaroonAbuomar Thanks for the detailed review. I pushed fixes covering all six findings:

  • hardened the shipped command patterns and added regression coverage for ;, &&, newlines, $(), and backticks
  • every present command-bearing key is now evaluated, with malformed/non-string command values failing closed
  • g, y, m, and s regex flags are rejected
  • ambiguous HTTP(S) authorities containing backslashes are denied before parsing
  • the README now documents the actual URL hook boundary, HTTP/HTTPS domain behavior, and shell-command/hidden-redirect limitations
  • compatibility note: surfaced HTTP(S) values are canonicalized before existing urlRules, so forms such as https:example.com can now match the same existing deny/review rules

The OpenCode package build/tests and repository CI checks pass on the updated head. Please re-review.

@MohammadHaroonAbuomar MohammadHaroonAbuomar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  • Last item from the earlier review: please add a CHANGELOG.md [Unreleased] entry covering (a) slashless 'https:host' forms now canonicalizing into existing urlRules and (b) the new unconditional backslash-authority deny (it fires even with urlDefaultEffect: allow), and correct the PR body's 'existing policies keep their current behavior' claim, which both changes now overstate. Everything else from the review is verified fixed — this is the only remaining item.

Signed-off-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com>

@MohammadHaroonAbuomar MohammadHaroonAbuomar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  • One sentence left: the PR description's 'so existing policies keep their current behavior' (Security and compatibility section) is still unqualified — the backslash-authority deny fires even with urlDefaultEffect: allow, as your new CHANGELOG entry correctly states. Please qualify that sentence (e.g. '...keep their current behavior, except the two hardening changes noted in the changelog'). Everything else is verified and this is ready to go once edited.

@sylvesterkaczmarek

Copy link
Copy Markdown
Author

MohammadHaroonAbuomar Updated the PR description to qualify the compatibility statement as requested. The changelog entry is also in place, all earlier review items are addressed, and the review threads are resolved. Ready for re-review when convenient.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation needs-review:MEDIUM Contributor check flagged MEDIUM risk size/XL Extra large PR (500+ lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: add command and URL allowlist policy primitives

2 participants