fix: address review findings (path restriction, login cross-check, README)
CI / test (pull_request) Successful in 13s
CI / review (gpt-4.1, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 24s
CI / review (gpt-5, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 1m20s
CI / review (gpt-5, security, SECURITY_REVIEW.md, SONNET_REVIEW_TOKEN) (pull_request) Successful in 1m27s
CI / test (pull_request) Successful in 13s
CI / review (gpt-4.1, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 24s
CI / review (gpt-5, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 1m20s
CI / review (gpt-5, security, SECURITY_REVIEW.md, SONNET_REVIEW_TOKEN) (pull_request) Successful in 1m27s
- system-prompt-file: reject absolute paths and paths containing ".." Prevents reading arbitrary files outside the workspace on shared runners. - Cleanup: cross-check r.User.Login == posted.User.Login before deletion Defense-in-depth: only attempt to delete reviews from same author. Flagged by both sonnet and security reviewers. - README: fix wording (cleanup happens after posting, not before) Issues filed for deferred work: - #24: Consistent url.PathEscape across all client endpoints - #25: Binary signature verification for supply-chain hardening
This commit is contained in:
@@ -7,7 +7,7 @@ AI-powered code review bot for Gitea pull requests. Fetches diff + context, send
|
||||
- **Multi-provider**: OpenAI-compatible and Anthropic Messages API
|
||||
- **Context-aware**: Fetches full file content, conventions, language patterns, CI status
|
||||
- **Smart budget**: Automatically trims context to fit model token limits
|
||||
- **Idempotent reviews**: Deletes previous review before posting new one (one review per bot)
|
||||
- **Idempotent reviews**: Posts new review, then cleans up stale ones (one review per bot)
|
||||
- **Custom prompts**: Load additional instructions from a file (e.g. security-focused review)
|
||||
- **Zero dependencies**: Go stdlib only
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -154,6 +155,12 @@ func main() {
|
||||
// Step 6b: Load additional system prompt if specified
|
||||
additionalPrompt := ""
|
||||
if *systemPromptFile != "" {
|
||||
if filepath.IsAbs(*systemPromptFile) {
|
||||
log.Fatalf("system-prompt-file must be a relative path (got %q)", *systemPromptFile)
|
||||
}
|
||||
if strings.Contains(*systemPromptFile, "..") {
|
||||
log.Fatalf("system-prompt-file must not contain '..' (got %q)", *systemPromptFile)
|
||||
}
|
||||
data, err := os.ReadFile(*systemPromptFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read system prompt file %q: %v", *systemPromptFile, err)
|
||||
@@ -227,7 +234,7 @@ func main() {
|
||||
log.Printf("Warning: could not list existing reviews: %v", err)
|
||||
} else {
|
||||
for _, r := range reviews {
|
||||
if r.ID != posted.ID && strings.Contains(r.Body, sentinel) {
|
||||
if r.ID != posted.ID && r.User.Login == posted.User.Login && strings.Contains(r.Body, sentinel) {
|
||||
if err := giteaClient.DeleteReview(ctx, owner, repoName, prNumber, r.ID); err != nil {
|
||||
log.Printf("Warning: could not delete old review %d: %v", r.ID, err)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user