Skip to content

Commit d4173b2

Browse files
waleedlatif1claude
andcommitted
fix(otp): don't leak caught error.message; fail-closed on DB retry exhaust
- Chat/form OTP routes: replace `error.message || fallback` with generic `Failed to process request` in 500 responses (logger still captures detail). - otp.ts incrementOTPAttempts DB path: on MAX_RETRIES exhaustion, delete the verification row and return `'locked'` instead of trusting a possibly- undercounted final read. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent b5ca48d commit d4173b2

3 files changed

Lines changed: 16 additions & 24 deletions

File tree

apps/sim/app/api/chat/[identifier]/otp/route.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,9 @@ export const POST = withRouteHandler(
143143

144144
logger.info(`[${requestId}] OTP sent to ${email} for chat ${deployment.id}`)
145145
return addCorsHeaders(createSuccessResponse({ message: 'Verification code sent' }), request)
146-
} catch (error: any) {
146+
} catch (error) {
147147
logger.error(`[${requestId}] Error processing OTP request:`, error)
148-
return addCorsHeaders(
149-
createErrorResponse(error.message || 'Failed to process request', 500),
150-
request
151-
)
148+
return addCorsHeaders(createErrorResponse('Failed to process request', 500), request)
152149
}
153150
}
154151
)
@@ -239,12 +236,9 @@ export const PUT = withRouteHandler(
239236
setChatAuthCookie(response, deployment.id, deployment.authType, deployment.password)
240237

241238
return response
242-
} catch (error: any) {
239+
} catch (error) {
243240
logger.error(`[${requestId}] Error verifying OTP:`, error)
244-
return addCorsHeaders(
245-
createErrorResponse(error.message || 'Failed to process request', 500),
246-
request
247-
)
241+
return addCorsHeaders(createErrorResponse('Failed to process request', 500), request)
248242
}
249243
}
250244
)

apps/sim/app/api/form/[identifier]/otp/route.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,9 @@ export const POST = withRouteHandler(
149149

150150
logger.info(`[${requestId}] OTP sent to ${email} for form ${deployment.id}`)
151151
return addCorsHeaders(createSuccessResponse({ message: 'Verification code sent' }), request)
152-
} catch (error: any) {
152+
} catch (error) {
153153
logger.error(`[${requestId}] Error processing OTP request:`, error)
154-
return addCorsHeaders(
155-
createErrorResponse(error.message || 'Failed to process request', 500),
156-
request
157-
)
154+
return addCorsHeaders(createErrorResponse('Failed to process request', 500), request)
158155
}
159156
}
160157
)
@@ -256,12 +253,9 @@ export const PUT = withRouteHandler(
256253
setFormAuthCookie(response, deployment.id, deployment.authType, deployment.password)
257254

258255
return response
259-
} catch (error: any) {
256+
} catch (error) {
260257
logger.error(`[${requestId}] Error verifying OTP:`, error)
261-
return addCorsHeaders(
262-
createErrorResponse(error.message || 'Failed to process request', 500),
263-
request
264-
)
258+
return addCorsHeaders(createErrorResponse('Failed to process request', 500), request)
265259
}
266260
}
267261
)

apps/sim/lib/core/security/otp.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,14 @@ export async function incrementOTPAttempts(
220220
value = fresh
221221
}
222222

223-
const final = await getOTP(kind, deploymentId, email)
224-
if (!final) return 'locked'
225-
const { attempts: finalAttempts } = decodeOTPValue(final)
226-
return finalAttempts >= MAX_OTP_ATTEMPTS ? 'locked' : 'incremented'
223+
/**
224+
* Retry exhaustion under heavy DB-path contention: this request did not
225+
* succeed in writing its own +1, so the stored count may not reflect it.
226+
* Fail closed — invalidate the OTP rather than return `'incremented'` with
227+
* a possibly-undercounted attempt total.
228+
*/
229+
await db.delete(verification).where(eq(verification.identifier, identifier))
230+
return 'locked'
227231
}
228232

229233
export async function deleteOTP(

0 commit comments

Comments
 (0)