Why AI tools produce this: AI coding tools produce this regularly, typically when prompted for a shortcut or a quick fix.
Why this matters
A low work factor makes the hash cheap to compute, which lets an attacker brute-force stolen password hashes far too quickly. OWASP recommends a bcrypt cost of at least 10, and ≥ 12 for new applications, tuned so a single hash takes roughly 250ms on your hardware.
Common LLM-generated mistake: bcrypt.hash(pw, 8) or bcrypt.genSalt(5) because the literal "looks fast enough". Raise the cost factor to 12 or higher.
VULNERABLE
vulnerable.ts
// `bcrypt` here is the bcryptjs package — the API surface is identical and// LLMs frequently alias it to `bcrypt` on import.import bcrypt from 'bcryptjs';// ruleid: auth.flow.weak-bcrypt-roundsexport async function hashPassword(pw: string) { return bcrypt.hash(pw, 8);}// ruleid: auth.flow.weak-bcrypt-roundsexport async function makeSalt() { return bcrypt.genSalt(5);}// ruleid: auth.flow.weak-bcrypt-roundsexport function hashSyncPassword(pw: string) { return bcrypt.hashSync(pw, 9);}// ruleid: auth.flow.weak-bcrypt-rounds -- hash with 3-arg callback formexport function hashWithCallback(pw: string) { bcrypt.hash(pw, 4, (_err, _hash) => {});}// ruleid: auth.flow.weak-bcrypt-rounds -- genSaltSync low costexport function makeSaltSync() { return bcrypt.genSaltSync(6);}
SAFE
safe.ts
import bcrypt from 'bcrypt';const saltRounds = 12;// ok: auth.flow.weak-bcrypt-roundsexport async function hashPassword(pw: string) { return bcrypt.hash(pw, 12);}// ok: auth.flow.weak-bcrypt-rounds -- exactly at the recommended floorexport async function makeSalt() { return bcrypt.genSalt(10);}// ok: auth.flow.weak-bcrypt-rounds -- cost factor comes from a constantexport function hashWithConstant(pw: string) { return bcrypt.hashSync(pw, saltRounds);}// ok: auth.flow.weak-bcrypt-rounds -- cost factor from config variableexport function hashWithVar(pw: string, rounds: number) { return bcrypt.hash(pw, rounds);}
Suppressing this rule
If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.