Files
review-bot/CHANGELOG.md
Rodin b01e3c487f feat(#143): fetch doc-map config from trusted VCS ref
The doc-map YAML config was previously read from the local workspace
(the PR branch checkout). A malicious PR author could modify
.review-bot/doc-map.yml to map any path glob to sensitive design docs,
causing review-bot to fetch and inject those docs into the LLM prompt.

Fix: add --doc-map-trusted-ref (DOC_MAP_TRUSTED_REF) flag. When set to
a trusted ref (e.g. 'main'), the doc-map config is fetched from the VCS
API at that ref instead of from local workspace. A 404 from VCS is a
hard error (no silent fallback to local copy).

When unset, the local workspace is used with a security warning in the
logs pointing operators to the new flag.

Changes:
- review/docmap.go: add ParseDocMapConfigContent + parseDocMapBytes
  helper to parse from in-memory content (fetched via VCS API)
- cmd/review-bot/main.go: add --doc-map-trusted-ref flag; Step 6c
  branches on trusted-ref to fetch vs local-workspace load
- .gitea/actions/review/action.yml: add doc-map-trusted-ref input
- README.md: document new input
- CHANGELOG.md: security and feature entries

Tests:
- TestParseDocMapConfigContent_Valid/Empty/InvalidYAML/UnknownKeys
  in review/docmap_test.go

Coverage: 53.0% cmd/review-bot
2026-05-15 12:08:13 +00:00

3.7 KiB

CHANGELOG

Unreleased

Security

  • validateDocmapPath: add EvalSymlinks to close directory-symlink bypass (#150): The previous implementation used os.Lstat which only avoids following the final path component. An intermediate directory symlink (e.g. .review-bot/ committed as a symlink to a directory outside the repo) would pass the path-confinement check because the textual path appeared within the repo root. filepath.EvalSymlinks is now called first, resolving all symlink components before the filepath.Rel confinement check. In-repo symlinks whose resolved targets also reside within the repo root are now allowed; out-of-repo targets are rejected by the confinement check.
  • doc-map-trusted-ref: fetch doc-map config from trusted VCS ref (#143): New --doc-map-trusted-ref flag / DOC_MAP_TRUSTED_REF env var. When set, the doc-map YAML config is fetched from the specified VCS ref (e.g. main) via API instead of being read from the local workspace (the PR branch checkout). This prevents a malicious PR from modifying .review-bot/doc-map.yml to inject arbitrary design docs into the LLM prompt. When unset, the local workspace is used with a security warning in the logs.

Tests

  • TestValidateDocmapPath_DirSymlinkBypass: verifies that a directory symlink inside the repo pointing outside cannot be used to bypass path confinement (#150).

Added

  • doc-map-trusted-ref input (--doc-map-trusted-ref flag / DOC_MAP_TRUSTED_REF env var): Git ref (branch, tag, or SHA) from which to fetch the doc-map config via VCS API. Recommended for all doc-map users. Example: doc-map-trusted-ref: main. (#143)

  • doc-map input (--doc-map flag / DOC_MAP_FILE env var): Path to a YAML file mapping source path globs to governing design docs. review-bot intersects the map with changed PR paths and injects matching docs into the system prompt under a ## Design Documents heading. (#137)

  • doc-map-max-bytes input (--doc-map-max-bytes flag / DOC_MAP_MAX_BYTES env var): Cap on total injected design doc content in bytes. Default: 102400 (100 KB). Prevents accidental context overflow when a PR touches many modules.

  • DesignDocs budget section: Design docs are included in the context budget and trimmed after conventions, before file context, if the total exceeds the model's context limit.

Doc-map config format

mappings:
  - paths:
      - "lib/gargoyle/engine/signal_risk/**"
    docs:
      - docs/domain/contexts/risk/risk-controls.md
  - paths:
      - "lib/gargoyle/trading/**"
    docs:
      - docs/domain/contexts/trading/
  • paths — glob patterns (including **) matched against changed file paths in the PR
  • docs — local file paths or directories (all .md files under a directory) to inject
  • Multiple mappings can reference the same doc; docs are deduplicated
  • Missing doc files: warn and skip (review continues without them)
  • No matching paths: no docs injected, review runs normally
  • Absolute paths and path traversal (.. segments) in doc paths are rejected

Security

  • Path traversal guard: doc paths from the YAML config are validated to reject absolute paths and .. segments before VCS API calls
  • Prompt injection guard: design doc content is injected with an explicit instruction to treat it as reference data and not follow any instructions it may contain

v0.3.2

  • Previous releases tracked in Gitea release notes.