summaryrefslogtreecommitdiff
path: root/apps/web/app/api/account/data
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-08 09:21:50 -0800
committerFuwn <[email protected]>2026-02-08 09:21:50 -0800
commit396c3f450e0f17b77478a0525029aa2534e764a9 (patch)
treeac6d5f30c4cac6a7e9d07045d15b8388cd19791d /apps/web/app/api/account/data
parentsecurity: harden database functions and policies (diff)
downloadasa.news-396c3f450e0f17b77478a0525029aa2534e764a9.tar.xz
asa.news-396c3f450e0f17b77478a0525029aa2534e764a9.zip
security: harden API routes
- Add rate limiting to /api/share (30/min), /api/export (5/hr), /api/account/data (3/day) - Add client-side 30s throttle to forgot-password form - Remove immediate tier upgrade on plan change; let invoice.paid webhook handle tier promotion to prevent free upgrades on payment failure - Add SSRF validation to webhook URLs: block localhost, private IPs, link-local, and metadata endpoints - Log Stripe webhook signature verification errors instead of swallowing silently - Mask webhook secret in GET response (show first/last 4 chars only) - Add error logging to API key last_used_at update - Remove internal error message leaking from checkout session route
Diffstat (limited to 'apps/web/app/api/account/data')
-rw-r--r--apps/web/app/api/account/data/route.ts6
1 files changed, 6 insertions, 0 deletions
diff --git a/apps/web/app/api/account/data/route.ts b/apps/web/app/api/account/data/route.ts
index bec6ab9..f3a61ec 100644
--- a/apps/web/app/api/account/data/route.ts
+++ b/apps/web/app/api/account/data/route.ts
@@ -1,5 +1,6 @@
import { NextResponse } from "next/server"
import { createSupabaseServerClient } from "@/lib/supabase/server"
+import { rateLimit } from "@/lib/rate-limit"
export async function GET() {
const supabaseClient = await createSupabaseServerClient()
@@ -11,6 +12,11 @@ export async function GET() {
return NextResponse.json({ error: "not authenticated" }, { status: 401 })
}
+ const rateLimitResult = rateLimit(`gdpr-export:${user.id}`, 3, 86_400_000)
+ if (!rateLimitResult.success) {
+ return NextResponse.json({ error: "too many requests" }, { status: 429 })
+ }
+
const [
profileResult,
subscriptionsResult,