{"record":{"id":"9833cd7c6f75c8a2","repo":"ruvnet/ruflo","slug":"invalid-rounds","errorCode":"INVALID_ROUNDS","errorMessage":"Bcrypt rounds must be between 10 and 20 for security and performance balance","messagePattern":"Bcrypt rounds must be between 10 and 20 for security and performance balance","errorType":"exception","errorClass":"PasswordHashError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/security/src/password-hasher.ts","lineNumber":114,"sourceCode":" * ```\n */\nexport class PasswordHasher {\n  private readonly config: Required<PasswordHasherConfig>;\n\n  constructor(config: PasswordHasherConfig = {}) {\n    this.config = {\n      rounds: config.rounds ?? 12,\n      minLength: config.minLength ?? 8,\n      maxLength: config.maxLength ?? 128,\n      requireUppercase: config.requireUppercase ?? true,\n      requireLowercase: config.requireLowercase ?? true,\n      requireDigit: config.requireDigit ?? true,\n      requireSpecial: config.requireSpecial ?? false,\n    };\n\n    // Validate configuration\n    if (this.config.rounds < 10 || this.config.rounds > 20) {\n      throw new PasswordHashError(\n        'Bcrypt rounds must be between 10 and 20 for security and performance balance',\n        'INVALID_ROUNDS'\n      );\n    }\n\n    if (this.config.minLength < 8) {\n      throw new PasswordHashError(\n        'Minimum password length must be at least 8 characters',\n        'INVALID_MIN_LENGTH'\n      );\n    }\n  }\n\n  /**\n   * Validates password against configured requirements.\n   *\n   * @param password - The password to validate\n   * @returns Validation result with errors if any","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/security/src/password-hasher.ts#L96-L132","documentation":"PasswordHasher's constructor validates the bcrypt cost factor and refuses rounds outside [10, 20] with PasswordHashError INVALID_ROUNDS. Below 10, hashes become brute-forceable; above 20, hashing/verification gets slow enough to enable denial-of-service on auth paths. The default is 12.","triggerScenarios":"new PasswordHasher({ rounds: 4 }) copied from a benchmark or legacy config; rounds supplied via env as a string that slips through unvalidated; raising rounds above 20 for 'extra security' without benchmarking login latency.","commonSituations":"Configs ported from other bcrypt wrappers with lower floors; performance tuning dropping below 10; compliance-driven increases past 20 that stall auth throughput.","solutions":["Set rounds between 10 and 20 (12 — the default — is the usual sweet spot)","Parse and range-check env-provided values before constructing the hasher","If a policy truly demands >20, benchmark the verify path first and account for the latency cost on every login"],"exampleFix":"// before\nnew PasswordHasher({ rounds: 4 });\n\n// after\nnew PasswordHasher({ rounds: 12 });","handlingStrategy":"validation","validationCode":"const rounds = Number(process.env.BCRYPT_ROUNDS ?? 12);\nif (!(rounds >= 10 && rounds <= 20)) {\n  throw new Error(`bcrypt rounds must be 10-20, got ${rounds}`);\n}\nnew PasswordHasher({ rounds });","typeGuard":"function isPasswordHashError(e: unknown, code?: string): boolean {\n  return e instanceof Error && e.name === 'PasswordHashError'\n    && (code === undefined || (e as { code?: string }).code === code);\n}","tryCatchPattern":"try {\n  return new PasswordHasher(cfg);\n} catch (e) {\n  if (isPasswordHashError(e, 'INVALID_ROUNDS')) {\n    return new PasswordHasher({ ...cfg, rounds: 12 }); // fall back to the default\n  }\n  throw e;\n}","preventionTips":["Range-check env-provided bcrypt rounds at config load, not at hash time","Omit rounds to accept the default 12 unless you've benchmarked otherwise","Remember rounds affect every hash AND verify — budget login latency when changing it"],"tags":["security","bcrypt","configuration","validation"],"backgroundTag":"config-validation-failed","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}