Summary
The scheduledPurchaseTemplate (plain-text, for plan-based upcoming purchase notifications) in internal/email/templates.go:51-55 embeds the approval token in dashboard-facing query-string links:
[Review & Edit] {{.DashboardURL}}?action=edit&token={{urlquery .ApprovalToken}}
[Pause Plan] {{.DashboardURL}}?action=pause&token={{urlquery .ApprovalToken}}
[Cancel This Purchase] {{.DashboardURL}}?action=cancel&token={{urlquery .ApprovalToken}}
These links point at the dashboard SPA (not the /api/purchases/approve/ endpoint directly). The SPA reads the token from location.search and uses it for subsequent API calls. This means:
- The token appears in browser history against the dashboard URL.
- If the dashboard page loads any analytics, font, or tracking resource (now or in the future), the Referer header carries the full URL including the token.
- Server-side logging of the dashboard's CloudFront distribution will record the token in access logs.
The purchaseApprovalRequestTemplate (for direct/ad-hoc purchases) correctly directs the link to /purchases/approve/<id>?token=... (the API path), which is the same leakage category — but the scheduled purchase template additionally exposes the token in the dashboard SPA load, not just the API call.
Affected file
internal/email/templates.go:51-55 — scheduled purchase notification template
Suggested fix
For the scheduled purchase template, the action links should either:
- Direct the user to a dashboard route that does not pass the token in the URL (e.g.
/purchases/<id> which loads the execution and shows action buttons authenticated by session), or
- Follow the same POST-redirect pattern recommended in the companion issue about token leakage via GET query strings.
The token in the scheduled notification should ideally be used only to authenticate the cancel API call server-side after the user has logged in, not as a persistent URL parameter on the dashboard page itself.
Summary
The
scheduledPurchaseTemplate(plain-text, for plan-based upcoming purchase notifications) ininternal/email/templates.go:51-55embeds the approval token in dashboard-facing query-string links:These links point at the dashboard SPA (not the
/api/purchases/approve/endpoint directly). The SPA reads the token fromlocation.searchand uses it for subsequent API calls. This means:The
purchaseApprovalRequestTemplate(for direct/ad-hoc purchases) correctly directs the link to/purchases/approve/<id>?token=...(the API path), which is the same leakage category — but the scheduled purchase template additionally exposes the token in the dashboard SPA load, not just the API call.Affected file
internal/email/templates.go:51-55— scheduled purchase notification templateSuggested fix
For the scheduled purchase template, the action links should either:
/purchases/<id>which loads the execution and shows action buttons authenticated by session), orThe token in the scheduled notification should ideally be used only to authenticate the cancel API call server-side after the user has logged in, not as a persistent URL parameter on the dashboard page itself.