UNPKG

@aladas-org/cryptocalc

Version:
452 lines (346 loc) 17.2 kB
/** * ============================================================================ * Unit Tests - PasswordStrengthEvaluator * ============================================================================ * Tests the PasswordStrengthEvaluator singleton * Location: www/js/crypto/password_strength_evaluator.js * ============================================================================ */ const { PasswordStrengthEvaluator } = require('@crypto/password_strength_evaluator.js'); const { PWD_STR_AS_SCORE, PWD_STR_AS_ADJECTIVE } = require('@www/js/const_keywords.js'); describe('PasswordStrengthEvaluator', () => { let pse; beforeAll(() => { pse = PasswordStrengthEvaluator.This; }); // ========================================================================== // Singleton // ========================================================================== describe('Singleton Pattern', () => { test('This retourne une instance définie', () => { expect(pse).toBeDefined(); }); test('This retourne toujours la même instance', () => { expect(PasswordStrengthEvaluator.This).toBe(pse); }); test("appel direct du constructeur lève une TypeError", () => { expect(() => new PasswordStrengthEvaluator()).toThrow(TypeError); }); test('ALPHABET_ENTROPIES est défini comme propriété statique', () => { expect(PasswordStrengthEvaluator.ALPHABET_ENTROPIES).toBeDefined(); }); }); // ========================================================================== // is_binary_string // ========================================================================== describe('is_binary_string', () => { test('retourne true pour "010101"', () => { expect(pse.is_binary_string('010101')).toBe(true); }); test('retourne true pour "0"', () => { expect(pse.is_binary_string('0')).toBe(true); }); test('retourne true pour "1"', () => { expect(pse.is_binary_string('1')).toBe(true); }); test('retourne true pour une longue chaîne binaire', () => { expect(pse.is_binary_string('0'.repeat(128))).toBe(true); }); test('retourne false pour "012" (contient 2)', () => { expect(pse.is_binary_string('012')).toBe(false); }); test('retourne false pour une chaîne vide', () => { expect(pse.is_binary_string('')).toBe(false); }); test('retourne false pour undefined', () => { expect(pse.is_binary_string(undefined)).toBe(false); }); test('retourne false pour null', () => { expect(pse.is_binary_string(null)).toBe(false); }); }); // ========================================================================== // is_hexa_string // ========================================================================== describe('is_hexa_string', () => { test('retourne true pour "deadbeef"', () => { expect(pse.is_hexa_string('deadbeef')).toBe(true); }); test('retourne true pour "DEADBEEF" (uppercase converti)', () => { expect(pse.is_hexa_string('DEADBEEF')).toBe(true); }); test('retourne true pour une clé privée de 64 chars', () => { expect(pse.is_hexa_string('0'.repeat(64))).toBe(true); }); test('retourne true pour "0xdeadbeef" (préfixe 0x accepté)', () => { expect(pse.is_hexa_string('0xdeadbeef')).toBe(true); }); test('retourne false pour "xyz"', () => { expect(pse.is_hexa_string('xyz')).toBe(false); }); test('retourne false pour une chaîne vide', () => { expect(pse.is_hexa_string('')).toBe(false); }); test('retourne false pour undefined', () => { expect(pse.is_hexa_string(undefined)).toBe(false); }); }); // ========================================================================== // is_base58_string // ========================================================================== describe('is_base58_string', () => { test('retourne true pour une adresse Bitcoin valide', () => { expect(pse.is_base58_string('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa')).toBe(true); }); test('retourne false pour une chaîne contenant "0" (zéro)', () => { expect(pse.is_base58_string('1A1z0P1e')).toBe(false); }); test('retourne false pour une chaîne vide', () => { expect(pse.is_base58_string('')).toBe(false); }); test('retourne false pour undefined', () => { expect(pse.is_base58_string(undefined)).toBe(false); }); }); // ========================================================================== // is_base64_string // ========================================================================== describe('is_base64_string', () => { test('retourne true pour une chaîne base64 valide', () => { expect(pse.is_base64_string('SGVsbG8gV29ybGQ=')).toBe(true); }); test('retourne true pour une chaîne base64 sans padding', () => { expect(pse.is_base64_string('SGVsbG8gV29ybGQ')).toBe(true); }); test('retourne false pour une chaîne contenant des caractères invalides', () => { expect(pse.is_base64_string('Hello World!')).toBe(false); }); test('retourne false pour une chaîne vide', () => { expect(pse.is_base64_string('')).toBe(false); }); }); // ========================================================================== // is_octal_string // ========================================================================== describe('is_octal_string', () => { test('retourne true pour "01234567"', () => { expect(pse.is_octal_string('01234567')).toBe(true); }); test('retourne false pour "8" (hors plage octal)', () => { expect(pse.is_octal_string('8')).toBe(false); }); test('retourne false pour une chaîne vide', () => { expect(pse.is_octal_string('')).toBe(false); }); }); // ========================================================================== // is_upper_case / is_lower_case / is_digit / is_special_character // ========================================================================== describe('is_upper_case', () => { test('retourne true pour "A"', () => expect(pse.is_upper_case('A')).toBe(true)); test('retourne false pour "a"', () => expect(pse.is_upper_case('a')).toBe(false)); test('retourne false pour "1"', () => expect(pse.is_upper_case('1')).toBe(false)); }); describe('is_lower_case', () => { test('retourne true pour "a"', () => expect(pse.is_lower_case('a')).toBe(true)); test('retourne false pour "A"', () => expect(pse.is_lower_case('A')).toBe(false)); test('retourne false pour "1"', () => expect(pse.is_lower_case('1')).toBe(false)); }); describe('is_digit', () => { test('retourne true pour "5"', () => expect(pse.is_digit('5')).toBe(true)); test('retourne true pour "0"', () => expect(pse.is_digit('0')).toBe(true)); test('retourne false pour "a"', () => expect(pse.is_digit('a')).toBe(false)); }); describe('is_special_character', () => { test('retourne true pour "#"', () => expect(pse.is_special_character('#')).toBe(true)); test('retourne true pour "*"', () => expect(pse.is_special_character('*')).toBe(true)); test('retourne true pour "!"', () => expect(pse.is_special_character('!')).toBe(true)); test('retourne false pour "a"', () => expect(pse.is_special_character('a')).toBe(false)); test('retourne false pour "5"', () => expect(pse.is_special_character('5')).toBe(false)); }); // ========================================================================== // getEntropyForAlphabetAsBits // ========================================================================== describe('getEntropyForAlphabetAsBits', () => { test('alphabet de 2 → 1.00 bit', () => { expect(pse.getEntropyForAlphabetAsBits(2)).toBeCloseTo(1.00, 2); }); test('alphabet de 16 → 4.00 bits (hex)', () => { expect(pse.getEntropyForAlphabetAsBits(16)).toBeCloseTo(4.00, 2); }); test('alphabet de 58 → ~5.86 bits (base58)', () => { expect(pse.getEntropyForAlphabetAsBits(58)).toBeCloseTo(5.86, 1); }); test('alphabet de 64 → 6.00 bits (base64)', () => { expect(pse.getEntropyForAlphabetAsBits(64)).toBeCloseTo(6.00, 2); }); test('retourne 0 pour alphabet_size ≤ 1', () => { expect(pse.getEntropyForAlphabetAsBits(1)).toBe(0); expect(pse.getEntropyForAlphabetAsBits(0)).toBe(0); }); test("augmente avec la taille de l'alphabet", () => { expect(pse.getEntropyForAlphabetAsBits(32)) .toBeGreaterThan(pse.getEntropyForAlphabetAsBits(16)); }); }); // ========================================================================== // getPasswordStrengthAsBits // ========================================================================== describe('getPasswordStrengthAsBits', () => { test('chaîne binaire "010101" → 6 * 1.00 = 6 bits', () => { expect(pse.getPasswordStrengthAsBits('010101')).toBeCloseTo(6.00, 1); }); test('chaîne hex "deadbeef" (8 chars) → 8 * 4.00 = 32 bits', () => { expect(pse.getPasswordStrengthAsBits('deadbeef')).toBeCloseTo(32.00, 1); }); test('retourne un nombre positif pour un mot de passe classique', () => { expect(pse.getPasswordStrengthAsBits('Hello123!')).toBeGreaterThan(0); }); test('retourne un nombre plus élevé pour un mot de passe plus long', () => { const short = pse.getPasswordStrengthAsBits('abc'); const longer = pse.getPasswordStrengthAsBits('abcdefghijklmnop'); expect(longer).toBeGreaterThan(short); }); test('une clé privée hex 64 chars → 256 bits', () => { // '0'.repeat(64) est aussi du binaire valide → détecté comme binary (64 bits) // On utilise 'deadbeef'.repeat(8) : 64 chars non-binaires, clairement hex expect(pse.getPasswordStrengthAsBits('deadbeef'.repeat(8))).toBeCloseTo(256, 0); }); }); // ========================================================================== // getPasswordStrengthBitsAsAdjective // ========================================================================== describe('getPasswordStrengthBitsAsAdjective', () => { test('"password" (faible) → "Very Weak"', () => { expect(pse.getPasswordStrengthBitsAsAdjective('pass')).toBe('Very Weak'); }); test('chaîne hex courte (8 chars, 32 bits) → "Very Weak"', () => { // 32 bits > 27.99 et < 35.99 → Weak const adj = pse.getPasswordStrengthBitsAsAdjective('deadbeef'); expect(['Very Weak', 'Weak']).toContain(adj); }); test('clé privée hex 64 chars → "Very Secure" (256 bits ≥ 128)', () => { // 'deadbeef'.repeat(8) = 64 chars hex non-ambigus → 256 bits → "Very Secure" expect(pse.getPasswordStrengthBitsAsAdjective('deadbeef'.repeat(8))).toBe('Very Secure'); }); test('retourne une valeur parmi les valeurs attendues', () => { const VALID_ADJECTIVES = ['Very Weak', 'Weak', 'Fair', 'Good', 'Strong', 'Very Secure']; const result = pse.getPasswordStrengthBitsAsAdjective('TestPassword123!'); expect(VALID_ADJECTIVES).toContain(result); }); }); // ========================================================================== // getPasswordStrengthScore (zxcvbn) // ========================================================================== describe('getPasswordStrengthScore', () => { test('retourne un entier entre 0 et 4', () => { const score = pse.getPasswordStrengthScore('Test123!'); expect(score).toBeGreaterThanOrEqual(0); expect(score).toBeLessThanOrEqual(4); expect(Number.isInteger(score)).toBe(true); }); test('"password" retourne 0 (très faible)', () => { expect(pse.getPasswordStrengthScore('password')).toBe(0); }); test('"123456" retourne 0 (très faible)', () => { expect(pse.getPasswordStrengthScore('123456')).toBe(0); }); test('un mot de passe aléatoire long retourne un score élevé (≥ 3)', () => { expect(pse.getPasswordStrengthScore('xK9!mP2#qR7&nL4@')).toBeGreaterThanOrEqual(3); }); }); // ========================================================================== // getPasswordScoreAsAdjective // ========================================================================== describe('getPasswordScoreAsAdjective', () => { test('"password" → "Very Weak" (score 0)', () => { expect(pse.getPasswordScoreAsAdjective('password')).toBe('Very Weak'); }); test('retourne une valeur parmi les adjectifs définis', () => { const VALID = ['Very Weak', 'Weak', 'Good', 'Strong', 'Very Strong']; expect(VALID).toContain(pse.getPasswordScoreAsAdjective('Test123')); }); }); // ========================================================================== // getPasswordStrengthInfo // ========================================================================== describe('getPasswordStrengthInfo', () => { test('retourne un objet avec les clés PWD_STR_AS_SCORE et PWD_STR_AS_ADJECTIVE', () => { const info = pse.getPasswordStrengthInfo('Test123!'); expect(info).toHaveProperty(PWD_STR_AS_SCORE); expect(info).toHaveProperty(PWD_STR_AS_ADJECTIVE); }); test('le score est un entier entre 0 et 4', () => { const info = pse.getPasswordStrengthInfo('Test123!'); expect(info[PWD_STR_AS_SCORE]).toBeGreaterThanOrEqual(0); expect(info[PWD_STR_AS_SCORE]).toBeLessThanOrEqual(4); }); test("l'adjectif est une chaîne non vide", () => { const info = pse.getPasswordStrengthInfo('Test123!'); expect(typeof info[PWD_STR_AS_ADJECTIVE]).toBe('string'); expect(info[PWD_STR_AS_ADJECTIVE].length).toBeGreaterThan(0); }); }); // ========================================================================== // DS_calculateAdjustedEntropy // ========================================================================== describe('DS_calculateAdjustedEntropy', () => { test('retourne un nombre positif pour un mot de passe non vide', () => { expect(pse.DS_calculateAdjustedEntropy('Test123!')).toBeGreaterThan(0); }); test('retourne 0 pour une chaîne vide', () => { expect(pse.DS_calculateAdjustedEntropy('')).toBe(0); }); test('augmente avec la longueur du mot de passe (caractères similaires)', () => { const short = pse.DS_calculateAdjustedEntropy('Abc1!'); const longer = pse.DS_calculateAdjustedEntropy('Abc1!Abc1!Abc1!'); expect(longer).toBeGreaterThan(short); }); }); // ========================================================================== // DS_entropyToScore // ========================================================================== describe('DS_entropyToScore', () => { test('< 28 → score 0 (Very Weak)', () => { expect(pse.DS_entropyToScore(10)).toBe(0); }); test('28..34 → score 1 (Weak)', () => { expect(pse.DS_entropyToScore(30)).toBe(1); }); test('35..44 → score 2 (Fair)', () => { expect(pse.DS_entropyToScore(40)).toBe(2); }); test('45..54 → score 3 (Strong)', () => { expect(pse.DS_entropyToScore(50)).toBe(3); }); test('≥ 55 → score 4 (Very Strong)', () => { expect(pse.DS_entropyToScore(60)).toBe(4); }); }); // ========================================================================== // DS_comprehensivePasswordStrength // ========================================================================== describe('DS_comprehensivePasswordStrength', () => { test('retourne un objet avec zxcvbnScore, entropy, feedback et finalAssessment', () => { const result = pse.DS_comprehensivePasswordStrength('Test123!'); expect(result).toHaveProperty('zxcvbnScore'); expect(result).toHaveProperty('entropy'); expect(result).toHaveProperty('feedback'); expect(result).toHaveProperty('finalAssessment'); }); test('zxcvbnScore est entre 0 et 4', () => { const result = pse.DS_comprehensivePasswordStrength('Test123!'); expect(result.zxcvbnScore).toBeGreaterThanOrEqual(0); expect(result.zxcvbnScore).toBeLessThanOrEqual(4); }); test('finalAssessment est entre 0 et 4', () => { const result = pse.DS_comprehensivePasswordStrength('Test123!'); expect(result.finalAssessment).toBeGreaterThanOrEqual(0); expect(result.finalAssessment).toBeLessThanOrEqual(4); }); test('finalAssessment ≥ zxcvbnScore (max des deux métriques)', () => { const result = pse.DS_comprehensivePasswordStrength('Test123!'); expect(result.finalAssessment).toBeGreaterThanOrEqual(result.zxcvbnScore); }); }); });