Problem. SendScheduledPurchaseNotification (internal/email/templates.go:354-362) and SendRIExchangePendingApproval (:439-447) render bodies with live ?token=... approve/pause/cancel/reject links and ship them through SendNotification → SNS broadcast (sender.go:104-125). Every topic subscriber gets a working token. This is exactly the leak the purchase-approval path was hardened against (sender.go:386-391, templates.go:698-704 route tokens through targeted SES, never SNS). RI-exchange links hit /api/ri-exchange/approve/... → real spend.
Evidence. templates.go:354-362, :439-447; scheduledPurchaseTemplate L51/53/55; riExchangePendingApprovalTemplate L302-303; contrast templates.go:698-704.
Impact. Any SNS subscriber can pause plans, cancel purchases, or approve RI exchanges that trigger spend.
Suggested fix. Route token-bearing bodies through SendToEmailWithCC to a resolved recipient (return ErrNoRecipient rather than broadcasting). For broadcast, send a tokenless "sign in to act" message. Structurally: make a body carrying ApprovalToken unable to reach SendNotification (distinct TokenizedNotification type or a guard rejecting token= bodies).
References. Source: report 07 (C1, architecture note). Related #296/PR #813 (HTML+plaintext multipart — does NOT fix the routing).
Filed from automated adversarial code review (see docs/code-review/). Source finding(s): 07-C1.
Problem.
SendScheduledPurchaseNotification(internal/email/templates.go:354-362) andSendRIExchangePendingApproval(:439-447) render bodies with live?token=...approve/pause/cancel/reject links and ship them throughSendNotification→ SNS broadcast (sender.go:104-125). Every topic subscriber gets a working token. This is exactly the leak the purchase-approval path was hardened against (sender.go:386-391,templates.go:698-704route tokens through targeted SES, never SNS). RI-exchange links hit/api/ri-exchange/approve/...→ real spend.Evidence.
templates.go:354-362,:439-447; scheduledPurchaseTemplate L51/53/55; riExchangePendingApprovalTemplate L302-303; contrasttemplates.go:698-704.Impact. Any SNS subscriber can pause plans, cancel purchases, or approve RI exchanges that trigger spend.
Suggested fix. Route token-bearing bodies through
SendToEmailWithCCto a resolved recipient (returnErrNoRecipientrather than broadcasting). For broadcast, send a tokenless "sign in to act" message. Structurally: make a body carryingApprovalTokenunable to reachSendNotification(distinctTokenizedNotificationtype or a guard rejectingtoken=bodies).References. Source: report 07 (C1, architecture note). Related #296/PR #813 (HTML+plaintext multipart — does NOT fix the routing).
Filed from automated adversarial code review (see docs/code-review/). Source finding(s): 07-C1.