{"record":{"id":"51abdff840ad0190","repo":"ruvnet/ruflo","slug":"validation-failed","errorCode":"VALIDATION_FAILED","errorMessage":"validation.errors.join('; ')","messagePattern":"validation\\.errors\\.join\\('; '\\)","errorType":"exception","errorClass":"PasswordHashError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/security/src/password-hasher.ts","lineNumber":183,"sourceCode":"\n    return {\n      isValid: errors.length === 0,\n      errors,\n    };\n  }\n\n  /**\n   * Hashes a password using bcrypt.\n   *\n   * @param password - The plaintext password to hash\n   * @returns The bcrypt hash\n   * @throws PasswordHashError if password is invalid\n   */\n  async hash(password: string): Promise<string> {\n    const validation = this.validate(password);\n\n    if (!validation.isValid) {\n      throw new PasswordHashError(\n        validation.errors.join('; '),\n        'VALIDATION_FAILED'\n      );\n    }\n\n    try {\n      // bcrypt automatically generates a random salt per hash\n      return await bcrypt.hash(password, this.config.rounds);\n    } catch (error) {\n      throw new PasswordHashError(\n        'Failed to hash password',\n        'HASH_FAILED'\n      );\n    }\n  }\n\n  /**\n   * Verifies a password against a bcrypt hash.","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/security/src/password-hasher.ts#L165-L201","documentation":"PasswordHasher.hash() runs its configured password policy through validate() before ever calling bcrypt. When the plaintext violates that policy, it throws a PasswordHashError with code VALIDATION_FAILED whose message is the policy failures joined by '; '. Defaults require 8-128 chars, at least one uppercase, one lowercase, and one digit.","triggerScenarios":"await hasher.hash('') (empty), hash('short1A' is fine but hash('short') is not), hash('alllowercase123'), hash('NOLOWERCASE123') — any input failing the minLength/maxLength/requireUppercase/requireLowercase/requireDigit/requireSpecial checks. Typical when requireSpecial was enabled in config but the caller's form does not enforce it.","commonSituations":"Signup/change-password flows where UI validation is looser than the hasher config; importing legacy users whose passwords predate the policy; tests using passwords like 'test' or 'password'; enabling requireSpecial: true without updating the client-side checker.","solutions":["Read the thrown message — it lists every failed rule (e.g. 'Password must contain at least one digit'), so fix the password to satisfy each named rule.","Call hasher.validate(password) before hash() and return its errors array to the UI instead of relying on the throw.","If your product policy intentionally differs, construct PasswordHasher with matching config (e.g. { requireDigit: false, minLength: 10 }) instead of mutating defaults after the fact.","Keep client-side validation derived from the same PasswordHasherConfig so the two never diverge."],"exampleFix":"// before\nconst hash = await hasher.hash('weak');\n// throws PasswordHashError: Password must contain at least one uppercase letter; ...\n\n// after\nconst check = hasher.validate('WeakPass1');\nif (!check.isValid) throw new UserInputError(check.errors);\nconst hash = await hasher.hash('WeakPass1');","handlingStrategy":"validation","validationCode":"const check = hasher.validate(password);\nif (!check.isValid) {\n  return badRequest(check.errors); // surface policy errors to the caller\n}\nconst hash = await hasher.hash(password);","typeGuard":"function isPolicyCompliant(hasher: PasswordHasher, pw: string): boolean {\n  return hasher.validate(pw).isValid;\n}","tryCatchPattern":"try {\n  const hash = await hasher.hash(password);\n} catch (err) {\n  if (err instanceof PasswordHashError && err.code === 'VALIDATION_FAILED') {\n    return res.status(400).json({ errors: err.message.split('; ') });\n  }\n  throw err;\n}","preventionTips":["Derive client-side validation from the same PasswordHasherConfig object you pass to the constructor.","Always run hasher.validate() in request handlers before hash() so users get field-level feedback.","In tests, use a known-good password like 'TestPass1' that satisfies all default rules."],"tags":["password","bcrypt","validation","security"],"backgroundTag":"password-policy-validation","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}