From 53e6524d8c9aee756b7b2b26e086a18599018e74 Mon Sep 17 00:00:00 2001 From: Rodin Date: Fri, 1 May 2026 21:16:16 -0700 Subject: [PATCH] fix: symlink traversal + worst-wins pre-check + user scoping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security (MAJOR): - Add filepath.EvalSymlinks after Clean for system-prompt-file - Re-validate resolved path is still within workspace - Prevents symlink → /etc/shadow exfiltration via malicious repo Worst-wins: - Check BEFORE posting (not after) — no delete+repost dance - Identify sibling bots by ", *reviewerName) - if !strings.Contains(r.Body, sentinelCheck) { - log.Printf("Sibling review %d has REQUEST_CHANGES; escalating to REQUEST_CHANGES", r.ID) - event = "REQUEST_CHANGES" - break - } - } + // Validate reviewer-name: only safe characters allowed in sentinel + if *reviewerName != "" { + for _, ch := range *reviewerName { + if !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '_') { + log.Fatalf("reviewer-name must contain only [a-zA-Z0-9_-] (got %q)", *reviewerName) } } } + sentinel := fmt.Sprintf("", *reviewerName) log.Printf("Posting review (event=%s)...", event) posted, err := giteaClient.PostReview(ctx, owner, repoName, prNumber, event, reviewBody) @@ -254,7 +252,6 @@ func main() { log.Printf("Review posted (id=%d, user=%s)", posted.ID, posted.User.Login) // Delete stale reviews from this bot using sentinel matching - sentinel := fmt.Sprintf("", *reviewerName) if *updateExisting && *reviewerName != "" { reviews, err := giteaClient.ListReviews(ctx, owner, repoName, prNumber) if err != nil { @@ -270,6 +267,27 @@ func main() { } } + // Worst-wins: if we posted APPROVE but a sibling review from the + // same user (same token, different role) has REQUEST_CHANGES, + // delete ours and re-post as REQUEST_CHANGES to maintain the block. + if event == "APPROVED" { + for _, r := range reviews { + if r.ID != posted.ID && !r.Stale && r.User.Login == posted.User.Login && r.State == "REQUEST_CHANGES" && strings.Contains(r.Body, "