summaryrefslogtreecommitdiff
path: root/apps/web/app/api/share
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/share
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/share')
-rw-r--r--apps/web/app/api/share/route.ts6
1 files changed, 6 insertions, 0 deletions
diff --git a/apps/web/app/api/share/route.ts b/apps/web/app/api/share/route.ts
index 324d1e9..5f67bc6 100644
--- a/apps/web/app/api/share/route.ts
+++ b/apps/web/app/api/share/route.ts
@@ -2,6 +2,7 @@ import { NextResponse } from "next/server"
import { randomBytes } from "crypto"
import { createSupabaseServerClient } from "@/lib/supabase/server"
import { checkBotId } from "botid/server"
+import { rateLimit } from "@/lib/rate-limit"
const MAX_NOTE_LENGTH = 1000
@@ -27,6 +28,11 @@ export async function POST(request: Request) {
return NextResponse.json({ error: "not authenticated" }, { status: 401 })
}
+ const rateLimitResult = rateLimit(`share:${user.id}`, 30, 60_000)
+ if (!rateLimitResult.success) {
+ return NextResponse.json({ error: "too many requests" }, { status: 429 })
+ }
+
const { data: userProfile } = await supabaseClient
.from("user_profiles")
.select("tier")