voyage/SECURITY.md
Kjell Tore Guttormsen 2ee8137e7c
feat(hooks): wave-1 guards -- approval ask, plan gate, test lock, post-edit check
Wave-1 order 4 (P7, P12, the P9 test lock, the P3 plan-approval guard).
All command hooks; none runs a model.

- P12: pre-bash-executor answers permissionDecision "ask" before merge
  into main, push, tag/publish and destructive git (holds in auto mode).
  post-approval-log writes exactly what the person was asked to the K2
  approval log: merge/push to main as `merge`, tag/publish as `release`,
  with a one-hour expiry; nothing when no person was asked.
- P3: pre-plan-gate runs the K3 plan gate on a Voyage plan leaving plan
  mode and sends a refused plan back; "not measured" asks, never passes.
- P9: lib/util/test-lock.mjs (K8). pre-write-executor refuses edits to
  locked test files; stop-test-lock refuses "done" for the locking
  session (stop_hook_active guarded).
- P7: post-edit-check runs the target repo's own formatter/linter per
  edit, only during a Voyage plan run or after opt-in
  (.claude/voyage-guards.json); lib/verification/step-check.mjs (K9) fails
  the step on findings; no tool reads "not measured", never green.
- STORM cap matcher also names `Agent` (the tool's name since 2.1.63).
- Mod precondition stated in README, SECURITY.md and every PreToolUse stop
  guard: Voyage's guards are deterministic only when no installed mod
  handles `tool.check`.

K2, K3 and K5 are stubbed in tests until orders 2 and 3 are merged.
Two independent review rounds; every finding has a regression test.
Suite 1600 (1598 pass / 0 fail / 2 skipped).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:09:32 +02:00

5.6 KiB

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.

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:
    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.