Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/sim/app/api/chat/[identifier]/otp/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { randomUUID } from 'crypto'
import { randomInt, randomUUID } from 'crypto'
import { db } from '@sim/db'
import { chat, verification } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
Expand All @@ -17,7 +17,7 @@ import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/
const logger = createLogger('ChatOtpAPI')

function generateOTP() {
return Math.floor(100000 + Math.random() * 900000).toString()
return randomInt(100000, 1000000).toString()
}

const OTP_EXPIRY = 15 * 60 // 15 minutes
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/lib/core/security/encryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ export function generatePassword(length = 24): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+='
let result = ''

const bytes = randomBytes(length)
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length))
result += chars.charAt(bytes[i] % chars.length)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modulo bias in cryptographic password generation

Medium Severity

bytes[i] % chars.length introduces modulo bias because 256 is not evenly divisible by 76 (the charset length). The first 28 characters have a ~33% higher chance of being selected than the remaining 48 characters, reducing the effective entropy of generated passwords. Ironically, this PR aims to improve cryptographic security but introduces a well-known CSPRNG pitfall. crypto.randomInt(0, chars.length) avoids this by using rejection sampling internally.

Fix in Cursor Fix in Web

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modulo bias reduces character distribution uniformity

bytes[i] % chars.length introduces a modulo bias because 256 is not evenly divisible by chars.length (76). This means the first 256 % 76 = 28 characters in the charset each have a slightly higher probability of selection (4/256) than the remaining characters (3/256), rather than the ideal uniform 1/76.

While this is astronomically more secure than Math.random(), a password generator should strive for uniform distribution. randomInt (already imported in the OTP route) performs rejection sampling internally and provides a perfectly uniform result:

for (let i = 0; i < length; i++) {
  result += chars.charAt(randomInt(0, chars.length))
}

Note that randomBytes would no longer be needed for this function (though it remains used elsewhere in the file).

}

return result
Expand Down