Skip to content

chore(utils): migrate to shared random/ID utilities and add enforcement linting#4622

Open
waleedlatif1 wants to merge 2 commits into
stagingfrom
chore/utils-enforcement
Open

chore(utils): migrate to shared random/ID utilities and add enforcement linting#4622
waleedlatif1 wants to merge 2 commits into
stagingfrom
chore/utils-enforcement

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • Replaced all Math.random(), crypto.randomUUID(), crypto.randomBytes(), nanoid, and uuid usages across 72 files with the shared @sim/utils/random and @sim/utils/id helpers
  • Added Biome noRestrictedImports rule to catch nanoid, uuid, and crypto.randomUUID/randomBytes imports at lint time (errors, not warnings)
  • Added scripts/check-utils-enforcement.ts to catch Math.random and crypto.* global property access — patterns that import analysis alone can't catch
  • Added check:utils script to package.json
  • Excluded .claude/worktrees from Biome scanning to prevent false positives from agent worktrees

Type of Change

  • Improvement / refactor (no behavior change)

Testing

Tested manually — bun run check:utils passes, bunx biome lint reports no noRestrictedImports violations on the actual codebase

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment May 15, 2026 11:19pm

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 15, 2026

PR Summary

Medium Risk
Wide-scope refactor touching auth tokens, storage keys, retry jitter, and webhook signature code; mistakes could cause runtime errors in environments lacking Web Crypto or subtle behavior changes in randomness/signing.

Overview
This PR standardizes all randomness and ID generation on shared helpers by introducing @sim/utils/random (secure bytes/hex/strings plus unbiased randomInt/randomItem and randomFloat) and wiring @sim/utils/id to use it.

It then migrates callsites across the repo away from Math.random(), crypto.randomBytes(), and crypto.randomUUID() (including sampling/jitter, storage keys/boundaries, OTP generation, and test factories) and updates the Twilio voice webhook signature implementation to use Node createHmac.

Finally, it adds enforcement: Biome noRestrictedImports bans nanoid/uuid and crypto randomUUID/randomBytes, a new scripts/check-utils-enforcement.ts scans for global Math.random/crypto.* usage (hooked up as bun run check:utils), and Biome ignores .claude/worktrees to avoid false positives.

Reviewed by Cursor Bugbot for commit 4ad2f31. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 15, 2026

Greptile Summary

This PR consolidates all randomness and ID generation across 72 files into two shared packages — @sim/utils/random (new) and @sim/utils/id (refactored) — and pairs the migration with Biome lint rules and a custom enforcement script to prevent future drift.

  • New packages/utils/src/random.ts: exports generateRandomBytes, generateRandomHex, generateRandomString, randomFloat, randomInt, and randomItem, all backed by crypto.getRandomValues; handles the 65 536-byte per-call limit and uses rejection sampling for unbiased randomInt.
  • Enforcement layer: biome.json adds a noRestrictedImports error rule for nanoid, uuid, and named crypto imports; scripts/check-utils-enforcement.ts catches property-access patterns that static import analysis cannot.
  • Notable correctness swap: OTP generation replaces crypto.randomInt(100000, 1000000) with randomInt(900000) + 100000 — both produce a uniform draw from [100 000, 999 999]; and Twilio HMAC-SHA1 moves from async Web Crypto to synchronous node:crypto createHmac.

Confidence Score: 4/5

Safe to merge — the migration is mechanically correct across all 72 call sites, but the enforcement layer has a gap that allows the rules to be bypassed via node:crypto destructured imports.

All migrated call sites are functionally correct: OTP generation, token/key derivation, and Twilio HMAC signing all produce equivalent outputs with the shared utilities. The random.ts implementation is solid — rejection sampling for randomInt, proper 65 536-byte chunking in generateRandomBytes, and no Math.random fallbacks. The weakness is that both the Biome rule and the enforcement script target the bare "crypto" specifier but miss "node:crypto", which is already the project's canonical prefix for crypto imports.

biome.json and scripts/check-utils-enforcement.ts have a complementary gap around node:crypto destructured imports; everything else in the PR can be merged with confidence.

Important Files Changed

