From b395dc566f5461916d052530dfba645ae8972f5a Mon Sep 17 00:00:00 2001 From: Cho Young-Hwi Date: Sun, 15 Mar 2026 10:29:31 +0000 Subject: [PATCH] [#92] Bind comment to signed rating message to prevent replay Include the comment string in the signed message so a valid signature cannot be replayed with a different comment. Empty comments use an empty string in the message format. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/api/ratings/route.ts | 5 +++-- src/components/RatingWidget.tsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/api/ratings/route.ts b/src/app/api/ratings/route.ts index 311085e2..c5099dbc 100644 --- a/src/app/api/ratings/route.ts +++ b/src/app/api/ratings/route.ts @@ -78,8 +78,9 @@ export async function POST(req: NextRequest) { return error("Missing address, signature, or message"); } - // Validate signed message binds to this specific action - const expectedMessage = `Rate storyline ${storylineId} with rating ${rating}`; + // Validate signed message binds to this specific action (including comment) + const boundComment = comment ?? ""; + const expectedMessage = `Rate storyline ${storylineId} with rating ${rating} comment:${boundComment}`; if (message !== expectedMessage) { return error( `Signed message must be exactly: "${expectedMessage}"`, diff --git a/src/components/RatingWidget.tsx b/src/components/RatingWidget.tsx index 67fc668c..dd6378ba 100644 --- a/src/components/RatingWidget.tsx +++ b/src/components/RatingWidget.tsx @@ -111,7 +111,7 @@ export function RatingWidget({ storylineId, tokenAddress }: RatingWidgetProps) { setSuccess(false); setSubmitting(true); - const message = `Rate storyline ${storylineId} with rating ${selectedRating}`; + const message = `Rate storyline ${storylineId} with rating ${selectedRating} comment:${comment || ""}`; const signature = await signMessageAsync({ message }); const res = await fetch("/api/ratings", {