From 2133f8d5bb05ff7ddf14534be9a0295e2f1cb621 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Tue, 30 Jun 2026 22:45:15 +0000 Subject: [PATCH 1/2] fix(sms): remove unauthenticated open SMS relay endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DELETE /api/sms/send-notification — it accepted an arbitrary phoneNumber + message from any anonymous caller and sent it via the app's Twilio creds with no auth, rate limiting, or origin check. It was orphaned (no caller anywhere in the app) and is the likely vector behind the runaway SMS billing (SMS-pumping/toll fraud). The legitimate offline-message SMS path is unaffected: it runs through SMSNotificationService, invoked server-side from the WebSocket handler. Co-Authored-By: Claude Opus 4.8 --- src/app/api/sms/send-notification/route.js | 59 ---------------------- 1 file changed, 59 deletions(-) delete mode 100644 src/app/api/sms/send-notification/route.js diff --git a/src/app/api/sms/send-notification/route.js b/src/app/api/sms/send-notification/route.js deleted file mode 100644 index 2d4b123a..00000000 --- a/src/app/api/sms/send-notification/route.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @fileoverview SMS notification API endpoint - * Handles sending SMS notifications via Twilio API - */ - -import { NextResponse } from 'next/server'; -import { TwilioSMSProvider } from '@/lib/services/twilio-sms-provider.js'; - -/** - * Send SMS notification - * @param {Object} params - SvelteKit request parameters - * @param {Request} params.request - The request object - */ -export async function POST(request) { - try { - const { phoneNumber, message } = await request.json(); - - if (!phoneNumber || !message) { - return NextResponse.json( - { error: 'Phone number and message are required' }, - { status: 400 } - ); - } - - console.log('📱 [SMS-API] Sending SMS via Twilio:', { - to: phoneNumber, - messageLength: message.length, - timestamp: new Date().toISOString() - }); - - // Initialize Twilio SMS provider - const twilioProvider = new TwilioSMSProvider(); - - // Send SMS via Twilio - const result = await twilioProvider.sendSMS(phoneNumber, message); - - const messageId = (result && typeof result === 'object' && 'messageId' in result) ? result.messageId : 'unknown'; - const status = (result && typeof result === 'object' && 'status' in result) ? result.status : 'sent'; - - console.log('📱 [SMS-API] SMS sent successfully:', { - messageId, - status - }); - - return NextResponse.json({ - success: true, - messageId, - status - }); - - } catch (error) { - console.error('📱 [SMS-API] Error sending SMS:', error); - const errorMessage = error instanceof Error ? error.message : 'Failed to send SMS notification'; - return NextResponse.json( - { error: errorMessage }, - { status: 500 } - ); - } -} \ No newline at end of file From 59379f6f147f2a85bd2e15e78c9cec54ff68608f Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Tue, 30 Jun 2026 22:56:30 +0000 Subject: [PATCH 2/2] fix(auth): loosen key-backup restore rate limit (5/hr -> 10/15min) Restoring E2EE keys on a new device fires several GET /api/auth/key-backup calls per attempt (on-mount hasBackup check + the restore itself), so the old 5-per-hour cap locked users out after ~2 restore attempts for a full hour. Raise to 10 requests and shorten the window to 15 min so legitimate multi-attempt restores work and any lockout clears quickly. Co-Authored-By: Claude Opus 4.8 --- src/middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware.js b/src/middleware.js index 7bc62218..70d8b915 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -4,7 +4,7 @@ import { RateLimiter } from '@/lib/server/rate-limiter.js'; const authRateLimiter = new RateLimiter({ maxRequests: 10, windowMs: 60 * 1000 }); const webhookRateLimiter = new RateLimiter({ maxRequests: 100, windowMs: 60 * 1000 }); const apiRateLimiter = new RateLimiter({ maxRequests: 60, windowMs: 60 * 1000 }); -const keyBackupRateLimiter = new RateLimiter({ maxRequests: 5, windowMs: 60 * 60 * 1000 }); +const keyBackupRateLimiter = new RateLimiter({ maxRequests: 10, windowMs: 15 * 60 * 1000 }); function getClientIp(request) { // See the detailed note in src/lib/server/rate-limiter.js.