Filename Overview
packages/utils/src/random.ts New shared CSPRNG module using crypto.getRandomValues; all six exported helpers are correct, with chunking for >65 536-byte requests handled properly.
packages/utils/src/id.ts Delegated generateShortId to generateRandomString and simplified generateId fallback to use generateRandomBytes; logic is equivalent to the previous inline implementation.
biome.json Added noRestrictedImports error rule; bans only the bare "crypto" module specifier, missing "node:crypto", which the project uses as its canonical prefix.
scripts/check-utils-enforcement.ts New enforcement script scanning apps/ and packages/; misses direct-call style after import { randomBytes } from 'node:crypto'.
packages/security/src/encryption.ts Replaced randomBytes(16) with Buffer.from(generateRandomBytes(16)); semantically equivalent.
packages/security/src/tokens.ts Replaced randomBytes(byteLength).toString('base64url') with Buffer.from(generateRandomBytes(byteLength)).toString('base64url'); semantically identical.
apps/sim/app/api/chat/[identifier]/otp/route.ts OTP generation changed from crypto.randomInt(100000, 1000000) to randomInt(900000) + 100000; both produce a uniform draw from [100000, 999999] using a CSPRNG.
apps/sim/lib/webhooks/providers/twilio-voice.ts Replaced async Web Crypto HMAC-SHA1 with synchronous createHmac('sha1') from node:crypto; functionally equivalent and more readable.

Reviews (1): Last reviewed commit: "chore: remove stray files" | Re-trigger Greptile

Comment thread biome.json
Comment on lines 116 to +127
"noInferrableTypes": "error",
"noUselessElse": "error"
"noUselessElse": "error",
"noRestrictedImports": {
"level": "error",
"options": {
"paths": {
"nanoid": "Use generateShortId() from @sim/utils/id instead",
"uuid": "Use generateId() from @sim/utils/id instead",
"crypto": {
"importNames": ["randomUUID", "randomBytes"],
"message": "Use generateId()/generateShortId() from @sim/utils/id or generateRandomBytes()/generateRandomHex() from @sim/utils/random instead"
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Biome ban covers "crypto" but not "node:crypto"

Biome treats "crypto" and "node:crypto" as distinct module specifiers. The restriction only fires for the bare "crypto" form, so import { randomBytes } from 'node:crypto' silently passes the linter. The project already uses the node: prefix convention for legitimate crypto imports (see twilio-voice.ts in this PR), so the gap is live. The check-utils-enforcement.ts script only catches crypto.randomBytes( as a property-access pattern; a direct call like randomBytes(16) after a destructured node:crypto import bypasses both enforcement layers simultaneously.

Adding a parallel "node:crypto" entry to the paths object would close the gap.

Comment on lines +35 to +55
description: 'Math.random()',
suggestion: 'randomInt / randomFloat / randomItem from @sim/utils/random',
},
{
pattern: /\bcrypto\.randomUUID\s*\(/g,
description: 'crypto.randomUUID()',
suggestion: 'generateId() or generateShortId() from @sim/utils/id',
},
{
pattern: /\bcrypto\.randomBytes\s*\(/g,
description: 'crypto.randomBytes()',
suggestion: 'generateRandomBytes() or generateRandomHex() from @sim/utils/random',
},
]

async function walk(dir: string, results: string[] = []): Promise<string[]> {
let entries
try {
entries = await readdir(dir, { withFileTypes: true })
} catch {
return results
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Pattern matching misses destructured node:crypto imports

The BANNED_PATTERNS match crypto.randomUUID( and crypto.randomBytes( as property-access calls. A developer who writes import { randomBytes } from 'node:crypto' and calls randomBytes(16) directly would bypass this check (no crypto. prefix). This is the same gap as in the Biome rule — the two enforcement mechanisms fail in the same way for node:crypto destructured imports, so the gap isn't recovered by running the script.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4ad2f31. Configure here.

Comment thread biome.json
"importNames": ["randomUUID", "randomBytes"],
"message": "Use generateId()/generateShortId() from @sim/utils/id or generateRandomBytes()/generateRandomHex() from @sim/utils/random instead"
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Biome lint rule misses node:crypto restricted imports

Low Severity

The noRestrictedImports rule only restricts "crypto" but not "node:crypto". Biome treats these as distinct module specifiers, so import { randomBytes } from 'node:crypto' completely bypasses the lint rule. The codebase already uses node:crypto exclusively for all remaining crypto imports (e.g., createHmac, createHash), making it the natural pattern a developer would follow when adding a new import. The companion check-utils-enforcement.ts script only catches crypto.randomBytes(...) call-site patterns, so a destructured randomBytes(...) call from node:crypto would evade both enforcement mechanisms.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4ad2f31. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant