feat(providers): add opencode_cli semantic-scan provider - #536
Yoseph-Zuskin wants to merge 1 commit into
Conversation
- Add providers/opencode_cli/provider.py and __init__.py mirroring codex_cli - Register opencode in _agent_cli.py: argv builder (opencode run --format json), JSON-event parser, auth check (opencode auth list) - Wire SKILLSPECTOR_PROVIDER=opencode_cli in providers/__init__.py and inference_usage.py - Update docs trio: README provider table, .env.example, docs/DEVELOPMENT.md - Add model-registry entries for opencode/nemotron-3-ultra-free and opencode/nemotron-3.5-lightning-free (1M context) - Add tests/provider/test_opencode_cli.py (31 tests: argv, auth, parser, wiring, registry label) - Verified: live probe and self-scan with opencode 1.18.30 (CLI-default model), llm_available=true, 3/3 semantic calls succeeded; self-scan of the provider package found 0 semantic issues Signed-off-by: Yoseph Zuskin <zuskinyoseph@gmail.com> Co-Authored-By: OpenCode Muse Spark 1.3 Free (1M context) <noreply@opencode.ai> Co-Authored-By: OpenCode Nemotron 3 Ultra Free (1M context) <noreply@opencode.ai>
rng1995
left a comment
There was a problem hiding this comment.
[SkillSpector Review]
Reviewed current head 4e36534712752b3c2a61762f6f0e435b01f1a3ca. Required checks pass, but the new transport is not safe for its trust boundary: untrusted skill text is sent to OpenCode while built-in host tools remain enabled under permissive defaults. --pure removes plugins only; the temporary working directory and scrubbed environment do not prevent shell/network side effects or ambient auto-sharing. Establish a fixed deny-all, non-overridable OpenCode policy and prove it with adversarial tests before enabling this provider.
The security change and mergeStateStatus=BEHIND block merging.
| return [ | ||
| binary, | ||
| "run", | ||
| "--pure", |
There was a problem hiding this comment.
[P1] Deny OpenCode's built-in tools before scanning untrusted skills
--pure disables external plugins, not built-in tools. OpenCode's default policy permits most host capabilities, and noninteractive run rejects only operations that resolve to ask, so attacker-controlled skill text can induce shell, filesystem, or network side effects. A temporary CWD and environment scrubbing are not an OS sandbox. Launch with a fixed, non-overridable deny-all tool/permission configuration (including ambient sharing/config), and add an adversarial test proving bash/web/file side effects cannot occur; otherwise this provider must not be exposed.
Add
opencode_clisemantic-scan providerProblem
SkillSpector ships CLI providers for Claude, Codex, and Gemini, but none
for OpenCode, so OpenCode users get static-only scans (
llm_availablestays false and the three semantic analyzers are skipped).
Fixes: #534
Approach
opencode_cliprovider mirroring thecodex_clishape:providers/opencode_cli/{provider.py,__init__.py}, registry entry,SKILLSPECTOR_PROVIDER=opencode_cliselection,provider_name()label,CLI help text.
opencode run --format jsonwith
--modeladded only whenSKILLSPECTOR_MODELis set. Verifiedlive that
opencode runconsumes piped stdin (nonce round-trip)despite no documented stdin flag; list form throughout, shell never
invoked. Windows hostile-prompt roundtrips covered by test
(spaces, quotes, unicode, backslashes).
--format jsonenvelopes, skips non-JSON noise lines, raises fail-closed on empty
output. Auth check is
opencode auth listwith a 15s timeout,fail-closed.
.env.example,docs/DEVELOPMENT.md); provider tests live intests/provider/per repo convention.
of this change (separate chore).
Verification
tests/provider/test_opencode_cli.py: 31 passed (TDD: argv, auth,parser, wiring, registry label).
tests/unit/test_agent_cli.py: 87 passed.ruff check+ruff format --check: clean on all touched files.tests/nodes/test_build_context.py(Windows symlink limits),test_security_end_to_end.py,test_compare_scan_accuracy.py,test_create_github_release.py,test_input_handler.py— none infiles this PR touches; pre-existing/environmental.
opencode1.18.30, CLI-default model):llm_available: true, 3/3 semantic calls succeeded, risk 0/LOW,0 findings.
opencode1.18.30,CLI-default model):
llm_available: true, 3/3 semantic callssucceeded, 100% coverage, 0 semantic issues — 3 SC8 bytecode FPs
from stale local
__pycache__, since cleaned.opencode/nemotron-3-ultra-freeandopencode/muse-spark-1.3-contributor-free(both free-tier models via OpenCode's zen endpoint); semantic stage
degraded on shared runtime budget exhaustion (4 parallel calls at once),
not model failure — confirmed by spacing calls manually.
Sample
Probe scan of one small skill returned
risk: score 0 / LOW / CAUTION,llm_calls_attempted: 3,llm_calls_succeeded: 3, zero findings.Risks
opencode run --format jsonshapechange degrades to fail-closed (empty-output raise), never silent
heuristic-only.
inference_usageis empty on this path (no token counters from CLIsubprocesses); scan integrity unaffected, cost accounting unavailable.
Signed-off-by(maintainer: verify on push).budget instantly; users on free models should use local Ollama or
add credits for genuine semantic analysis.