🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@sentinel-password/core

Package Overview
Dependencies
Maintainers
1
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sentinel-password/core - npm Package Compare versions

Comparing version
1.2.0
to
1.2.1
+18
-20
dist/index.cjs

@@ -324,2 +324,12 @@ "use strict";

];
function escapeRegex(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
var KEYBOARD_REGEX = new RegExp(
KEYBOARD_PATTERNS.flatMap((p) => {
const reversed = p.split("").reverse().join("");
return [escapeRegex(p), escapeRegex(reversed)];
}).join("|"),
"i"
);
function validateKeyboardPattern(password, options = {}) {

@@ -330,22 +340,10 @@ const { checkKeyboardPatterns = true } = options;

}
const lowercase = password.toLowerCase();
const emptyParams = {};
for (const pattern of KEYBOARD_PATTERNS) {
if (lowercase.includes(pattern)) {
return {
passed: false,
code: "keyboardPattern.found",
params: emptyParams,
message: resolveMessage("keyboardPattern.found", emptyParams, options)
};
}
const reversed = pattern.split("").reverse().join("");
if (lowercase.includes(reversed)) {
return {
passed: false,
code: "keyboardPattern.found",
params: emptyParams,
message: resolveMessage("keyboardPattern.found", emptyParams, options)
};
}
if (KEYBOARD_REGEX.test(password)) {
const emptyParams = {};
return {
passed: false,
code: "keyboardPattern.found",
params: emptyParams,
message: resolveMessage("keyboardPattern.found", emptyParams, options)
};
}

@@ -352,0 +350,0 @@ return { passed: true };

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/index.ts","../src/messages.ts","../src/validators/length.ts","../src/validators/character-types.ts","../src/validators/repetition.ts","../src/validators/sequential.ts","../src/validators/keyboard-pattern.ts","../src/validators/common-password.ts","../src/validators/personal-info.ts"],"sourcesContent":["/**\n * @sentinel-password/core\n * Zero-dependency TypeScript password validation library\n */\n\nexport type {\n StrengthScore,\n StrengthLabel,\n ValidationResult,\n ValidatorOptions,\n ValidatorCheck,\n Validator,\n CheckId,\n MessageCode,\n MessageParams,\n MessageFormatter,\n} from './types'\n\nexport { DEFAULT_TEMPLATES } from './messages'\n\nexport { validateLength } from './validators/length'\nexport {\n hasUppercase,\n hasLowercase,\n hasDigit,\n hasSymbol,\n validateCharacterTypes,\n} from './validators/character-types'\nexport { validateRepetition } from './validators/repetition'\nexport { validateSequential } from './validators/sequential'\nexport { validateKeyboardPattern } from './validators/keyboard-pattern'\nexport { validateCommonPassword } from './validators/common-password'\nexport { validatePersonalInfo } from './validators/personal-info'\n\nimport type {\n ValidationResult,\n ValidatorOptions,\n StrengthScore,\n StrengthLabel,\n ValidatorCheck,\n CheckId,\n} from './types'\nimport { validateLength } from './validators/length'\nimport { validateCharacterTypes } from './validators/character-types'\nimport { validateRepetition } from './validators/repetition'\nimport { validateSequential } from './validators/sequential'\nimport { validateKeyboardPattern } from './validators/keyboard-pattern'\nimport { validateCommonPassword } from './validators/common-password'\nimport { validatePersonalInfo } from './validators/personal-info'\n\nconst STRENGTH_LABELS: readonly StrengthLabel[] = [\n 'very-weak',\n 'weak',\n 'medium',\n 'strong',\n 'very-strong',\n] as const\n\n/**\n * Validate a password with comprehensive security checks\n *\n * Performs multiple validation checks including length, character types, repetition,\n * sequential patterns, keyboard patterns, common passwords, and personal information.\n * Returns detailed feedback with strength score and actionable suggestions.\n *\n * @param password - The password string to validate\n * @param options - Optional validation configuration\n * @returns Validation result with score, strength label, feedback, and individual check results\n *\n * @example\n * **Basic usage (zero-config)**\n * ```typescript\n * import { validatePassword } from '@sentinel-password/core'\n *\n * const result = validatePassword('MySecure!Pass_w0rd')\n * console.log(result.valid) // true\n * console.log(result.score) // 4 (0-4 scale)\n * console.log(result.strength) // 'very-strong'\n * console.log(result.feedback.warning) // undefined (no issues)\n * console.log(result.feedback.suggestions) // []\n * ```\n *\n * @example\n * **With a known-common password**\n * ```typescript\n * const result = validatePassword('password')\n * console.log(result.valid) // false (commonPassword check rejected it)\n * console.log(result.score) // 4\n * console.log(result.strength) // 'very-strong'\n * console.log(result.feedback.warning)\n * // \"Password is too common. Please choose a more unique password.\"\n * console.log(result.feedback.suggestions)\n * // [\"Password is too common. Please choose a more unique password.\"]\n * console.log(result.checks)\n * // { length: true, characterTypes: true, repetition: true, sequential: true,\n * // keyboardPattern: true, commonPassword: false, personalInfo: true }\n * // ↑ 6 of 7 checks pass, so score is 4 (\"very-strong\") even though valid is false.\n * // Always use `valid` (or `result.checks`) for acceptance decisions, not `strength`.\n * ```\n *\n * @example\n * **Custom length requirements**\n * ```typescript\n * const result = validatePassword('MyP@ss', {\n * minLength: 12,\n * maxLength: 64\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password must be at least 12 characters\"\n * ```\n *\n * @example\n * **Require specific character types**\n * ```typescript\n * const result = validatePassword('password123', {\n * requireUppercase: true,\n * requireLowercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password must contain at least one uppercase letter, symbol\"\n * ```\n *\n * @example\n * **Prevent personal information**\n * ```typescript\n * const result = validatePassword('john1234!', {\n * personalInfo: ['john', 'john.doe@example.com', 'Doe']\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password contains personal information\"\n * ```\n *\n * @example\n * **Disable specific checks**\n * ```typescript\n * const result = validatePassword('qwerty123', {\n * checkKeyboardPatterns: false, // Allow keyboard patterns\n * checkSequential: false, // Allow sequential chars\n * checkCommonPasswords: false // Allow common passwords\n * })\n * // More permissive validation\n * ```\n *\n * @example\n * **Comprehensive configuration**\n * ```typescript\n * const result = validatePassword('MyP@ssw0rd2024!', {\n * minLength: 12,\n * maxLength: 128,\n * requireUppercase: true,\n * requireLowercase: true,\n * requireDigit: true,\n * requireSymbol: true,\n * maxRepeatedChars: 2,\n * checkSequential: true,\n * checkKeyboardPatterns: true,\n * checkCommonPasswords: true,\n * personalInfo: ['user', 'admin', 'test']\n * })\n * ```\n *\n * @remarks\n * **Default behavior:**\n * - Minimum length: 8 characters\n * - Maximum length: 128 characters\n * - No character type requirements (but recommended to enable)\n * - Max repeated characters: 3\n * - Sequential check: enabled\n * - Keyboard pattern check: enabled\n * - Common password check: enabled (top 1,000 passwords)\n * - Personal info check: disabled (provide personalInfo array to enable)\n *\n * **Scoring:**\n * - `score` = `Math.min(4, Math.floor((passedChecks / 7) * 5))` — purely a\n * passed-check ratio.\n * - `strength` is the human label for that score (`very-weak` … `very-strong`).\n * - Because scoring is ratio-based, a password that fails *only* the\n * common-password (or personal-info, or sequential, etc.) check still passes\n * 6 of 7 checks and lands on `score: 4 / strength: 'very-strong'` while\n * `valid` is `false`. Use `valid` (or inspect `result.checks`) for\n * acceptance decisions; use `strength` for UX cues like progress bars.\n *\n * **Performance:**\n * - All validators run in O(n) time or better\n * - Typical validation: < 1ms for passwords up to 128 characters\n * - Bloom filter for common passwords: O(1) lookup\n *\n * **Security:**\n * - All checks are case-insensitive where applicable\n * - No password data is logged or stored\n * - Runs purely in-process — no network calls, the password never leaves the\n * caller's runtime\n * - This is a *strength* validator, not a password-comparison primitive. The\n * validators use early-return `includes()`/loops and are not constant-time,\n * but timing is not a relevant attack surface here: the patterns being\n * checked (length, character types, common-password list, keyboard layouts)\n * are all public — there's no secret to leak via timing. When you compare\n * a password against a stored hash, use a library like Argon2/bcrypt that\n * provides constant-time verification — that's a separate concern from\n * strength validation.\n */\nexport function validatePassword(\n password: string,\n options: ValidatorOptions = {}\n): ValidationResult {\n const checks: Record<CheckId, boolean> = {\n length: false,\n characterTypes: false,\n repetition: false,\n sequential: false,\n keyboardPattern: false,\n commonPassword: false,\n personalInfo: false,\n }\n const suggestions: string[] = []\n\n // Run all validators\n const lengthResult: ValidatorCheck = validateLength(password, options)\n checks['length'] = lengthResult.passed\n if (!lengthResult.passed && lengthResult.message) {\n suggestions.push(lengthResult.message)\n }\n\n const charTypesResult: ValidatorCheck = validateCharacterTypes(password, options)\n checks['characterTypes'] = charTypesResult.passed\n if (!charTypesResult.passed && charTypesResult.message) {\n suggestions.push(charTypesResult.message)\n }\n\n const repetitionResult: ValidatorCheck = validateRepetition(password, options)\n checks['repetition'] = repetitionResult.passed\n if (!repetitionResult.passed && repetitionResult.message) {\n suggestions.push(repetitionResult.message)\n }\n\n const sequentialResult: ValidatorCheck = validateSequential(password, options)\n checks['sequential'] = sequentialResult.passed\n if (!sequentialResult.passed && sequentialResult.message) {\n suggestions.push(sequentialResult.message)\n }\n\n const commonPasswordResult: ValidatorCheck = validateCommonPassword(password, options)\n checks['commonPassword'] = commonPasswordResult.passed\n if (!commonPasswordResult.passed && commonPasswordResult.message) {\n suggestions.push(commonPasswordResult.message)\n }\n\n const personalInfoResult: ValidatorCheck = validatePersonalInfo(password, options)\n checks['personalInfo'] = personalInfoResult.passed\n if (!personalInfoResult.passed && personalInfoResult.message) {\n suggestions.push(personalInfoResult.message)\n }\n\n const keyboardPatternResult: ValidatorCheck = validateKeyboardPattern(password, options)\n checks['keyboardPattern'] = keyboardPatternResult.passed\n if (!keyboardPatternResult.passed && keyboardPatternResult.message) {\n suggestions.push(keyboardPatternResult.message)\n }\n\n // Calculate score based on passed checks\n const passedChecks: number = Object.values(checks).filter(Boolean).length\n const totalChecks: number = Object.keys(checks).length\n /* v8 ignore next */\n const ratio: number = totalChecks > 0 ? passedChecks / totalChecks : 0\n const score: StrengthScore = Math.min(4, Math.floor(ratio * 5)) as StrengthScore\n\n const firstSuggestion: string | undefined = suggestions.length > 0 ? suggestions[0] : undefined\n\n return {\n valid: Object.values(checks).every(Boolean),\n score,\n /* v8 ignore next */\n strength: STRENGTH_LABELS[score] ?? 'very-weak',\n feedback: {\n ...(firstSuggestion !== undefined && { warning: firstSuggestion }),\n suggestions,\n },\n checks,\n }\n}\n","import type { MessageCode, MessageParams, ValidatorOptions } from './types'\n\n/**\n * Built-in English templates for every {@link MessageCode}.\n *\n * The default rendering of a failed `ValidatorCheck.message` comes from this\n * map. Strings are stable across patch and minor releases — consumers can\n * still use them as translation keys with the legacy lookup-table pattern.\n * Prefer the `messages` / `formatMessage` options on {@link ValidatorOptions}.\n *\n * Placeholders use `{name}` syntax and are substituted by {@link formatTemplate}.\n */\nexport const DEFAULT_TEMPLATES: Readonly<Record<MessageCode, string>> = {\n 'length.tooShort': 'Password must be at least {minLength} characters',\n 'length.tooLong': 'Password must be at most {maxLength} characters',\n 'characterTypes.missing': 'Password must contain at least one {missing}',\n 'repetition.tooMany': 'Password contains too many repeated characters (max {maxRepeatedChars})',\n 'sequential.found': 'Password contains sequential characters (e.g., abc, 123)',\n 'keyboardPattern.found': 'Password contains common keyboard patterns',\n 'commonPassword.found': 'Password is too common. Please choose a more unique password.',\n 'personalInfo.found': 'Password contains personal information',\n} as const\n\nconst PLACEHOLDER_PATTERN: RegExp = /\\{(\\w+)\\}/g\n\n/**\n * Substitute `{name}` placeholders in `template` with values from `params`.\n *\n * Unknown placeholders are left intact (visible in the output) so missing\n * data surfaces as a noticeable bug rather than a silent omission. Values\n * are coerced to strings via the `String` constructor.\n *\n * @example\n * formatTemplate('Min {n} chars', { n: 8 }) // → \"Min 8 chars\"\n * formatTemplate('Need {a} and {b}', { a: 'X' }) // → \"Need X and {b}\"\n */\nexport function formatTemplate(template: string, params: MessageParams): string {\n return template.replace(PLACEHOLDER_PATTERN, (match, key: string): string => {\n const value: string | number | undefined = params[key]\n return value === undefined ? match : String(value)\n })\n}\n\n/**\n * Render a message via the fallback chain:\n * 1. `options.formatMessage(code, params, defaultMessage)` if provided\n * 2. `formatTemplate(options.messages[code], params)` if that override is provided\n * 3. `formatTemplate(DEFAULT_TEMPLATES[code], params)` (built-in English)\n *\n * Used by every validator's failure branch. Validators stay declarative —\n * they describe *what* failed (`code` + `params`) and delegate rendering.\n *\n * If `options.formatMessage` throws, this function swallows the error and\n * returns the default English rendering instead. Validators in this library\n * promise never to throw (see `Validator` in `./types`), and that promise\n * holds even when consumer-provided formatters misbehave.\n */\nexport function resolveMessage(\n code: MessageCode,\n params: MessageParams,\n options: ValidatorOptions\n): string {\n const defaultMessage: string = formatTemplate(DEFAULT_TEMPLATES[code], params)\n\n if (options.formatMessage) {\n try {\n return options.formatMessage(code, params, defaultMessage)\n } catch {\n return defaultMessage\n }\n }\n\n const override: string | undefined = options.messages?.[code]\n if (override !== undefined) {\n return formatTemplate(override, params)\n }\n\n return defaultMessage\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Validates password length against minimum and maximum requirements\n *\n * @param password - Password to validate\n * @param options - Validation options containing minLength and maxLength\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateLength } from '@sentinel-password/core'\n *\n * // Default: min 8, max 128 characters\n * validateLength('short') // { passed: false, message: \"Password must be at least 8 characters\" }\n * validateLength('longenough') // { passed: true }\n *\n * // Custom length requirements\n * validateLength('password', { minLength: 12 }) // { passed: false, message: \"...\" }\n * validateLength('verylongpassword', { minLength: 12, maxLength: 20 }) // { passed: true }\n * ```\n *\n * @remarks\n * Default minimum length is 8 characters (OWASP recommendation).\n * Default maximum length is 128 characters (prevents DoS attacks).\n */\nexport const validateLength: Validator = (password, options = {}) => {\n const { minLength = 8, maxLength = 128 }: Partial<{ minLength: number; maxLength: number }> =\n options\n\n const length: number = password.length\n\n if (length < minLength) {\n const params: MessageParams = { minLength }\n return {\n passed: false,\n code: 'length.tooShort',\n params,\n message: resolveMessage('length.tooShort', params, options),\n }\n }\n\n if (length > maxLength) {\n const params: MessageParams = { maxLength }\n return {\n passed: false,\n code: 'length.tooLong',\n params,\n message: resolveMessage('length.tooLong', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Check if password contains at least one uppercase letter (A-Z)\n *\n * @param password - Password to check\n * @returns True if password contains at least one uppercase letter\n *\n * @example\n * ```typescript\n * hasUppercase('password') // false\n * hasUppercase('Password') // true\n * hasUppercase('PASSWORD') // true\n * ```\n */\nexport const hasUppercase = (password: string): boolean => /[A-Z]/.test(password)\n\n/**\n * Check if password contains at least one lowercase letter (a-z)\n *\n * @param password - Password to check\n * @returns True if password contains at least one lowercase letter\n *\n * @example\n * ```typescript\n * hasLowercase('PASSWORD') // false\n * hasLowercase('Password') // true\n * hasLowercase('password') // true\n * ```\n */\nexport const hasLowercase = (password: string): boolean => /[a-z]/.test(password)\n\n/**\n * Check if password contains at least one digit (0-9)\n *\n * @param password - Password to check\n * @returns True if password contains at least one digit\n *\n * @example\n * ```typescript\n * hasDigit('password') // false\n * hasDigit('password1') // true\n * hasDigit('123') // true\n * ```\n */\nexport const hasDigit = (password: string): boolean => /\\d/.test(password)\n\n/**\n * Check if password contains at least one symbol/special character\n *\n * @param password - Password to check\n * @returns True if password contains at least one special character\n *\n * @example\n * ```typescript\n * hasSymbol('password') // false\n * hasSymbol('password!') // true\n * hasSymbol('p@ssw0rd') // true\n * ```\n *\n * @remarks\n * Accepted symbols: ! @ # $ % ^ & * ( ) _ + - = [ ] { } ; ' : \" \\ | , . < > / ?\n */\nexport const hasSymbol = (password: string): boolean =>\n /[!@#$%^&*()_+\\-=[\\]{};':\"\\\\|,.<>/?]/.test(password)\n\n/**\n * Validates character type requirements (uppercase, lowercase, digits, symbols)\n *\n * @param password - Password to validate\n * @param options - Validation options containing character type requirements\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateCharacterTypes } from '@sentinel-password/core'\n *\n * // No requirements by default\n * validateCharacterTypes('password') // { passed: true }\n *\n * // Require uppercase\n * validateCharacterTypes('password', { requireUppercase: true })\n * // { passed: false, message: \"Password must contain at least one uppercase letter\" }\n *\n * validateCharacterTypes('Password', { requireUppercase: true }) // { passed: true }\n *\n * // Require multiple types\n * validateCharacterTypes('password', {\n * requireUppercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * })\n * // { passed: false, message: \"Password must contain at least one uppercase letter, digit, symbol\" }\n *\n * validateCharacterTypes('Password1!', {\n * requireUppercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * }) // { passed: true }\n * ```\n *\n * @remarks\n * By default, no character types are required. Enable specific requirements via options.\n */\nexport const validateCharacterTypes: Validator = (password, options = {}) => {\n const {\n requireUppercase = false,\n requireLowercase = false,\n requireDigit = false,\n requireSymbol = false,\n }: Partial<{\n requireUppercase: boolean\n requireLowercase: boolean\n requireDigit: boolean\n requireSymbol: boolean\n }> = options\n\n const missing: string[] = []\n const missingTypes: string[] = []\n\n if (requireUppercase && !hasUppercase(password)) {\n missing.push('uppercase letter')\n missingTypes.push('uppercase')\n }\n\n if (requireLowercase && !hasLowercase(password)) {\n missing.push('lowercase letter')\n missingTypes.push('lowercase')\n }\n\n if (requireDigit && !hasDigit(password)) {\n missing.push('digit')\n missingTypes.push('digit')\n }\n\n if (requireSymbol && !hasSymbol(password)) {\n missing.push('symbol')\n missingTypes.push('symbol')\n }\n\n if (missing.length > 0) {\n const params: MessageParams = {\n missing: missing.join(', '),\n missingTypes: missingTypes.join(','),\n }\n return {\n passed: false,\n code: 'characterTypes.missing',\n params,\n message: resolveMessage('characterTypes.missing', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Validates that password doesn't contain excessive repeated characters\n *\n * Uses a single-pass algorithm to detect consecutive repeated characters.\n * Helps prevent weak passwords like \"aaaa1111\" or \"passwordddd\".\n *\n * @param password - Password to validate\n * @param options - Validation options containing maxRepeatedChars\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateRepetition } from '@sentinel-password/core'\n *\n * // Default: max 3 repeated characters\n * validateRepetition('password') // { passed: true }\n * validateRepetition('passsword') // { passed: true } (3 s's)\n * validateRepetition('passssword') // { passed: false } (4 s's)\n *\n * // Custom limit\n * validateRepetition('passssword', { maxRepeatedChars: 5 }) // { passed: true }\n * validateRepetition('aaa') // { passed: true }\n * validateRepetition('aaaa') // { passed: false, message: \"Password contains too many repeated characters (max 3)\" }\n * ```\n *\n * @remarks\n * Default maximum is 3 consecutive repeated characters.\n * Only checks for consecutive repetition, not overall character frequency.\n */\nexport const validateRepetition: Validator = (password, options = {}) => {\n const { maxRepeatedChars = 3 }: Partial<{ maxRepeatedChars: number }> = options\n\n if (password.length === 0) {\n return { passed: true }\n }\n\n let currentChar: string = password.charAt(0)\n let count: number = 1\n\n for (let i: number = 1; i < password.length; i++) {\n const char: string = password.charAt(i)\n if (char === currentChar) {\n count++\n if (count > maxRepeatedChars) {\n const params: MessageParams = { maxRepeatedChars }\n return {\n passed: false,\n code: 'repetition.tooMany',\n params,\n message: resolveMessage('repetition.tooMany', params, options),\n }\n }\n } else {\n currentChar = char\n count = 1\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Detects sequential character patterns (ascending or descending)\n * Checks for sequences of 3 or more consecutive characters\n *\n * @param str - String to check for sequences\n * @returns true if sequential pattern found, false otherwise\n */\nconst hasSequentialPattern = (str: string): boolean => {\n const minSequenceLength: number = 3\n\n for (let i: number = 0; i <= str.length - minSequenceLength; i++) {\n const charCode1: number = str.charCodeAt(i)\n const charCode2: number = str.charCodeAt(i + 1)\n const charCode3: number = str.charCodeAt(i + 2)\n\n // Check ascending sequence (e.g., abc, 123)\n if (charCode2 === charCode1 + 1 && charCode3 === charCode2 + 1) {\n return true\n }\n\n // Check descending sequence (e.g., cba, 321)\n if (charCode2 === charCode1 - 1 && charCode3 === charCode2 - 1) {\n return true\n }\n }\n\n return false\n}\n\n/**\n * Validates that password doesn't contain sequential character patterns\n *\n * Detects sequences like: abc, ABC, 123, 321, xyz, etc.\n * Uses character code comparison for efficient detection.\n * Helps prevent predictable passwords with keyboard sequences.\n *\n * @param password - Password to validate\n * @param options - Validation options containing checkSequential flag\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateSequential } from '@sentinel-password/core'\n *\n * // Detects ascending sequences\n * validateSequential('password') // { passed: true }\n * validateSequential('abc123') // { passed: false } (contains \"abc\" and \"123\")\n * validateSequential('xyz') // { passed: false } (contains \"xyz\")\n *\n * // Detects descending sequences\n * validateSequential('cba321') // { passed: false } (contains \"cba\" and \"321\")\n *\n * // Disable check\n * validateSequential('abc123', { checkSequential: false }) // { passed: true }\n * ```\n *\n * @remarks\n * Enabled by default. Checks for 3 or more consecutive characters in sequence.\n * Case-sensitive: detects both \"abc\" and \"ABC\" as separate patterns.\n *\n * **Overlap with `validateKeyboardPattern`:** The numeric runs `123`, `456`,\n * `789` (and their reverses) are also matched by the keyboard-pattern\n * validator's numeric-keypad list. To allow simple numeric runs in a\n * password you must set BOTH `checkSequential: false` AND\n * `checkKeyboardPatterns: false` — disabling either alone will not let\n * `password123` through. The two checks are deliberately independent\n * defences (code-point runs vs. keyboard-locality runs).\n */\nexport const validateSequential: Validator = (password, options = {}) => {\n const { checkSequential = true }: Partial<{ checkSequential: boolean }> = options\n\n if (!checkSequential) {\n return { passed: true }\n }\n\n if (hasSequentialPattern(password)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'sequential.found',\n params,\n message: resolveMessage('sequential.found', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, ValidatorCheck, ValidatorOptions } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Common keyboard patterns to detect across multiple layouts\n * Supports: QWERTY, AZERTY, QWERTZ, Dvorak, Colemak, and Cyrillic\n */\nconst KEYBOARD_PATTERNS: readonly string[] = [\n // === QWERTY (English, US, UK) ===\n // Full rows\n 'qwertyuiop',\n 'asdfghjkl',\n 'zxcvbnm',\n // Common typing patterns (adjacent keys)\n 'qwert',\n 'werty',\n 'asdfg',\n 'sdfgh',\n 'zxcvb',\n 'xcvbn',\n // Short sequences (3+ chars)\n 'qwe',\n 'asd',\n 'zxc',\n 'rty',\n 'fgh',\n 'cvb',\n 'poi',\n 'lkj',\n 'mnb',\n // Columns (top to bottom)\n '1qaz',\n '2wsx',\n '3edc',\n '4rfv',\n '5tgb',\n '6yhn',\n '7ujm',\n '8ik',\n '9ol',\n '0p',\n // Diagonals\n 'qaz',\n 'wsx',\n 'edc',\n 'zaq',\n 'xsw',\n 'cde',\n\n // === AZERTY (French, Belgian) ===\n // Full rows\n 'azertyuiop',\n 'qsdfghjklm',\n 'wxcvbn',\n // Common patterns\n 'azert',\n 'zerty',\n 'qsdfg',\n 'sdfgh',\n 'wxcvb',\n // Short sequences\n 'aze',\n 'qsd',\n 'wxc',\n\n // === QWERTZ (German, Central European) ===\n // Full rows\n 'qwertzuiop',\n 'yxcvbnm',\n // Common patterns\n 'qwertz',\n 'yxcvb',\n // Short sequences\n 'yxc',\n // Note: Patterns 'qwe', 'asd', 'asdfg', and 'asdfghjkl' are shared with QWERTY and listed above for efficiency.\n\n // === Dvorak ===\n // Full rows\n 'pyfgcrl',\n 'aoeuidhtns',\n 'qjkxbmwvz',\n // Common patterns\n 'aoeu',\n 'htns',\n 'qjkx',\n\n // === Colemak ===\n // Full rows\n 'qwfpgjluy',\n 'arstdhneio',\n 'zxcvbkm',\n // Common patterns\n 'arst',\n 'dhne',\n 'zxcv',\n\n // === Cyrillic (ЙЦУКЕН - Russian) ===\n // Full rows (Cyrillic characters)\n 'йцукенгшщзхъ',\n 'фывапролджэ',\n 'ячсмитьбю',\n // Common patterns\n 'йцукен',\n 'фывап',\n 'ячсми',\n 'цукен',\n\n // === Numeric patterns (universal) ===\n // Number row\n '1234567890',\n '0987654321',\n // Numeric keypad (rows)\n '789',\n '456',\n '123',\n // Numeric keypad (columns)\n '741',\n '852',\n '963',\n '7410',\n '8520',\n '9630',\n] as const\n\n/**\n * Validates that password does not contain common keyboard patterns\n *\n * Detects patterns across multiple keyboard layouts:\n * - QWERTY (English, US, UK): qwerty, asdfgh, 1qaz2wsx\n * - AZERTY (French, Belgian): azerty, qsdfg\n * - QWERTZ (German, Central European): qwertz, yxcvb\n * - Dvorak (Alternative English): aoeu, htns\n * - Colemak (Alternative English): arst, dhne\n * - Cyrillic (Russian ЙЦУКЕН): йцукен, фывап\n * - Universal numeric: 123, 789, numeric keypad patterns\n * - Both forward and reverse patterns\n *\n * @param password - The password to validate\n * @param options - Validation options\n * @returns Validation result\n *\n * @example\n * ```typescript\n * validateKeyboardPattern('qwerty123') // { passed: false, message: '...' }\n * validateKeyboardPattern('azerty456') // { passed: false, message: '...' } (AZERTY)\n * validateKeyboardPattern('йцукен') // { passed: false, message: '...' } (Cyrillic)\n * validateKeyboardPattern('MyP@ssw0rd') // { passed: true }\n * validateKeyboardPattern('asdfgh', { checkKeyboardPatterns: false }) // { passed: true }\n * ```\n *\n * @remarks\n * **Overlap with `validateSequential`:** The numeric-keypad rows `123`,\n * `456`, `789` (and their reverses) are also caught by the sequential\n * validator's `charCodeAt`-consecutive check. To allow simple numeric\n * runs in a password you must set BOTH `checkKeyboardPatterns: false`\n * AND `checkSequential: false` — disabling either alone will not let\n * `password123` through. The two checks are deliberately independent\n * defences (keyboard-locality runs vs. code-point runs).\n */\nexport function validateKeyboardPattern(\n password: string,\n options: ValidatorOptions = {}\n): ValidatorCheck {\n const { checkKeyboardPatterns = true }: Partial<{ checkKeyboardPatterns: boolean }> = options\n\n if (!checkKeyboardPatterns) {\n return { passed: true }\n }\n\n const lowercase: string = password.toLowerCase()\n const emptyParams: MessageParams = {}\n\n // Check for keyboard patterns (forward and reverse)\n for (const pattern of KEYBOARD_PATTERNS) {\n // Check forward pattern\n if (lowercase.includes(pattern)) {\n return {\n passed: false,\n code: 'keyboardPattern.found',\n params: emptyParams,\n message: resolveMessage('keyboardPattern.found', emptyParams, options),\n }\n }\n\n // Check reverse pattern\n const reversed: string = pattern.split('').reverse().join('')\n if (lowercase.includes(reversed)) {\n return {\n passed: false,\n code: 'keyboardPattern.found',\n params: emptyParams,\n message: resolveMessage('keyboardPattern.found', emptyParams, options),\n }\n }\n }\n\n return { passed: true }\n}\n","import type { MessageParams, Validator, ValidatorOptions } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Bloom filter for common passwords\n *\n * Source: SecLists https://github.com/danielmiessler/SecLists\n * File: Passwords/Common-Credentials/10k-most-common.txt (top 1,000)\n * Local: packages/core/data/common-passwords.txt\n *\n * Regenerate with: pnpm --filter @sentinel-password/core generate:bloom\n */\n\n// --- BEGIN GENERATED BLOOM FILTER ---\n// Generated from: packages/core/data/common-passwords.txt\n// Passwords: 1000 | Bloom size: 12000 bits | Hash functions: 7\nconst BLOOM_SIZE: number = 12000\nconst BLOOM_HASH_COUNT: number = 7\n\nconst BLOOM_BUCKETS: Int32Array = new Int32Array([\n -1274142440, -1983615455, -2110842727, -1440077142, 1782964852, 956870738, 48445478, 290080800,\n -1961772502, -1869995330, 787111091, 140549329, -1508237141, -800970204, -1987272632, -1439602637,\n 1837731885, 240419586, 2143496680, 547359246, -435494235, -1549227926, 151650466, -1742731903,\n -1535043550, -1607849886, -464376123, 1858481975, -1339118942, 410034491, -1397081975, 1217017604,\n -1934873593, -1769402149, -1107107031, -1098029453, 4380674, -393017311, -1465113568, 183557329,\n 1713935653, -99073116, 179529869, -1372675933, 78050105, 313074313, -1696462848, 404392594,\n -252409893, -1320442777, -1474295681, -1360120173, -743767380, 135925863, -1462755274, 562596610,\n -483730736, -2002210214, 1803176, 448811114, -1408849403, -735411494, -1872045334, 914538658,\n -374794234, 710617115, -1909840320, -2119695701, -1979682099, -1671944535, -1872492857, 570434164,\n -1334628885, 438020137, -1087528694, 464528967, -359521705, -1407023569, -1759335432, -1181701078,\n -1959746004, -1910872796, 1771191076, -1699312926, -1837137785, 132360706, -1002778102,\n 1384163571, 1786784659, -1354832568, -1364547569, -269830616, -1970133308, 848887818, -827577818,\n -1103500629, 2071996096, 1760287282, 1768617098, -1845466197, -1877054343, -223851966,\n -1037686738, 581441802, -761237365, -1282923217, 1328736770, 557363947, 369254184, -1708971466,\n -874268952, -2010768709, -1735196024, -1023761881, 772024867, -328714721, -125771638, 1644593802,\n 1762927155, -1509621238, 671919842, -1102294492, -2010455282, 302591302, -2136446493, -1986908626,\n -2018566016, 671347107, 842040354, 243352234, -534627656, -2011035123, -350834429, -844096647,\n -1598189902, -1833872134, -2021613470, -1968994020, -1297357224, 1210222837, -399712104,\n 906129314, -1430115578, -2113618941, -906361552, 551676011, 2819256, -1710063476, 1099626796,\n 1026172967, 640491579, -1440579096, -1887270648, -1710185214, 1721813836, 984101107, 1944258615,\n 724137993, -947645533, 1913070606, 44829860, 101219306, 1011556358, -1432173338, -482828238,\n -2103243204, 447353024, -329610536, 1398671502, 1623196423, 348309551, -211221782, 1980906030,\n 778830798, 968125066, -1458011638, -1337934774, 671089894, 52600876, -120418625, -1163255178,\n 680202936, -1597565152, 36835260, 1845733400, 153924331, 40509970, 411221088, 2114095983,\n 171043813, 1488604610, 172814850, -1702382524, 1502872105, 747635458, -501052624, 411301928,\n 731823798, -1333221971, -398552509, 134621864, -1872819037, 1822818465, -1893684382, -1316869470,\n -931632595, -1951260093, 598498019, -1290239178, -989323246, 573220082, 675809232, 169874120,\n -559880256, -910750974, 194548323, 736774690, -719202768, 1814594698, -1155290520, 237063072,\n -488083322, -924144083, 36385696, 715956226, -1568572256, 635709488, 1336062594, 320907784,\n 1124501765, -1406433280, -1963307926, -391509877, 1063369670, -1566479450, -1472058776,\n -1985331186, 1377869964, 942987938, 1109713448, 831578699, 853962256, 92835849, 2035317046,\n -2001761759, 216178811, 463827086, 1675819625, -1301764214, -234217277, -1459402461, 579365994,\n -1878875352, -1973245719, -1598478333, 1611084451, 1659513348, -1539122450, 837944629, 154749747,\n -500636954, 1746899746, 714271018, -1150679461, -1538612630, 1814069472, -492197206, -192202641,\n -756936190, -939252957, -1240063770, 1279410916, -1302054220, 1410991882, 1000401066, -198048178,\n -1419228998, -1742550398, -1991360862, -2111688732, -1700640206, 2061912618, -2103274201,\n -288731098, 578232, 53128594, 1755908256, 1116379816, 797411910, -1589060869, -1072342375,\n 845812237, 1749465810, 872549054, -1498077050, -1766137746, -376831803, -2084952921, 1202203194,\n 1216389770, -1104576382, 818061341, 942317755, -1578565146, 2083914250, -1012266815, 579371193,\n -461075653, 710981226, 1578192039, 681770739, -376523066, 1244735807, -490842006, -781193759,\n 556277799, 1984167471, -2000977760, -1265456598, 151665664, -1842697570, 579340290, 1082298021,\n 245211512, -1867881849, -1341630964, -90545664, 1806344898, 47088170, 992486842, -1558631407,\n -1449508691, -2006144829, 547374756, 912919200, 708120146, 616106151, 1172449414, 2124751394,\n -2073427929, 1009261122, 1210336770, -1973924734, 144458353, -1155209078, -1962399613, 897229836,\n 12357639, -1299101013, 312943930, 708920290, -1310023134, 639040320, -821838839, -2014506238,\n -274610648, -1442508704, 177432331, 1721041571, 414065321, -1272924654, 756208161, 1772773934,\n -1484380630, 1101004848, -1054523758, -2116504793, 1118667297, 86058657, 975856680, 749906058,\n 1937961650, 620890537, 918765730, -1366546774,\n])\n// --- END GENERATED BLOOM FILTER ---\n\n/**\n * Hash function for bloom filter\n */\nfunction hashString(str: string, seed: number): number {\n let hash: number = seed\n\n for (let i: number = 0; i < str.length; i++) {\n const char: number = str.charCodeAt(i)\n hash = (hash << 5) - hash + char\n hash = hash | 0 // Convert to 32-bit integer\n }\n\n return Math.abs(hash)\n}\n\n/**\n * Get multiple hash positions for a password\n */\nfunction getHashes(password: string): number[] {\n const hashes: number[] = []\n const hash1: number = hashString(password, 0)\n const hash2: number = hashString(password, 1)\n\n for (let i: number = 0; i < BLOOM_HASH_COUNT; i++) {\n const hash: number = (hash1 + i * hash2) >>> 0\n hashes.push(hash % BLOOM_SIZE)\n }\n\n return hashes\n}\n\n/**\n * Check if password might be in the common password list\n * Note: Bloom filters can have false positives (~0.84%) but never false negatives\n */\nfunction mightBeCommon(password: string): boolean {\n const hashes: number[] = getHashes(password.toLowerCase())\n\n for (const hash of hashes) {\n const arrayIndex: number = Math.floor(hash / 32)\n const bitIndex: number = hash % 32\n\n // Bounds check for TypeScript strict mode\n const bucket: number | undefined = BLOOM_BUCKETS[arrayIndex]\n if (bucket === undefined || (bucket & (1 << bitIndex)) === 0) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Validates that a password is not in the common password list\n *\n * Uses a Bloom filter to efficiently check against the top 1,000 most common passwords.\n * Case-insensitive matching prevents simple case variations of common passwords.\n *\n * @param password - Password to validate\n * @param options - Validation options containing checkCommonPasswords flag\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateCommonPassword } from '@sentinel-password/core'\n *\n * // Detects common passwords\n * validateCommonPassword('password')\n * // { passed: false, message: \"Password is too common. Please choose a more unique password.\" }\n *\n * validateCommonPassword('123456')\n * // { passed: false }\n *\n * // Case-insensitive\n * validateCommonPassword('PASSWORD')\n * // { passed: false }\n *\n * // Unique passwords pass\n * validateCommonPassword('MyUn1qu3P@ssw0rd!')\n * // { passed: true }\n *\n * // Disable check\n * validateCommonPassword('password', { checkCommonPasswords: false })\n * // { passed: true }\n * ```\n *\n * @remarks\n * Checks against top 1,000 most common passwords from SecLists.\n * Uses Bloom filter for space efficiency (~1.5KB vs ~8KB for raw array).\n * False positive rate: ~0.84% (may rarely flag uncommon passwords).\n * Enabled by default for security.\n */\nexport const validateCommonPassword: Validator = (\n password: string,\n options: ValidatorOptions = {}\n) => {\n const { checkCommonPasswords = true }: { checkCommonPasswords?: boolean } = options\n\n if (!checkCommonPasswords || password.length === 0) {\n return { passed: true }\n }\n\n // Case-insensitive check using bloom filter\n if (mightBeCommon(password)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'commonPassword.found',\n params,\n message: resolveMessage('commonPassword.found', params, options),\n }\n }\n\n return { passed: true }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Extracts username from email address\n * @param email - Email address\n * @returns Username part before @ symbol\n */\nconst extractUsername = (email: string): string => {\n const atIndex: number = email.indexOf('@')\n /* v8 ignore next */\n return atIndex > 0 ? email.substring(0, atIndex) : email\n}\n\n/**\n * Normalizes personal info strings for comparison\n * Converts to lowercase and extracts username from emails\n *\n * @param info - Personal information string\n * @returns Normalized string for comparison\n */\nconst normalizePersonalInfo = (info: string): string => {\n const normalized: string = info.toLowerCase().trim()\n // Extract username from email if it looks like an email\n return normalized.includes('@') ? extractUsername(normalized) : normalized\n}\n\n/**\n * Validates that password doesn't contain personal information\n *\n * Checks against provided personal info (username, email, name, etc.)\n * Uses case-insensitive comparison and extracts usernames from email addresses.\n * Ignores very short strings (< 3 characters) to avoid false positives.\n *\n * @param password - Password to validate\n * @param options - Validation options containing personalInfo array\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validatePersonalInfo } from '@sentinel-password/core'\n *\n * // No personal info by default\n * validatePersonalInfo('password') // { passed: true }\n *\n * // Detects username in password\n * validatePersonalInfo('johnpassword', { personalInfo: ['john'] })\n * // { passed: false, message: \"Password contains personal information\" }\n *\n * // Emails are reduced to the *entire local part* (everything before `@`),\n * // matched as a literal substring — not split into name fragments.\n * validatePersonalInfo('john.doe123', { personalInfo: ['john.doe@example.com'] })\n * // { passed: false } (matches the full local part \"john.doe\")\n *\n * validatePersonalInfo('john123', { personalInfo: ['john.doe@example.com'] })\n * // { passed: true } (the local part is \"john.doe\", not \"john\" — no match)\n *\n * // To reject passwords containing just \"john\", pass it explicitly:\n * validatePersonalInfo('john123', { personalInfo: ['john', 'john.doe@example.com'] })\n * // { passed: false } (matches the literal \"john\")\n *\n * // Case-insensitive\n * validatePersonalInfo('JOHN123', { personalInfo: ['john'] })\n * // { passed: false }\n *\n * // Ignores short strings\n * validatePersonalInfo('password', { personalInfo: ['pw'] }) // { passed: true } (too short)\n *\n * // Multiple personal info items\n * validatePersonalInfo('secretpass', {\n * personalInfo: ['john', 'doe', 'john.doe@example.com']\n * }) // { passed: true }\n * ```\n *\n * @remarks\n * Best practice: Provide username, email, first name, last name, and company name.\n * Strings shorter than 3 characters are ignored to prevent false positives.\n */\nexport const validatePersonalInfo: Validator = (password, options = {}) => {\n const { personalInfo = [] }: Partial<{ personalInfo: string[] }> = options\n\n if (personalInfo.length === 0 || password.length === 0) {\n return { passed: true }\n }\n\n const lowerPassword: string = password.toLowerCase()\n\n for (const info of personalInfo) {\n const normalized: string = normalizePersonalInfo(info)\n\n // Skip very short strings to avoid false positives\n if (normalized.length < 3) {\n continue\n }\n\n // Check if password contains this personal information\n if (lowerPassword.includes(normalized)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'personalInfo.found',\n params,\n message: resolveMessage('personalInfo.found', params, options),\n }\n }\n }\n\n return {\n passed: true,\n }\n}\n"],"mappings":"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","names":[]}
{"version":3,"sources":["../src/index.ts","../src/messages.ts","../src/validators/length.ts","../src/validators/character-types.ts","../src/validators/repetition.ts","../src/validators/sequential.ts","../src/validators/keyboard-pattern.ts","../src/validators/common-password.ts","../src/validators/personal-info.ts"],"sourcesContent":["/**\n * @sentinel-password/core\n * Zero-dependency TypeScript password validation library\n */\n\nexport type {\n StrengthScore,\n StrengthLabel,\n ValidationResult,\n ValidatorOptions,\n ValidatorCheck,\n Validator,\n CheckId,\n MessageCode,\n MessageParams,\n MessageFormatter,\n} from './types'\n\nexport { DEFAULT_TEMPLATES } from './messages'\n\nexport { validateLength } from './validators/length'\nexport {\n hasUppercase,\n hasLowercase,\n hasDigit,\n hasSymbol,\n validateCharacterTypes,\n} from './validators/character-types'\nexport { validateRepetition } from './validators/repetition'\nexport { validateSequential } from './validators/sequential'\nexport { validateKeyboardPattern } from './validators/keyboard-pattern'\nexport { validateCommonPassword } from './validators/common-password'\nexport { validatePersonalInfo } from './validators/personal-info'\n\nimport type {\n ValidationResult,\n ValidatorOptions,\n StrengthScore,\n StrengthLabel,\n ValidatorCheck,\n CheckId,\n} from './types'\nimport { validateLength } from './validators/length'\nimport { validateCharacterTypes } from './validators/character-types'\nimport { validateRepetition } from './validators/repetition'\nimport { validateSequential } from './validators/sequential'\nimport { validateKeyboardPattern } from './validators/keyboard-pattern'\nimport { validateCommonPassword } from './validators/common-password'\nimport { validatePersonalInfo } from './validators/personal-info'\n\nconst STRENGTH_LABELS: readonly StrengthLabel[] = [\n 'very-weak',\n 'weak',\n 'medium',\n 'strong',\n 'very-strong',\n] as const\n\n/**\n * Validate a password with comprehensive security checks\n *\n * Performs multiple validation checks including length, character types, repetition,\n * sequential patterns, keyboard patterns, common passwords, and personal information.\n * Returns detailed feedback with strength score and actionable suggestions.\n *\n * @param password - The password string to validate\n * @param options - Optional validation configuration\n * @returns Validation result with score, strength label, feedback, and individual check results\n *\n * @example\n * **Basic usage (zero-config)**\n * ```typescript\n * import { validatePassword } from '@sentinel-password/core'\n *\n * const result = validatePassword('MySecure!Pass_w0rd')\n * console.log(result.valid) // true\n * console.log(result.score) // 4 (0-4 scale)\n * console.log(result.strength) // 'very-strong'\n * console.log(result.feedback.warning) // undefined (no issues)\n * console.log(result.feedback.suggestions) // []\n * ```\n *\n * @example\n * **With a known-common password**\n * ```typescript\n * const result = validatePassword('password')\n * console.log(result.valid) // false (commonPassword check rejected it)\n * console.log(result.score) // 4\n * console.log(result.strength) // 'very-strong'\n * console.log(result.feedback.warning)\n * // \"Password is too common. Please choose a more unique password.\"\n * console.log(result.feedback.suggestions)\n * // [\"Password is too common. Please choose a more unique password.\"]\n * console.log(result.checks)\n * // { length: true, characterTypes: true, repetition: true, sequential: true,\n * // keyboardPattern: true, commonPassword: false, personalInfo: true }\n * // ↑ 6 of 7 checks pass, so score is 4 (\"very-strong\") even though valid is false.\n * // Always use `valid` (or `result.checks`) for acceptance decisions, not `strength`.\n * ```\n *\n * @example\n * **Custom length requirements**\n * ```typescript\n * const result = validatePassword('MyP@ss', {\n * minLength: 12,\n * maxLength: 64\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password must be at least 12 characters\"\n * ```\n *\n * @example\n * **Require specific character types**\n * ```typescript\n * const result = validatePassword('password123', {\n * requireUppercase: true,\n * requireLowercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password must contain at least one uppercase letter, symbol\"\n * ```\n *\n * @example\n * **Prevent personal information**\n * ```typescript\n * const result = validatePassword('john1234!', {\n * personalInfo: ['john', 'john.doe@example.com', 'Doe']\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password contains personal information\"\n * ```\n *\n * @example\n * **Disable specific checks**\n * ```typescript\n * const result = validatePassword('qwerty123', {\n * checkKeyboardPatterns: false, // Allow keyboard patterns\n * checkSequential: false, // Allow sequential chars\n * checkCommonPasswords: false // Allow common passwords\n * })\n * // More permissive validation\n * ```\n *\n * @example\n * **Comprehensive configuration**\n * ```typescript\n * const result = validatePassword('MyP@ssw0rd2024!', {\n * minLength: 12,\n * maxLength: 128,\n * requireUppercase: true,\n * requireLowercase: true,\n * requireDigit: true,\n * requireSymbol: true,\n * maxRepeatedChars: 2,\n * checkSequential: true,\n * checkKeyboardPatterns: true,\n * checkCommonPasswords: true,\n * personalInfo: ['user', 'admin', 'test']\n * })\n * ```\n *\n * @remarks\n * **Default behavior:**\n * - Minimum length: 8 characters\n * - Maximum length: 128 characters\n * - No character type requirements (but recommended to enable)\n * - Max repeated characters: 3\n * - Sequential check: enabled\n * - Keyboard pattern check: enabled\n * - Common password check: enabled (top 1,000 passwords)\n * - Personal info check: disabled (provide personalInfo array to enable)\n *\n * **Scoring:**\n * - `score` = `Math.min(4, Math.floor((passedChecks / 7) * 5))` — purely a\n * passed-check ratio.\n * - `strength` is the human label for that score (`very-weak` … `very-strong`).\n * - Because scoring is ratio-based, a password that fails *only* the\n * common-password (or personal-info, or sequential, etc.) check still passes\n * 6 of 7 checks and lands on `score: 4 / strength: 'very-strong'` while\n * `valid` is `false`. Use `valid` (or inspect `result.checks`) for\n * acceptance decisions; use `strength` for UX cues like progress bars.\n *\n * **Performance:**\n * - All validators run in O(n) time or better\n * - Typical validation: < 1ms for passwords up to 128 characters\n * - Bloom filter for common passwords: O(1) lookup\n *\n * **Security:**\n * - All checks are case-insensitive where applicable\n * - No password data is logged or stored\n * - Runs purely in-process — no network calls, the password never leaves the\n * caller's runtime\n * - This is a *strength* validator, not a password-comparison primitive. The\n * validators use early-return `includes()`/loops and are not constant-time,\n * but timing is not a relevant attack surface here: the patterns being\n * checked (length, character types, common-password list, keyboard layouts)\n * are all public — there's no secret to leak via timing. When you compare\n * a password against a stored hash, use a library like Argon2/bcrypt that\n * provides constant-time verification — that's a separate concern from\n * strength validation.\n */\nexport function validatePassword(\n password: string,\n options: ValidatorOptions = {}\n): ValidationResult {\n const checks: Record<CheckId, boolean> = {\n length: false,\n characterTypes: false,\n repetition: false,\n sequential: false,\n keyboardPattern: false,\n commonPassword: false,\n personalInfo: false,\n }\n const suggestions: string[] = []\n\n // Run all validators\n const lengthResult: ValidatorCheck = validateLength(password, options)\n checks['length'] = lengthResult.passed\n if (!lengthResult.passed && lengthResult.message) {\n suggestions.push(lengthResult.message)\n }\n\n const charTypesResult: ValidatorCheck = validateCharacterTypes(password, options)\n checks['characterTypes'] = charTypesResult.passed\n if (!charTypesResult.passed && charTypesResult.message) {\n suggestions.push(charTypesResult.message)\n }\n\n const repetitionResult: ValidatorCheck = validateRepetition(password, options)\n checks['repetition'] = repetitionResult.passed\n if (!repetitionResult.passed && repetitionResult.message) {\n suggestions.push(repetitionResult.message)\n }\n\n const sequentialResult: ValidatorCheck = validateSequential(password, options)\n checks['sequential'] = sequentialResult.passed\n if (!sequentialResult.passed && sequentialResult.message) {\n suggestions.push(sequentialResult.message)\n }\n\n const commonPasswordResult: ValidatorCheck = validateCommonPassword(password, options)\n checks['commonPassword'] = commonPasswordResult.passed\n if (!commonPasswordResult.passed && commonPasswordResult.message) {\n suggestions.push(commonPasswordResult.message)\n }\n\n const personalInfoResult: ValidatorCheck = validatePersonalInfo(password, options)\n checks['personalInfo'] = personalInfoResult.passed\n if (!personalInfoResult.passed && personalInfoResult.message) {\n suggestions.push(personalInfoResult.message)\n }\n\n const keyboardPatternResult: ValidatorCheck = validateKeyboardPattern(password, options)\n checks['keyboardPattern'] = keyboardPatternResult.passed\n if (!keyboardPatternResult.passed && keyboardPatternResult.message) {\n suggestions.push(keyboardPatternResult.message)\n }\n\n // Calculate score based on passed checks\n const passedChecks: number = Object.values(checks).filter(Boolean).length\n const totalChecks: number = Object.keys(checks).length\n /* v8 ignore next */\n const ratio: number = totalChecks > 0 ? passedChecks / totalChecks : 0\n const score: StrengthScore = Math.min(4, Math.floor(ratio * 5)) as StrengthScore\n\n const firstSuggestion: string | undefined = suggestions.length > 0 ? suggestions[0] : undefined\n\n return {\n valid: Object.values(checks).every(Boolean),\n score,\n /* v8 ignore next */\n strength: STRENGTH_LABELS[score] ?? 'very-weak',\n feedback: {\n ...(firstSuggestion !== undefined && { warning: firstSuggestion }),\n suggestions,\n },\n checks,\n }\n}\n","import type { MessageCode, MessageParams, ValidatorOptions } from './types'\n\n/**\n * Built-in English templates for every {@link MessageCode}.\n *\n * The default rendering of a failed `ValidatorCheck.message` comes from this\n * map. Strings are stable across patch and minor releases — consumers can\n * still use them as translation keys with the legacy lookup-table pattern.\n * Prefer the `messages` / `formatMessage` options on {@link ValidatorOptions}.\n *\n * Placeholders use `{name}` syntax and are substituted by {@link formatTemplate}.\n */\nexport const DEFAULT_TEMPLATES: Readonly<Record<MessageCode, string>> = {\n 'length.tooShort': 'Password must be at least {minLength} characters',\n 'length.tooLong': 'Password must be at most {maxLength} characters',\n 'characterTypes.missing': 'Password must contain at least one {missing}',\n 'repetition.tooMany': 'Password contains too many repeated characters (max {maxRepeatedChars})',\n 'sequential.found': 'Password contains sequential characters (e.g., abc, 123)',\n 'keyboardPattern.found': 'Password contains common keyboard patterns',\n 'commonPassword.found': 'Password is too common. Please choose a more unique password.',\n 'personalInfo.found': 'Password contains personal information',\n} as const\n\nconst PLACEHOLDER_PATTERN: RegExp = /\\{(\\w+)\\}/g\n\n/**\n * Substitute `{name}` placeholders in `template` with values from `params`.\n *\n * Unknown placeholders are left intact (visible in the output) so missing\n * data surfaces as a noticeable bug rather than a silent omission. Values\n * are coerced to strings via the `String` constructor.\n *\n * @example\n * formatTemplate('Min {n} chars', { n: 8 }) // → \"Min 8 chars\"\n * formatTemplate('Need {a} and {b}', { a: 'X' }) // → \"Need X and {b}\"\n */\nexport function formatTemplate(template: string, params: MessageParams): string {\n return template.replace(PLACEHOLDER_PATTERN, (match, key: string): string => {\n const value: string | number | undefined = params[key]\n return value === undefined ? match : String(value)\n })\n}\n\n/**\n * Render a message via the fallback chain:\n * 1. `options.formatMessage(code, params, defaultMessage)` if provided\n * 2. `formatTemplate(options.messages[code], params)` if that override is provided\n * 3. `formatTemplate(DEFAULT_TEMPLATES[code], params)` (built-in English)\n *\n * Used by every validator's failure branch. Validators stay declarative —\n * they describe *what* failed (`code` + `params`) and delegate rendering.\n *\n * If `options.formatMessage` throws, this function swallows the error and\n * returns the default English rendering instead. Validators in this library\n * promise never to throw (see `Validator` in `./types`), and that promise\n * holds even when consumer-provided formatters misbehave.\n */\nexport function resolveMessage(\n code: MessageCode,\n params: MessageParams,\n options: ValidatorOptions\n): string {\n const defaultMessage: string = formatTemplate(DEFAULT_TEMPLATES[code], params)\n\n if (options.formatMessage) {\n try {\n return options.formatMessage(code, params, defaultMessage)\n } catch {\n return defaultMessage\n }\n }\n\n const override: string | undefined = options.messages?.[code]\n if (override !== undefined) {\n return formatTemplate(override, params)\n }\n\n return defaultMessage\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Validates password length against minimum and maximum requirements\n *\n * @param password - Password to validate\n * @param options - Validation options containing minLength and maxLength\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateLength } from '@sentinel-password/core'\n *\n * // Default: min 8, max 128 characters\n * validateLength('short') // { passed: false, message: \"Password must be at least 8 characters\" }\n * validateLength('longenough') // { passed: true }\n *\n * // Custom length requirements\n * validateLength('password', { minLength: 12 }) // { passed: false, message: \"...\" }\n * validateLength('verylongpassword', { minLength: 12, maxLength: 20 }) // { passed: true }\n * ```\n *\n * @remarks\n * Default minimum length is 8 characters (OWASP recommendation).\n * Default maximum length is 128 characters (prevents DoS attacks).\n */\nexport const validateLength: Validator = (password, options = {}) => {\n const { minLength = 8, maxLength = 128 }: Partial<{ minLength: number; maxLength: number }> =\n options\n\n const length: number = password.length\n\n if (length < minLength) {\n const params: MessageParams = { minLength }\n return {\n passed: false,\n code: 'length.tooShort',\n params,\n message: resolveMessage('length.tooShort', params, options),\n }\n }\n\n if (length > maxLength) {\n const params: MessageParams = { maxLength }\n return {\n passed: false,\n code: 'length.tooLong',\n params,\n message: resolveMessage('length.tooLong', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Check if password contains at least one uppercase letter (A-Z)\n *\n * @param password - Password to check\n * @returns True if password contains at least one uppercase letter\n *\n * @example\n * ```typescript\n * hasUppercase('password') // false\n * hasUppercase('Password') // true\n * hasUppercase('PASSWORD') // true\n * ```\n */\nexport const hasUppercase = (password: string): boolean => /[A-Z]/.test(password)\n\n/**\n * Check if password contains at least one lowercase letter (a-z)\n *\n * @param password - Password to check\n * @returns True if password contains at least one lowercase letter\n *\n * @example\n * ```typescript\n * hasLowercase('PASSWORD') // false\n * hasLowercase('Password') // true\n * hasLowercase('password') // true\n * ```\n */\nexport const hasLowercase = (password: string): boolean => /[a-z]/.test(password)\n\n/**\n * Check if password contains at least one digit (0-9)\n *\n * @param password - Password to check\n * @returns True if password contains at least one digit\n *\n * @example\n * ```typescript\n * hasDigit('password') // false\n * hasDigit('password1') // true\n * hasDigit('123') // true\n * ```\n */\nexport const hasDigit = (password: string): boolean => /\\d/.test(password)\n\n/**\n * Check if password contains at least one symbol/special character\n *\n * @param password - Password to check\n * @returns True if password contains at least one special character\n *\n * @example\n * ```typescript\n * hasSymbol('password') // false\n * hasSymbol('password!') // true\n * hasSymbol('p@ssw0rd') // true\n * ```\n *\n * @remarks\n * Accepted symbols: ! @ # $ % ^ & * ( ) _ + - = [ ] { } ; ' : \" \\ | , . < > / ?\n */\nexport const hasSymbol = (password: string): boolean =>\n /[!@#$%^&*()_+\\-=[\\]{};':\"\\\\|,.<>/?]/.test(password)\n\n/**\n * Validates character type requirements (uppercase, lowercase, digits, symbols)\n *\n * @param password - Password to validate\n * @param options - Validation options containing character type requirements\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateCharacterTypes } from '@sentinel-password/core'\n *\n * // No requirements by default\n * validateCharacterTypes('password') // { passed: true }\n *\n * // Require uppercase\n * validateCharacterTypes('password', { requireUppercase: true })\n * // { passed: false, message: \"Password must contain at least one uppercase letter\" }\n *\n * validateCharacterTypes('Password', { requireUppercase: true }) // { passed: true }\n *\n * // Require multiple types\n * validateCharacterTypes('password', {\n * requireUppercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * })\n * // { passed: false, message: \"Password must contain at least one uppercase letter, digit, symbol\" }\n *\n * validateCharacterTypes('Password1!', {\n * requireUppercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * }) // { passed: true }\n * ```\n *\n * @remarks\n * By default, no character types are required. Enable specific requirements via options.\n */\nexport const validateCharacterTypes: Validator = (password, options = {}) => {\n const {\n requireUppercase = false,\n requireLowercase = false,\n requireDigit = false,\n requireSymbol = false,\n }: Partial<{\n requireUppercase: boolean\n requireLowercase: boolean\n requireDigit: boolean\n requireSymbol: boolean\n }> = options\n\n const missing: string[] = []\n const missingTypes: string[] = []\n\n if (requireUppercase && !hasUppercase(password)) {\n missing.push('uppercase letter')\n missingTypes.push('uppercase')\n }\n\n if (requireLowercase && !hasLowercase(password)) {\n missing.push('lowercase letter')\n missingTypes.push('lowercase')\n }\n\n if (requireDigit && !hasDigit(password)) {\n missing.push('digit')\n missingTypes.push('digit')\n }\n\n if (requireSymbol && !hasSymbol(password)) {\n missing.push('symbol')\n missingTypes.push('symbol')\n }\n\n if (missing.length > 0) {\n const params: MessageParams = {\n missing: missing.join(', '),\n missingTypes: missingTypes.join(','),\n }\n return {\n passed: false,\n code: 'characterTypes.missing',\n params,\n message: resolveMessage('characterTypes.missing', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Validates that password doesn't contain excessive repeated characters\n *\n * Uses a single-pass algorithm to detect consecutive repeated characters.\n * Helps prevent weak passwords like \"aaaa1111\" or \"passwordddd\".\n *\n * @param password - Password to validate\n * @param options - Validation options containing maxRepeatedChars\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateRepetition } from '@sentinel-password/core'\n *\n * // Default: max 3 repeated characters\n * validateRepetition('password') // { passed: true }\n * validateRepetition('passsword') // { passed: true } (3 s's)\n * validateRepetition('passssword') // { passed: false } (4 s's)\n *\n * // Custom limit\n * validateRepetition('passssword', { maxRepeatedChars: 5 }) // { passed: true }\n * validateRepetition('aaa') // { passed: true }\n * validateRepetition('aaaa') // { passed: false, message: \"Password contains too many repeated characters (max 3)\" }\n * ```\n *\n * @remarks\n * Default maximum is 3 consecutive repeated characters.\n * Only checks for consecutive repetition, not overall character frequency.\n */\nexport const validateRepetition: Validator = (password, options = {}) => {\n const { maxRepeatedChars = 3 }: Partial<{ maxRepeatedChars: number }> = options\n\n if (password.length === 0) {\n return { passed: true }\n }\n\n let currentChar: string = password.charAt(0)\n let count: number = 1\n\n for (let i: number = 1; i < password.length; i++) {\n const char: string = password.charAt(i)\n if (char === currentChar) {\n count++\n if (count > maxRepeatedChars) {\n const params: MessageParams = { maxRepeatedChars }\n return {\n passed: false,\n code: 'repetition.tooMany',\n params,\n message: resolveMessage('repetition.tooMany', params, options),\n }\n }\n } else {\n currentChar = char\n count = 1\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Detects sequential character patterns (ascending or descending)\n * Checks for sequences of 3 or more consecutive characters\n *\n * @param str - String to check for sequences\n * @returns true if sequential pattern found, false otherwise\n */\nconst hasSequentialPattern = (str: string): boolean => {\n const minSequenceLength: number = 3\n\n for (let i: number = 0; i <= str.length - minSequenceLength; i++) {\n const charCode1: number = str.charCodeAt(i)\n const charCode2: number = str.charCodeAt(i + 1)\n const charCode3: number = str.charCodeAt(i + 2)\n\n // Check ascending sequence (e.g., abc, 123)\n if (charCode2 === charCode1 + 1 && charCode3 === charCode2 + 1) {\n return true\n }\n\n // Check descending sequence (e.g., cba, 321)\n if (charCode2 === charCode1 - 1 && charCode3 === charCode2 - 1) {\n return true\n }\n }\n\n return false\n}\n\n/**\n * Validates that password doesn't contain sequential character patterns\n *\n * Detects sequences like: abc, ABC, 123, 321, xyz, etc.\n * Uses character code comparison for efficient detection.\n * Helps prevent predictable passwords with keyboard sequences.\n *\n * @param password - Password to validate\n * @param options - Validation options containing checkSequential flag\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateSequential } from '@sentinel-password/core'\n *\n * // Detects ascending sequences\n * validateSequential('password') // { passed: true }\n * validateSequential('abc123') // { passed: false } (contains \"abc\" and \"123\")\n * validateSequential('xyz') // { passed: false } (contains \"xyz\")\n *\n * // Detects descending sequences\n * validateSequential('cba321') // { passed: false } (contains \"cba\" and \"321\")\n *\n * // Disable check\n * validateSequential('abc123', { checkSequential: false }) // { passed: true }\n * ```\n *\n * @remarks\n * Enabled by default. Checks for 3 or more consecutive characters in sequence.\n * Case-sensitive: detects both \"abc\" and \"ABC\" as separate patterns.\n *\n * **Overlap with `validateKeyboardPattern`:** The numeric runs `123`, `456`,\n * `789` (and their reverses) are also matched by the keyboard-pattern\n * validator's numeric-keypad list. To allow simple numeric runs in a\n * password you must set BOTH `checkSequential: false` AND\n * `checkKeyboardPatterns: false` — disabling either alone will not let\n * `password123` through. The two checks are deliberately independent\n * defences (code-point runs vs. keyboard-locality runs).\n */\nexport const validateSequential: Validator = (password, options = {}) => {\n const { checkSequential = true }: Partial<{ checkSequential: boolean }> = options\n\n if (!checkSequential) {\n return { passed: true }\n }\n\n if (hasSequentialPattern(password)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'sequential.found',\n params,\n message: resolveMessage('sequential.found', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, ValidatorCheck, ValidatorOptions } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Common keyboard patterns to detect across multiple layouts\n * Supports: QWERTY, AZERTY, QWERTZ, Dvorak, Colemak, and Cyrillic\n */\nconst KEYBOARD_PATTERNS: readonly string[] = [\n // === QWERTY (English, US, UK) ===\n // Full rows\n 'qwertyuiop',\n 'asdfghjkl',\n 'zxcvbnm',\n // Common typing patterns (adjacent keys)\n 'qwert',\n 'werty',\n 'asdfg',\n 'sdfgh',\n 'zxcvb',\n 'xcvbn',\n // Short sequences (3+ chars)\n 'qwe',\n 'asd',\n 'zxc',\n 'rty',\n 'fgh',\n 'cvb',\n 'poi',\n 'lkj',\n 'mnb',\n // Columns (top to bottom)\n '1qaz',\n '2wsx',\n '3edc',\n '4rfv',\n '5tgb',\n '6yhn',\n '7ujm',\n '8ik',\n '9ol',\n '0p',\n // Diagonals\n 'qaz',\n 'wsx',\n 'edc',\n 'zaq',\n 'xsw',\n 'cde',\n\n // === AZERTY (French, Belgian) ===\n // Full rows\n 'azertyuiop',\n 'qsdfghjklm',\n 'wxcvbn',\n // Common patterns\n 'azert',\n 'zerty',\n 'qsdfg',\n 'sdfgh',\n 'wxcvb',\n // Short sequences\n 'aze',\n 'qsd',\n 'wxc',\n\n // === QWERTZ (German, Central European) ===\n // Full rows\n 'qwertzuiop',\n 'yxcvbnm',\n // Common patterns\n 'qwertz',\n 'yxcvb',\n // Short sequences\n 'yxc',\n // Note: Patterns 'qwe', 'asd', 'asdfg', and 'asdfghjkl' are shared with QWERTY and listed above for efficiency.\n\n // === Dvorak ===\n // Full rows\n 'pyfgcrl',\n 'aoeuidhtns',\n 'qjkxbmwvz',\n // Common patterns\n 'aoeu',\n 'htns',\n 'qjkx',\n\n // === Colemak ===\n // Full rows\n 'qwfpgjluy',\n 'arstdhneio',\n 'zxcvbkm',\n // Common patterns\n 'arst',\n 'dhne',\n 'zxcv',\n\n // === Cyrillic (ЙЦУКЕН - Russian) ===\n // Full rows (Cyrillic characters)\n 'йцукенгшщзхъ',\n 'фывапролджэ',\n 'ячсмитьбю',\n // Common patterns\n 'йцукен',\n 'фывап',\n 'ячсми',\n 'цукен',\n\n // === Numeric patterns (universal) ===\n // Number row\n '1234567890',\n '0987654321',\n // Numeric keypad (rows)\n '789',\n '456',\n '123',\n // Numeric keypad (columns)\n '741',\n '852',\n '963',\n '7410',\n '8520',\n '9630',\n] as const\n\n/**\n * Escape regex metacharacters in a string so it matches literally.\n * Defensive: none of the current patterns contain metacharacters, but this\n * keeps future additions safe (e.g., if a layout's pattern contains `+` or `$`).\n */\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/**\n * Single regex matching ANY keyboard pattern in either forward or reverse\n * direction, case-insensitively. Built once at module load.\n *\n * Performance note: this replaced an earlier implementation that ran a\n * `for` loop over `KEYBOARD_PATTERNS`, calling `pattern.split('').reverse().join('')`\n * to build the reverse string AND `lowercase.includes(pattern)` on every iteration\n * (~480 allocations per call). A single regex test against an NFA-compiled\n * alternation is ~12× faster on typical passwords on V8/Node 22.\n *\n * If a future V8 regex bug surfaces (e.g., a Unicode case-folding regression\n * affecting Cyrillic with the `/i` flag), there's a side-by-side comparison\n * against a precomputed-array loop variant in\n * `packages/core/tests/performance.bench.ts` → `Keyboard pattern: implementation comparison`.\n * The loop variant is ~2.5× faster than the original split/reverse/join code\n * and ~5× slower than the regex, so it's a usable rollback target.\n */\nconst KEYBOARD_REGEX: RegExp = new RegExp(\n KEYBOARD_PATTERNS.flatMap((p: string): readonly string[] => {\n const reversed: string = p.split('').reverse().join('')\n return [escapeRegex(p), escapeRegex(reversed)]\n }).join('|'),\n 'i'\n)\n\n/**\n * Validates that password does not contain common keyboard patterns\n *\n * Detects patterns across multiple keyboard layouts:\n * - QWERTY (English, US, UK): qwerty, asdfgh, 1qaz2wsx\n * - AZERTY (French, Belgian): azerty, qsdfg\n * - QWERTZ (German, Central European): qwertz, yxcvb\n * - Dvorak (Alternative English): aoeu, htns\n * - Colemak (Alternative English): arst, dhne\n * - Cyrillic (Russian ЙЦУКЕН): йцукен, фывап\n * - Universal numeric: 123, 789, numeric keypad patterns\n * - Both forward and reverse patterns\n *\n * @param password - The password to validate\n * @param options - Validation options\n * @returns Validation result\n *\n * @example\n * ```typescript\n * validateKeyboardPattern('qwerty123') // { passed: false, message: '...' }\n * validateKeyboardPattern('azerty456') // { passed: false, message: '...' } (AZERTY)\n * validateKeyboardPattern('йцукен') // { passed: false, message: '...' } (Cyrillic)\n * validateKeyboardPattern('MyP@ssw0rd') // { passed: true }\n * validateKeyboardPattern('asdfgh', { checkKeyboardPatterns: false }) // { passed: true }\n * ```\n *\n * @remarks\n * **Overlap with `validateSequential`:** The numeric-keypad rows `123`,\n * `456`, `789` (and their reverses) are also caught by the sequential\n * validator's `charCodeAt`-consecutive check. To allow simple numeric\n * runs in a password you must set BOTH `checkKeyboardPatterns: false`\n * AND `checkSequential: false` — disabling either alone will not let\n * `password123` through. The two checks are deliberately independent\n * defences (keyboard-locality runs vs. code-point runs).\n */\nexport function validateKeyboardPattern(\n password: string,\n options: ValidatorOptions = {}\n): ValidatorCheck {\n const { checkKeyboardPatterns = true }: Partial<{ checkKeyboardPatterns: boolean }> = options\n\n if (!checkKeyboardPatterns) {\n return { passed: true }\n }\n\n if (KEYBOARD_REGEX.test(password)) {\n const emptyParams: MessageParams = {}\n return {\n passed: false,\n code: 'keyboardPattern.found',\n params: emptyParams,\n message: resolveMessage('keyboardPattern.found', emptyParams, options),\n }\n }\n\n return { passed: true }\n}\n","import type { MessageParams, Validator, ValidatorOptions } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Bloom filter for common passwords\n *\n * Source: SecLists https://github.com/danielmiessler/SecLists\n * File: Passwords/Common-Credentials/10k-most-common.txt (top 1,000)\n * Local: packages/core/data/common-passwords.txt\n *\n * Regenerate with: pnpm --filter @sentinel-password/core generate:bloom\n */\n\n// --- BEGIN GENERATED BLOOM FILTER ---\n// Generated from: packages/core/data/common-passwords.txt\n// Passwords: 1000 | Bloom size: 12000 bits | Hash functions: 7\nconst BLOOM_SIZE: number = 12000\nconst BLOOM_HASH_COUNT: number = 7\n\nconst BLOOM_BUCKETS: Int32Array = new Int32Array([\n -1274142440, -1983615455, -2110842727, -1440077142, 1782964852, 956870738, 48445478, 290080800,\n -1961772502, -1869995330, 787111091, 140549329, -1508237141, -800970204, -1987272632, -1439602637,\n 1837731885, 240419586, 2143496680, 547359246, -435494235, -1549227926, 151650466, -1742731903,\n -1535043550, -1607849886, -464376123, 1858481975, -1339118942, 410034491, -1397081975, 1217017604,\n -1934873593, -1769402149, -1107107031, -1098029453, 4380674, -393017311, -1465113568, 183557329,\n 1713935653, -99073116, 179529869, -1372675933, 78050105, 313074313, -1696462848, 404392594,\n -252409893, -1320442777, -1474295681, -1360120173, -743767380, 135925863, -1462755274, 562596610,\n -483730736, -2002210214, 1803176, 448811114, -1408849403, -735411494, -1872045334, 914538658,\n -374794234, 710617115, -1909840320, -2119695701, -1979682099, -1671944535, -1872492857, 570434164,\n -1334628885, 438020137, -1087528694, 464528967, -359521705, -1407023569, -1759335432, -1181701078,\n -1959746004, -1910872796, 1771191076, -1699312926, -1837137785, 132360706, -1002778102,\n 1384163571, 1786784659, -1354832568, -1364547569, -269830616, -1970133308, 848887818, -827577818,\n -1103500629, 2071996096, 1760287282, 1768617098, -1845466197, -1877054343, -223851966,\n -1037686738, 581441802, -761237365, -1282923217, 1328736770, 557363947, 369254184, -1708971466,\n -874268952, -2010768709, -1735196024, -1023761881, 772024867, -328714721, -125771638, 1644593802,\n 1762927155, -1509621238, 671919842, -1102294492, -2010455282, 302591302, -2136446493, -1986908626,\n -2018566016, 671347107, 842040354, 243352234, -534627656, -2011035123, -350834429, -844096647,\n -1598189902, -1833872134, -2021613470, -1968994020, -1297357224, 1210222837, -399712104,\n 906129314, -1430115578, -2113618941, -906361552, 551676011, 2819256, -1710063476, 1099626796,\n 1026172967, 640491579, -1440579096, -1887270648, -1710185214, 1721813836, 984101107, 1944258615,\n 724137993, -947645533, 1913070606, 44829860, 101219306, 1011556358, -1432173338, -482828238,\n -2103243204, 447353024, -329610536, 1398671502, 1623196423, 348309551, -211221782, 1980906030,\n 778830798, 968125066, -1458011638, -1337934774, 671089894, 52600876, -120418625, -1163255178,\n 680202936, -1597565152, 36835260, 1845733400, 153924331, 40509970, 411221088, 2114095983,\n 171043813, 1488604610, 172814850, -1702382524, 1502872105, 747635458, -501052624, 411301928,\n 731823798, -1333221971, -398552509, 134621864, -1872819037, 1822818465, -1893684382, -1316869470,\n -931632595, -1951260093, 598498019, -1290239178, -989323246, 573220082, 675809232, 169874120,\n -559880256, -910750974, 194548323, 736774690, -719202768, 1814594698, -1155290520, 237063072,\n -488083322, -924144083, 36385696, 715956226, -1568572256, 635709488, 1336062594, 320907784,\n 1124501765, -1406433280, -1963307926, -391509877, 1063369670, -1566479450, -1472058776,\n -1985331186, 1377869964, 942987938, 1109713448, 831578699, 853962256, 92835849, 2035317046,\n -2001761759, 216178811, 463827086, 1675819625, -1301764214, -234217277, -1459402461, 579365994,\n -1878875352, -1973245719, -1598478333, 1611084451, 1659513348, -1539122450, 837944629, 154749747,\n -500636954, 1746899746, 714271018, -1150679461, -1538612630, 1814069472, -492197206, -192202641,\n -756936190, -939252957, -1240063770, 1279410916, -1302054220, 1410991882, 1000401066, -198048178,\n -1419228998, -1742550398, -1991360862, -2111688732, -1700640206, 2061912618, -2103274201,\n -288731098, 578232, 53128594, 1755908256, 1116379816, 797411910, -1589060869, -1072342375,\n 845812237, 1749465810, 872549054, -1498077050, -1766137746, -376831803, -2084952921, 1202203194,\n 1216389770, -1104576382, 818061341, 942317755, -1578565146, 2083914250, -1012266815, 579371193,\n -461075653, 710981226, 1578192039, 681770739, -376523066, 1244735807, -490842006, -781193759,\n 556277799, 1984167471, -2000977760, -1265456598, 151665664, -1842697570, 579340290, 1082298021,\n 245211512, -1867881849, -1341630964, -90545664, 1806344898, 47088170, 992486842, -1558631407,\n -1449508691, -2006144829, 547374756, 912919200, 708120146, 616106151, 1172449414, 2124751394,\n -2073427929, 1009261122, 1210336770, -1973924734, 144458353, -1155209078, -1962399613, 897229836,\n 12357639, -1299101013, 312943930, 708920290, -1310023134, 639040320, -821838839, -2014506238,\n -274610648, -1442508704, 177432331, 1721041571, 414065321, -1272924654, 756208161, 1772773934,\n -1484380630, 1101004848, -1054523758, -2116504793, 1118667297, 86058657, 975856680, 749906058,\n 1937961650, 620890537, 918765730, -1366546774,\n])\n// --- END GENERATED BLOOM FILTER ---\n\n/**\n * Hash function for bloom filter\n */\nfunction hashString(str: string, seed: number): number {\n let hash: number = seed\n\n for (let i: number = 0; i < str.length; i++) {\n const char: number = str.charCodeAt(i)\n hash = (hash << 5) - hash + char\n hash = hash | 0 // Convert to 32-bit integer\n }\n\n return Math.abs(hash)\n}\n\n/**\n * Get multiple hash positions for a password\n */\nfunction getHashes(password: string): number[] {\n const hashes: number[] = []\n const hash1: number = hashString(password, 0)\n const hash2: number = hashString(password, 1)\n\n for (let i: number = 0; i < BLOOM_HASH_COUNT; i++) {\n const hash: number = (hash1 + i * hash2) >>> 0\n hashes.push(hash % BLOOM_SIZE)\n }\n\n return hashes\n}\n\n/**\n * Check if password might be in the common password list\n * Note: Bloom filters can have false positives (~0.84%) but never false negatives\n */\nfunction mightBeCommon(password: string): boolean {\n const hashes: number[] = getHashes(password.toLowerCase())\n\n for (const hash of hashes) {\n const arrayIndex: number = Math.floor(hash / 32)\n const bitIndex: number = hash % 32\n\n // Bounds check for TypeScript strict mode\n const bucket: number | undefined = BLOOM_BUCKETS[arrayIndex]\n if (bucket === undefined || (bucket & (1 << bitIndex)) === 0) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Validates that a password is not in the common password list\n *\n * Uses a Bloom filter to efficiently check against the top 1,000 most common passwords.\n * Case-insensitive matching prevents simple case variations of common passwords.\n *\n * @param password - Password to validate\n * @param options - Validation options containing checkCommonPasswords flag\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateCommonPassword } from '@sentinel-password/core'\n *\n * // Detects common passwords\n * validateCommonPassword('password')\n * // { passed: false, message: \"Password is too common. Please choose a more unique password.\" }\n *\n * validateCommonPassword('123456')\n * // { passed: false }\n *\n * // Case-insensitive\n * validateCommonPassword('PASSWORD')\n * // { passed: false }\n *\n * // Unique passwords pass\n * validateCommonPassword('MyUn1qu3P@ssw0rd!')\n * // { passed: true }\n *\n * // Disable check\n * validateCommonPassword('password', { checkCommonPasswords: false })\n * // { passed: true }\n * ```\n *\n * @remarks\n * Checks against top 1,000 most common passwords from SecLists.\n * Uses Bloom filter for space efficiency (~1.5KB vs ~8KB for raw array).\n * False positive rate: ~0.84% (may rarely flag uncommon passwords).\n * Enabled by default for security.\n */\nexport const validateCommonPassword: Validator = (\n password: string,\n options: ValidatorOptions = {}\n) => {\n const { checkCommonPasswords = true }: { checkCommonPasswords?: boolean } = options\n\n if (!checkCommonPasswords || password.length === 0) {\n return { passed: true }\n }\n\n // Case-insensitive check using bloom filter\n if (mightBeCommon(password)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'commonPassword.found',\n params,\n message: resolveMessage('commonPassword.found', params, options),\n }\n }\n\n return { passed: true }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Extracts username from email address\n * @param email - Email address\n * @returns Username part before @ symbol\n */\nconst extractUsername = (email: string): string => {\n const atIndex: number = email.indexOf('@')\n /* v8 ignore next */\n return atIndex > 0 ? email.substring(0, atIndex) : email\n}\n\n/**\n * Normalizes personal info strings for comparison\n * Converts to lowercase and extracts username from emails\n *\n * @param info - Personal information string\n * @returns Normalized string for comparison\n */\nconst normalizePersonalInfo = (info: string): string => {\n const normalized: string = info.toLowerCase().trim()\n // Extract username from email if it looks like an email\n return normalized.includes('@') ? extractUsername(normalized) : normalized\n}\n\n/**\n * Validates that password doesn't contain personal information\n *\n * Checks against provided personal info (username, email, name, etc.)\n * Uses case-insensitive comparison and extracts usernames from email addresses.\n * Ignores very short strings (< 3 characters) to avoid false positives.\n *\n * @param password - Password to validate\n * @param options - Validation options containing personalInfo array\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validatePersonalInfo } from '@sentinel-password/core'\n *\n * // No personal info by default\n * validatePersonalInfo('password') // { passed: true }\n *\n * // Detects username in password\n * validatePersonalInfo('johnpassword', { personalInfo: ['john'] })\n * // { passed: false, message: \"Password contains personal information\" }\n *\n * // Emails are reduced to the *entire local part* (everything before `@`),\n * // matched as a literal substring — not split into name fragments.\n * validatePersonalInfo('john.doe123', { personalInfo: ['john.doe@example.com'] })\n * // { passed: false } (matches the full local part \"john.doe\")\n *\n * validatePersonalInfo('john123', { personalInfo: ['john.doe@example.com'] })\n * // { passed: true } (the local part is \"john.doe\", not \"john\" — no match)\n *\n * // To reject passwords containing just \"john\", pass it explicitly:\n * validatePersonalInfo('john123', { personalInfo: ['john', 'john.doe@example.com'] })\n * // { passed: false } (matches the literal \"john\")\n *\n * // Case-insensitive\n * validatePersonalInfo('JOHN123', { personalInfo: ['john'] })\n * // { passed: false }\n *\n * // Ignores short strings\n * validatePersonalInfo('password', { personalInfo: ['pw'] }) // { passed: true } (too short)\n *\n * // Multiple personal info items\n * validatePersonalInfo('secretpass', {\n * personalInfo: ['john', 'doe', 'john.doe@example.com']\n * }) // { passed: true }\n * ```\n *\n * @remarks\n * Best practice: Provide username, email, first name, last name, and company name.\n * Strings shorter than 3 characters are ignored to prevent false positives.\n */\nexport const validatePersonalInfo: Validator = (password, options = {}) => {\n const { personalInfo = [] }: Partial<{ personalInfo: string[] }> = options\n\n if (personalInfo.length === 0 || password.length === 0) {\n return { passed: true }\n }\n\n const lowerPassword: string = password.toLowerCase()\n\n for (const info of personalInfo) {\n const normalized: string = normalizePersonalInfo(info)\n\n // Skip very short strings to avoid false positives\n if (normalized.length < 3) {\n continue\n }\n\n // Check if password contains this personal information\n if (lowerPassword.includes(normalized)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'personalInfo.found',\n params,\n message: resolveMessage('personalInfo.found', params, options),\n }\n }\n }\n\n return {\n passed: true,\n }\n}\n"],"mappings":"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","names":[]}

@@ -286,2 +286,12 @@ // src/messages.ts

];
function escapeRegex(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
var KEYBOARD_REGEX = new RegExp(
KEYBOARD_PATTERNS.flatMap((p) => {
const reversed = p.split("").reverse().join("");
return [escapeRegex(p), escapeRegex(reversed)];
}).join("|"),
"i"
);
function validateKeyboardPattern(password, options = {}) {

@@ -292,22 +302,10 @@ const { checkKeyboardPatterns = true } = options;

}
const lowercase = password.toLowerCase();
const emptyParams = {};
for (const pattern of KEYBOARD_PATTERNS) {
if (lowercase.includes(pattern)) {
return {
passed: false,
code: "keyboardPattern.found",
params: emptyParams,
message: resolveMessage("keyboardPattern.found", emptyParams, options)
};
}
const reversed = pattern.split("").reverse().join("");
if (lowercase.includes(reversed)) {
return {
passed: false,
code: "keyboardPattern.found",
params: emptyParams,
message: resolveMessage("keyboardPattern.found", emptyParams, options)
};
}
if (KEYBOARD_REGEX.test(password)) {
const emptyParams = {};
return {
passed: false,
code: "keyboardPattern.found",
params: emptyParams,
message: resolveMessage("keyboardPattern.found", emptyParams, options)
};
}

@@ -314,0 +312,0 @@ return { passed: true };

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/messages.ts","../src/validators/length.ts","../src/validators/character-types.ts","../src/validators/repetition.ts","../src/validators/sequential.ts","../src/validators/keyboard-pattern.ts","../src/validators/common-password.ts","../src/validators/personal-info.ts","../src/index.ts"],"sourcesContent":["import type { MessageCode, MessageParams, ValidatorOptions } from './types'\n\n/**\n * Built-in English templates for every {@link MessageCode}.\n *\n * The default rendering of a failed `ValidatorCheck.message` comes from this\n * map. Strings are stable across patch and minor releases — consumers can\n * still use them as translation keys with the legacy lookup-table pattern.\n * Prefer the `messages` / `formatMessage` options on {@link ValidatorOptions}.\n *\n * Placeholders use `{name}` syntax and are substituted by {@link formatTemplate}.\n */\nexport const DEFAULT_TEMPLATES: Readonly<Record<MessageCode, string>> = {\n 'length.tooShort': 'Password must be at least {minLength} characters',\n 'length.tooLong': 'Password must be at most {maxLength} characters',\n 'characterTypes.missing': 'Password must contain at least one {missing}',\n 'repetition.tooMany': 'Password contains too many repeated characters (max {maxRepeatedChars})',\n 'sequential.found': 'Password contains sequential characters (e.g., abc, 123)',\n 'keyboardPattern.found': 'Password contains common keyboard patterns',\n 'commonPassword.found': 'Password is too common. Please choose a more unique password.',\n 'personalInfo.found': 'Password contains personal information',\n} as const\n\nconst PLACEHOLDER_PATTERN: RegExp = /\\{(\\w+)\\}/g\n\n/**\n * Substitute `{name}` placeholders in `template` with values from `params`.\n *\n * Unknown placeholders are left intact (visible in the output) so missing\n * data surfaces as a noticeable bug rather than a silent omission. Values\n * are coerced to strings via the `String` constructor.\n *\n * @example\n * formatTemplate('Min {n} chars', { n: 8 }) // → \"Min 8 chars\"\n * formatTemplate('Need {a} and {b}', { a: 'X' }) // → \"Need X and {b}\"\n */\nexport function formatTemplate(template: string, params: MessageParams): string {\n return template.replace(PLACEHOLDER_PATTERN, (match, key: string): string => {\n const value: string | number | undefined = params[key]\n return value === undefined ? match : String(value)\n })\n}\n\n/**\n * Render a message via the fallback chain:\n * 1. `options.formatMessage(code, params, defaultMessage)` if provided\n * 2. `formatTemplate(options.messages[code], params)` if that override is provided\n * 3. `formatTemplate(DEFAULT_TEMPLATES[code], params)` (built-in English)\n *\n * Used by every validator's failure branch. Validators stay declarative —\n * they describe *what* failed (`code` + `params`) and delegate rendering.\n *\n * If `options.formatMessage` throws, this function swallows the error and\n * returns the default English rendering instead. Validators in this library\n * promise never to throw (see `Validator` in `./types`), and that promise\n * holds even when consumer-provided formatters misbehave.\n */\nexport function resolveMessage(\n code: MessageCode,\n params: MessageParams,\n options: ValidatorOptions\n): string {\n const defaultMessage: string = formatTemplate(DEFAULT_TEMPLATES[code], params)\n\n if (options.formatMessage) {\n try {\n return options.formatMessage(code, params, defaultMessage)\n } catch {\n return defaultMessage\n }\n }\n\n const override: string | undefined = options.messages?.[code]\n if (override !== undefined) {\n return formatTemplate(override, params)\n }\n\n return defaultMessage\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Validates password length against minimum and maximum requirements\n *\n * @param password - Password to validate\n * @param options - Validation options containing minLength and maxLength\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateLength } from '@sentinel-password/core'\n *\n * // Default: min 8, max 128 characters\n * validateLength('short') // { passed: false, message: \"Password must be at least 8 characters\" }\n * validateLength('longenough') // { passed: true }\n *\n * // Custom length requirements\n * validateLength('password', { minLength: 12 }) // { passed: false, message: \"...\" }\n * validateLength('verylongpassword', { minLength: 12, maxLength: 20 }) // { passed: true }\n * ```\n *\n * @remarks\n * Default minimum length is 8 characters (OWASP recommendation).\n * Default maximum length is 128 characters (prevents DoS attacks).\n */\nexport const validateLength: Validator = (password, options = {}) => {\n const { minLength = 8, maxLength = 128 }: Partial<{ minLength: number; maxLength: number }> =\n options\n\n const length: number = password.length\n\n if (length < minLength) {\n const params: MessageParams = { minLength }\n return {\n passed: false,\n code: 'length.tooShort',\n params,\n message: resolveMessage('length.tooShort', params, options),\n }\n }\n\n if (length > maxLength) {\n const params: MessageParams = { maxLength }\n return {\n passed: false,\n code: 'length.tooLong',\n params,\n message: resolveMessage('length.tooLong', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Check if password contains at least one uppercase letter (A-Z)\n *\n * @param password - Password to check\n * @returns True if password contains at least one uppercase letter\n *\n * @example\n * ```typescript\n * hasUppercase('password') // false\n * hasUppercase('Password') // true\n * hasUppercase('PASSWORD') // true\n * ```\n */\nexport const hasUppercase = (password: string): boolean => /[A-Z]/.test(password)\n\n/**\n * Check if password contains at least one lowercase letter (a-z)\n *\n * @param password - Password to check\n * @returns True if password contains at least one lowercase letter\n *\n * @example\n * ```typescript\n * hasLowercase('PASSWORD') // false\n * hasLowercase('Password') // true\n * hasLowercase('password') // true\n * ```\n */\nexport const hasLowercase = (password: string): boolean => /[a-z]/.test(password)\n\n/**\n * Check if password contains at least one digit (0-9)\n *\n * @param password - Password to check\n * @returns True if password contains at least one digit\n *\n * @example\n * ```typescript\n * hasDigit('password') // false\n * hasDigit('password1') // true\n * hasDigit('123') // true\n * ```\n */\nexport const hasDigit = (password: string): boolean => /\\d/.test(password)\n\n/**\n * Check if password contains at least one symbol/special character\n *\n * @param password - Password to check\n * @returns True if password contains at least one special character\n *\n * @example\n * ```typescript\n * hasSymbol('password') // false\n * hasSymbol('password!') // true\n * hasSymbol('p@ssw0rd') // true\n * ```\n *\n * @remarks\n * Accepted symbols: ! @ # $ % ^ & * ( ) _ + - = [ ] { } ; ' : \" \\ | , . < > / ?\n */\nexport const hasSymbol = (password: string): boolean =>\n /[!@#$%^&*()_+\\-=[\\]{};':\"\\\\|,.<>/?]/.test(password)\n\n/**\n * Validates character type requirements (uppercase, lowercase, digits, symbols)\n *\n * @param password - Password to validate\n * @param options - Validation options containing character type requirements\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateCharacterTypes } from '@sentinel-password/core'\n *\n * // No requirements by default\n * validateCharacterTypes('password') // { passed: true }\n *\n * // Require uppercase\n * validateCharacterTypes('password', { requireUppercase: true })\n * // { passed: false, message: \"Password must contain at least one uppercase letter\" }\n *\n * validateCharacterTypes('Password', { requireUppercase: true }) // { passed: true }\n *\n * // Require multiple types\n * validateCharacterTypes('password', {\n * requireUppercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * })\n * // { passed: false, message: \"Password must contain at least one uppercase letter, digit, symbol\" }\n *\n * validateCharacterTypes('Password1!', {\n * requireUppercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * }) // { passed: true }\n * ```\n *\n * @remarks\n * By default, no character types are required. Enable specific requirements via options.\n */\nexport const validateCharacterTypes: Validator = (password, options = {}) => {\n const {\n requireUppercase = false,\n requireLowercase = false,\n requireDigit = false,\n requireSymbol = false,\n }: Partial<{\n requireUppercase: boolean\n requireLowercase: boolean\n requireDigit: boolean\n requireSymbol: boolean\n }> = options\n\n const missing: string[] = []\n const missingTypes: string[] = []\n\n if (requireUppercase && !hasUppercase(password)) {\n missing.push('uppercase letter')\n missingTypes.push('uppercase')\n }\n\n if (requireLowercase && !hasLowercase(password)) {\n missing.push('lowercase letter')\n missingTypes.push('lowercase')\n }\n\n if (requireDigit && !hasDigit(password)) {\n missing.push('digit')\n missingTypes.push('digit')\n }\n\n if (requireSymbol && !hasSymbol(password)) {\n missing.push('symbol')\n missingTypes.push('symbol')\n }\n\n if (missing.length > 0) {\n const params: MessageParams = {\n missing: missing.join(', '),\n missingTypes: missingTypes.join(','),\n }\n return {\n passed: false,\n code: 'characterTypes.missing',\n params,\n message: resolveMessage('characterTypes.missing', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Validates that password doesn't contain excessive repeated characters\n *\n * Uses a single-pass algorithm to detect consecutive repeated characters.\n * Helps prevent weak passwords like \"aaaa1111\" or \"passwordddd\".\n *\n * @param password - Password to validate\n * @param options - Validation options containing maxRepeatedChars\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateRepetition } from '@sentinel-password/core'\n *\n * // Default: max 3 repeated characters\n * validateRepetition('password') // { passed: true }\n * validateRepetition('passsword') // { passed: true } (3 s's)\n * validateRepetition('passssword') // { passed: false } (4 s's)\n *\n * // Custom limit\n * validateRepetition('passssword', { maxRepeatedChars: 5 }) // { passed: true }\n * validateRepetition('aaa') // { passed: true }\n * validateRepetition('aaaa') // { passed: false, message: \"Password contains too many repeated characters (max 3)\" }\n * ```\n *\n * @remarks\n * Default maximum is 3 consecutive repeated characters.\n * Only checks for consecutive repetition, not overall character frequency.\n */\nexport const validateRepetition: Validator = (password, options = {}) => {\n const { maxRepeatedChars = 3 }: Partial<{ maxRepeatedChars: number }> = options\n\n if (password.length === 0) {\n return { passed: true }\n }\n\n let currentChar: string = password.charAt(0)\n let count: number = 1\n\n for (let i: number = 1; i < password.length; i++) {\n const char: string = password.charAt(i)\n if (char === currentChar) {\n count++\n if (count > maxRepeatedChars) {\n const params: MessageParams = { maxRepeatedChars }\n return {\n passed: false,\n code: 'repetition.tooMany',\n params,\n message: resolveMessage('repetition.tooMany', params, options),\n }\n }\n } else {\n currentChar = char\n count = 1\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Detects sequential character patterns (ascending or descending)\n * Checks for sequences of 3 or more consecutive characters\n *\n * @param str - String to check for sequences\n * @returns true if sequential pattern found, false otherwise\n */\nconst hasSequentialPattern = (str: string): boolean => {\n const minSequenceLength: number = 3\n\n for (let i: number = 0; i <= str.length - minSequenceLength; i++) {\n const charCode1: number = str.charCodeAt(i)\n const charCode2: number = str.charCodeAt(i + 1)\n const charCode3: number = str.charCodeAt(i + 2)\n\n // Check ascending sequence (e.g., abc, 123)\n if (charCode2 === charCode1 + 1 && charCode3 === charCode2 + 1) {\n return true\n }\n\n // Check descending sequence (e.g., cba, 321)\n if (charCode2 === charCode1 - 1 && charCode3 === charCode2 - 1) {\n return true\n }\n }\n\n return false\n}\n\n/**\n * Validates that password doesn't contain sequential character patterns\n *\n * Detects sequences like: abc, ABC, 123, 321, xyz, etc.\n * Uses character code comparison for efficient detection.\n * Helps prevent predictable passwords with keyboard sequences.\n *\n * @param password - Password to validate\n * @param options - Validation options containing checkSequential flag\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateSequential } from '@sentinel-password/core'\n *\n * // Detects ascending sequences\n * validateSequential('password') // { passed: true }\n * validateSequential('abc123') // { passed: false } (contains \"abc\" and \"123\")\n * validateSequential('xyz') // { passed: false } (contains \"xyz\")\n *\n * // Detects descending sequences\n * validateSequential('cba321') // { passed: false } (contains \"cba\" and \"321\")\n *\n * // Disable check\n * validateSequential('abc123', { checkSequential: false }) // { passed: true }\n * ```\n *\n * @remarks\n * Enabled by default. Checks for 3 or more consecutive characters in sequence.\n * Case-sensitive: detects both \"abc\" and \"ABC\" as separate patterns.\n *\n * **Overlap with `validateKeyboardPattern`:** The numeric runs `123`, `456`,\n * `789` (and their reverses) are also matched by the keyboard-pattern\n * validator's numeric-keypad list. To allow simple numeric runs in a\n * password you must set BOTH `checkSequential: false` AND\n * `checkKeyboardPatterns: false` — disabling either alone will not let\n * `password123` through. The two checks are deliberately independent\n * defences (code-point runs vs. keyboard-locality runs).\n */\nexport const validateSequential: Validator = (password, options = {}) => {\n const { checkSequential = true }: Partial<{ checkSequential: boolean }> = options\n\n if (!checkSequential) {\n return { passed: true }\n }\n\n if (hasSequentialPattern(password)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'sequential.found',\n params,\n message: resolveMessage('sequential.found', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, ValidatorCheck, ValidatorOptions } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Common keyboard patterns to detect across multiple layouts\n * Supports: QWERTY, AZERTY, QWERTZ, Dvorak, Colemak, and Cyrillic\n */\nconst KEYBOARD_PATTERNS: readonly string[] = [\n // === QWERTY (English, US, UK) ===\n // Full rows\n 'qwertyuiop',\n 'asdfghjkl',\n 'zxcvbnm',\n // Common typing patterns (adjacent keys)\n 'qwert',\n 'werty',\n 'asdfg',\n 'sdfgh',\n 'zxcvb',\n 'xcvbn',\n // Short sequences (3+ chars)\n 'qwe',\n 'asd',\n 'zxc',\n 'rty',\n 'fgh',\n 'cvb',\n 'poi',\n 'lkj',\n 'mnb',\n // Columns (top to bottom)\n '1qaz',\n '2wsx',\n '3edc',\n '4rfv',\n '5tgb',\n '6yhn',\n '7ujm',\n '8ik',\n '9ol',\n '0p',\n // Diagonals\n 'qaz',\n 'wsx',\n 'edc',\n 'zaq',\n 'xsw',\n 'cde',\n\n // === AZERTY (French, Belgian) ===\n // Full rows\n 'azertyuiop',\n 'qsdfghjklm',\n 'wxcvbn',\n // Common patterns\n 'azert',\n 'zerty',\n 'qsdfg',\n 'sdfgh',\n 'wxcvb',\n // Short sequences\n 'aze',\n 'qsd',\n 'wxc',\n\n // === QWERTZ (German, Central European) ===\n // Full rows\n 'qwertzuiop',\n 'yxcvbnm',\n // Common patterns\n 'qwertz',\n 'yxcvb',\n // Short sequences\n 'yxc',\n // Note: Patterns 'qwe', 'asd', 'asdfg', and 'asdfghjkl' are shared with QWERTY and listed above for efficiency.\n\n // === Dvorak ===\n // Full rows\n 'pyfgcrl',\n 'aoeuidhtns',\n 'qjkxbmwvz',\n // Common patterns\n 'aoeu',\n 'htns',\n 'qjkx',\n\n // === Colemak ===\n // Full rows\n 'qwfpgjluy',\n 'arstdhneio',\n 'zxcvbkm',\n // Common patterns\n 'arst',\n 'dhne',\n 'zxcv',\n\n // === Cyrillic (ЙЦУКЕН - Russian) ===\n // Full rows (Cyrillic characters)\n 'йцукенгшщзхъ',\n 'фывапролджэ',\n 'ячсмитьбю',\n // Common patterns\n 'йцукен',\n 'фывап',\n 'ячсми',\n 'цукен',\n\n // === Numeric patterns (universal) ===\n // Number row\n '1234567890',\n '0987654321',\n // Numeric keypad (rows)\n '789',\n '456',\n '123',\n // Numeric keypad (columns)\n '741',\n '852',\n '963',\n '7410',\n '8520',\n '9630',\n] as const\n\n/**\n * Validates that password does not contain common keyboard patterns\n *\n * Detects patterns across multiple keyboard layouts:\n * - QWERTY (English, US, UK): qwerty, asdfgh, 1qaz2wsx\n * - AZERTY (French, Belgian): azerty, qsdfg\n * - QWERTZ (German, Central European): qwertz, yxcvb\n * - Dvorak (Alternative English): aoeu, htns\n * - Colemak (Alternative English): arst, dhne\n * - Cyrillic (Russian ЙЦУКЕН): йцукен, фывап\n * - Universal numeric: 123, 789, numeric keypad patterns\n * - Both forward and reverse patterns\n *\n * @param password - The password to validate\n * @param options - Validation options\n * @returns Validation result\n *\n * @example\n * ```typescript\n * validateKeyboardPattern('qwerty123') // { passed: false, message: '...' }\n * validateKeyboardPattern('azerty456') // { passed: false, message: '...' } (AZERTY)\n * validateKeyboardPattern('йцукен') // { passed: false, message: '...' } (Cyrillic)\n * validateKeyboardPattern('MyP@ssw0rd') // { passed: true }\n * validateKeyboardPattern('asdfgh', { checkKeyboardPatterns: false }) // { passed: true }\n * ```\n *\n * @remarks\n * **Overlap with `validateSequential`:** The numeric-keypad rows `123`,\n * `456`, `789` (and their reverses) are also caught by the sequential\n * validator's `charCodeAt`-consecutive check. To allow simple numeric\n * runs in a password you must set BOTH `checkKeyboardPatterns: false`\n * AND `checkSequential: false` — disabling either alone will not let\n * `password123` through. The two checks are deliberately independent\n * defences (keyboard-locality runs vs. code-point runs).\n */\nexport function validateKeyboardPattern(\n password: string,\n options: ValidatorOptions = {}\n): ValidatorCheck {\n const { checkKeyboardPatterns = true }: Partial<{ checkKeyboardPatterns: boolean }> = options\n\n if (!checkKeyboardPatterns) {\n return { passed: true }\n }\n\n const lowercase: string = password.toLowerCase()\n const emptyParams: MessageParams = {}\n\n // Check for keyboard patterns (forward and reverse)\n for (const pattern of KEYBOARD_PATTERNS) {\n // Check forward pattern\n if (lowercase.includes(pattern)) {\n return {\n passed: false,\n code: 'keyboardPattern.found',\n params: emptyParams,\n message: resolveMessage('keyboardPattern.found', emptyParams, options),\n }\n }\n\n // Check reverse pattern\n const reversed: string = pattern.split('').reverse().join('')\n if (lowercase.includes(reversed)) {\n return {\n passed: false,\n code: 'keyboardPattern.found',\n params: emptyParams,\n message: resolveMessage('keyboardPattern.found', emptyParams, options),\n }\n }\n }\n\n return { passed: true }\n}\n","import type { MessageParams, Validator, ValidatorOptions } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Bloom filter for common passwords\n *\n * Source: SecLists https://github.com/danielmiessler/SecLists\n * File: Passwords/Common-Credentials/10k-most-common.txt (top 1,000)\n * Local: packages/core/data/common-passwords.txt\n *\n * Regenerate with: pnpm --filter @sentinel-password/core generate:bloom\n */\n\n// --- BEGIN GENERATED BLOOM FILTER ---\n// Generated from: packages/core/data/common-passwords.txt\n// Passwords: 1000 | Bloom size: 12000 bits | Hash functions: 7\nconst BLOOM_SIZE: number = 12000\nconst BLOOM_HASH_COUNT: number = 7\n\nconst BLOOM_BUCKETS: Int32Array = new Int32Array([\n -1274142440, -1983615455, -2110842727, -1440077142, 1782964852, 956870738, 48445478, 290080800,\n -1961772502, -1869995330, 787111091, 140549329, -1508237141, -800970204, -1987272632, -1439602637,\n 1837731885, 240419586, 2143496680, 547359246, -435494235, -1549227926, 151650466, -1742731903,\n -1535043550, -1607849886, -464376123, 1858481975, -1339118942, 410034491, -1397081975, 1217017604,\n -1934873593, -1769402149, -1107107031, -1098029453, 4380674, -393017311, -1465113568, 183557329,\n 1713935653, -99073116, 179529869, -1372675933, 78050105, 313074313, -1696462848, 404392594,\n -252409893, -1320442777, -1474295681, -1360120173, -743767380, 135925863, -1462755274, 562596610,\n -483730736, -2002210214, 1803176, 448811114, -1408849403, -735411494, -1872045334, 914538658,\n -374794234, 710617115, -1909840320, -2119695701, -1979682099, -1671944535, -1872492857, 570434164,\n -1334628885, 438020137, -1087528694, 464528967, -359521705, -1407023569, -1759335432, -1181701078,\n -1959746004, -1910872796, 1771191076, -1699312926, -1837137785, 132360706, -1002778102,\n 1384163571, 1786784659, -1354832568, -1364547569, -269830616, -1970133308, 848887818, -827577818,\n -1103500629, 2071996096, 1760287282, 1768617098, -1845466197, -1877054343, -223851966,\n -1037686738, 581441802, -761237365, -1282923217, 1328736770, 557363947, 369254184, -1708971466,\n -874268952, -2010768709, -1735196024, -1023761881, 772024867, -328714721, -125771638, 1644593802,\n 1762927155, -1509621238, 671919842, -1102294492, -2010455282, 302591302, -2136446493, -1986908626,\n -2018566016, 671347107, 842040354, 243352234, -534627656, -2011035123, -350834429, -844096647,\n -1598189902, -1833872134, -2021613470, -1968994020, -1297357224, 1210222837, -399712104,\n 906129314, -1430115578, -2113618941, -906361552, 551676011, 2819256, -1710063476, 1099626796,\n 1026172967, 640491579, -1440579096, -1887270648, -1710185214, 1721813836, 984101107, 1944258615,\n 724137993, -947645533, 1913070606, 44829860, 101219306, 1011556358, -1432173338, -482828238,\n -2103243204, 447353024, -329610536, 1398671502, 1623196423, 348309551, -211221782, 1980906030,\n 778830798, 968125066, -1458011638, -1337934774, 671089894, 52600876, -120418625, -1163255178,\n 680202936, -1597565152, 36835260, 1845733400, 153924331, 40509970, 411221088, 2114095983,\n 171043813, 1488604610, 172814850, -1702382524, 1502872105, 747635458, -501052624, 411301928,\n 731823798, -1333221971, -398552509, 134621864, -1872819037, 1822818465, -1893684382, -1316869470,\n -931632595, -1951260093, 598498019, -1290239178, -989323246, 573220082, 675809232, 169874120,\n -559880256, -910750974, 194548323, 736774690, -719202768, 1814594698, -1155290520, 237063072,\n -488083322, -924144083, 36385696, 715956226, -1568572256, 635709488, 1336062594, 320907784,\n 1124501765, -1406433280, -1963307926, -391509877, 1063369670, -1566479450, -1472058776,\n -1985331186, 1377869964, 942987938, 1109713448, 831578699, 853962256, 92835849, 2035317046,\n -2001761759, 216178811, 463827086, 1675819625, -1301764214, -234217277, -1459402461, 579365994,\n -1878875352, -1973245719, -1598478333, 1611084451, 1659513348, -1539122450, 837944629, 154749747,\n -500636954, 1746899746, 714271018, -1150679461, -1538612630, 1814069472, -492197206, -192202641,\n -756936190, -939252957, -1240063770, 1279410916, -1302054220, 1410991882, 1000401066, -198048178,\n -1419228998, -1742550398, -1991360862, -2111688732, -1700640206, 2061912618, -2103274201,\n -288731098, 578232, 53128594, 1755908256, 1116379816, 797411910, -1589060869, -1072342375,\n 845812237, 1749465810, 872549054, -1498077050, -1766137746, -376831803, -2084952921, 1202203194,\n 1216389770, -1104576382, 818061341, 942317755, -1578565146, 2083914250, -1012266815, 579371193,\n -461075653, 710981226, 1578192039, 681770739, -376523066, 1244735807, -490842006, -781193759,\n 556277799, 1984167471, -2000977760, -1265456598, 151665664, -1842697570, 579340290, 1082298021,\n 245211512, -1867881849, -1341630964, -90545664, 1806344898, 47088170, 992486842, -1558631407,\n -1449508691, -2006144829, 547374756, 912919200, 708120146, 616106151, 1172449414, 2124751394,\n -2073427929, 1009261122, 1210336770, -1973924734, 144458353, -1155209078, -1962399613, 897229836,\n 12357639, -1299101013, 312943930, 708920290, -1310023134, 639040320, -821838839, -2014506238,\n -274610648, -1442508704, 177432331, 1721041571, 414065321, -1272924654, 756208161, 1772773934,\n -1484380630, 1101004848, -1054523758, -2116504793, 1118667297, 86058657, 975856680, 749906058,\n 1937961650, 620890537, 918765730, -1366546774,\n])\n// --- END GENERATED BLOOM FILTER ---\n\n/**\n * Hash function for bloom filter\n */\nfunction hashString(str: string, seed: number): number {\n let hash: number = seed\n\n for (let i: number = 0; i < str.length; i++) {\n const char: number = str.charCodeAt(i)\n hash = (hash << 5) - hash + char\n hash = hash | 0 // Convert to 32-bit integer\n }\n\n return Math.abs(hash)\n}\n\n/**\n * Get multiple hash positions for a password\n */\nfunction getHashes(password: string): number[] {\n const hashes: number[] = []\n const hash1: number = hashString(password, 0)\n const hash2: number = hashString(password, 1)\n\n for (let i: number = 0; i < BLOOM_HASH_COUNT; i++) {\n const hash: number = (hash1 + i * hash2) >>> 0\n hashes.push(hash % BLOOM_SIZE)\n }\n\n return hashes\n}\n\n/**\n * Check if password might be in the common password list\n * Note: Bloom filters can have false positives (~0.84%) but never false negatives\n */\nfunction mightBeCommon(password: string): boolean {\n const hashes: number[] = getHashes(password.toLowerCase())\n\n for (const hash of hashes) {\n const arrayIndex: number = Math.floor(hash / 32)\n const bitIndex: number = hash % 32\n\n // Bounds check for TypeScript strict mode\n const bucket: number | undefined = BLOOM_BUCKETS[arrayIndex]\n if (bucket === undefined || (bucket & (1 << bitIndex)) === 0) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Validates that a password is not in the common password list\n *\n * Uses a Bloom filter to efficiently check against the top 1,000 most common passwords.\n * Case-insensitive matching prevents simple case variations of common passwords.\n *\n * @param password - Password to validate\n * @param options - Validation options containing checkCommonPasswords flag\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateCommonPassword } from '@sentinel-password/core'\n *\n * // Detects common passwords\n * validateCommonPassword('password')\n * // { passed: false, message: \"Password is too common. Please choose a more unique password.\" }\n *\n * validateCommonPassword('123456')\n * // { passed: false }\n *\n * // Case-insensitive\n * validateCommonPassword('PASSWORD')\n * // { passed: false }\n *\n * // Unique passwords pass\n * validateCommonPassword('MyUn1qu3P@ssw0rd!')\n * // { passed: true }\n *\n * // Disable check\n * validateCommonPassword('password', { checkCommonPasswords: false })\n * // { passed: true }\n * ```\n *\n * @remarks\n * Checks against top 1,000 most common passwords from SecLists.\n * Uses Bloom filter for space efficiency (~1.5KB vs ~8KB for raw array).\n * False positive rate: ~0.84% (may rarely flag uncommon passwords).\n * Enabled by default for security.\n */\nexport const validateCommonPassword: Validator = (\n password: string,\n options: ValidatorOptions = {}\n) => {\n const { checkCommonPasswords = true }: { checkCommonPasswords?: boolean } = options\n\n if (!checkCommonPasswords || password.length === 0) {\n return { passed: true }\n }\n\n // Case-insensitive check using bloom filter\n if (mightBeCommon(password)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'commonPassword.found',\n params,\n message: resolveMessage('commonPassword.found', params, options),\n }\n }\n\n return { passed: true }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Extracts username from email address\n * @param email - Email address\n * @returns Username part before @ symbol\n */\nconst extractUsername = (email: string): string => {\n const atIndex: number = email.indexOf('@')\n /* v8 ignore next */\n return atIndex > 0 ? email.substring(0, atIndex) : email\n}\n\n/**\n * Normalizes personal info strings for comparison\n * Converts to lowercase and extracts username from emails\n *\n * @param info - Personal information string\n * @returns Normalized string for comparison\n */\nconst normalizePersonalInfo = (info: string): string => {\n const normalized: string = info.toLowerCase().trim()\n // Extract username from email if it looks like an email\n return normalized.includes('@') ? extractUsername(normalized) : normalized\n}\n\n/**\n * Validates that password doesn't contain personal information\n *\n * Checks against provided personal info (username, email, name, etc.)\n * Uses case-insensitive comparison and extracts usernames from email addresses.\n * Ignores very short strings (< 3 characters) to avoid false positives.\n *\n * @param password - Password to validate\n * @param options - Validation options containing personalInfo array\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validatePersonalInfo } from '@sentinel-password/core'\n *\n * // No personal info by default\n * validatePersonalInfo('password') // { passed: true }\n *\n * // Detects username in password\n * validatePersonalInfo('johnpassword', { personalInfo: ['john'] })\n * // { passed: false, message: \"Password contains personal information\" }\n *\n * // Emails are reduced to the *entire local part* (everything before `@`),\n * // matched as a literal substring — not split into name fragments.\n * validatePersonalInfo('john.doe123', { personalInfo: ['john.doe@example.com'] })\n * // { passed: false } (matches the full local part \"john.doe\")\n *\n * validatePersonalInfo('john123', { personalInfo: ['john.doe@example.com'] })\n * // { passed: true } (the local part is \"john.doe\", not \"john\" — no match)\n *\n * // To reject passwords containing just \"john\", pass it explicitly:\n * validatePersonalInfo('john123', { personalInfo: ['john', 'john.doe@example.com'] })\n * // { passed: false } (matches the literal \"john\")\n *\n * // Case-insensitive\n * validatePersonalInfo('JOHN123', { personalInfo: ['john'] })\n * // { passed: false }\n *\n * // Ignores short strings\n * validatePersonalInfo('password', { personalInfo: ['pw'] }) // { passed: true } (too short)\n *\n * // Multiple personal info items\n * validatePersonalInfo('secretpass', {\n * personalInfo: ['john', 'doe', 'john.doe@example.com']\n * }) // { passed: true }\n * ```\n *\n * @remarks\n * Best practice: Provide username, email, first name, last name, and company name.\n * Strings shorter than 3 characters are ignored to prevent false positives.\n */\nexport const validatePersonalInfo: Validator = (password, options = {}) => {\n const { personalInfo = [] }: Partial<{ personalInfo: string[] }> = options\n\n if (personalInfo.length === 0 || password.length === 0) {\n return { passed: true }\n }\n\n const lowerPassword: string = password.toLowerCase()\n\n for (const info of personalInfo) {\n const normalized: string = normalizePersonalInfo(info)\n\n // Skip very short strings to avoid false positives\n if (normalized.length < 3) {\n continue\n }\n\n // Check if password contains this personal information\n if (lowerPassword.includes(normalized)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'personalInfo.found',\n params,\n message: resolveMessage('personalInfo.found', params, options),\n }\n }\n }\n\n return {\n passed: true,\n }\n}\n","/**\n * @sentinel-password/core\n * Zero-dependency TypeScript password validation library\n */\n\nexport type {\n StrengthScore,\n StrengthLabel,\n ValidationResult,\n ValidatorOptions,\n ValidatorCheck,\n Validator,\n CheckId,\n MessageCode,\n MessageParams,\n MessageFormatter,\n} from './types'\n\nexport { DEFAULT_TEMPLATES } from './messages'\n\nexport { validateLength } from './validators/length'\nexport {\n hasUppercase,\n hasLowercase,\n hasDigit,\n hasSymbol,\n validateCharacterTypes,\n} from './validators/character-types'\nexport { validateRepetition } from './validators/repetition'\nexport { validateSequential } from './validators/sequential'\nexport { validateKeyboardPattern } from './validators/keyboard-pattern'\nexport { validateCommonPassword } from './validators/common-password'\nexport { validatePersonalInfo } from './validators/personal-info'\n\nimport type {\n ValidationResult,\n ValidatorOptions,\n StrengthScore,\n StrengthLabel,\n ValidatorCheck,\n CheckId,\n} from './types'\nimport { validateLength } from './validators/length'\nimport { validateCharacterTypes } from './validators/character-types'\nimport { validateRepetition } from './validators/repetition'\nimport { validateSequential } from './validators/sequential'\nimport { validateKeyboardPattern } from './validators/keyboard-pattern'\nimport { validateCommonPassword } from './validators/common-password'\nimport { validatePersonalInfo } from './validators/personal-info'\n\nconst STRENGTH_LABELS: readonly StrengthLabel[] = [\n 'very-weak',\n 'weak',\n 'medium',\n 'strong',\n 'very-strong',\n] as const\n\n/**\n * Validate a password with comprehensive security checks\n *\n * Performs multiple validation checks including length, character types, repetition,\n * sequential patterns, keyboard patterns, common passwords, and personal information.\n * Returns detailed feedback with strength score and actionable suggestions.\n *\n * @param password - The password string to validate\n * @param options - Optional validation configuration\n * @returns Validation result with score, strength label, feedback, and individual check results\n *\n * @example\n * **Basic usage (zero-config)**\n * ```typescript\n * import { validatePassword } from '@sentinel-password/core'\n *\n * const result = validatePassword('MySecure!Pass_w0rd')\n * console.log(result.valid) // true\n * console.log(result.score) // 4 (0-4 scale)\n * console.log(result.strength) // 'very-strong'\n * console.log(result.feedback.warning) // undefined (no issues)\n * console.log(result.feedback.suggestions) // []\n * ```\n *\n * @example\n * **With a known-common password**\n * ```typescript\n * const result = validatePassword('password')\n * console.log(result.valid) // false (commonPassword check rejected it)\n * console.log(result.score) // 4\n * console.log(result.strength) // 'very-strong'\n * console.log(result.feedback.warning)\n * // \"Password is too common. Please choose a more unique password.\"\n * console.log(result.feedback.suggestions)\n * // [\"Password is too common. Please choose a more unique password.\"]\n * console.log(result.checks)\n * // { length: true, characterTypes: true, repetition: true, sequential: true,\n * // keyboardPattern: true, commonPassword: false, personalInfo: true }\n * // ↑ 6 of 7 checks pass, so score is 4 (\"very-strong\") even though valid is false.\n * // Always use `valid` (or `result.checks`) for acceptance decisions, not `strength`.\n * ```\n *\n * @example\n * **Custom length requirements**\n * ```typescript\n * const result = validatePassword('MyP@ss', {\n * minLength: 12,\n * maxLength: 64\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password must be at least 12 characters\"\n * ```\n *\n * @example\n * **Require specific character types**\n * ```typescript\n * const result = validatePassword('password123', {\n * requireUppercase: true,\n * requireLowercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password must contain at least one uppercase letter, symbol\"\n * ```\n *\n * @example\n * **Prevent personal information**\n * ```typescript\n * const result = validatePassword('john1234!', {\n * personalInfo: ['john', 'john.doe@example.com', 'Doe']\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password contains personal information\"\n * ```\n *\n * @example\n * **Disable specific checks**\n * ```typescript\n * const result = validatePassword('qwerty123', {\n * checkKeyboardPatterns: false, // Allow keyboard patterns\n * checkSequential: false, // Allow sequential chars\n * checkCommonPasswords: false // Allow common passwords\n * })\n * // More permissive validation\n * ```\n *\n * @example\n * **Comprehensive configuration**\n * ```typescript\n * const result = validatePassword('MyP@ssw0rd2024!', {\n * minLength: 12,\n * maxLength: 128,\n * requireUppercase: true,\n * requireLowercase: true,\n * requireDigit: true,\n * requireSymbol: true,\n * maxRepeatedChars: 2,\n * checkSequential: true,\n * checkKeyboardPatterns: true,\n * checkCommonPasswords: true,\n * personalInfo: ['user', 'admin', 'test']\n * })\n * ```\n *\n * @remarks\n * **Default behavior:**\n * - Minimum length: 8 characters\n * - Maximum length: 128 characters\n * - No character type requirements (but recommended to enable)\n * - Max repeated characters: 3\n * - Sequential check: enabled\n * - Keyboard pattern check: enabled\n * - Common password check: enabled (top 1,000 passwords)\n * - Personal info check: disabled (provide personalInfo array to enable)\n *\n * **Scoring:**\n * - `score` = `Math.min(4, Math.floor((passedChecks / 7) * 5))` — purely a\n * passed-check ratio.\n * - `strength` is the human label for that score (`very-weak` … `very-strong`).\n * - Because scoring is ratio-based, a password that fails *only* the\n * common-password (or personal-info, or sequential, etc.) check still passes\n * 6 of 7 checks and lands on `score: 4 / strength: 'very-strong'` while\n * `valid` is `false`. Use `valid` (or inspect `result.checks`) for\n * acceptance decisions; use `strength` for UX cues like progress bars.\n *\n * **Performance:**\n * - All validators run in O(n) time or better\n * - Typical validation: < 1ms for passwords up to 128 characters\n * - Bloom filter for common passwords: O(1) lookup\n *\n * **Security:**\n * - All checks are case-insensitive where applicable\n * - No password data is logged or stored\n * - Runs purely in-process — no network calls, the password never leaves the\n * caller's runtime\n * - This is a *strength* validator, not a password-comparison primitive. The\n * validators use early-return `includes()`/loops and are not constant-time,\n * but timing is not a relevant attack surface here: the patterns being\n * checked (length, character types, common-password list, keyboard layouts)\n * are all public — there's no secret to leak via timing. When you compare\n * a password against a stored hash, use a library like Argon2/bcrypt that\n * provides constant-time verification — that's a separate concern from\n * strength validation.\n */\nexport function validatePassword(\n password: string,\n options: ValidatorOptions = {}\n): ValidationResult {\n const checks: Record<CheckId, boolean> = {\n length: false,\n characterTypes: false,\n repetition: false,\n sequential: false,\n keyboardPattern: false,\n commonPassword: false,\n personalInfo: false,\n }\n const suggestions: string[] = []\n\n // Run all validators\n const lengthResult: ValidatorCheck = validateLength(password, options)\n checks['length'] = lengthResult.passed\n if (!lengthResult.passed && lengthResult.message) {\n suggestions.push(lengthResult.message)\n }\n\n const charTypesResult: ValidatorCheck = validateCharacterTypes(password, options)\n checks['characterTypes'] = charTypesResult.passed\n if (!charTypesResult.passed && charTypesResult.message) {\n suggestions.push(charTypesResult.message)\n }\n\n const repetitionResult: ValidatorCheck = validateRepetition(password, options)\n checks['repetition'] = repetitionResult.passed\n if (!repetitionResult.passed && repetitionResult.message) {\n suggestions.push(repetitionResult.message)\n }\n\n const sequentialResult: ValidatorCheck = validateSequential(password, options)\n checks['sequential'] = sequentialResult.passed\n if (!sequentialResult.passed && sequentialResult.message) {\n suggestions.push(sequentialResult.message)\n }\n\n const commonPasswordResult: ValidatorCheck = validateCommonPassword(password, options)\n checks['commonPassword'] = commonPasswordResult.passed\n if (!commonPasswordResult.passed && commonPasswordResult.message) {\n suggestions.push(commonPasswordResult.message)\n }\n\n const personalInfoResult: ValidatorCheck = validatePersonalInfo(password, options)\n checks['personalInfo'] = personalInfoResult.passed\n if (!personalInfoResult.passed && personalInfoResult.message) {\n suggestions.push(personalInfoResult.message)\n }\n\n const keyboardPatternResult: ValidatorCheck = validateKeyboardPattern(password, options)\n checks['keyboardPattern'] = keyboardPatternResult.passed\n if (!keyboardPatternResult.passed && keyboardPatternResult.message) {\n suggestions.push(keyboardPatternResult.message)\n }\n\n // Calculate score based on passed checks\n const passedChecks: number = Object.values(checks).filter(Boolean).length\n const totalChecks: number = Object.keys(checks).length\n /* v8 ignore next */\n const ratio: number = totalChecks > 0 ? passedChecks / totalChecks : 0\n const score: StrengthScore = Math.min(4, Math.floor(ratio * 5)) as StrengthScore\n\n const firstSuggestion: string | undefined = suggestions.length > 0 ? suggestions[0] : undefined\n\n return {\n valid: Object.values(checks).every(Boolean),\n score,\n /* v8 ignore next */\n strength: STRENGTH_LABELS[score] ?? 'very-weak',\n feedback: {\n ...(firstSuggestion !== undefined && { warning: firstSuggestion }),\n suggestions,\n },\n checks,\n }\n}\n"],"mappings":"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","names":[]}
{"version":3,"sources":["../src/messages.ts","../src/validators/length.ts","../src/validators/character-types.ts","../src/validators/repetition.ts","../src/validators/sequential.ts","../src/validators/keyboard-pattern.ts","../src/validators/common-password.ts","../src/validators/personal-info.ts","../src/index.ts"],"sourcesContent":["import type { MessageCode, MessageParams, ValidatorOptions } from './types'\n\n/**\n * Built-in English templates for every {@link MessageCode}.\n *\n * The default rendering of a failed `ValidatorCheck.message` comes from this\n * map. Strings are stable across patch and minor releases — consumers can\n * still use them as translation keys with the legacy lookup-table pattern.\n * Prefer the `messages` / `formatMessage` options on {@link ValidatorOptions}.\n *\n * Placeholders use `{name}` syntax and are substituted by {@link formatTemplate}.\n */\nexport const DEFAULT_TEMPLATES: Readonly<Record<MessageCode, string>> = {\n 'length.tooShort': 'Password must be at least {minLength} characters',\n 'length.tooLong': 'Password must be at most {maxLength} characters',\n 'characterTypes.missing': 'Password must contain at least one {missing}',\n 'repetition.tooMany': 'Password contains too many repeated characters (max {maxRepeatedChars})',\n 'sequential.found': 'Password contains sequential characters (e.g., abc, 123)',\n 'keyboardPattern.found': 'Password contains common keyboard patterns',\n 'commonPassword.found': 'Password is too common. Please choose a more unique password.',\n 'personalInfo.found': 'Password contains personal information',\n} as const\n\nconst PLACEHOLDER_PATTERN: RegExp = /\\{(\\w+)\\}/g\n\n/**\n * Substitute `{name}` placeholders in `template` with values from `params`.\n *\n * Unknown placeholders are left intact (visible in the output) so missing\n * data surfaces as a noticeable bug rather than a silent omission. Values\n * are coerced to strings via the `String` constructor.\n *\n * @example\n * formatTemplate('Min {n} chars', { n: 8 }) // → \"Min 8 chars\"\n * formatTemplate('Need {a} and {b}', { a: 'X' }) // → \"Need X and {b}\"\n */\nexport function formatTemplate(template: string, params: MessageParams): string {\n return template.replace(PLACEHOLDER_PATTERN, (match, key: string): string => {\n const value: string | number | undefined = params[key]\n return value === undefined ? match : String(value)\n })\n}\n\n/**\n * Render a message via the fallback chain:\n * 1. `options.formatMessage(code, params, defaultMessage)` if provided\n * 2. `formatTemplate(options.messages[code], params)` if that override is provided\n * 3. `formatTemplate(DEFAULT_TEMPLATES[code], params)` (built-in English)\n *\n * Used by every validator's failure branch. Validators stay declarative —\n * they describe *what* failed (`code` + `params`) and delegate rendering.\n *\n * If `options.formatMessage` throws, this function swallows the error and\n * returns the default English rendering instead. Validators in this library\n * promise never to throw (see `Validator` in `./types`), and that promise\n * holds even when consumer-provided formatters misbehave.\n */\nexport function resolveMessage(\n code: MessageCode,\n params: MessageParams,\n options: ValidatorOptions\n): string {\n const defaultMessage: string = formatTemplate(DEFAULT_TEMPLATES[code], params)\n\n if (options.formatMessage) {\n try {\n return options.formatMessage(code, params, defaultMessage)\n } catch {\n return defaultMessage\n }\n }\n\n const override: string | undefined = options.messages?.[code]\n if (override !== undefined) {\n return formatTemplate(override, params)\n }\n\n return defaultMessage\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Validates password length against minimum and maximum requirements\n *\n * @param password - Password to validate\n * @param options - Validation options containing minLength and maxLength\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateLength } from '@sentinel-password/core'\n *\n * // Default: min 8, max 128 characters\n * validateLength('short') // { passed: false, message: \"Password must be at least 8 characters\" }\n * validateLength('longenough') // { passed: true }\n *\n * // Custom length requirements\n * validateLength('password', { minLength: 12 }) // { passed: false, message: \"...\" }\n * validateLength('verylongpassword', { minLength: 12, maxLength: 20 }) // { passed: true }\n * ```\n *\n * @remarks\n * Default minimum length is 8 characters (OWASP recommendation).\n * Default maximum length is 128 characters (prevents DoS attacks).\n */\nexport const validateLength: Validator = (password, options = {}) => {\n const { minLength = 8, maxLength = 128 }: Partial<{ minLength: number; maxLength: number }> =\n options\n\n const length: number = password.length\n\n if (length < minLength) {\n const params: MessageParams = { minLength }\n return {\n passed: false,\n code: 'length.tooShort',\n params,\n message: resolveMessage('length.tooShort', params, options),\n }\n }\n\n if (length > maxLength) {\n const params: MessageParams = { maxLength }\n return {\n passed: false,\n code: 'length.tooLong',\n params,\n message: resolveMessage('length.tooLong', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Check if password contains at least one uppercase letter (A-Z)\n *\n * @param password - Password to check\n * @returns True if password contains at least one uppercase letter\n *\n * @example\n * ```typescript\n * hasUppercase('password') // false\n * hasUppercase('Password') // true\n * hasUppercase('PASSWORD') // true\n * ```\n */\nexport const hasUppercase = (password: string): boolean => /[A-Z]/.test(password)\n\n/**\n * Check if password contains at least one lowercase letter (a-z)\n *\n * @param password - Password to check\n * @returns True if password contains at least one lowercase letter\n *\n * @example\n * ```typescript\n * hasLowercase('PASSWORD') // false\n * hasLowercase('Password') // true\n * hasLowercase('password') // true\n * ```\n */\nexport const hasLowercase = (password: string): boolean => /[a-z]/.test(password)\n\n/**\n * Check if password contains at least one digit (0-9)\n *\n * @param password - Password to check\n * @returns True if password contains at least one digit\n *\n * @example\n * ```typescript\n * hasDigit('password') // false\n * hasDigit('password1') // true\n * hasDigit('123') // true\n * ```\n */\nexport const hasDigit = (password: string): boolean => /\\d/.test(password)\n\n/**\n * Check if password contains at least one symbol/special character\n *\n * @param password - Password to check\n * @returns True if password contains at least one special character\n *\n * @example\n * ```typescript\n * hasSymbol('password') // false\n * hasSymbol('password!') // true\n * hasSymbol('p@ssw0rd') // true\n * ```\n *\n * @remarks\n * Accepted symbols: ! @ # $ % ^ & * ( ) _ + - = [ ] { } ; ' : \" \\ | , . < > / ?\n */\nexport const hasSymbol = (password: string): boolean =>\n /[!@#$%^&*()_+\\-=[\\]{};':\"\\\\|,.<>/?]/.test(password)\n\n/**\n * Validates character type requirements (uppercase, lowercase, digits, symbols)\n *\n * @param password - Password to validate\n * @param options - Validation options containing character type requirements\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateCharacterTypes } from '@sentinel-password/core'\n *\n * // No requirements by default\n * validateCharacterTypes('password') // { passed: true }\n *\n * // Require uppercase\n * validateCharacterTypes('password', { requireUppercase: true })\n * // { passed: false, message: \"Password must contain at least one uppercase letter\" }\n *\n * validateCharacterTypes('Password', { requireUppercase: true }) // { passed: true }\n *\n * // Require multiple types\n * validateCharacterTypes('password', {\n * requireUppercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * })\n * // { passed: false, message: \"Password must contain at least one uppercase letter, digit, symbol\" }\n *\n * validateCharacterTypes('Password1!', {\n * requireUppercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * }) // { passed: true }\n * ```\n *\n * @remarks\n * By default, no character types are required. Enable specific requirements via options.\n */\nexport const validateCharacterTypes: Validator = (password, options = {}) => {\n const {\n requireUppercase = false,\n requireLowercase = false,\n requireDigit = false,\n requireSymbol = false,\n }: Partial<{\n requireUppercase: boolean\n requireLowercase: boolean\n requireDigit: boolean\n requireSymbol: boolean\n }> = options\n\n const missing: string[] = []\n const missingTypes: string[] = []\n\n if (requireUppercase && !hasUppercase(password)) {\n missing.push('uppercase letter')\n missingTypes.push('uppercase')\n }\n\n if (requireLowercase && !hasLowercase(password)) {\n missing.push('lowercase letter')\n missingTypes.push('lowercase')\n }\n\n if (requireDigit && !hasDigit(password)) {\n missing.push('digit')\n missingTypes.push('digit')\n }\n\n if (requireSymbol && !hasSymbol(password)) {\n missing.push('symbol')\n missingTypes.push('symbol')\n }\n\n if (missing.length > 0) {\n const params: MessageParams = {\n missing: missing.join(', '),\n missingTypes: missingTypes.join(','),\n }\n return {\n passed: false,\n code: 'characterTypes.missing',\n params,\n message: resolveMessage('characterTypes.missing', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Validates that password doesn't contain excessive repeated characters\n *\n * Uses a single-pass algorithm to detect consecutive repeated characters.\n * Helps prevent weak passwords like \"aaaa1111\" or \"passwordddd\".\n *\n * @param password - Password to validate\n * @param options - Validation options containing maxRepeatedChars\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateRepetition } from '@sentinel-password/core'\n *\n * // Default: max 3 repeated characters\n * validateRepetition('password') // { passed: true }\n * validateRepetition('passsword') // { passed: true } (3 s's)\n * validateRepetition('passssword') // { passed: false } (4 s's)\n *\n * // Custom limit\n * validateRepetition('passssword', { maxRepeatedChars: 5 }) // { passed: true }\n * validateRepetition('aaa') // { passed: true }\n * validateRepetition('aaaa') // { passed: false, message: \"Password contains too many repeated characters (max 3)\" }\n * ```\n *\n * @remarks\n * Default maximum is 3 consecutive repeated characters.\n * Only checks for consecutive repetition, not overall character frequency.\n */\nexport const validateRepetition: Validator = (password, options = {}) => {\n const { maxRepeatedChars = 3 }: Partial<{ maxRepeatedChars: number }> = options\n\n if (password.length === 0) {\n return { passed: true }\n }\n\n let currentChar: string = password.charAt(0)\n let count: number = 1\n\n for (let i: number = 1; i < password.length; i++) {\n const char: string = password.charAt(i)\n if (char === currentChar) {\n count++\n if (count > maxRepeatedChars) {\n const params: MessageParams = { maxRepeatedChars }\n return {\n passed: false,\n code: 'repetition.tooMany',\n params,\n message: resolveMessage('repetition.tooMany', params, options),\n }\n }\n } else {\n currentChar = char\n count = 1\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Detects sequential character patterns (ascending or descending)\n * Checks for sequences of 3 or more consecutive characters\n *\n * @param str - String to check for sequences\n * @returns true if sequential pattern found, false otherwise\n */\nconst hasSequentialPattern = (str: string): boolean => {\n const minSequenceLength: number = 3\n\n for (let i: number = 0; i <= str.length - minSequenceLength; i++) {\n const charCode1: number = str.charCodeAt(i)\n const charCode2: number = str.charCodeAt(i + 1)\n const charCode3: number = str.charCodeAt(i + 2)\n\n // Check ascending sequence (e.g., abc, 123)\n if (charCode2 === charCode1 + 1 && charCode3 === charCode2 + 1) {\n return true\n }\n\n // Check descending sequence (e.g., cba, 321)\n if (charCode2 === charCode1 - 1 && charCode3 === charCode2 - 1) {\n return true\n }\n }\n\n return false\n}\n\n/**\n * Validates that password doesn't contain sequential character patterns\n *\n * Detects sequences like: abc, ABC, 123, 321, xyz, etc.\n * Uses character code comparison for efficient detection.\n * Helps prevent predictable passwords with keyboard sequences.\n *\n * @param password - Password to validate\n * @param options - Validation options containing checkSequential flag\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateSequential } from '@sentinel-password/core'\n *\n * // Detects ascending sequences\n * validateSequential('password') // { passed: true }\n * validateSequential('abc123') // { passed: false } (contains \"abc\" and \"123\")\n * validateSequential('xyz') // { passed: false } (contains \"xyz\")\n *\n * // Detects descending sequences\n * validateSequential('cba321') // { passed: false } (contains \"cba\" and \"321\")\n *\n * // Disable check\n * validateSequential('abc123', { checkSequential: false }) // { passed: true }\n * ```\n *\n * @remarks\n * Enabled by default. Checks for 3 or more consecutive characters in sequence.\n * Case-sensitive: detects both \"abc\" and \"ABC\" as separate patterns.\n *\n * **Overlap with `validateKeyboardPattern`:** The numeric runs `123`, `456`,\n * `789` (and their reverses) are also matched by the keyboard-pattern\n * validator's numeric-keypad list. To allow simple numeric runs in a\n * password you must set BOTH `checkSequential: false` AND\n * `checkKeyboardPatterns: false` — disabling either alone will not let\n * `password123` through. The two checks are deliberately independent\n * defences (code-point runs vs. keyboard-locality runs).\n */\nexport const validateSequential: Validator = (password, options = {}) => {\n const { checkSequential = true }: Partial<{ checkSequential: boolean }> = options\n\n if (!checkSequential) {\n return { passed: true }\n }\n\n if (hasSequentialPattern(password)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'sequential.found',\n params,\n message: resolveMessage('sequential.found', params, options),\n }\n }\n\n return {\n passed: true,\n }\n}\n","import type { MessageParams, ValidatorCheck, ValidatorOptions } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Common keyboard patterns to detect across multiple layouts\n * Supports: QWERTY, AZERTY, QWERTZ, Dvorak, Colemak, and Cyrillic\n */\nconst KEYBOARD_PATTERNS: readonly string[] = [\n // === QWERTY (English, US, UK) ===\n // Full rows\n 'qwertyuiop',\n 'asdfghjkl',\n 'zxcvbnm',\n // Common typing patterns (adjacent keys)\n 'qwert',\n 'werty',\n 'asdfg',\n 'sdfgh',\n 'zxcvb',\n 'xcvbn',\n // Short sequences (3+ chars)\n 'qwe',\n 'asd',\n 'zxc',\n 'rty',\n 'fgh',\n 'cvb',\n 'poi',\n 'lkj',\n 'mnb',\n // Columns (top to bottom)\n '1qaz',\n '2wsx',\n '3edc',\n '4rfv',\n '5tgb',\n '6yhn',\n '7ujm',\n '8ik',\n '9ol',\n '0p',\n // Diagonals\n 'qaz',\n 'wsx',\n 'edc',\n 'zaq',\n 'xsw',\n 'cde',\n\n // === AZERTY (French, Belgian) ===\n // Full rows\n 'azertyuiop',\n 'qsdfghjklm',\n 'wxcvbn',\n // Common patterns\n 'azert',\n 'zerty',\n 'qsdfg',\n 'sdfgh',\n 'wxcvb',\n // Short sequences\n 'aze',\n 'qsd',\n 'wxc',\n\n // === QWERTZ (German, Central European) ===\n // Full rows\n 'qwertzuiop',\n 'yxcvbnm',\n // Common patterns\n 'qwertz',\n 'yxcvb',\n // Short sequences\n 'yxc',\n // Note: Patterns 'qwe', 'asd', 'asdfg', and 'asdfghjkl' are shared with QWERTY and listed above for efficiency.\n\n // === Dvorak ===\n // Full rows\n 'pyfgcrl',\n 'aoeuidhtns',\n 'qjkxbmwvz',\n // Common patterns\n 'aoeu',\n 'htns',\n 'qjkx',\n\n // === Colemak ===\n // Full rows\n 'qwfpgjluy',\n 'arstdhneio',\n 'zxcvbkm',\n // Common patterns\n 'arst',\n 'dhne',\n 'zxcv',\n\n // === Cyrillic (ЙЦУКЕН - Russian) ===\n // Full rows (Cyrillic characters)\n 'йцукенгшщзхъ',\n 'фывапролджэ',\n 'ячсмитьбю',\n // Common patterns\n 'йцукен',\n 'фывап',\n 'ячсми',\n 'цукен',\n\n // === Numeric patterns (universal) ===\n // Number row\n '1234567890',\n '0987654321',\n // Numeric keypad (rows)\n '789',\n '456',\n '123',\n // Numeric keypad (columns)\n '741',\n '852',\n '963',\n '7410',\n '8520',\n '9630',\n] as const\n\n/**\n * Escape regex metacharacters in a string so it matches literally.\n * Defensive: none of the current patterns contain metacharacters, but this\n * keeps future additions safe (e.g., if a layout's pattern contains `+` or `$`).\n */\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/**\n * Single regex matching ANY keyboard pattern in either forward or reverse\n * direction, case-insensitively. Built once at module load.\n *\n * Performance note: this replaced an earlier implementation that ran a\n * `for` loop over `KEYBOARD_PATTERNS`, calling `pattern.split('').reverse().join('')`\n * to build the reverse string AND `lowercase.includes(pattern)` on every iteration\n * (~480 allocations per call). A single regex test against an NFA-compiled\n * alternation is ~12× faster on typical passwords on V8/Node 22.\n *\n * If a future V8 regex bug surfaces (e.g., a Unicode case-folding regression\n * affecting Cyrillic with the `/i` flag), there's a side-by-side comparison\n * against a precomputed-array loop variant in\n * `packages/core/tests/performance.bench.ts` → `Keyboard pattern: implementation comparison`.\n * The loop variant is ~2.5× faster than the original split/reverse/join code\n * and ~5× slower than the regex, so it's a usable rollback target.\n */\nconst KEYBOARD_REGEX: RegExp = new RegExp(\n KEYBOARD_PATTERNS.flatMap((p: string): readonly string[] => {\n const reversed: string = p.split('').reverse().join('')\n return [escapeRegex(p), escapeRegex(reversed)]\n }).join('|'),\n 'i'\n)\n\n/**\n * Validates that password does not contain common keyboard patterns\n *\n * Detects patterns across multiple keyboard layouts:\n * - QWERTY (English, US, UK): qwerty, asdfgh, 1qaz2wsx\n * - AZERTY (French, Belgian): azerty, qsdfg\n * - QWERTZ (German, Central European): qwertz, yxcvb\n * - Dvorak (Alternative English): aoeu, htns\n * - Colemak (Alternative English): arst, dhne\n * - Cyrillic (Russian ЙЦУКЕН): йцукен, фывап\n * - Universal numeric: 123, 789, numeric keypad patterns\n * - Both forward and reverse patterns\n *\n * @param password - The password to validate\n * @param options - Validation options\n * @returns Validation result\n *\n * @example\n * ```typescript\n * validateKeyboardPattern('qwerty123') // { passed: false, message: '...' }\n * validateKeyboardPattern('azerty456') // { passed: false, message: '...' } (AZERTY)\n * validateKeyboardPattern('йцукен') // { passed: false, message: '...' } (Cyrillic)\n * validateKeyboardPattern('MyP@ssw0rd') // { passed: true }\n * validateKeyboardPattern('asdfgh', { checkKeyboardPatterns: false }) // { passed: true }\n * ```\n *\n * @remarks\n * **Overlap with `validateSequential`:** The numeric-keypad rows `123`,\n * `456`, `789` (and their reverses) are also caught by the sequential\n * validator's `charCodeAt`-consecutive check. To allow simple numeric\n * runs in a password you must set BOTH `checkKeyboardPatterns: false`\n * AND `checkSequential: false` — disabling either alone will not let\n * `password123` through. The two checks are deliberately independent\n * defences (keyboard-locality runs vs. code-point runs).\n */\nexport function validateKeyboardPattern(\n password: string,\n options: ValidatorOptions = {}\n): ValidatorCheck {\n const { checkKeyboardPatterns = true }: Partial<{ checkKeyboardPatterns: boolean }> = options\n\n if (!checkKeyboardPatterns) {\n return { passed: true }\n }\n\n if (KEYBOARD_REGEX.test(password)) {\n const emptyParams: MessageParams = {}\n return {\n passed: false,\n code: 'keyboardPattern.found',\n params: emptyParams,\n message: resolveMessage('keyboardPattern.found', emptyParams, options),\n }\n }\n\n return { passed: true }\n}\n","import type { MessageParams, Validator, ValidatorOptions } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Bloom filter for common passwords\n *\n * Source: SecLists https://github.com/danielmiessler/SecLists\n * File: Passwords/Common-Credentials/10k-most-common.txt (top 1,000)\n * Local: packages/core/data/common-passwords.txt\n *\n * Regenerate with: pnpm --filter @sentinel-password/core generate:bloom\n */\n\n// --- BEGIN GENERATED BLOOM FILTER ---\n// Generated from: packages/core/data/common-passwords.txt\n// Passwords: 1000 | Bloom size: 12000 bits | Hash functions: 7\nconst BLOOM_SIZE: number = 12000\nconst BLOOM_HASH_COUNT: number = 7\n\nconst BLOOM_BUCKETS: Int32Array = new Int32Array([\n -1274142440, -1983615455, -2110842727, -1440077142, 1782964852, 956870738, 48445478, 290080800,\n -1961772502, -1869995330, 787111091, 140549329, -1508237141, -800970204, -1987272632, -1439602637,\n 1837731885, 240419586, 2143496680, 547359246, -435494235, -1549227926, 151650466, -1742731903,\n -1535043550, -1607849886, -464376123, 1858481975, -1339118942, 410034491, -1397081975, 1217017604,\n -1934873593, -1769402149, -1107107031, -1098029453, 4380674, -393017311, -1465113568, 183557329,\n 1713935653, -99073116, 179529869, -1372675933, 78050105, 313074313, -1696462848, 404392594,\n -252409893, -1320442777, -1474295681, -1360120173, -743767380, 135925863, -1462755274, 562596610,\n -483730736, -2002210214, 1803176, 448811114, -1408849403, -735411494, -1872045334, 914538658,\n -374794234, 710617115, -1909840320, -2119695701, -1979682099, -1671944535, -1872492857, 570434164,\n -1334628885, 438020137, -1087528694, 464528967, -359521705, -1407023569, -1759335432, -1181701078,\n -1959746004, -1910872796, 1771191076, -1699312926, -1837137785, 132360706, -1002778102,\n 1384163571, 1786784659, -1354832568, -1364547569, -269830616, -1970133308, 848887818, -827577818,\n -1103500629, 2071996096, 1760287282, 1768617098, -1845466197, -1877054343, -223851966,\n -1037686738, 581441802, -761237365, -1282923217, 1328736770, 557363947, 369254184, -1708971466,\n -874268952, -2010768709, -1735196024, -1023761881, 772024867, -328714721, -125771638, 1644593802,\n 1762927155, -1509621238, 671919842, -1102294492, -2010455282, 302591302, -2136446493, -1986908626,\n -2018566016, 671347107, 842040354, 243352234, -534627656, -2011035123, -350834429, -844096647,\n -1598189902, -1833872134, -2021613470, -1968994020, -1297357224, 1210222837, -399712104,\n 906129314, -1430115578, -2113618941, -906361552, 551676011, 2819256, -1710063476, 1099626796,\n 1026172967, 640491579, -1440579096, -1887270648, -1710185214, 1721813836, 984101107, 1944258615,\n 724137993, -947645533, 1913070606, 44829860, 101219306, 1011556358, -1432173338, -482828238,\n -2103243204, 447353024, -329610536, 1398671502, 1623196423, 348309551, -211221782, 1980906030,\n 778830798, 968125066, -1458011638, -1337934774, 671089894, 52600876, -120418625, -1163255178,\n 680202936, -1597565152, 36835260, 1845733400, 153924331, 40509970, 411221088, 2114095983,\n 171043813, 1488604610, 172814850, -1702382524, 1502872105, 747635458, -501052624, 411301928,\n 731823798, -1333221971, -398552509, 134621864, -1872819037, 1822818465, -1893684382, -1316869470,\n -931632595, -1951260093, 598498019, -1290239178, -989323246, 573220082, 675809232, 169874120,\n -559880256, -910750974, 194548323, 736774690, -719202768, 1814594698, -1155290520, 237063072,\n -488083322, -924144083, 36385696, 715956226, -1568572256, 635709488, 1336062594, 320907784,\n 1124501765, -1406433280, -1963307926, -391509877, 1063369670, -1566479450, -1472058776,\n -1985331186, 1377869964, 942987938, 1109713448, 831578699, 853962256, 92835849, 2035317046,\n -2001761759, 216178811, 463827086, 1675819625, -1301764214, -234217277, -1459402461, 579365994,\n -1878875352, -1973245719, -1598478333, 1611084451, 1659513348, -1539122450, 837944629, 154749747,\n -500636954, 1746899746, 714271018, -1150679461, -1538612630, 1814069472, -492197206, -192202641,\n -756936190, -939252957, -1240063770, 1279410916, -1302054220, 1410991882, 1000401066, -198048178,\n -1419228998, -1742550398, -1991360862, -2111688732, -1700640206, 2061912618, -2103274201,\n -288731098, 578232, 53128594, 1755908256, 1116379816, 797411910, -1589060869, -1072342375,\n 845812237, 1749465810, 872549054, -1498077050, -1766137746, -376831803, -2084952921, 1202203194,\n 1216389770, -1104576382, 818061341, 942317755, -1578565146, 2083914250, -1012266815, 579371193,\n -461075653, 710981226, 1578192039, 681770739, -376523066, 1244735807, -490842006, -781193759,\n 556277799, 1984167471, -2000977760, -1265456598, 151665664, -1842697570, 579340290, 1082298021,\n 245211512, -1867881849, -1341630964, -90545664, 1806344898, 47088170, 992486842, -1558631407,\n -1449508691, -2006144829, 547374756, 912919200, 708120146, 616106151, 1172449414, 2124751394,\n -2073427929, 1009261122, 1210336770, -1973924734, 144458353, -1155209078, -1962399613, 897229836,\n 12357639, -1299101013, 312943930, 708920290, -1310023134, 639040320, -821838839, -2014506238,\n -274610648, -1442508704, 177432331, 1721041571, 414065321, -1272924654, 756208161, 1772773934,\n -1484380630, 1101004848, -1054523758, -2116504793, 1118667297, 86058657, 975856680, 749906058,\n 1937961650, 620890537, 918765730, -1366546774,\n])\n// --- END GENERATED BLOOM FILTER ---\n\n/**\n * Hash function for bloom filter\n */\nfunction hashString(str: string, seed: number): number {\n let hash: number = seed\n\n for (let i: number = 0; i < str.length; i++) {\n const char: number = str.charCodeAt(i)\n hash = (hash << 5) - hash + char\n hash = hash | 0 // Convert to 32-bit integer\n }\n\n return Math.abs(hash)\n}\n\n/**\n * Get multiple hash positions for a password\n */\nfunction getHashes(password: string): number[] {\n const hashes: number[] = []\n const hash1: number = hashString(password, 0)\n const hash2: number = hashString(password, 1)\n\n for (let i: number = 0; i < BLOOM_HASH_COUNT; i++) {\n const hash: number = (hash1 + i * hash2) >>> 0\n hashes.push(hash % BLOOM_SIZE)\n }\n\n return hashes\n}\n\n/**\n * Check if password might be in the common password list\n * Note: Bloom filters can have false positives (~0.84%) but never false negatives\n */\nfunction mightBeCommon(password: string): boolean {\n const hashes: number[] = getHashes(password.toLowerCase())\n\n for (const hash of hashes) {\n const arrayIndex: number = Math.floor(hash / 32)\n const bitIndex: number = hash % 32\n\n // Bounds check for TypeScript strict mode\n const bucket: number | undefined = BLOOM_BUCKETS[arrayIndex]\n if (bucket === undefined || (bucket & (1 << bitIndex)) === 0) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Validates that a password is not in the common password list\n *\n * Uses a Bloom filter to efficiently check against the top 1,000 most common passwords.\n * Case-insensitive matching prevents simple case variations of common passwords.\n *\n * @param password - Password to validate\n * @param options - Validation options containing checkCommonPasswords flag\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validateCommonPassword } from '@sentinel-password/core'\n *\n * // Detects common passwords\n * validateCommonPassword('password')\n * // { passed: false, message: \"Password is too common. Please choose a more unique password.\" }\n *\n * validateCommonPassword('123456')\n * // { passed: false }\n *\n * // Case-insensitive\n * validateCommonPassword('PASSWORD')\n * // { passed: false }\n *\n * // Unique passwords pass\n * validateCommonPassword('MyUn1qu3P@ssw0rd!')\n * // { passed: true }\n *\n * // Disable check\n * validateCommonPassword('password', { checkCommonPasswords: false })\n * // { passed: true }\n * ```\n *\n * @remarks\n * Checks against top 1,000 most common passwords from SecLists.\n * Uses Bloom filter for space efficiency (~1.5KB vs ~8KB for raw array).\n * False positive rate: ~0.84% (may rarely flag uncommon passwords).\n * Enabled by default for security.\n */\nexport const validateCommonPassword: Validator = (\n password: string,\n options: ValidatorOptions = {}\n) => {\n const { checkCommonPasswords = true }: { checkCommonPasswords?: boolean } = options\n\n if (!checkCommonPasswords || password.length === 0) {\n return { passed: true }\n }\n\n // Case-insensitive check using bloom filter\n if (mightBeCommon(password)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'commonPassword.found',\n params,\n message: resolveMessage('commonPassword.found', params, options),\n }\n }\n\n return { passed: true }\n}\n","import type { MessageParams, Validator } from '../types'\nimport { resolveMessage } from '../messages'\n\n/**\n * Extracts username from email address\n * @param email - Email address\n * @returns Username part before @ symbol\n */\nconst extractUsername = (email: string): string => {\n const atIndex: number = email.indexOf('@')\n /* v8 ignore next */\n return atIndex > 0 ? email.substring(0, atIndex) : email\n}\n\n/**\n * Normalizes personal info strings for comparison\n * Converts to lowercase and extracts username from emails\n *\n * @param info - Personal information string\n * @returns Normalized string for comparison\n */\nconst normalizePersonalInfo = (info: string): string => {\n const normalized: string = info.toLowerCase().trim()\n // Extract username from email if it looks like an email\n return normalized.includes('@') ? extractUsername(normalized) : normalized\n}\n\n/**\n * Validates that password doesn't contain personal information\n *\n * Checks against provided personal info (username, email, name, etc.)\n * Uses case-insensitive comparison and extracts usernames from email addresses.\n * Ignores very short strings (< 3 characters) to avoid false positives.\n *\n * @param password - Password to validate\n * @param options - Validation options containing personalInfo array\n * @returns Validator check result with passed status and optional error message\n *\n * @example\n * ```typescript\n * import { validatePersonalInfo } from '@sentinel-password/core'\n *\n * // No personal info by default\n * validatePersonalInfo('password') // { passed: true }\n *\n * // Detects username in password\n * validatePersonalInfo('johnpassword', { personalInfo: ['john'] })\n * // { passed: false, message: \"Password contains personal information\" }\n *\n * // Emails are reduced to the *entire local part* (everything before `@`),\n * // matched as a literal substring — not split into name fragments.\n * validatePersonalInfo('john.doe123', { personalInfo: ['john.doe@example.com'] })\n * // { passed: false } (matches the full local part \"john.doe\")\n *\n * validatePersonalInfo('john123', { personalInfo: ['john.doe@example.com'] })\n * // { passed: true } (the local part is \"john.doe\", not \"john\" — no match)\n *\n * // To reject passwords containing just \"john\", pass it explicitly:\n * validatePersonalInfo('john123', { personalInfo: ['john', 'john.doe@example.com'] })\n * // { passed: false } (matches the literal \"john\")\n *\n * // Case-insensitive\n * validatePersonalInfo('JOHN123', { personalInfo: ['john'] })\n * // { passed: false }\n *\n * // Ignores short strings\n * validatePersonalInfo('password', { personalInfo: ['pw'] }) // { passed: true } (too short)\n *\n * // Multiple personal info items\n * validatePersonalInfo('secretpass', {\n * personalInfo: ['john', 'doe', 'john.doe@example.com']\n * }) // { passed: true }\n * ```\n *\n * @remarks\n * Best practice: Provide username, email, first name, last name, and company name.\n * Strings shorter than 3 characters are ignored to prevent false positives.\n */\nexport const validatePersonalInfo: Validator = (password, options = {}) => {\n const { personalInfo = [] }: Partial<{ personalInfo: string[] }> = options\n\n if (personalInfo.length === 0 || password.length === 0) {\n return { passed: true }\n }\n\n const lowerPassword: string = password.toLowerCase()\n\n for (const info of personalInfo) {\n const normalized: string = normalizePersonalInfo(info)\n\n // Skip very short strings to avoid false positives\n if (normalized.length < 3) {\n continue\n }\n\n // Check if password contains this personal information\n if (lowerPassword.includes(normalized)) {\n const params: MessageParams = {}\n return {\n passed: false,\n code: 'personalInfo.found',\n params,\n message: resolveMessage('personalInfo.found', params, options),\n }\n }\n }\n\n return {\n passed: true,\n }\n}\n","/**\n * @sentinel-password/core\n * Zero-dependency TypeScript password validation library\n */\n\nexport type {\n StrengthScore,\n StrengthLabel,\n ValidationResult,\n ValidatorOptions,\n ValidatorCheck,\n Validator,\n CheckId,\n MessageCode,\n MessageParams,\n MessageFormatter,\n} from './types'\n\nexport { DEFAULT_TEMPLATES } from './messages'\n\nexport { validateLength } from './validators/length'\nexport {\n hasUppercase,\n hasLowercase,\n hasDigit,\n hasSymbol,\n validateCharacterTypes,\n} from './validators/character-types'\nexport { validateRepetition } from './validators/repetition'\nexport { validateSequential } from './validators/sequential'\nexport { validateKeyboardPattern } from './validators/keyboard-pattern'\nexport { validateCommonPassword } from './validators/common-password'\nexport { validatePersonalInfo } from './validators/personal-info'\n\nimport type {\n ValidationResult,\n ValidatorOptions,\n StrengthScore,\n StrengthLabel,\n ValidatorCheck,\n CheckId,\n} from './types'\nimport { validateLength } from './validators/length'\nimport { validateCharacterTypes } from './validators/character-types'\nimport { validateRepetition } from './validators/repetition'\nimport { validateSequential } from './validators/sequential'\nimport { validateKeyboardPattern } from './validators/keyboard-pattern'\nimport { validateCommonPassword } from './validators/common-password'\nimport { validatePersonalInfo } from './validators/personal-info'\n\nconst STRENGTH_LABELS: readonly StrengthLabel[] = [\n 'very-weak',\n 'weak',\n 'medium',\n 'strong',\n 'very-strong',\n] as const\n\n/**\n * Validate a password with comprehensive security checks\n *\n * Performs multiple validation checks including length, character types, repetition,\n * sequential patterns, keyboard patterns, common passwords, and personal information.\n * Returns detailed feedback with strength score and actionable suggestions.\n *\n * @param password - The password string to validate\n * @param options - Optional validation configuration\n * @returns Validation result with score, strength label, feedback, and individual check results\n *\n * @example\n * **Basic usage (zero-config)**\n * ```typescript\n * import { validatePassword } from '@sentinel-password/core'\n *\n * const result = validatePassword('MySecure!Pass_w0rd')\n * console.log(result.valid) // true\n * console.log(result.score) // 4 (0-4 scale)\n * console.log(result.strength) // 'very-strong'\n * console.log(result.feedback.warning) // undefined (no issues)\n * console.log(result.feedback.suggestions) // []\n * ```\n *\n * @example\n * **With a known-common password**\n * ```typescript\n * const result = validatePassword('password')\n * console.log(result.valid) // false (commonPassword check rejected it)\n * console.log(result.score) // 4\n * console.log(result.strength) // 'very-strong'\n * console.log(result.feedback.warning)\n * // \"Password is too common. Please choose a more unique password.\"\n * console.log(result.feedback.suggestions)\n * // [\"Password is too common. Please choose a more unique password.\"]\n * console.log(result.checks)\n * // { length: true, characterTypes: true, repetition: true, sequential: true,\n * // keyboardPattern: true, commonPassword: false, personalInfo: true }\n * // ↑ 6 of 7 checks pass, so score is 4 (\"very-strong\") even though valid is false.\n * // Always use `valid` (or `result.checks`) for acceptance decisions, not `strength`.\n * ```\n *\n * @example\n * **Custom length requirements**\n * ```typescript\n * const result = validatePassword('MyP@ss', {\n * minLength: 12,\n * maxLength: 64\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password must be at least 12 characters\"\n * ```\n *\n * @example\n * **Require specific character types**\n * ```typescript\n * const result = validatePassword('password123', {\n * requireUppercase: true,\n * requireLowercase: true,\n * requireDigit: true,\n * requireSymbol: true\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password must contain at least one uppercase letter, symbol\"\n * ```\n *\n * @example\n * **Prevent personal information**\n * ```typescript\n * const result = validatePassword('john1234!', {\n * personalInfo: ['john', 'john.doe@example.com', 'Doe']\n * })\n * console.log(result.valid) // false\n * console.log(result.feedback.warning) // \"Password contains personal information\"\n * ```\n *\n * @example\n * **Disable specific checks**\n * ```typescript\n * const result = validatePassword('qwerty123', {\n * checkKeyboardPatterns: false, // Allow keyboard patterns\n * checkSequential: false, // Allow sequential chars\n * checkCommonPasswords: false // Allow common passwords\n * })\n * // More permissive validation\n * ```\n *\n * @example\n * **Comprehensive configuration**\n * ```typescript\n * const result = validatePassword('MyP@ssw0rd2024!', {\n * minLength: 12,\n * maxLength: 128,\n * requireUppercase: true,\n * requireLowercase: true,\n * requireDigit: true,\n * requireSymbol: true,\n * maxRepeatedChars: 2,\n * checkSequential: true,\n * checkKeyboardPatterns: true,\n * checkCommonPasswords: true,\n * personalInfo: ['user', 'admin', 'test']\n * })\n * ```\n *\n * @remarks\n * **Default behavior:**\n * - Minimum length: 8 characters\n * - Maximum length: 128 characters\n * - No character type requirements (but recommended to enable)\n * - Max repeated characters: 3\n * - Sequential check: enabled\n * - Keyboard pattern check: enabled\n * - Common password check: enabled (top 1,000 passwords)\n * - Personal info check: disabled (provide personalInfo array to enable)\n *\n * **Scoring:**\n * - `score` = `Math.min(4, Math.floor((passedChecks / 7) * 5))` — purely a\n * passed-check ratio.\n * - `strength` is the human label for that score (`very-weak` … `very-strong`).\n * - Because scoring is ratio-based, a password that fails *only* the\n * common-password (or personal-info, or sequential, etc.) check still passes\n * 6 of 7 checks and lands on `score: 4 / strength: 'very-strong'` while\n * `valid` is `false`. Use `valid` (or inspect `result.checks`) for\n * acceptance decisions; use `strength` for UX cues like progress bars.\n *\n * **Performance:**\n * - All validators run in O(n) time or better\n * - Typical validation: < 1ms for passwords up to 128 characters\n * - Bloom filter for common passwords: O(1) lookup\n *\n * **Security:**\n * - All checks are case-insensitive where applicable\n * - No password data is logged or stored\n * - Runs purely in-process — no network calls, the password never leaves the\n * caller's runtime\n * - This is a *strength* validator, not a password-comparison primitive. The\n * validators use early-return `includes()`/loops and are not constant-time,\n * but timing is not a relevant attack surface here: the patterns being\n * checked (length, character types, common-password list, keyboard layouts)\n * are all public — there's no secret to leak via timing. When you compare\n * a password against a stored hash, use a library like Argon2/bcrypt that\n * provides constant-time verification — that's a separate concern from\n * strength validation.\n */\nexport function validatePassword(\n password: string,\n options: ValidatorOptions = {}\n): ValidationResult {\n const checks: Record<CheckId, boolean> = {\n length: false,\n characterTypes: false,\n repetition: false,\n sequential: false,\n keyboardPattern: false,\n commonPassword: false,\n personalInfo: false,\n }\n const suggestions: string[] = []\n\n // Run all validators\n const lengthResult: ValidatorCheck = validateLength(password, options)\n checks['length'] = lengthResult.passed\n if (!lengthResult.passed && lengthResult.message) {\n suggestions.push(lengthResult.message)\n }\n\n const charTypesResult: ValidatorCheck = validateCharacterTypes(password, options)\n checks['characterTypes'] = charTypesResult.passed\n if (!charTypesResult.passed && charTypesResult.message) {\n suggestions.push(charTypesResult.message)\n }\n\n const repetitionResult: ValidatorCheck = validateRepetition(password, options)\n checks['repetition'] = repetitionResult.passed\n if (!repetitionResult.passed && repetitionResult.message) {\n suggestions.push(repetitionResult.message)\n }\n\n const sequentialResult: ValidatorCheck = validateSequential(password, options)\n checks['sequential'] = sequentialResult.passed\n if (!sequentialResult.passed && sequentialResult.message) {\n suggestions.push(sequentialResult.message)\n }\n\n const commonPasswordResult: ValidatorCheck = validateCommonPassword(password, options)\n checks['commonPassword'] = commonPasswordResult.passed\n if (!commonPasswordResult.passed && commonPasswordResult.message) {\n suggestions.push(commonPasswordResult.message)\n }\n\n const personalInfoResult: ValidatorCheck = validatePersonalInfo(password, options)\n checks['personalInfo'] = personalInfoResult.passed\n if (!personalInfoResult.passed && personalInfoResult.message) {\n suggestions.push(personalInfoResult.message)\n }\n\n const keyboardPatternResult: ValidatorCheck = validateKeyboardPattern(password, options)\n checks['keyboardPattern'] = keyboardPatternResult.passed\n if (!keyboardPatternResult.passed && keyboardPatternResult.message) {\n suggestions.push(keyboardPatternResult.message)\n }\n\n // Calculate score based on passed checks\n const passedChecks: number = Object.values(checks).filter(Boolean).length\n const totalChecks: number = Object.keys(checks).length\n /* v8 ignore next */\n const ratio: number = totalChecks > 0 ? passedChecks / totalChecks : 0\n const score: StrengthScore = Math.min(4, Math.floor(ratio * 5)) as StrengthScore\n\n const firstSuggestion: string | undefined = suggestions.length > 0 ? suggestions[0] : undefined\n\n return {\n valid: Object.values(checks).every(Boolean),\n score,\n /* v8 ignore next */\n strength: STRENGTH_LABELS[score] ?? 'very-weak',\n feedback: {\n ...(firstSuggestion !== undefined && { warning: firstSuggestion }),\n suggestions,\n },\n checks,\n }\n}\n"],"mappings":"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","names":[]}
{
"name": "@sentinel-password/core",
"version": "1.2.0",
"version": "1.2.1",
"type": "module",

@@ -5,0 +5,0 @@ "description": "Modern, zero-dependency TypeScript password validation with bloom filter-based common password detection. 100% test coverage (enforced); ~5.5 KB gzipped (10 KB CI limit).",