diff --git a/cmd/review-bot/main.go b/cmd/review-bot/main.go index e967981..788408e 100644 --- a/cmd/review-bot/main.go +++ b/cmd/review-bot/main.go @@ -429,7 +429,7 @@ func main() { currentSHA = currentPR.Head.SHA } if shouldSkipStaleReview(evaluatedSHA, currentSHA) { - slog.Warn("HEAD moved during review — skipping stale review", + slog.Warn("HEAD moved during review -- skipping stale review", "evaluated", evaluatedSHA, "current", currentSHA, "pr", prNumber) @@ -477,12 +477,12 @@ func main() { } // Self-request as reviewer (Gitea-specific; ensures we appear in required-reviewer checks) - if giteaAdapter, ok := client.(*gitea.Adapter); ok { + if selfReq, ok := client.(vcs.ReviewerSelfRequester); ok { authUser, err := client.GetAuthenticatedUser(ctx) if err != nil { slog.Warn("could not determine authenticated user for reviewer self-request", "error", err) } else if authUser != "" { - if err := giteaAdapter.Underlying().RequestReviewer(ctx, owner, repoName, prNumber, authUser); err != nil { + if err := selfReq.RequestReviewerSelf(ctx, owner, repoName, prNumber, authUser); err != nil { slog.Warn("could not self-request as reviewer", "user", authUser, "error", err) } else { slog.Debug("self-requested as reviewer", "user", authUser, "pr", prNumber) @@ -563,6 +563,10 @@ func supersedeOldReviews(ctx context.Context, client vcs.Client, provider, vcsUR } underlying := giteaAdapter.Underlying() + // Validate vcsURL scheme before embedding in Markdown link (defense-in-depth). + if !strings.HasPrefix(vcsURL, "http://") && !strings.HasPrefix(vcsURL, "https://") { + return fmt.Errorf("supersedeOldReviews: vcsURL must have http or https scheme, got %q", vcsURL) + } newReviewURL := fmt.Sprintf("%s/%s/%s/pulls/%d#pullrequestreview-%d", strings.TrimRight(vcsURL, "/"), owner, repoName, prNumber, newReviewID) for _, oldReview := range oldReviews { cid, err := underlying.GetTimelineReviewCommentIDForReview(ctx, owner, repoName, prNumber, oldReview.ID) @@ -757,14 +761,6 @@ func envOrDefaultInt(key string, defaultVal int) int { return defaultVal } -func envOrDefaultBool(key string, defaultVal bool) bool { - v := strings.TrimSpace(strings.ToLower(os.Getenv(key))) - if v == "" { - return defaultVal - } - return v == "true" || v == "1" || v == "yes" -} - // validateReviewerName checks that the name contains only safe characters // for embedding in an HTML comment sentinel ([a-zA-Z0-9_-]). func validateReviewerName(name string) error { @@ -858,7 +854,7 @@ func hasSharedToken(reviews []vcs.Review, ownSentinel string) bool { } for _, r := range reviews { if r.User.Login == ownLogin && strings.Contains(r.Body, "