Commit Graph

2 Commits

Author SHA1 Message Date
Garry Tan
3df8ea8695 security: harden migration + context-save after adversarial review
Adversarial review (Claude + Codex, both high confidence) identified 6
critical production-harm findings in the /ship pre-landing pass.
All folded in.

Migration v1.0.1.0.sh hardening:
- Add explicit `[ -z "${HOME:-}" ]` guard. HOME="" survives set -u and
  expands paths to /.claude/skills/... which could hit absolute paths
  under root/containers/sudo-without-H.
- Add python3 fallback inside resolve_real() (was missing; broken
  symlinks silently defeated ownership check).
- Ownership-guard Shape 2 (~/.claude/skills/gstack/checkpoint/). Was
  unconditional rm -rf. Now: if symlink, check target resolves inside
  gstack; if regular dir, check realpath resolves inside gstack. A
  user's hand-edited customization or a symlink pointing outside gstack
  is preserved with a notice.
- Use `rm --` and `rm -r --` consistently to resist hostile basenames.
- Use `find -type f -not -name .DS_Store -not -name ._*` instead of
  `ls -A | grep`. macOS sidecars no longer mask a legit prefix-mode
  install. Strip sidecars explicitly before removing the dir.

context-save/SKILL.md.tmpl:
- Sanitize title in bash, not LLM prose. Allowlist [a-z0-9.-], cap 60
  chars, default to "untitled". Closes a prompt-injection surface where
  `/context-save $(rm -rf ~)` could propagate into subsequent commands.
- Collision-safe filename. If ${TIMESTAMP}-${SLUG}.md already exists
  (same-second double-save with same title), append a 4-char random
  suffix. The skill contract says "saved files are append-only" — this
  enforces it. Silent overwrite was a data-loss bug.

context-restore/SKILL.md.tmpl:
- Cap `find ... | sort -r` at 20 entries via `| head -20`. A user with
  10k+ saved files no longer blows the context window just to pick one.
  /context-save list still handles the full-history listing path.

test/skill-e2e-autoplan-dual-voice.test.ts:
- Filter transcript to tool_use / tool_result / assistant entries
  before matching, so prompt-text mentions of "plan-ceo-review" don't
  force the reachedPhase1 assertion to pass. Phase-1 assertion now
  requires completion markers ("Phase 1 complete", "Phase 2 started"),
  not mere name occurrence.
- claudeVoiceFired now requires JSON evidence of an Agent tool_use
  (name:"Agent" or subagent_type field), not the literal string
  "Agent(" which could appear anywhere.
- codexVoiceFired now requires a Bash tool_use with a `codex exec/review`
  command string, not prompt-text mentions.

All SKILL.md files regenerated. Golden fixtures updated. bun test: 0
failures across 80+ targeted tests and the full suite.

Review source: /ship Step 11 adversarial pass (claude subagent + codex
exec). Same findings independently surfaced by both reviewers — this is
cross-model high confidence.
2026-04-18 23:28:12 +08:00
Garry Tan
229d9a6505 merge: origin/main v1.0.0.0 into garrytan/fix-checkpoints
Main shipped the v1 prompts rewrite (simpler writing style + real LOC
receipts + /plan-tune observational substrate). Resolved conflicts:

- VERSION / package.json: bumped 0.18.5.0 → 1.0.1.0 (main is 1.0.0.0,
  this branch lands next).
- CHANGELOG: moved the /context-save + /context-restore entry to the
  top as v1.0.1.0, above main's v1.0.0.0. Also removed the em-dash
  variants in the new entry (ship voice rule).
- TODOS: kept both sections — Context skills (lane feature TODO) first,
  main's PACING_UPDATES_V0 + Plan Tune v2 deferrals below.
- Migration: renamed gstack-upgrade/migrations/v0.18.5.0.sh →
  v1.0.1.0.sh (matches new version). Test path updated.

preamble.ts auto-merged cleanly: main's question-tuning, explain_level,
and writing-style sections composed with my context-save/context-restore
routing rule.

All SKILL.md files regenerated via `bun run gen:skill-docs --host all`
per CLAUDE.md's "never resolve generated files by accepting either
side" rule. Golden fixtures (claude/codex/factory ship) also regenerated.

bun test: 0 failures.
2026-04-18 17:24:03 +08:00