Skip to content

Plan 32 — wsvalidate extract + config validate + snapshot hardening#80

Merged
LastStep merged 7 commits into
mainfrom
worktree-agent-a04bedaaeb91a4c3d
Apr 25, 2026
Merged

Plan 32 — wsvalidate extract + config validate + snapshot hardening#80
LastStep merged 7 commits into
mainfrom
worktree-agent-a04bedaaeb91a4c3d

Conversation

@LastStep

Copy link
Copy Markdown
Owner

Summary

Plan 32 follow-up bundle from Plan 29 + Plan 31 review backlog — 6 commits, file-disjoint refactors and hardening.

  • Phase A (20a8f76) — extract internal/wsvalidate/ package; migrate addflow/ground.go + initflow/vessel.go to shared validator
  • Phase B (7c792c1) — reject backslash + pure-root in wsvalidate.InvalidReason (closes Plan-29-sec-hardening 2 + 3)
  • Phase C (053c6bc) — strengthen TestConflicts_ColorTonesDifferPerAction (Keep-vs-Backup), positive companions for vessel + ground (AcceptsCleanRelative, AcceptsNestedRelative), rename shortNameconflictsShortName
  • Phase D (d7b6fde) — collapse hasAbilityslices.Contains; collapse agentsToSlice + requiredToSlicecompatToSlice
  • Phase E (12707d0) — TestWriteCatalogSnapshot_TrailingNewline + TestSerializeCatalog_VersionPassThrough
  • Phase F (42252b4) — ProjectConfig.Validate() chokepoint (wired into Load()); O_NOFOLLOW symlink-resistant write at WriteCatalogSnapshot

Test plan

  • go vet ./... clean
  • go test ./... all green (cached)
  • TestWriteCatalogSnapshot_RefusesSymlink exercises O_NOFOLLOW
  • TestValidate_* covers required field, bad workspace, bad docs_path, all 9 forbidden shell metachars
  • Rebased clean onto origin/main

🤖 Generated with Claude Code

LastStep and others added 7 commits April 25, 2026 20:26
Move NormaliseWorkspace + invalidWorkspaceReason from
internal/tui/addflow/ground.go into a new internal/wsvalidate
package so addflow + initflow + cmd share a single source of
truth for the trim/Clean/trailing-slash + IsAbs + ".." segment
scan rules. Update absolute-path error to "absolute paths not
allowed (no leading / or drive letter)" so Windows users (C:\foo)
aren't misled by the prior "(no leading /)" wording.

Plan 32 Phase A — closes Plan-29-cosmetic 3 + Plan-29-security-hardening 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add two defence-in-depth rules to InvalidReason:
1. Reject backslash anywhere in the path. POSIX treats `\` as a
   legal literal in file names so "foo\bar" sneaks past IsAbs but
   is almost certainly a Windows-style separator the user typed
   by mistake — better to flag than silently accept.
2. Reject pure root. Inputs that Clean to "." (e.g. "", ".",
   "foo/..") would install at the project root, which is almost
   never intended.

Add 4 negative tests (absolute, backslash, pure root, parent
escape) + 2 positive (nested relative accepted, Normalise
canonicalises) in new internal/wsvalidate/wsvalidate_test.go.

Plan 32 Phase B — closes Plan-29-security-hardening 2 + 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- conflicts_test.go: rename shortName→conflictsShortName, add Keep-vs-Backup tone assertion
- ground_test.go: TestGround_AcceptsNestedRelative (positive companion)
- vessel_test.go: TestVessel_AcceptsCleanRelative (positive companion)
@LastStep LastStep merged commit 99e9948 into main Apr 25, 2026
5 checks passed
LastStep added a commit that referenced this pull request Apr 25, 2026
PR #80 squash 99e9948 — wsvalidate extract, defence-in-depth,
test strengthening, generate.go consolidations, snapshot test gaps,
ProjectConfig.Validate + symlink-resistant write. 13 of 17 review
items closed across Plan-29-cosmetic / Plan-29-test-gap /
Plan-29-sec-hardening / Plan-31-cosmetic / Plan-31-test-gap /
Plan-31-sec-hardening.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
LastStep added a commit that referenced this pull request May 4, 2026
)

Windows lacks syscall.O_NOFOLLOW. Cross-compile to GOOS=windows
fails since Plan 32 (#80). Split openSnapshotFile into platform
files: unix variant keeps O_NOFOLLOW symlink defense; windows
variant uses plain OpenFile.

Unblocks v0.4.0 GoReleaser cross-compile (release run 25313754104
failed at undefined: syscall.O_NOFOLLOW).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant