- tests/lib/review-rounds.test.mjs: round 2 whose Claude Security payload holds the round-1 BLOCKER only in out_of_scope keeps it counted through the real finalize and verdict gate (BLOCK, gate exit 1, not under Resolved since round 1); a clean run resolves it (F-V4; red under the mutation that drops out_of_scope from raisedOutside). - README.md, SECURITY.md: "blocks only at confidence high" names the exception of decision 222 (counted at high in an earlier round, it stays counted when it comes back below high or outside the scope) (F-V5). - commands/trekreview.md Phase 5: an engine finding is held by a matching finding in review.md (engine MAJOR/BLOCKER put back first) or a matching below_threshold / out_of_scope entry; an engine MINOR or SUGGESTION the coordinator left out holds nothing, a known issue (decision 226). - tests/lib/review-prose-pins.test.mjs: both sentences pinned; red on the old prose. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
133 lines
7.1 KiB
Markdown
133 lines
7.1 KiB
Markdown
# Security Policy — trekplan
|
|
|
|
## Reporting a vulnerability
|
|
|
|
Report privately to <security@fromaitochitta.com> — do not open a public
|
|
issue. There is no SLA — this is a solo-maintained plugin — but
|
|
acknowledged reports are usually triaged within 7 days.
|
|
|
|
If you already have an account on the Forgejo instance, you may instead
|
|
open a **private** issue on the repo, tagged `security`:
|
|
|
|
> https://git.fromaitochitta.com/open/voyage
|
|
|
|
## Supported versions
|
|
|
|
Only the **current minor version** receives security fixes. When v3.2.0
|
|
ships, v3.1.x stops receiving patches. Pin to the latest minor and
|
|
update on the next bump.
|
|
|
|
| Version | Supported |
|
|
|---------|-----------|
|
|
| 3.1.x | Yes |
|
|
| 3.0.x | No (upgrade to 3.1.x) |
|
|
| < 3.0 | No |
|
|
|
|
## Scope
|
|
|
|
The plugin's security posture covers:
|
|
|
|
### Plugin-owned hooks (`hooks/scripts/`)
|
|
|
|
| Hook | Trigger | Purpose |
|
|
|------|---------|---------|
|
|
| `pre-bash-executor.mjs` | `PreToolUse` for Bash | BLOCKs known-dangerous shell patterns; WARNs on suspicious ones; answers `ask` before merge into `main`, push, tag/publish and destructive git (P12); fails open on parse errors |
|
|
| `pre-write-executor.mjs` | `PreToolUse` for Write, Edit, NotebookEdit | BLOCKs writes to `.git/hooks/`, `~/.ssh/`, `.env`, and other sensitive paths, to Voyage's own state in the git directory (`voyage/`), and to test files a step has locked (P9); fails closed when it cannot read the target path or the lock, or cannot load the lock code |
|
|
| `pre-plan-gate.mjs` | `PreToolUse` for ExitPlanMode | Runs Voyage's plan gate on a Voyage plan (it carries `plan_version`) and denies one the gate refuses; asks with "not measured" when the gate cannot run; other plans pass (P3) |
|
|
| `post-approval-log.mjs` | `PostToolUse` for Bash, ExitPlanMode | Writes an approved merge/push to `main`, tag/publish, or Voyage plan to the approval log; says "not logged" when it cannot |
|
|
| `post-edit-check.mjs` | `PostToolUse` for Edit, Write | Runs the target repo's own formatter and linter on the edited file, check-only, while Voyage executes a plan there or the repo has switched it on (P7) |
|
|
| `stop-test-lock.mjs` | `Stop` | Refuses "done" while the session's step holds its test lock; lets the stop through when `stop_hook_active` is set (P9) |
|
|
| `pre-compact-flush.mjs` | `PreCompact` | Flushes `progress.json` from git history before compaction (P0 drift fix); read-only beyond `progress.json` |
|
|
| `session-title.mjs` *(planned, F9)* | `UserPromptSubmit` | Sets session title `voyage:<command>:<slug>` for telling parallel sessions apart |
|
|
|
|
All hooks are zero-dependency Node.js (`.mjs`) scripts. Most are designed
|
|
to **fail open** — a hook crash never blocks the user's work. The
|
|
exceptions are stated per hook above: the write guard fails closed when it
|
|
cannot read a path or a test lock, and the plan gate asks rather than
|
|
passes when it cannot run. No hook runs a model. Hooks never write to user
|
|
files outside their declared scope; the test lock and the post-edit log
|
|
live in the repository's git directory (`<git dir>/voyage/`), where they
|
|
cannot be committed.
|
|
|
|
### Precondition: mods
|
|
|
|
Voyage's guards are deterministic only when no installed mod handles `tool.check`.
|
|
A Claude Code mod that handles `tool.check` answers after the permission
|
|
rules and the `PreToolUse` hooks have decided, and its answer can replace
|
|
theirs: it can approve a call a plugin hook blocked or asked about, and
|
|
outside managed settings and Team/Enterprise plans it can approve a call a
|
|
deny rule refuses (code.claude.com/docs/en/permissions, § Extend
|
|
permissions with hooks). Voyage's hooks cannot tell whether such a mod is installed:
|
|
no hook input names the loaded mods. `/plugin` shows them to a person
|
|
(`N mods active · …`). Voyage itself ships no mods.
|
|
|
|
### Prompt-level denylist (`commands/trekexecute.md`)
|
|
|
|
The execute command embeds a denylist that takes effect even in a session
|
|
where a hook does not fire. This is layer 2 of the three-layer
|
|
defense-in-depth model (README, Security hardening) and protects against
|
|
plan-injected destructive commands.
|
|
|
|
### Validators (`lib/validators/*.mjs`)
|
|
|
|
Read-only. Never write to user files. Used both by hooks and by command
|
|
phases to detect malformed artifacts before they propagate.
|
|
|
|
### The review's security engine (`/trekreview`)
|
|
|
|
`/trekreview` hands security review of a delivery to one engine and never
|
|
reports a 0 it did not measure. Claude Security is recommended: Anthropic's
|
|
plugin, with a proprietary licence, so Voyage calls it by name
|
|
(`claude-security:claude-security`, `scan changes` only, after its own cost
|
|
question to the operator) and it is never a dependency in Voyage's manifest.
|
|
Without it, Claude Code's `/security-review` is used. With neither, security
|
|
is "not measured" and the verdict gate exits 2 (never green).
|
|
`lib/review/security-engine.mjs` chooses the engine and converts Claude
|
|
Security's report (`CLAUDE-SECURITY-<ts>/`, written by Claude Security in the
|
|
repo root behind its own `.gitignore`) as data: nothing in it is run or
|
|
followed, and a stamp counts only when it names exactly the reviewed head and
|
|
base. That check is a trace, not a signature (a known issue): code the
|
|
reviewed run executed can write a report folder of its own, which `convert`
|
|
reads when its name sorts last among the matching ones, or delete the real
|
|
report before `convert` reads it, and Voyage does not detect either. A
|
|
Claude Security finding blocks only at confidence `high`, or when it was
|
|
counted at `high` in an earlier round of the same run: it then stays counted
|
|
when it comes back below `high` or outside the review's scope, until it is
|
|
fixed. The rest are listed under Declined to Judge and keep the review from
|
|
`ALLOW`. Neither engine edits code.
|
|
|
|
## Out of scope
|
|
|
|
- **Opt-in upstream architect step.** Any external producer of
|
|
`architecture/overview.md` ships its own security posture. The
|
|
architecture-discovery validator in this plugin treats
|
|
`architecture/overview.md` as an external contract (drift-WARN, never
|
|
drift-FAIL).
|
|
- **LLM output content.** The plugin validates artifact *shape*, not
|
|
artifact *truthfulness*. A plan that passes `plan-validator --strict`
|
|
may still contain hallucinated file paths or unsafe commands; that is
|
|
why `pre-bash-executor` exists.
|
|
- **The Claude Code CLI itself.** Report Claude Code vulnerabilities to
|
|
Anthropic via https://github.com/anthropics/claude-code/issues.
|
|
|
|
## Hardening recommendations
|
|
|
|
For fork-ers handling untrusted task briefs or plans:
|
|
|
|
1. **Set `disableSkillShellExecution: true`** in `~/.claude/settings.json`
|
|
(CC v2.1.91+) to prevent Skills from invoking arbitrary shell.
|
|
2. **Run plan validation in `--strict` mode** before any execute:
|
|
```bash
|
|
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/plan-validator.mjs --strict plan.md
|
|
```
|
|
3. **Review the plan-critic adversarial output** before approving plans
|
|
from external sources — semantic rubric (rule #7) catches deferred
|
|
decisions that an attacker could exploit.
|
|
4. **Pin a CC version floor.** v3.1.0 of this plugin assumes CC ≥
|
|
2.1.85 for the `if`-field on hooks; older CC silently ignores the
|
|
field, weakening the scoping.
|
|
|
|
## Past advisories
|
|
|
|
None as of v3.1.0. This section will list CVE-style entries if any are
|
|
discovered.
|