1. Refactor err2 to use scoped loadErr variable (MINOR - sonnet-review-bot)
The else-if branches are mutually exclusive, so the error variable
should be scoped inside the block, not declared outside with err2.
2. Sanitize DisplayName before embedding in Markdown (MINOR - security-review-bot)
Remote persona metadata is untrusted. Added sanitizeMarkdownText() to
escape Markdown special characters and strip control characters.
Applied to both the header title and the footer attribution.
3. Document YAML DoS mitigations (MINOR - security-review-bot)
Added comprehensive comment in remote_persona.go explaining existing
defenses: file size limit, file count cap, depth limit, node count cap,
and alias cycle detection. These collectively mitigate billion-laughs
and stack exhaustion attacks.
- Remove duplicate flag.Parse() call
- Fix nil map panic in LoadRemotePersonas error path by assigning
empty map when LoadRemotePersonas returns an error
- Tighten isNotFoundError to only check HTTP 404 (remove broad
'not found' substring check to avoid false positives)
- Clean up personaErr variable scope using narrower-scoped err variables
- Add proper doc comment to LoadRemotePersonasFromPath (Go convention)
- Add file count cap (50 files) in LoadRemotePersonasFromPath to
prevent resource exhaustion from repos with thousands of small files
- Update test expectation for tightened isNotFoundError
Adds support for repository-specific personas. When --persona is
specified, review-bot now:
1. Checks the target repo's .review-bot/personas/<name>.yaml directory
2. Falls back to built-in persona if not found in repo
This allows repos to define domain-specific personas (trading, regulatory,
etc.) or override built-in personas with project-specific rules, without
requiring changes to CI configuration.
Implementation:
- New review.PersonaFetcher interface for abstracting Gitea API access
- review.LoadRemotePersonas() with graceful fallback on 404
- review.MergePersonas() for combining remote and built-in personas
- giteaFetcher adapter in main.go to bridge gitea.Client
The feature follows a partial-success model: invalid YAML files or
network errors for individual persona files are logged and skipped,
allowing other valid personas to load.
Closes#60