feat(#141): validate-docmap subcommand — CI hard-fail on missing docmap coverage #142

Merged
rodin merged 11 commits from issue-141 into main 2026-05-15 07:39:22 +00:00

11 Commits

Author SHA1 Message Date
Rodin af8b29fa5d fix(#141): restore runValidateDocmap doc comment inadvertently truncated
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 17s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 24s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 35s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Failing after 1m6s
2026-05-15 07:34:18 +00:00
Rodin 7d7a49e967 fix(#141): harden docmap file path — confine to repo-root, reject symlinks, cap size
PR Ready Gate / clear-labels (pull_request) Successful in 1s
CI / test (pull_request) Successful in 17s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 39s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 1m22s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m46s
Address security-review-bot REQUEST_CHANGES findings on PR #142:

MAJOR (Finding #1): Docmap file path was read directly without validating it
is within --repo-root or checking for symlinks. A malicious PR could create
.review-bot/doc-map.yml as a symlink to /dev/zero (resource exhaustion) or an
arbitrary host file (information disclosure).

Fix: Add validateDocmapPath() called before ParseDocMapConfig(). It:
  - Resolves --repo-root first (filepath.Abs + EvalSymlinks), moved up before
    docmap parsing so both checks share the same resolved root
  - Uses os.Lstat to detect symlinks and rejects them outright
  - Confirms the docmap path is within resolvedRoot via filepath.Rel
  - Checks file size against maxDocmapBytes (10 MB) before reading

MINOR (Finding #2): No upper bound on docmap YAML size.
Fix: os.Lstat size check enforces maxDocmapBytes cap before os.ReadFile.

Tests:
  - TestValidateDocmapPath_Symlink: docmap is a symlink → exit 2
  - TestValidateDocmapPath_OutsideRepoRoot: docmap outside repo-root → exit 2
  - TestValidateDocmapPath_SizeLimit: docmap exceeds 10 MB cap → exit 2
  - Updated all existing tests to use makeDocmapInDir() so the docmap
    lives inside the repo-root, satisfying the new confinement check
2026-05-15 07:33:49 +00:00
Rodin 83a1835474 chore(#141): remove TODO.md — dev-loop artifact, not project documentation
PR Ready Gate / clear-labels (pull_request) Successful in 1s
CI / test (pull_request) Successful in 16s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 38s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m22s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 1m43s
2026-05-15 00:24:32 -07:00
Rodin 5c6758e990 fix(#141): address review feedback — tighten escape check, improve error messages, add comments 2026-05-15 00:24:28 -07:00
Rodin 24247a8550 chore(#141): update dev-loop status — ready for PR submission
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 25s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 40s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 51s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m17s
2026-05-15 07:03:45 +00:00
Rodin b22de19aa1 fix(#141): address security-review-bot REQUEST_CHANGES findings
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 25s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 41s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 48s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 54s
Finding #1 [MAJOR]: replace os.Stat with os.Lstat in checkStaleDocs to
prevent symlink traversal. Symlinks under repoRoot could probe arbitrary
host file existence; Lstat never follows them. Symlinked docs are now
treated as stale.

Finding #2 [MINOR]: resolve --repo-root with filepath.Abs +
filepath.EvalSymlinks before passing to checkStaleDocs, so a symlinked
repo-root cannot bypass the filepath.Rel escape guard.

Finding #3 [NIT]: reject backslashes in ValidateDocPath to prevent
Windows platform edge cases where a path separator may be normalised
differently by the host OS or VCS backend.

Tests added:
- TestCheckStaleDocs_SymlinkOutside: symlink inside repo → outside
- TestCheckStaleDocs_SymlinkInsideRepo: intra-repo symlink also rejected
- TestRunValidateDocmap_SymlinkRepoRoot: symlinked --repo-root resolves OK
- TestValidateDocPath_Backslash: backslash paths rejected
- Backslash cases added to TestValidateDocPath invalid slice

All go test ./... pass, go vet ./... clean.
2026-05-14 23:50:12 -07:00
Rodin 3f8da76b42 fix(#141): harden checkStaleDocs against path traversal
PR Ready Gate / clear-labels (pull_request) Successful in 1s
CI / test (pull_request) Successful in 17s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 32s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m12s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 1m13s
Export review.ValidateDocPath and use it in checkStaleDocs before
calling os.Stat. Add filepath.Clean + filepath.Rel confinement check
as defense-in-depth to ensure doc paths from PR-controlled YAML
cannot probe filesystem locations outside repoRoot.

Also add tests covering: ../../etc/passwd, /etc/passwd, ../outside,
a valid present path, and a valid missing path.

Addresses security finding from security-review-bot on PR #142.
2026-05-14 23:43:24 -07:00
Rodin 2ecbd86e24 fix(#141): use stdinValidateDocmap in Clean test — avoid real os.Stdin dependency
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 16s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 41s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 41s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 1m12s
TestRunValidateDocmap_Clean was reading real os.Stdin (fragile in CI).
Switch to stdinValidateDocmap with a covered file and empty-stdin test
already covered by TestRunValidateDocmap_EmptyStdin.
2026-05-15 04:50:21 +00:00
Rodin 7cdba14181 docs(#141): add validate-docmap subcommand to README
CI / test (pull_request) Successful in 18s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 30s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 1m11s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m41s
Documents the new validate-docmap subcommand under a new '## Subcommands' section,
alongside the existing validate-url documentation.
2026-05-15 04:48:32 +00:00
Rodin 69da5df254 feat(#141): add validate-docmap subcommand
Adds 'review-bot validate-docmap' for CI hard-fail on docmap coverage gaps.

Usage:
  git diff --name-only origin/main HEAD | \
    review-bot validate-docmap --docmap .review-bot/doc-map.yml --repo-root .

Flags:
  --docmap     (required) path to doc-map YAML file
  --repo-root  (optional, default '.') root for resolving docs: paths

Two checks, both always run:
1. Coverage: every stdin file must match at least one paths: glob.
2. Stale docs: every docs: entry must exist on disk under --repo-root.

Exit codes: 0=clean, 1=failures found, 2=usage/parse error.

Tests cover: clean pass, uncovered file, stale doc, both failures,
empty stdin, blank-line stdin, and duplicate docs: deduplication.
2026-05-15 04:47:59 +00:00
Rodin 93268869c5 feat(#141): export FileCoveredByDocMap helper in review/docmap.go
Adds FileCoveredByDocMap(cfg *DocMapConfig, file string) bool — a thin wrapper
over the existing unexported mappingMatches that lets cmd/ check per-file docmap
coverage without duplicating glob logic.

Also adds unit tests covering matched globs, non-matching paths, empty file,
and empty config.
2026-05-15 04:46:38 +00:00