mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-08-22 11:28:55 +08:00
fix: filter PR reviews and inline review comments to trigger time (#1385)
Issue/PR comments (#512) and the issue/PR body (#710) are filtered to the trigger timestamp so content created or edited after an authorized trigger cannot be injected into Claude's prompt (TOCTOU protection). Reviews and inline review comments were not: fetchGitHubData returned reviewData filtered by actor only, and formatReviewComments renders it into the prompt, so a review submitted or edited after the trigger reached Claude verbatim. filterReviewsToTriggerTime already existed (added alongside the comment filter in #512) but was only wired to the image-download list, never to the returned reviewData. Filter reviewData.nodes through filterReviewsToTriggerTime and each review's inline comments through filterCommentsToTriggerTime, alongside the existing actor filter, then build the review image-processing lists from those already-filtered nodes (removing a now-redundant second filter pass). Strengthen the two integration tests to assert post-trigger and edited-after reviews/comments are dropped.
This commit is contained in:
+23
-26
@@ -378,34 +378,26 @@ export async function fetchGitHubData({
|
||||
body: c.body,
|
||||
}));
|
||||
|
||||
// Filter review bodies to trigger time
|
||||
const filteredReviewBodies = reviewData?.nodes
|
||||
? filterReviewsToTriggerTime(reviewData.nodes, triggerTime).filter(
|
||||
(r) => r.body,
|
||||
)
|
||||
: [];
|
||||
|
||||
const reviewBodies: CommentWithImages[] = filteredReviewBodies.map((r) => ({
|
||||
type: "review_body" as const,
|
||||
id: r.databaseId,
|
||||
pullNumber: prNumber,
|
||||
body: r.body,
|
||||
}));
|
||||
|
||||
// Filter review comments to trigger time and by actor
|
||||
// Filter reviews and inline review comments to trigger time and by actor
|
||||
// before building anything from them. The trigger-time filter is the TOCTOU
|
||||
// protection applied to issue/PR comments and the body above: it drops
|
||||
// anything submitted, created, or edited at/after the trigger so an attacker
|
||||
// cannot inject content into the prompt after an authorized trigger. Without
|
||||
// it, review bodies and inline review comments would reach the prompt
|
||||
// verbatim regardless of when they landed.
|
||||
if (reviewData && reviewData.nodes) {
|
||||
// Filter reviews by actor
|
||||
// Drop reviews submitted or edited after the trigger, then filter by actor.
|
||||
reviewData.nodes = filterCommentsByActor(
|
||||
reviewData.nodes,
|
||||
filterReviewsToTriggerTime(reviewData.nodes, triggerTime),
|
||||
includeCommentsByActor,
|
||||
excludeCommentsByActor,
|
||||
);
|
||||
|
||||
// Also filter inline review comments within each review
|
||||
// Apply the same trigger-time + actor filtering to inline review comments.
|
||||
reviewData.nodes.forEach((review) => {
|
||||
if (review.comments?.nodes) {
|
||||
review.comments.nodes = filterCommentsByActor(
|
||||
review.comments.nodes,
|
||||
filterCommentsToTriggerTime(review.comments.nodes, triggerTime),
|
||||
includeCommentsByActor,
|
||||
excludeCommentsByActor,
|
||||
);
|
||||
@@ -413,14 +405,19 @@ export async function fetchGitHubData({
|
||||
});
|
||||
}
|
||||
|
||||
const allReviewComments =
|
||||
reviewData?.nodes?.flatMap((r) => r.comments?.nodes ?? []) ?? [];
|
||||
const filteredReviewComments = filterCommentsToTriggerTime(
|
||||
allReviewComments,
|
||||
triggerTime,
|
||||
);
|
||||
// Build the image-processing lists from the already-filtered review nodes,
|
||||
// so reviews/comments excluded from the prompt are not processed for images.
|
||||
const reviewBodies: CommentWithImages[] = (reviewData?.nodes ?? [])
|
||||
.filter((r) => r.body)
|
||||
.map((r) => ({
|
||||
type: "review_body" as const,
|
||||
id: r.databaseId,
|
||||
pullNumber: prNumber,
|
||||
body: r.body,
|
||||
}));
|
||||
|
||||
const reviewComments: CommentWithImages[] = filteredReviewComments
|
||||
const reviewComments: CommentWithImages[] = (reviewData?.nodes ?? [])
|
||||
.flatMap((r) => r.comments?.nodes ?? [])
|
||||
.filter((c) => c.body && !c.isMinimized)
|
||||
.map((c) => ({
|
||||
type: "review_comment" as const,
|
||||
|
||||
Reference in New Issue
Block a user