Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ configuration directory:
gates). Each is a Markdown file: YAML frontmatter (mode, permissions, color,
temperature) over a system-prompt body.
2. **Commands** (`commands/*.md`) — slash commands (`/goal`, `/goal-contract`,
`/goal-review`, `/goal-evidence-map`, `/goal-status`, `/goal-repair`,
`/goal-final`) that bind a prompt template to an agent, some forced to run as
subtasks.
`/goal-review`, `/goal-evidence`, `/goal-evidence-map`, `/goal-status`,
`/goal-repair`, `/goal-reset`, `/goal-final`, `/goal-mode-customize`) that bind a
prompt template to an agent, some forced to run as subtasks.
3. **The `goal-guard` plugin** (`plugins/goal-guard.js` + `plugins/goal-guard/`)
— a runtime guard that enforces review discipline, blocks destructive shell
commands, preserves state across compaction and restarts, and exposes
Expand Down Expand Up @@ -224,13 +224,18 @@ destructive blocking, network-exec blocking, completion enforcement,
`autoContinue`, `programmaticReview`, `reviewIdleDeferMs`, `reviewIdleRetryMs`,
`maxReviewIdleRetries`, review timeouts/polling, `maxReviewCycles`,
system-state injection, persistence, contextual gates, subagent restriction,
session cache size/TTL, sidebar colours, and toasts. See README.md for the full
option table.
session cache size/TTL, sidebar colours, and toasts. The guard is also fully
relaxable: `yolo` drops the soft gates, `allowDestructive` drops destructive
guarding, and `allowCommands` / `extraDestructive` are per-command regex allow/deny
lists; `CONFIG_DOCS` documents every key. `scripts/goal-config.mjs` (the
`opencode-goal-mode-config` bin) lists, explains, and previews any configuration,
and a no-drift test keeps `CONFIG_DOCS` aligned with `DEFAULT_CONFIG`. See README.md
for the full option table.

## Installer

`scripts/install.mjs` recursively copies `agents/`, `commands/`, and `plugins/`
(including the nested module directory) into the target config dir, merge-registers
`scripts/install.mjs` recursively copies `agents/`, `commands/`, `skills/`, and
`plugins/` (including the nested module directory) into the target config dir, merge-registers
the sidebar package in `tui.json`, clears stale TUI plugin cache entries, and
records a manifest of the file hashes it wrote. Global `npm install -g` also
triggers the same installer via `postinstall.mjs`. On upgrade it distinguishes
Expand All @@ -240,7 +245,7 @@ supports `--uninstall` (which leaves locally-modified files in place).

## Testing

`node --test` runs the suite across 20 files:
`node --test` runs the full suite across the `tests/` directory, including:

- `tests/shell.test.mjs` / `tests/shell.property.test.mjs` — analyzer against bypass and false-positive corpora.
- `tests/plugin.test.mjs` — hook behavior, gating, verdicts, completion, tools, isolation.
Expand Down
378 changes: 199 additions & 179 deletions README.md

Large diffs are not rendered by default.

33 changes: 18 additions & 15 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ so always upgrade to the newest version.

| Version | Supported |
| --- | --- |
| Latest published release (`0.6.x`) | |
| Older `0.x` releases | — upgrade to the latest |
| Latest published release (`0.7.x`) | Yes |
| Older `0.x` releases | No — upgrade to the latest |

## Reporting a vulnerability

Expand All @@ -47,9 +47,10 @@ OpenCode Goal Mode is a defense-in-depth tool for an AI coding agent. The
`goal-guard` plugin blocks destructive and remote-execution shell commands using
a quote-aware tokenizer, but it is **not a sandbox**:

- The analyzer **fails open** on un-analyzable / highly dynamic commands,
deferring to OpenCode's own permission rules. Treat it as a guardrail, not a
jail.
- The analyzer **fails open** on un-analyzable or highly dynamic commands it cannot
resolve to a concrete form, deferring to OpenCode's own permission rules. (A
genuine parser *error*, by contrast, fails **closed** — the command is treated as
destructive and blocked.) Treat the guard as a guardrail, not a jail.
- Gate freshness is only as trustworthy as the reviewer subagents' verdicts.
- The installer copies `agents/*.md`, `commands/*.md`, and the `plugins/` tree,
merge-registers the sidebar in `tui.json`, and writes a manifest — never auth
Expand All @@ -64,18 +65,20 @@ welcome.
The shell analyzer is a heuristic classifier, not a sandbox, so a measurable
fraction of genuinely destructive commands are **not** blocked.

- **Fail-open rate (~6.7%).** On the external corpus of **704 real third-party
commands** the analyzer detects **93.3%** of destructive commands, so roughly
**6.7%** are not blocked and fall through to OpenCode's own permission rules.
See [research/benchmarks.md](research/benchmarks.md) for the methodology.
- **Fail-open rate (about 7.7%).** On the external corpus of **704 real
third-party commands** the analyzer detects **92.3%** of destructive commands, so
roughly **7.7%** (8 of the 104 destructive commands) are not blocked and fall
through to OpenCode's own permission rules. See
[research/benchmarks.md](research/benchmarks.md) for the methodology.
- **What fails open.** The remaining misses are dominated by two categories:
1. **Intentionally permitted forms** — a plain single-target `rm <file>`
(and `rm -i`/`-v`/`-d`) is not blocked by design; the guard targets
`rm -r`/`rm -f`, command-substitution / `bash -c` / interpreter deletes,
and remote exec.
2. **Un-analyzable / highly dynamic commands** — when the tokenizer cannot
resolve a concrete command (variable interpolation, runtime-built strings,
parse failures) it returns "not blocked" rather than guessing.
(and `rm -i`, `-v`, `-d`) is not blocked by design; the guard targets
`rm -r` and `rm -f`, command-substitution, `bash -c`, and interpreter
deletes, and remote execution.
2. **Un-analyzable or highly dynamic commands** — when the tokenizer cannot
resolve a concrete command (variable interpolation, runtime-built strings) it
returns "not blocked" rather than guessing. A parser *error*, distinct from an
un-resolvable command, instead fails closed and blocks.
- **Recommended mitigations** for the un-blocked tail: keep OpenCode's own
permission rules enabled, run the agent against a clean working tree under
version control, and add repo-side guards (git pre-commit hooks, protected
Expand Down
2 changes: 1 addition & 1 deletion commands/goal-mode-customize.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ env var, or the plugin `options` object in `opencode.json`. A bundled tool,

2. **Apply** — edit the user's `opencode.json` so the plugin entry carries the options:
```jsonc
"plugin": [["opencode-goal-mode", { "yolo": true, "allowCommands": ["^ruff( |$)"] }]]
"plugin": [["./plugins/goal-guard.js", { "yolo": true, "allowCommands": ["^ruff( |$)"] }]]
```
(or export the `GOAL_GUARD_*` env equivalents). Lists accept an array or a comma/newline string; an invalid regex is ignored, never fatal.

Expand Down
38 changes: 37 additions & 1 deletion docs/benchmarks/detection-by-family.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 32 additions & 1 deletion docs/benchmarks/external-scorecard.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading