Repository: KomodoPlatform/komodo-defi-sdk-flutter
Pull request #58: fix(rpc-password-generator): update password validation to match KDF password policy
Author: login: takenagain
Labels: name: bug
description: Something isn't working
Created: 2025-05-21T15:24:43Z | Merged: 2025-05-21T16:54:27Z

The password generator could intermittently generate passwords with 3 repeated characters, which would be rejected by KDF according to the password policy.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Introduced robust password validation with detailed error feedback for requirements such as length, digit, letter case, special characters, forbidden substrings, and repeated characters.
  - Enhanced password generation to ensure all security requirements are met before returning a password.

- **Bug Fixes**
  - Passwords are now guaranteed to include all required character types and avoid forbidden patterns.

- **Tests**
  - Added comprehensive tests covering password validation and generation, including Unicode and edge cases.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart b/packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart
index 53d43204..62a66292 100644
--- a/packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart
+++ b/packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart
@@ -6,6 +6,7 @@ import 'dart:io';
 import 'package:flutter/foundation.dart';
 import 'package:http/http.dart' as http;
 import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
 import 'package:path/path.dart' as path;
 import 'package:path_provider/path_provider.dart';
 import 'package:komodo_coins/komodo_coins.dart';
@@ -90,7 +91,7 @@ class KdfStartupConfig {
     return KdfStartupConfig._(
       walletName: walletName,
       walletPassword: walletPassword,
-      rpcPassword: rpcPassword ?? generatePassword(),
+      rpcPassword: rpcPassword ?? SecurityUtils.generatePasswordSecure(32),
       seed: seed,
       dbDir: dbPath,
       userHome: userHomePath,
@@ -136,7 +137,7 @@ class KdfStartupConfig {
       walletName: null,
       walletPassword: null,
       seed: null,
-      rpcPassword: rpcPassword ?? generatePassword(),
+      rpcPassword: rpcPassword ?? SecurityUtils.generatePasswordSecure(32),
       userHome: home,
       dbDir: dbDir,
       allowWeakPassword: true,
@@ -208,20 +209,4 @@ class KdfStartupConfig {
   }
 
   static JsonList? _memoizedCoins;
-
-  static String generatePassword() {
-    var result = '';
-    while (!_validateRPCPassword(result)) {
-      result = SecurityUtils.generatePasswordSecure(32);
-    }
-    return result;
-  }
-
-  static bool _validateRPCPassword(String src) {
-    final exp =
-        RegExp(r'^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[^A-Za-z0-9]).{8,32}$');
-    if (!exp.hasMatch(src)) return false;
-    if (RegExp(r'(.)\1\1').hasMatch(src)) return false;
-    return true;
-  }
 }
diff --git a/packages/komodo_defi_types/lib/src/utils/security_utils.dart b/packages/komodo_defi_types/lib/src/utils/security_utils.dart
index 31651f25..5f678dd7 100644
--- a/packages/komodo_defi_types/lib/src/utils/security_utils.dart
+++ b/packages/komodo_defi_types/lib/src/utils/security_utils.dart
@@ -1,70 +1,143 @@
 import 'dart:math';
 
+import 'package:characters/characters.dart';
 import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
 
+/// Enum representing different types of password validation errors
+enum PasswordValidationError {
+  containsPassword,
+  tooShort,
+  missingDigit,
+  missingLowercase,
+  missingUppercase,
+  missingSpecialCharacter,
+  consecutiveCharacters,
+  none;
+
+  bool get isValid => this == PasswordValidationError.none;
+}
+
 // ignore: one_member_abstracts
 abstract class SecurityUtils {
   static String generatePasswordSecure(
     int length, {
     bool extendedSpecialCharacters = false,
-  }) =>
-      _generateSecurePassword(
+  }) {
+    var result = '';
+    while (!SecurityUtils.checkPasswordRequirements(result).isValid) {
+      result = _generateSecurePassword(
         length,
         extendedSpecialCharacters: extendedSpecialCharacters,
       );
-}
+    }
 
-// TODO: unit tests
-
-String _generateSecurePassword(
-  int length, {
-  bool extendedSpecialCharacters = false,
-}) {
-  const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-  const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz';
-  const digits = '0123456789';
+    return result;
+  }
 
-  // Standard special characters that are generally safe in most contexts,
-  // including JSON
-  const specialCharacters = '@';
-  // const specialCharacters = r"*.!@#$%^(){}:;',.?/~`_+-=|";
+  /// /// Validates password according to KDF password policy
+  ///
+  /// Password requirements:
+  /// - At least 8 characters long
+  /// - Can't contain the word "password"
+  /// - At least 1 digit
+  /// - At least 1 lowercase character
+  /// - At least 1 uppercase character
+  /// - At least 1 special character
+  /// - No same character 3 times in a row
+  static PasswordValidationError checkPasswordRequirements(String password) {
+    // Use Unicode-aware character counting
+    if (password.characters.length < 8) {
+      return PasswordValidationError.tooShort;
+    }
+
+    if (password
+        .toLowerCase()
+        .contains(RegExp('password', caseSensitive: false, unicode: true))) {
+      return PasswordValidationError.containsPassword;
+    }
+
+    // Check for digits (any numerical digit in any script)
+    if (!RegExp(r'.*\p{N}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingDigit;
+    }
+
+    // Check for lowercase (any lowercase letter in any script)
+    if (!RegExp(r'.*\p{Ll}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingLowercase;
+    }
+
+    // Check for uppercase (any uppercase letter in any script)
+    if (!RegExp(r'.*\p{Lu}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingUppercase;
+    }
+
+    // Check for special characters
+    if (!RegExp(r'.*[^\p{L}\p{N}].*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingSpecialCharacter;
+    }
+
+    // Unicode-aware check for consecutive repeated characters using Characters class
+    final charactersList = password.characters.toList();
+    for (var i = 0; i < charactersList.length - 2; i++) {
+      if (charactersList[i] == charactersList[i + 1] &&
+          charactersList[i] == charactersList[i + 2]) {
+        return PasswordValidationError.consecutiveCharacters;
+      }
+    }
+
+    return PasswordValidationError.none;
+  }
 
-  const extendedSpecial = r'~`$^*+=<>?';
+  static String _generateSecurePassword(
+    int length, {
+    bool extendedSpecialCharacters = false,
+  }) {
+    const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz';
+    const digits = '0123456789';
+
+    // Standard special characters that are generally safe in most contexts,
+    // including JSON
+    const specialCharacters = '@';
+    // const specialCharacters = r"*.!@#$%^(){}:;',.?/~`_+-=|";
+
+    const extendedSpecial = r'~`$^*+=<>?';
+
+    final allCharacters = upperCaseLetters +
+        lowerCaseLetters +
+        digits +
+        specialCharacters +
+        (extendedSpecialCharacters ? extendedSpecial : '');
+
+    // Ensure the password length is at least 8 characters
+    if (length < 8) {
+      throw ArgumentError('Password length must be at least 8 characters.');
+    }
+
+    // Random number generator
+    final random = Random.secure();
+
+    // Pick one character from each category to ensure password strength
+    final password = <String>[
+      upperCaseLetters[random.nextInt(upperCaseLetters.length)],
+      lowerCaseLetters[random.nextInt(lowerCaseLetters.length)],
+      digits[random.nextInt(digits.length)],
+      specialCharacters[random.nextInt(specialCharacters.length)],
+      if (extendedSpecialCharacters)
+        extendedSpecial[random.nextInt(extendedSpecial.length)],
+    ];
 
-  final allCharacters = upperCaseLetters +
-      lowerCaseLetters +
-      digits +
-      specialCharacters +
-      (extendedSpecialCharacters ? extendedSpecial : '');
+    // Fill the rest of the password length with random characters from the pool
+    for (var i = password.length; i < length; i++) {
+      password.add(allCharacters[random.nextInt(allCharacters.length)]);
+    }
 
-  // Ensure the password length is at least 8 characters
-  if (length < 8) {
-    throw ArgumentError('Password length must be at least 8 characters.');
-  }
+    // Shuffle the password to ensure randomness
+    password.shuffle(random);
 
-  // Random number generator
-  final random = Random.secure();
-
-  // Pick one character from each category to ensure password strength
-  final password = <String>[
-    upperCaseLetters[random.nextInt(upperCaseLetters.length)],
-    lowerCaseLetters[random.nextInt(lowerCaseLetters.length)],
-    digits[random.nextInt(digits.length)],
-    specialCharacters[random.nextInt(specialCharacters.length)],
-    if (extendedSpecialCharacters)
-      extendedSpecial[random.nextInt(extendedSpecial.length)],
-  ];
-
-  // Fill the rest of the password length with random characters from the pool
-  for (var i = 4; i < length; i++) {
-    password.add(allCharacters[random.nextInt(allCharacters.length)]);
+    // Join the list into a string and return it
+    return password.join();
   }
-
-  // Shuffle the password to ensure randomness
-  password.shuffle(random);
-
-  // Join the list into a string and return it
-  return password.join();
 }
 
 extension CensoredJsonMap on JsonMap {
diff --git a/packages/komodo_defi_types/test/utils/security_utils_test.dart b/packages/komodo_defi_types/test/utils/security_utils_test.dart
new file mode 100644
index 00000000..68df925f
--- /dev/null
+++ b/packages/komodo_defi_types/test/utils/security_utils_test.dart
@@ -0,0 +1,667 @@
+import 'dart:math';
+
+import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('Password validation tests', () {
+    test('Too short passwords should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc1!'),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(''),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1b!'),
+        PasswordValidationError.tooShort,
+      );
+    });
+
+    test('Passwords containing "password" should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('myPassword123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('PASSWORDabc123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('pAsSwOrD123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('My-password-is-secure!123'),
+        PasswordValidationError.containsPassword,
+      );
+    });
+
+    test('Passwords without digits should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('StrongPass!'),
+        PasswordValidationError.missingDigit,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('NoDigitsHere!@#'),
+        PasswordValidationError.missingDigit,
+      );
+    });
+
+    test('Passwords without lowercase should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('STRONG123!'),
+        PasswordValidationError.missingLowercase,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('ALL123CAPS!@#'),
+        PasswordValidationError.missingLowercase,
+      );
+    });
+
+    test('Passwords without uppercase should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('strong123!'),
+        PasswordValidationError.missingUppercase,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('all123lower!@#'),
+        PasswordValidationError.missingUppercase,
+      );
+    });
+
+    test('Passwords without special characters should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('NoSpecial1Characters2'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+    });
+
+    test('Multiple validation errors should return most critical first', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('pass'),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('passwordddd'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abcaaa1234*%'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abcde123'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+    });
+
+    test('Edge cases with spaces and special formatting', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass 123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Tab\t123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Line\nBreak123!A'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Passwords with numbers in various positions', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('1AbcSpecial!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc1Special!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcSpecial!1'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Various special characters', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123@'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc_Def123#'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'AbcDef123$'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123%'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123&'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Valid passwords should not fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Very!hard!pass!77'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Komodo2024!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Complex!P4ssword123'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'!P4ssword#$@'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Mix3d_Ch4r4ct3rs!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Password specifically mentioned in the issue should be rejected', () {
+      // Should fail (has consecutive characters)
+      expect(
+        SecurityUtils.checkPasswordRequirements('Very!hard!pass!777'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test(
+        'Passwords with three or more consecutive identical '
+        'characters should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong111Security!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Secure222!A'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!Secure333'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('aaaStrong1!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!bbb'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!CCC'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!!!Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1###Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'Strong1$$$Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!aaaaa'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!44444'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!!!!!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test(
+        'Valid passwords with two consecutive identical characters should pass',
+        () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong11Secured!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong!!Secured1'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('aaStrong1!Secured'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Special case - passwords with unicode characters', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Пароль123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('密码Abc123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Mötley123!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Extended Unicode character password tests', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('علي123!Abc'), // Arabic
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('こんにちは123!Ab'), // Japanese
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('안녕하세요123!Ab'), // Korean
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Привет123!Ab'), // Cyrillic
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Γειά123!Aa'), // Greek
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('שלום123!Aa'), // Hebrew
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('नमस्ते123!Ab'), // Devanagari
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Unicode edge cases and challenging patterns', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Раssw0rd!',
+        ), // Cyrillic 'Р' (not Latin 'P')
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass\u200Bword123!'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        // a + combining acute accent
+        SecurityUtils.checkPasswordRequirements('Pa\u0301ssword123!'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong🔑123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!🎮🎲🎯aa'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strоng123!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Unicode sequential characters detection', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong爱爱爱123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong😊😊😊123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+
+      // Characters that look similar but are actually different code points
+      expect(
+        SecurityUtils.checkPasswordRequirements('StrongАААbc123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test('Bidirectional text and special Unicode formatting', () {
+      // Right-to-left marks and embedding
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u200Eword123!A',
+        ), // Contains LTR mark
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u200Fword123!A',
+        ), // Contains RTL mark
+        PasswordValidationError.none,
+      );
+
+      // Mixed directionality
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Abcהמסיסמ123!',
+        ), // Hebrew mixed with Latin
+        PasswordValidationError.none,
+      );
+
+      // Special spaces
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u2007word123!A',
+        ), // Figure space
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u00A0word123!A',
+        ), // Non-breaking space
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Advanced emoji password tests in valid passwords', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123!🔒'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔑Abcasba123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass🔥123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc123!🌟✨🚀'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🎮🎯A1!abaa'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong👨‍👩‍👧‍👦123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!👍🏽Strong1234'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass🇺🇸123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123A🎯'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1A🎯🎯🎯'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔥🔥🔥Strong1A!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+    test('Complex emoji sequences and ZWJ', () {
+      expect(
+        // ZWJ sequence (man technologist)
+        SecurityUtils.checkPasswordRequirements('Strong123A👨‍💻'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Complex ZWJ sequence
+        SecurityUtils.checkPasswordRequirements('Strong123A👁️‍🗨️'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Emoji presentation selector
+        SecurityUtils.checkPasswordRequirements('Strong123A☺️'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Mixed emoji and text patterns', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Aaba🔒1🔑!🚀'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Se🔒cure123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('St🔑r🔒ng1!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Should not trigger containsPassword
+        SecurityUtils.checkPasswordRequirements('p🔑ssw🔒rd123A!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔒🚀🎮��Aa1!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Limited fuzzy testing', () {
+      final random = Random();
+      const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123'
+          r'456789!@#$%^&*()';
+
+      for (int i = 0; i < 10; i++) {
+        final int length = random.nextInt(15) + 1;
+        final StringBuffer passwordBuffer = StringBuffer();
+
+        for (int j = 0; j < length; j++) {
+          passwordBuffer.write(chars[random.nextInt(chars.length)]);
+        }
+
+        // Test the random password - we don't assert specific errors,
+        // just verify the validator properly handles random input
+        SecurityUtils.checkPasswordRequirements(passwordBuffer.toString());
+      }
+
+      final List<String> problematicInputs = [
+        // Password too short
+        'a',
+        // Repeated characters
+        'aaaPassword1!',
+        'Password111!',
+        'Password!!!1',
+        // Mixed borderline cases
+        'pass A1!',
+        'PASS a1!',
+        'Pass A!',
+        'Pass A1',
+        // Contains "password"
+        'MyPasswordIs1!',
+        'password123A!',
+        '!PASSWORDabc1',
+      ];
+
+      for (final String input in problematicInputs) {
+        SecurityUtils.checkPasswordRequirements(input);
+      }
+    });
+  });
+
+  group('Password generation tests', () {
+    test('Should throw error when length is less than 8', () {
+      expect(
+        () => SecurityUtils.generatePasswordSecure(7),
+        throwsA(isA<ArgumentError>()),
+      );
+      expect(
+        () => SecurityUtils.generatePasswordSecure(0),
+        throwsA(isA<ArgumentError>()),
+      );
+      expect(
+        () => SecurityUtils.generatePasswordSecure(-1),
+        throwsA(isA<ArgumentError>()),
+      );
+    });
+
+    test('Should generate password of specified length', () {
+      final password8 = SecurityUtils.generatePasswordSecure(8);
+      final password16 = SecurityUtils.generatePasswordSecure(16);
+      final password32 = SecurityUtils.generatePasswordSecure(32);
+
+      expect(password8.length, equals(8));
+      expect(password16.length, equals(16));
+      expect(password32.length, equals(32));
+    });
+
+    test('Should include all required character types', () {
+      final password = SecurityUtils.generatePasswordSecure(12);
+
+      // Check for at least one uppercase letter
+      expect(RegExp(r'[A-Z]').hasMatch(password), isTrue);
+
+      // Check for at least one lowercase letter
+      expect(RegExp(r'[a-z]').hasMatch(password), isTrue);
+
+      // Check for at least one digit
+      expect(RegExp(r'[0-9]').hasMatch(password), isTrue);
+
+      // Check for at least one special character
+      expect(RegExp(r'[@]').hasMatch(password), isTrue);
+    });
+
+    test('Should include extended special characters when flag is set', () {
+      final password = SecurityUtils.generatePasswordSecure(
+        20,
+        extendedSpecialCharacters: true,
+      );
+
+      expect(RegExp(r'[~`$^*+=<>?]').hasMatch(password), isTrue);
+      expect(password.length, equals(20));
+    });
+
+    test('Generated passwords should be different (randomness check)', () {
+      final passwords = <String>{};
+
+      // Generate multiple passwords and check they're unique
+      for (var i = 0; i < 10; i++) {
+        passwords.add(SecurityUtils.generatePasswordSecure(12));
+      }
+
+      // If passwords are truly random, they should all be different
+      expect(passwords.length, equals(10));
+    });
+
+    test('Generated passwords should pass checkPasswordRequirements validation',
+        () {
+      for (var i = 0; i < 10; i++) {
+        final password = SecurityUtils.generatePasswordSecure(12);
+        final validationResult =
+            SecurityUtils.checkPasswordRequirements(password);
+        expect(validationResult, equals(PasswordValidationError.none));
+      }
+
+      // Test with extended characters too
+      for (var i = 0; i < 10; i++) {
+        final password = SecurityUtils.generatePasswordSecure(
+          12,
+          extendedSpecialCharacters: true,
+        );
+        final validationResult =
+            SecurityUtils.checkPasswordRequirements(password);
+        expect(validationResult, equals(PasswordValidationError.none));
+      }
+    });
+
+    test('Should not generate passwords with consecutive identical characters',
+        () {
+      // Generate multiple passwords and confirm none have 3+ consecutive identical characters
+      for (var i = 0; i < 20; i++) {
+        final password = SecurityUtils.generatePasswordSecure(32);
+
+        // Check for consecutive identical characters with regular expressions
+        final hasConsecutiveChars = RegExp(r'(.)\1{2,}').hasMatch(password);
+
+        expect(hasConsecutiveChars, isFalse);
+      }
+    });
+
+    test('Should generate valid passwords at minimum length (8)', () {
+      final password = SecurityUtils.generatePasswordSecure(8);
+      expect(password.length, equals(8));
+      expect(
+        SecurityUtils.checkPasswordRequirements(password),
+        equals(PasswordValidationError.none),
+      );
+    });
+
+    test('Should generate valid passwords at large lengths', () {
+      final password = SecurityUtils.generatePasswordSecure(100);
+      expect(password.length, equals(100));
+      expect(
+        SecurityUtils.checkPasswordRequirements(password),
+        equals(PasswordValidationError.none),
+      );
+    });
+
+    test(
+        'Should generate exact length password with extended special characters',
+        () {
+      final password12 = SecurityUtils.generatePasswordSecure(
+        12,
+        extendedSpecialCharacters: true,
+      );
+      final password16 = SecurityUtils.generatePasswordSecure(
+        16,
+        extendedSpecialCharacters: true,
+      );
+      final password24 = SecurityUtils.generatePasswordSecure(
+        24,
+        extendedSpecialCharacters: true,
+      );
+
+      expect(password12.length, equals(12));
+      expect(password16.length, equals(16));
+      expect(password24.length, equals(24));
+
+      expect(
+        SecurityUtils.checkPasswordRequirements(password12),
+        equals(PasswordValidationError.none),
+      );
+    });
+  });
+}


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>🧹 Nitpick comments (6)</summary><blockquote>

<details>
<summary>packages/komodo_defi_types/lib/src/utils/security_utils.dart (3)</summary><blockquote>

`37-46`: **Doc-comment header has double `///` prefix**

Nit: the first two slashes are duplicated (`/// ///`).  
This confuses some IDE doc-formatters and lints such as `dartdoc`’s “redundant prefix” rule.  
Simply remove the extra pair.

---

`26-33`: **Potentially unbounded retry loop in `generatePasswordSecure`**

`result` is initialised to an empty string, then a `while` loop keeps calling `_generateSecurePassword` until the validator passes.  
If a future change introduces a systematic bug in `_generateSecurePassword` (e.g. always producing three consecutive characters), this becomes an infinite loop that hangs the UI / isolate.

Consider:

```diff
-    while (!SecurityUtils.checkPasswordRequirements(result).isValid) {
+    // Fail‐safe: try a reasonable number of times, then throw.
+    const maxAttempts = 100;
+    var attempts = 0;
+    do {
+      if (++attempts > maxAttempts) {
+        throw StateError('Unable to generate a valid password '
+            'after $maxAttempts attempts');
+      }
       result = _generateSecurePassword(
         length,
         extendedSpecialCharacters: extendedSpecialCharacters,
       );
-    }
+    } while (!checkPasswordRequirements(result).isValid);
```

---

`117-119`: **`Random.secure()` is recreated on every call**

Instantiating `Random.secure()` is relatively expensive; generating multiple passwords in tight loops (e.g. unit tests) repeatedly hits the system CSPRNG.

Make a shared generator:

```diff
-    final random = Random.secure();
+    // Re-use a single secure RNG instance for the whole process.
+    final Random random = _rng;
```

and add at top-level:

```dart
static final Random _rng = Random.secure();
```

</blockquote></details>
<details>
<summary>packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart (2)</summary><blockquote>

`9-10`: **`komodo_defi_types.dart` import might be redundant**

After the refactor the file already imports
`komodo_defi_type_utils.dart`, which (usually) re-exports `JsonMap`,
`JsonList`, etc.  
If the symbols from `komodo_defi_types.dart` are not referenced elsewhere
in this file, the additional import will trigger the
`unused_import` lint in `analysis_options.yaml`.

Please verify and remove or annotate with
`// ignore: unused_import` if it is indeed superfluous.

---

`94-96`: **Hard-coded RPC password length duplicated**

Both `generateWithDefaults` and `noAuthStartup` use the magic number `32`.
Extract it into a `const int _defaultRpcPasswordLength = 32;`
to avoid divergence if the policy changes.

Also consider passing `extendedSpecialCharacters: true` to increase entropy once the off-by-one bug is fixed.

</blockquote></details>
<details>
<summary>packages/komodo_defi_types/test/utils/security_utils_test.dart (1)</summary><blockquote>

`1-5`: **Tests rely on transitive export of `SecurityUtils`**

`komodo_defi_type_utils.dart` may not export `SecurityUtils` /
`PasswordValidationError`. If that export is ever removed, the tests will
break silently at compile time.

Be explicit:

```diff
-import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
+import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
+import 'package:komodo_defi_types/src/utils/security_utils.dart';
```

(or the public barrel export if one exists).

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d7dcb807b50b58df201e8599d58243182eb5ddcc and 9d799b0062f1de27204fbe68b14ec157726ede93.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart` (3 hunks)
* `packages/komodo_defi_types/lib/src/utils/security_utils.dart` (1 hunks)
* `packages/komodo_defi_types/test/utils/security_utils_test.dart` (1 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: setup

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T15:29:01Z
user: login: takenagain
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T15:35:50Z
user: login: takenagain
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T15:35:58Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T15:36:11Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T15:36:19Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR updates password validation and password generation to ensure that generated passwords comply with the KDF policy by enforcing required character criteria and avoiding security pitfalls such as consecutive repeated characters. Key changes include:
- Introducing the PasswordValidationError enum with detailed error cases.
- Implementing Unicode-aware password validation and refactoring generatePasswordSecure.
- Standardizing the RPC password generation in KdfStartupConfig to use the new secure password generator.

### Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

| File | Description |
| ---- | ----------- |
| packages/komodo_defi_types/lib/src/utils/security_utils.dart | Added password validation logic and updated password generation to ensure robust security checks. |
| packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart | Replaced the custom generatePassword method with the new secure password generator from SecurityUtils. |




state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T16:26:49Z
user: login: CharlVS
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-21T16:54:08Z

Review comments:
diff_hunk: @@ -1,70 +1,143 @@
 import 'dart:math';
 
+import 'package:characters/characters.dart';
 import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
 
+/// Enum representing different types of password validation errors
+enum PasswordValidationError {
+  containsPassword,
+  tooShort,
+  missingDigit,
+  missingLowercase,
+  missingUppercase,
+  missingSpecialCharacter,
+  consecutiveCharacters,
+  none;
+
+  bool get isValid => this == PasswordValidationError.none;
+}
+
 // ignore: one_member_abstracts
 abstract class SecurityUtils {
   static String generatePasswordSecure(
     int length, {
     bool extendedSpecialCharacters = false,
-  }) =>
-      _generateSecurePassword(
+  }) {
+    var result = '';
+    while (!SecurityUtils.checkPasswordRequirements(result).isValid) {
+      result = _generateSecurePassword(
         length,
         extendedSpecialCharacters: extendedSpecialCharacters,
       );
-}
+    }
 
-// TODO: unit tests
-
-String _generateSecurePassword(
-  int length, {
-  bool extendedSpecialCharacters = false,
-}) {
-  const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-  const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz';
-  const digits = '0123456789';
+    return result;
+  }
 
-  // Standard special characters that are generally safe in most contexts,
-  // including JSON
-  const specialCharacters = '@';
-  // const specialCharacters = r"*.!@#$%^(){}:;',.?/~`_+-=|";
+  /// /// Validates password according to KDF password policy
+  ///
+  /// Password requirements:
+  /// - At least 8 characters long
+  /// - Can't contain the word "password"
+  /// - At least 1 digit
+  /// - At least 1 lowercase character
+  /// - At least 1 uppercase character
+  /// - At least 1 special character
+  /// - No same character 3 times in a row
+  static PasswordValidationError checkPasswordRequirements(String password) {
+    // Use Unicode-aware character counting
+    if (password.characters.length < 8) {
+      return PasswordValidationError.tooShort;
+    }
+
+    if (password
+        .toLowerCase()
+        .contains(RegExp('password', caseSensitive: false, unicode: true))) {
+      return PasswordValidationError.containsPassword;
+    }
+
+    // Check for digits (any numerical digit in any script)
+    if (!RegExp(r'.*\p{N}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingDigit;
+    }
+
+    // Check for lowercase (any lowercase letter in any script)
+    if (!RegExp(r'.*\p{Ll}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingLowercase;
+    }
+
+    // Check for uppercase (any uppercase letter in any script)
+    if (!RegExp(r'.*\p{Lu}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingUppercase;
+    }
+
+    // Check for special characters
+    if (!RegExp(r'.*[^\p{L}\p{N}].*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingSpecialCharacter;
+    }
+
+    // Unicode-aware check for consecutive repeated characters using Characters class
+    final charactersList = password.characters.toList();
+    for (var i = 0; i < charactersList.length - 2; i++) {
+      if (charactersList[i] == charactersList[i + 1] &&
+          charactersList[i] == charactersList[i + 2]) {
+        return PasswordValidationError.consecutiveCharacters;
+      }
+    }
+
+    return PasswordValidationError.none;
+  }
 
-  const extendedSpecial = r'~`$^*+=<>?';
+  static String _generateSecurePassword(
+    int length, {
+    bool extendedSpecialCharacters = false,
+  }) {
+    const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz';
+    const digits = '0123456789';
+
+    // Standard special characters that are generally safe in most contexts,
+    // including JSON
+    const specialCharacters = '@';
+    // const specialCharacters = r"*.!@#$%^(){}:;',.?/~`_+-=|";
+
+    const extendedSpecial = r'~`$^*+=<>?';
+
+    final allCharacters = upperCaseLetters +
+        lowerCaseLetters +
+        digits +
+        specialCharacters +
+        (extendedSpecialCharacters ? extendedSpecial : '');
+
+    // Ensure the password length is at least 8 characters
+    if (length < 8) {
+      throw ArgumentError('Password length must be at least 8 characters.');
+    }
+
+    // Random number generator
+    final random = Random.secure();
+
+    // Pick one character from each category to ensure password strength
+    final password = <String>[
+      upperCaseLetters[random.nextInt(upperCaseLetters.length)],
+      lowerCaseLetters[random.nextInt(lowerCaseLetters.length)],
+      digits[random.nextInt(digits.length)],
+      specialCharacters[random.nextInt(specialCharacters.length)],
+      if (extendedSpecialCharacters)
+        extendedSpecial[random.nextInt(extendedSpecial.length)],
+    ];
 
-  final allCharacters = upperCaseLetters +
-      lowerCaseLetters +
-      digits +
-      specialCharacters +
-      (extendedSpecialCharacters ? extendedSpecial : '');
+    // Fill the rest of the password length with random characters from the pool
+    for (var i = 4; i < length; i++) {
+      password.add(allCharacters[random.nextInt(allCharacters.length)]);
+    }
path: packages/komodo_defi_types/lib/src/utils/security_utils.dart
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Off-by-one error when `extendedSpecialCharacters` is `true`**

The loop starts at a hard-coded index `4`, but the prefix list length is **5** when an extended special character is added.  
Result: generated password length = requested length + 1.

```diff
-    // Fill the rest of the password length with random characters from the pool
-    for (var i = 4; i < length; i++) {
+    // Fill the rest until we reach the requested length
+    for (var i = password.length; i < length; i++) {
       password.add(allCharacters[random.nextInt(allCharacters.length)]);
     }
```

Add a unit test that calls `generatePasswordSecure(12, extendedSpecialCharacters: true)` and asserts `password.length == 12`; it will currently fail.



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
    // Pick one character from each category to ensure password strength
    final password = <String>[
      upperCaseLetters[random.nextInt(upperCaseLetters.length)],
      lowerCaseLetters[random.nextInt(lowerCaseLetters.length)],
      digits[random.nextInt(digits.length)],
      specialCharacters[random.nextInt(specialCharacters.length)],
      if (extendedSpecialCharacters)
        extendedSpecial[random.nextInt(extendedSpecial.length)],
    ];

    // Fill the rest until we reach the requested length
    for (var i = password.length; i < length; i++) {
      password.add(allCharacters[random.nextInt(allCharacters.length)]);
    }
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/komodo_defi_types/lib/src/utils/security_utils.dart around lines 120
to 133, the for-loop that fills the rest of the password starts at a fixed index
4, but when extendedSpecialCharacters is true, the initial password list length
is 5, causing an off-by-one error and generating a password longer than
requested. Fix this by setting the loop start index dynamically to the current
length of the password list instead of a hard-coded 4, ensuring the final
password length matches the requested length. Also, add a unit test calling
generatePasswordSecure(12, extendedSpecialCharacters: true) to assert the
password length is exactly 12.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T15:29:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,70 +1,143 @@
 import 'dart:math';
 
+import 'package:characters/characters.dart';
 import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
 
+/// Enum representing different types of password validation errors
+enum PasswordValidationError {
+  containsPassword,
+  tooShort,
+  missingDigit,
+  missingLowercase,
+  missingUppercase,
+  missingSpecialCharacter,
+  consecutiveCharacters,
+  none;
+
+  bool get isValid => this == PasswordValidationError.none;
+}
+
 // ignore: one_member_abstracts
 abstract class SecurityUtils {
   static String generatePasswordSecure(
     int length, {
     bool extendedSpecialCharacters = false,
-  }) =>
-      _generateSecurePassword(
+  }) {
+    var result = '';
+    while (!SecurityUtils.checkPasswordRequirements(result).isValid) {
+      result = _generateSecurePassword(
         length,
         extendedSpecialCharacters: extendedSpecialCharacters,
       );
-}
+    }
 
-// TODO: unit tests
-
-String _generateSecurePassword(
-  int length, {
-  bool extendedSpecialCharacters = false,
-}) {
-  const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-  const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz';
-  const digits = '0123456789';
+    return result;
+  }
 
-  // Standard special characters that are generally safe in most contexts,
-  // including JSON
-  const specialCharacters = '@';
-  // const specialCharacters = r"*.!@#$%^(){}:;',.?/~`_+-=|";
+  /// /// Validates password according to KDF password policy
+  ///
+  /// Password requirements:
+  /// - At least 8 characters long
+  /// - Can't contain the word "password"
+  /// - At least 1 digit
+  /// - At least 1 lowercase character
+  /// - At least 1 uppercase character
+  /// - At least 1 special character
+  /// - No same character 3 times in a row
+  static PasswordValidationError checkPasswordRequirements(String password) {
+    // Use Unicode-aware character counting
+    if (password.characters.length < 8) {
+      return PasswordValidationError.tooShort;
+    }
+
+    if (password
+        .toLowerCase()
+        .contains(RegExp('password', caseSensitive: false, unicode: true))) {
+      return PasswordValidationError.containsPassword;
+    }
+
+    // Check for digits (any numerical digit in any script)
+    if (!RegExp(r'.*\p{N}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingDigit;
+    }
+
+    // Check for lowercase (any lowercase letter in any script)
+    if (!RegExp(r'.*\p{Ll}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingLowercase;
+    }
+
+    // Check for uppercase (any uppercase letter in any script)
+    if (!RegExp(r'.*\p{Lu}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingUppercase;
+    }
+
+    // Check for special characters
+    if (!RegExp(r'.*[^\p{L}\p{N}].*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingSpecialCharacter;
+    }
+
+    // Unicode-aware check for consecutive repeated characters using Characters class
+    final charactersList = password.characters.toList();
+    for (var i = 0; i < charactersList.length - 2; i++) {
+      if (charactersList[i] == charactersList[i + 1] &&
+          charactersList[i] == charactersList[i + 2]) {
+        return PasswordValidationError.consecutiveCharacters;
+      }
+    }
+
+    return PasswordValidationError.none;
+  }
 
-  const extendedSpecial = r'~`$^*+=<>?';
+  static String _generateSecurePassword(
+    int length, {
+    bool extendedSpecialCharacters = false,
+  }) {
+    const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz';
+    const digits = '0123456789';
+
+    // Standard special characters that are generally safe in most contexts,
+    // including JSON
+    const specialCharacters = '@';
+    // const specialCharacters = r"*.!@#$%^(){}:;',.?/~`_+-=|";
+
+    const extendedSpecial = r'~`$^*+=<>?';
+
path: packages/komodo_defi_types/lib/src/utils/security_utils.dart
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Only one “standard” special character drastically lowers entropy**

`const specialCharacters = '@';` means every generated password contains exactly one ‘@’.  
Attackers can now skip an entire character class in brute-force attacks.

Unless there is a hard technical reason, expose a richer, _configurable_ set, e.g.:

```diff
-const specialCharacters = '@';
+const specialCharacters = r'!@#$%^&*()-_=+[]{};:\'",.<>?/|\`~';
```

If JSON compatibility is the concern, you can still allow more characters and escape them on serialisation rather than weakening the password itself.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/komodo_defi_types/lib/src/utils/security_utils.dart around lines 99
to 105, the current definition of specialCharacters is limited to a single
character '@', which reduces password entropy by forcing every password to
include exactly that character. To fix this, replace the single-character string
with a richer, configurable set of special characters, such as the commented-out
string or a similar expanded set. Ensure the password generator uses this
broader set to increase entropy, and if JSON compatibility is a concern, handle
escaping during serialization rather than restricting the character set.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-21T15:29:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,641 @@
+import 'dart:math';
+
+import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('Password validation tests', () {
+    test('Too short passwords should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc1!'),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(''),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1b!'),
+        PasswordValidationError.tooShort,
+      );
+    });
+
+    test('Passwords containing "password" should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('myPassword123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('PASSWORDabc123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('pAsSwOrD123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('My-password-is-secure!123'),
+        PasswordValidationError.containsPassword,
+      );
+    });
+
+    test('Passwords without digits should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('StrongPass!'),
+        PasswordValidationError.missingDigit,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('NoDigitsHere!@#'),
+        PasswordValidationError.missingDigit,
+      );
+    });
+
+    test('Passwords without lowercase should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('STRONG123!'),
+        PasswordValidationError.missingLowercase,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('ALL123CAPS!@#'),
+        PasswordValidationError.missingLowercase,
+      );
+    });
+
+    test('Passwords without uppercase should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('strong123!'),
+        PasswordValidationError.missingUppercase,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('all123lower!@#'),
+        PasswordValidationError.missingUppercase,
+      );
+    });
+
+    test('Passwords without special characters should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('NoSpecial1Characters2'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+    });
+
+    test('Multiple validation errors should return most critical first', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('pass'),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('passwordddd'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abcaaa1234*%'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abcde123'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+    });
+
+    test('Edge cases with spaces and special formatting', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass 123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Tab\t123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Line\nBreak123!A'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Passwords with numbers in various positions', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('1AbcSpecial!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc1Special!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcSpecial!1'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Various special characters', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123@'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc_Def123#'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'AbcDef123$'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123%'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123&'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Valid passwords should not fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Very!hard!pass!77'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Komodo2024!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Complex!P4ssword123'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'!P4ssword#$@'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Mix3d_Ch4r4ct3rs!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Password specifically mentioned in the issue should be rejected', () {
+      // Should fail (has consecutive characters)
+      expect(
+        SecurityUtils.checkPasswordRequirements('Very!hard!pass!777'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test(
+        'Passwords with three or more consecutive identical '
+        'characters should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong111Security!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Secure222!A'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!Secure333'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('aaaStrong1!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!bbb'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!CCC'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!!!Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1###Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'Strong1$$$Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!aaaaa'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!44444'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!!!!!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test(
+        'Valid passwords with two consecutive identical characters should pass',
+        () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong11Secured!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong!!Secured1'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('aaStrong1!Secured'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Special case - passwords with unicode characters', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Пароль123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('密码Abc123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Mötley123!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Extended Unicode character password tests', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('علي123!Abc'), // Arabic
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('こんにちは123!Ab'), // Japanese
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('안녕하세요123!Ab'), // Korean
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Привет123!Ab'), // Cyrillic
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Γειά123!Aa'), // Greek
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('שלום123!Aa'), // Hebrew
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('नमस्ते123!Ab'), // Devanagari
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Unicode edge cases and challenging patterns', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Раssw0rd!',
+        ), // Cyrillic 'Р' (not Latin 'P')
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass\u200Bword123!'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        // a + combining acute accent
+        SecurityUtils.checkPasswordRequirements('Pa\u0301ssword123!'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong🔑123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!🎮🎲🎯aa'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strоng123!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Unicode sequential characters detection', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong爱爱爱123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong😊😊😊123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+
+      // Characters that look similar but are actually different code points
+      expect(
+        SecurityUtils.checkPasswordRequirements('StrongАААbc123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test('Bidirectional text and special Unicode formatting', () {
+      // Right-to-left marks and embedding
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u200Eword123!A',
+        ), // Contains LTR mark
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u200Fword123!A',
+        ), // Contains RTL mark
+        PasswordValidationError.none,
+      );
+
+      // Mixed directionality
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Abcהמסיסמ123!',
+        ), // Hebrew mixed with Latin
+        PasswordValidationError.none,
+      );
+
+      // Special spaces
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u2007word123!A',
+        ), // Figure space
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u00A0word123!A',
+        ), // Non-breaking space
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Advanced emoji password tests in valid passwords', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123!🔒'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔑Abcasba123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass🔥123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc123!🌟✨🚀'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🎮🎯A1!abaa'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong👨‍👩‍👧‍👦123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!👍🏽Strong1234'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass🇺🇸123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123A🎯'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1A🎯🎯🎯'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔥🔥🔥Strong1A!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+    test('Complex emoji sequences and ZWJ', () {
+      expect(
+        // ZWJ sequence (man technologist)
+        SecurityUtils.checkPasswordRequirements('Strong123A👨‍💻'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Complex ZWJ sequence
+        SecurityUtils.checkPasswordRequirements('Strong123A👁️‍🗨️'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Emoji presentation selector
+        SecurityUtils.checkPasswordRequirements('Strong123A☺️'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Mixed emoji and text patterns', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Aaba🔒1🔑!🚀'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Se🔒cure123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('St🔑r🔒ng1!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Should not trigger containsPassword
+        SecurityUtils.checkPasswordRequirements('p🔑ssw🔒rd123A!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔒🚀🎮��Aa1!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Limited fuzzy testing', () {
+      final random = Random();
+      const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123'
+          r'456789!@#$%^&*()';
+
+      for (int i = 0; i < 10; i++) {
+        final int length = random.nextInt(15) + 1;
+        final StringBuffer passwordBuffer = StringBuffer();
+
+        for (int j = 0; j < length; j++) {
+          passwordBuffer.write(chars[random.nextInt(chars.length)]);
+        }
+
+        // Test the random password - we don't assert specific errors,
+        // just verify the validator properly handles random input
+        SecurityUtils.checkPasswordRequirements(passwordBuffer.toString());
+      }
+
+      final List<String> problematicInputs = [
+        // Password too short
+        'a',
+        // Repeated characters
+        'aaaPassword1!',
+        'Password111!',
+        'Password!!!1',
+        // Mixed borderline cases
+        'pass A1!',
+        'PASS a1!',
+        'Pass A!',
+        'Pass A1',
+        // Contains "password"
+        'MyPasswordIs1!',
+        'password123A!',
+        '!PASSWORDabc1',
+      ];
+
+      for (final String input in problematicInputs) {
+        SecurityUtils.checkPasswordRequirements(input);
+      }
+    });
+  });
+
+  group('Password generation tests', () {
+    test('Should throw error when length is less than 8', () {
+      expect(
+        () => SecurityUtils.generatePasswordSecure(7),
+        throwsA(isA<ArgumentError>()),
+      );
+      expect(
+        () => SecurityUtils.generatePasswordSecure(0),
+        throwsA(isA<ArgumentError>()),
+      );
+      expect(
+        () => SecurityUtils.generatePasswordSecure(-1),
+        throwsA(isA<ArgumentError>()),
+      );
+    });
+
+    test('Should generate password of specified length', () {
+      final password8 = SecurityUtils.generatePasswordSecure(8);
+      final password16 = SecurityUtils.generatePasswordSecure(16);
+      final password32 = SecurityUtils.generatePasswordSecure(32);
+
+      expect(password8.length, equals(8));
+      expect(password16.length, equals(16));
+      expect(password32.length, equals(32));
+    });
+
+    test('Should include all required character types', () {
+      final password = SecurityUtils.generatePasswordSecure(12);
+
+      // Check for at least one uppercase letter
+      expect(RegExp(r'[A-Z]').hasMatch(password), isTrue);
+
+      // Check for at least one lowercase letter
+      expect(RegExp(r'[a-z]').hasMatch(password), isTrue);
+
+      // Check for at least one digit
+      expect(RegExp(r'[0-9]').hasMatch(password), isTrue);
+
+      // Check for at least one special character
+      expect(RegExp(r'[@]').hasMatch(password), isTrue);
+    });
+
+    test('Should include extended special characters when flag is set', () {
+      final password = SecurityUtils.generatePasswordSecure(
+        20,
+        extendedSpecialCharacters: true,
+      );
+
+      // Check for at least one extended special character
+      expect(RegExp(r'[~`$^*+=<>?]').hasMatch(password), isTrue);
+    });
path: packages/komodo_defi_types/test/utils/security_utils_test.dart
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Missing assertion on password length when using extended specials**

Because of the off-by-one bug highlighted in `security_utils.dart`,
`generatePasswordSecure(20, extendedSpecialCharacters: true)` currently
returns 21 characters.  
Add an assertion similar to the length tests for the base variant so the
unit suite would have caught this regression.

```dart
final pwd = SecurityUtils.generatePasswordSecure(
  20,
  extendedSpecialCharacters: true,
);
expect(pwd.length, equals(20));
```

---

🏁 Script executed:

```shell
#!/bin/bash
set -e
echo "Locating generatePasswordSecure implementation"
rg -n "generatePasswordSecure" -A 200 --glob "*.dart"
```

Length of output: 53768

---



</details>

**Ensure password length is correct when using extended specials**

Add an explicit length check to the existing test so it will catch the off-by-one bug in the implementation:

In packages/komodo_defi_types/test/utils/security_utils_test.dart, inside the  
test('Should include extended special characters when flag is set', …) block (lines 567–575), after the RegExp assertion add:

```dart
final pwd = SecurityUtils.generatePasswordSecure(
  20,
  extendedSpecialCharacters: true,
);
expect(pwd.length, equals(20));
```

This will ensure that `generatePasswordSecure(20, extendedSpecialCharacters: true)` actually returns 20 characters, rather than 21.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/komodo_defi_types/test/utils/security_utils_test.dart around lines
567 to 575, the test for generating a password with extended special characters
lacks an assertion verifying the password length. Add a length check after the
existing RegExp assertion by calling generatePasswordSecure with length 20 and
extendedSpecialCharacters true, then assert that the returned password's length
equals 20. This will catch any off-by-one errors in the password length.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T15:29:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,641 @@
+import 'dart:math';
+
+import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('Password validation tests', () {
+    test('Too short passwords should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc1!'),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(''),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1b!'),
+        PasswordValidationError.tooShort,
+      );
+    });
+
+    test('Passwords containing "password" should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('myPassword123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('PASSWORDabc123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('pAsSwOrD123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('My-password-is-secure!123'),
+        PasswordValidationError.containsPassword,
+      );
+    });
+
+    test('Passwords without digits should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('StrongPass!'),
+        PasswordValidationError.missingDigit,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('NoDigitsHere!@#'),
+        PasswordValidationError.missingDigit,
+      );
+    });
+
+    test('Passwords without lowercase should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('STRONG123!'),
+        PasswordValidationError.missingLowercase,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('ALL123CAPS!@#'),
+        PasswordValidationError.missingLowercase,
+      );
+    });
+
+    test('Passwords without uppercase should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('strong123!'),
+        PasswordValidationError.missingUppercase,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('all123lower!@#'),
+        PasswordValidationError.missingUppercase,
+      );
+    });
+
+    test('Passwords without special characters should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('NoSpecial1Characters2'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+    });
+
+    test('Multiple validation errors should return most critical first', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('pass'),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('passwordddd'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abcaaa1234*%'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abcde123'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+    });
+
+    test('Edge cases with spaces and special formatting', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass 123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Tab\t123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Line\nBreak123!A'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Passwords with numbers in various positions', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('1AbcSpecial!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc1Special!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcSpecial!1'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Various special characters', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123@'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc_Def123#'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'AbcDef123$'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123%'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123&'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Valid passwords should not fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Very!hard!pass!77'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Komodo2024!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Complex!P4ssword123'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'!P4ssword#$@'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Mix3d_Ch4r4ct3rs!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Password specifically mentioned in the issue should be rejected', () {
+      // Should fail (has consecutive characters)
+      expect(
+        SecurityUtils.checkPasswordRequirements('Very!hard!pass!777'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test(
+        'Passwords with three or more consecutive identical '
+        'characters should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong111Security!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Secure222!A'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!Secure333'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('aaaStrong1!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!bbb'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!CCC'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!!!Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1###Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'Strong1$$$Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!aaaaa'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!44444'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!!!!!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test(
+        'Valid passwords with two consecutive identical characters should pass',
+        () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong11Secured!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong!!Secured1'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('aaStrong1!Secured'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Special case - passwords with unicode characters', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Пароль123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('密码Abc123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Mötley123!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Extended Unicode character password tests', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('علي123!Abc'), // Arabic
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('こんにちは123!Ab'), // Japanese
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('안녕하세요123!Ab'), // Korean
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Привет123!Ab'), // Cyrillic
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Γειά123!Aa'), // Greek
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('שלום123!Aa'), // Hebrew
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('नमस्ते123!Ab'), // Devanagari
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Unicode edge cases and challenging patterns', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Раssw0rd!',
+        ), // Cyrillic 'Р' (not Latin 'P')
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass\u200Bword123!'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        // a + combining acute accent
+        SecurityUtils.checkPasswordRequirements('Pa\u0301ssword123!'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong🔑123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!🎮🎲🎯aa'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strоng123!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Unicode sequential characters detection', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong爱爱爱123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong😊😊😊123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+
+      // Characters that look similar but are actually different code points
+      expect(
+        SecurityUtils.checkPasswordRequirements('StrongАААbc123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test('Bidirectional text and special Unicode formatting', () {
+      // Right-to-left marks and embedding
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u200Eword123!A',
+        ), // Contains LTR mark
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u200Fword123!A',
+        ), // Contains RTL mark
+        PasswordValidationError.none,
+      );
+
+      // Mixed directionality
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Abcהמסיסמ123!',
+        ), // Hebrew mixed with Latin
+        PasswordValidationError.none,
+      );
+
+      // Special spaces
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u2007word123!A',
+        ), // Figure space
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u00A0word123!A',
+        ), // Non-breaking space
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Advanced emoji password tests in valid passwords', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123!🔒'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔑Abcasba123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass🔥123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc123!🌟✨🚀'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🎮🎯A1!abaa'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong👨‍👩‍👧‍👦123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!👍🏽Strong1234'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass🇺🇸123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123A🎯'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1A🎯🎯🎯'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔥🔥🔥Strong1A!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+    test('Complex emoji sequences and ZWJ', () {
+      expect(
+        // ZWJ sequence (man technologist)
+        SecurityUtils.checkPasswordRequirements('Strong123A👨‍💻'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Complex ZWJ sequence
+        SecurityUtils.checkPasswordRequirements('Strong123A👁️‍🗨️'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Emoji presentation selector
+        SecurityUtils.checkPasswordRequirements('Strong123A☺️'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Mixed emoji and text patterns', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Aaba🔒1🔑!🚀'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Se🔒cure123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('St🔑r🔒ng1!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Should not trigger containsPassword
+        SecurityUtils.checkPasswordRequirements('p🔑ssw🔒rd123A!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔒🚀🎮��Aa1!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Limited fuzzy testing', () {
+      final random = Random();
+      const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123'
+          r'456789!@#$%^&*()';
+
+      for (int i = 0; i < 10; i++) {
+        final int length = random.nextInt(15) + 1;
+        final StringBuffer passwordBuffer = StringBuffer();
+
+        for (int j = 0; j < length; j++) {
+          passwordBuffer.write(chars[random.nextInt(chars.length)]);
+        }
+
+        // Test the random password - we don't assert specific errors,
+        // just verify the validator properly handles random input
+        SecurityUtils.checkPasswordRequirements(passwordBuffer.toString());
+      }
+
+      final List<String> problematicInputs = [
+        // Password too short
+        'a',
+        // Repeated characters
+        'aaaPassword1!',
+        'Password111!',
+        'Password!!!1',
+        // Mixed borderline cases
+        'pass A1!',
+        'PASS a1!',
+        'Pass A!',
+        'Pass A1',
+        // Contains "password"
+        'MyPasswordIs1!',
+        'password123A!',
+        '!PASSWORDabc1',
+      ];
+
+      for (final String input in problematicInputs) {
+        SecurityUtils.checkPasswordRequirements(input);
+      }
+    });
+  });
+
+  group('Password generation tests', () {
+    test('Should throw error when length is less than 8', () {
+      expect(
+        () => SecurityUtils.generatePasswordSecure(7),
+        throwsA(isA<ArgumentError>()),
+      );
+      expect(
+        () => SecurityUtils.generatePasswordSecure(0),
+        throwsA(isA<ArgumentError>()),
+      );
+      expect(
+        () => SecurityUtils.generatePasswordSecure(-1),
+        throwsA(isA<ArgumentError>()),
+      );
+    });
+
+    test('Should generate password of specified length', () {
+      final password8 = SecurityUtils.generatePasswordSecure(8);
+      final password16 = SecurityUtils.generatePasswordSecure(16);
+      final password32 = SecurityUtils.generatePasswordSecure(32);
+
+      expect(password8.length, equals(8));
+      expect(password16.length, equals(16));
+      expect(password32.length, equals(32));
+    });
+
+    test('Should include all required character types', () {
+      final password = SecurityUtils.generatePasswordSecure(12);
+
+      // Check for at least one uppercase letter
+      expect(RegExp(r'[A-Z]').hasMatch(password), isTrue);
+
+      // Check for at least one lowercase letter
+      expect(RegExp(r'[a-z]').hasMatch(password), isTrue);
+
+      // Check for at least one digit
+      expect(RegExp(r'[0-9]').hasMatch(password), isTrue);
+
+      // Check for at least one special character
+      expect(RegExp(r'[@]').hasMatch(password), isTrue);
+    });
+
+    test('Should include extended special characters when flag is set', () {
+      final password = SecurityUtils.generatePasswordSecure(
+        20,
+        extendedSpecialCharacters: true,
+      );
+
+      // Check for at least one extended special character
+      expect(RegExp(r'[~`$^*+=<>?]').hasMatch(password), isTrue);
+    });
path: packages/komodo_defi_types/test/utils/security_utils_test.dart
user: login: takenagain
body: Done in [b793ea1](https://github.com/KomodoPlatform/komodo-defi-sdk-flutter/pull/58/commits/b793ea11adbf2b6cbcf0866ec3aaa946caf4dd4e)
created_at: 2025-05-21T15:35:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,70 +1,143 @@
 import 'dart:math';
 
+import 'package:characters/characters.dart';
 import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
 
+/// Enum representing different types of password validation errors
+enum PasswordValidationError {
+  containsPassword,
+  tooShort,
+  missingDigit,
+  missingLowercase,
+  missingUppercase,
+  missingSpecialCharacter,
+  consecutiveCharacters,
+  none;
+
+  bool get isValid => this == PasswordValidationError.none;
+}
+
 // ignore: one_member_abstracts
 abstract class SecurityUtils {
   static String generatePasswordSecure(
     int length, {
     bool extendedSpecialCharacters = false,
-  }) =>
-      _generateSecurePassword(
+  }) {
+    var result = '';
+    while (!SecurityUtils.checkPasswordRequirements(result).isValid) {
+      result = _generateSecurePassword(
         length,
         extendedSpecialCharacters: extendedSpecialCharacters,
       );
-}
+    }
 
-// TODO: unit tests
-
-String _generateSecurePassword(
-  int length, {
-  bool extendedSpecialCharacters = false,
-}) {
-  const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-  const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz';
-  const digits = '0123456789';
+    return result;
+  }
 
-  // Standard special characters that are generally safe in most contexts,
-  // including JSON
-  const specialCharacters = '@';
-  // const specialCharacters = r"*.!@#$%^(){}:;',.?/~`_+-=|";
+  /// /// Validates password according to KDF password policy
+  ///
+  /// Password requirements:
+  /// - At least 8 characters long
+  /// - Can't contain the word "password"
+  /// - At least 1 digit
+  /// - At least 1 lowercase character
+  /// - At least 1 uppercase character
+  /// - At least 1 special character
+  /// - No same character 3 times in a row
+  static PasswordValidationError checkPasswordRequirements(String password) {
+    // Use Unicode-aware character counting
+    if (password.characters.length < 8) {
+      return PasswordValidationError.tooShort;
+    }
+
+    if (password
+        .toLowerCase()
+        .contains(RegExp('password', caseSensitive: false, unicode: true))) {
+      return PasswordValidationError.containsPassword;
+    }
+
+    // Check for digits (any numerical digit in any script)
+    if (!RegExp(r'.*\p{N}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingDigit;
+    }
+
+    // Check for lowercase (any lowercase letter in any script)
+    if (!RegExp(r'.*\p{Ll}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingLowercase;
+    }
+
+    // Check for uppercase (any uppercase letter in any script)
+    if (!RegExp(r'.*\p{Lu}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingUppercase;
+    }
+
+    // Check for special characters
+    if (!RegExp(r'.*[^\p{L}\p{N}].*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingSpecialCharacter;
+    }
+
+    // Unicode-aware check for consecutive repeated characters using Characters class
+    final charactersList = password.characters.toList();
+    for (var i = 0; i < charactersList.length - 2; i++) {
+      if (charactersList[i] == charactersList[i + 1] &&
+          charactersList[i] == charactersList[i + 2]) {
+        return PasswordValidationError.consecutiveCharacters;
+      }
+    }
+
+    return PasswordValidationError.none;
+  }
 
-  const extendedSpecial = r'~`$^*+=<>?';
+  static String _generateSecurePassword(
+    int length, {
+    bool extendedSpecialCharacters = false,
+  }) {
+    const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz';
+    const digits = '0123456789';
+
+    // Standard special characters that are generally safe in most contexts,
+    // including JSON
+    const specialCharacters = '@';
+    // const specialCharacters = r"*.!@#$%^(){}:;',.?/~`_+-=|";
+
+    const extendedSpecial = r'~`$^*+=<>?';
+
+    final allCharacters = upperCaseLetters +
+        lowerCaseLetters +
+        digits +
+        specialCharacters +
+        (extendedSpecialCharacters ? extendedSpecial : '');
+
+    // Ensure the password length is at least 8 characters
+    if (length < 8) {
+      throw ArgumentError('Password length must be at least 8 characters.');
+    }
+
+    // Random number generator
+    final random = Random.secure();
+
+    // Pick one character from each category to ensure password strength
+    final password = <String>[
+      upperCaseLetters[random.nextInt(upperCaseLetters.length)],
+      lowerCaseLetters[random.nextInt(lowerCaseLetters.length)],
+      digits[random.nextInt(digits.length)],
+      specialCharacters[random.nextInt(specialCharacters.length)],
+      if (extendedSpecialCharacters)
+        extendedSpecial[random.nextInt(extendedSpecial.length)],
+    ];
 
-  final allCharacters = upperCaseLetters +
-      lowerCaseLetters +
-      digits +
-      specialCharacters +
-      (extendedSpecialCharacters ? extendedSpecial : '');
+    // Fill the rest of the password length with random characters from the pool
+    for (var i = 4; i < length; i++) {
+      password.add(allCharacters[random.nextInt(allCharacters.length)]);
+    }
path: packages/komodo_defi_types/lib/src/utils/security_utils.dart
user: login: takenagain
body: Done in [b793ea1](https://github.com/KomodoPlatform/komodo-defi-sdk-flutter/pull/58/commits/b793ea11adbf2b6cbcf0866ec3aaa946caf4dd4e)
created_at: 2025-05-21T15:35:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,641 @@
+import 'dart:math';
+
+import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('Password validation tests', () {
+    test('Too short passwords should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc1!'),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(''),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1b!'),
+        PasswordValidationError.tooShort,
+      );
+    });
+
+    test('Passwords containing "password" should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('myPassword123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('PASSWORDabc123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('pAsSwOrD123!'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('My-password-is-secure!123'),
+        PasswordValidationError.containsPassword,
+      );
+    });
+
+    test('Passwords without digits should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('StrongPass!'),
+        PasswordValidationError.missingDigit,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('NoDigitsHere!@#'),
+        PasswordValidationError.missingDigit,
+      );
+    });
+
+    test('Passwords without lowercase should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('STRONG123!'),
+        PasswordValidationError.missingLowercase,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('ALL123CAPS!@#'),
+        PasswordValidationError.missingLowercase,
+      );
+    });
+
+    test('Passwords without uppercase should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('strong123!'),
+        PasswordValidationError.missingUppercase,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('all123lower!@#'),
+        PasswordValidationError.missingUppercase,
+      );
+    });
+
+    test('Passwords without special characters should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('NoSpecial1Characters2'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+    });
+
+    test('Multiple validation errors should return most critical first', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('pass'),
+        PasswordValidationError.tooShort,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('passwordddd'),
+        PasswordValidationError.containsPassword,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abcaaa1234*%'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abcde123'),
+        PasswordValidationError.missingSpecialCharacter,
+      );
+    });
+
+    test('Edge cases with spaces and special formatting', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass 123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Tab\t123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Line\nBreak123!A'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Passwords with numbers in various positions', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('1AbcSpecial!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc1Special!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcSpecial!1'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Various special characters', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123@'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc_Def123#'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'AbcDef123$'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123%'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('AbcDef123&'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Valid passwords should not fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Very!hard!pass!77'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Komodo2024!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Complex!P4ssword123'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'!P4ssword#$@'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Mix3d_Ch4r4ct3rs!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Password specifically mentioned in the issue should be rejected', () {
+      // Should fail (has consecutive characters)
+      expect(
+        SecurityUtils.checkPasswordRequirements('Very!hard!pass!777'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test(
+        'Passwords with three or more consecutive identical '
+        'characters should fail', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong111Security!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Secure222!A'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!Secure333'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('aaaStrong1!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!bbb'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!CCC'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!!!Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1###Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(r'Strong1$$$Secure'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!aaaaa'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!44444'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1!!!!!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test(
+        'Valid passwords with two consecutive identical characters should pass',
+        () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong11Secured!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong!!Secured1'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('aaStrong1!Secured'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Special case - passwords with unicode characters', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Пароль123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('密码Abc123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Mötley123!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Extended Unicode character password tests', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('علي123!Abc'), // Arabic
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('こんにちは123!Ab'), // Japanese
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('안녕하세요123!Ab'), // Korean
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Привет123!Ab'), // Cyrillic
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Γειά123!Aa'), // Greek
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('שלום123!Aa'), // Hebrew
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('नमस्ते123!Ab'), // Devanagari
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Unicode edge cases and challenging patterns', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Раssw0rd!',
+        ), // Cyrillic 'Р' (not Latin 'P')
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass\u200Bword123!'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        // a + combining acute accent
+        SecurityUtils.checkPasswordRequirements('Pa\u0301ssword123!'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong🔑123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!🎮🎲🎯aa'),
+        PasswordValidationError.none,
+      );
+
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strоng123!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Unicode sequential characters detection', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong爱爱爱123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong😊😊😊123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+
+      // Characters that look similar but are actually different code points
+      expect(
+        SecurityUtils.checkPasswordRequirements('StrongАААbc123!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+
+    test('Bidirectional text and special Unicode formatting', () {
+      // Right-to-left marks and embedding
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u200Eword123!A',
+        ), // Contains LTR mark
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u200Fword123!A',
+        ), // Contains RTL mark
+        PasswordValidationError.none,
+      );
+
+      // Mixed directionality
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Abcהמסיסמ123!',
+        ), // Hebrew mixed with Latin
+        PasswordValidationError.none,
+      );
+
+      // Special spaces
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u2007word123!A',
+        ), // Figure space
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements(
+          'Pass\u00A0word123!A',
+        ), // Non-breaking space
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Advanced emoji password tests in valid passwords', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123!🔒'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔑Abcasba123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass🔥123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Abc123!🌟✨🚀'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🎮🎯A1!abaa'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong👨‍👩‍👧‍👦123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('A1!👍🏽Strong1234'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Pass🇺🇸123!A'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong123A🎯'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Strong1A🎯🎯🎯'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔥🔥🔥Strong1A!'),
+        PasswordValidationError.consecutiveCharacters,
+      );
+    });
+    test('Complex emoji sequences and ZWJ', () {
+      expect(
+        // ZWJ sequence (man technologist)
+        SecurityUtils.checkPasswordRequirements('Strong123A👨‍💻'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Complex ZWJ sequence
+        SecurityUtils.checkPasswordRequirements('Strong123A👁️‍🗨️'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Emoji presentation selector
+        SecurityUtils.checkPasswordRequirements('Strong123A☺️'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Mixed emoji and text patterns', () {
+      expect(
+        SecurityUtils.checkPasswordRequirements('Aaba🔒1🔑!🚀'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('Se🔒cure123!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('St🔑r🔒ng1!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        // Should not trigger containsPassword
+        SecurityUtils.checkPasswordRequirements('p🔑ssw🔒rd123A!'),
+        PasswordValidationError.none,
+      );
+      expect(
+        SecurityUtils.checkPasswordRequirements('🔒🚀🎮��Aa1!'),
+        PasswordValidationError.none,
+      );
+    });
+
+    test('Limited fuzzy testing', () {
+      final random = Random();
+      const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123'
+          r'456789!@#$%^&*()';
+
+      for (int i = 0; i < 10; i++) {
+        final int length = random.nextInt(15) + 1;
+        final StringBuffer passwordBuffer = StringBuffer();
+
+        for (int j = 0; j < length; j++) {
+          passwordBuffer.write(chars[random.nextInt(chars.length)]);
+        }
+
+        // Test the random password - we don't assert specific errors,
+        // just verify the validator properly handles random input
+        SecurityUtils.checkPasswordRequirements(passwordBuffer.toString());
+      }
+
+      final List<String> problematicInputs = [
+        // Password too short
+        'a',
+        // Repeated characters
+        'aaaPassword1!',
+        'Password111!',
+        'Password!!!1',
+        // Mixed borderline cases
+        'pass A1!',
+        'PASS a1!',
+        'Pass A!',
+        'Pass A1',
+        // Contains "password"
+        'MyPasswordIs1!',
+        'password123A!',
+        '!PASSWORDabc1',
+      ];
+
+      for (final String input in problematicInputs) {
+        SecurityUtils.checkPasswordRequirements(input);
+      }
+    });
+  });
+
+  group('Password generation tests', () {
+    test('Should throw error when length is less than 8', () {
+      expect(
+        () => SecurityUtils.generatePasswordSecure(7),
+        throwsA(isA<ArgumentError>()),
+      );
+      expect(
+        () => SecurityUtils.generatePasswordSecure(0),
+        throwsA(isA<ArgumentError>()),
+      );
+      expect(
+        () => SecurityUtils.generatePasswordSecure(-1),
+        throwsA(isA<ArgumentError>()),
+      );
+    });
+
+    test('Should generate password of specified length', () {
+      final password8 = SecurityUtils.generatePasswordSecure(8);
+      final password16 = SecurityUtils.generatePasswordSecure(16);
+      final password32 = SecurityUtils.generatePasswordSecure(32);
+
+      expect(password8.length, equals(8));
+      expect(password16.length, equals(16));
+      expect(password32.length, equals(32));
+    });
+
+    test('Should include all required character types', () {
+      final password = SecurityUtils.generatePasswordSecure(12);
+
+      // Check for at least one uppercase letter
+      expect(RegExp(r'[A-Z]').hasMatch(password), isTrue);
+
+      // Check for at least one lowercase letter
+      expect(RegExp(r'[a-z]').hasMatch(password), isTrue);
+
+      // Check for at least one digit
+      expect(RegExp(r'[0-9]').hasMatch(password), isTrue);
+
+      // Check for at least one special character
+      expect(RegExp(r'[@]').hasMatch(password), isTrue);
+    });
+
+    test('Should include extended special characters when flag is set', () {
+      final password = SecurityUtils.generatePasswordSecure(
+        20,
+        extendedSpecialCharacters: true,
+      );
+
+      // Check for at least one extended special character
+      expect(RegExp(r'[~`$^*+=<>?]').hasMatch(password), isTrue);
+    });
path: packages/komodo_defi_types/test/utils/security_utils_test.dart
user: login: coderabbitai[bot]
body: @takenagain Thank you for implementing the length check! This addition to the test suite will help catch any potential off-by-one errors in the password generation function when using extended special characters.



🐰 *happy rabbit noises*

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Catch%20off-by-one%20bugs%20in%20password%20generators%20by%20adding%20explicit%20length%20assertions%20in%20tests!%20%F0%9F%9B%A1%EF%B8%8F%20Ensure%20your%20secure%20passwords%20are%20exactly%20the%20length%20you%20expect.%20Try%20%40coderabbitai%20for%20smarter%20reviews!%20%F0%9F%94%8D&url=https%3A%2F%2Fgithub.com%2FKomodoPlatform%2Fkomodo-defi-sdk-flutter%2Fpull%2F58%23discussion_r2100588619)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@takenagain Thank you for implementing the length check! This addition to the test suite will help catch any potential off-by-one errors in the password generation function when using extended special characters.



🐰 *happy rabbit noises*

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Catch%20off-by-one%20bugs%20in%20password%20generators%20by%20adding%20explicit%20length%20assertions%20in%20tests!%20%F0%9F%9B%A1%EF%B8%8F%20Ensure%20your%20secure%20passwords%20are%20exactly%20the%20length%20you%20expect.%20Try%20%40coderabbitai%20for%20smarter%20reviews!%20%F0%9F%94%8D&url=https%3A%2F%2Fgithub.com%2FKomodoPlatform%2Fkomodo-defi-sdk-flutter%2Fpull%2F58%23discussion_r2100588619)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T15:36:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,70 +1,143 @@
 import 'dart:math';
 
+import 'package:characters/characters.dart';
 import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
 
+/// Enum representing different types of password validation errors
+enum PasswordValidationError {
+  containsPassword,
+  tooShort,
+  missingDigit,
+  missingLowercase,
+  missingUppercase,
+  missingSpecialCharacter,
+  consecutiveCharacters,
+  none;
+
+  bool get isValid => this == PasswordValidationError.none;
+}
+
 // ignore: one_member_abstracts
 abstract class SecurityUtils {
   static String generatePasswordSecure(
     int length, {
     bool extendedSpecialCharacters = false,
-  }) =>
-      _generateSecurePassword(
+  }) {
+    var result = '';
+    while (!SecurityUtils.checkPasswordRequirements(result).isValid) {
+      result = _generateSecurePassword(
         length,
         extendedSpecialCharacters: extendedSpecialCharacters,
       );
-}
+    }
 
-// TODO: unit tests
-
-String _generateSecurePassword(
-  int length, {
-  bool extendedSpecialCharacters = false,
-}) {
-  const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-  const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz';
-  const digits = '0123456789';
+    return result;
+  }
 
-  // Standard special characters that are generally safe in most contexts,
-  // including JSON
-  const specialCharacters = '@';
-  // const specialCharacters = r"*.!@#$%^(){}:;',.?/~`_+-=|";
+  /// /// Validates password according to KDF password policy
+  ///
+  /// Password requirements:
+  /// - At least 8 characters long
+  /// - Can't contain the word "password"
+  /// - At least 1 digit
+  /// - At least 1 lowercase character
+  /// - At least 1 uppercase character
+  /// - At least 1 special character
+  /// - No same character 3 times in a row
+  static PasswordValidationError checkPasswordRequirements(String password) {
+    // Use Unicode-aware character counting
+    if (password.characters.length < 8) {
+      return PasswordValidationError.tooShort;
+    }
+
+    if (password
+        .toLowerCase()
+        .contains(RegExp('password', caseSensitive: false, unicode: true))) {
+      return PasswordValidationError.containsPassword;
+    }
+
+    // Check for digits (any numerical digit in any script)
+    if (!RegExp(r'.*\p{N}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingDigit;
+    }
+
+    // Check for lowercase (any lowercase letter in any script)
+    if (!RegExp(r'.*\p{Ll}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingLowercase;
+    }
+
+    // Check for uppercase (any uppercase letter in any script)
+    if (!RegExp(r'.*\p{Lu}.*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingUppercase;
+    }
+
+    // Check for special characters
+    if (!RegExp(r'.*[^\p{L}\p{N}].*', unicode: true).hasMatch(password)) {
+      return PasswordValidationError.missingSpecialCharacter;
+    }
+
+    // Unicode-aware check for consecutive repeated characters using Characters class
+    final charactersList = password.characters.toList();
+    for (var i = 0; i < charactersList.length - 2; i++) {
+      if (charactersList[i] == charactersList[i + 1] &&
+          charactersList[i] == charactersList[i + 2]) {
+        return PasswordValidationError.consecutiveCharacters;
+      }
+    }
+
+    return PasswordValidationError.none;
+  }
 
-  const extendedSpecial = r'~`$^*+=<>?';
+  static String _generateSecurePassword(
+    int length, {
+    bool extendedSpecialCharacters = false,
+  }) {
+    const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz';
+    const digits = '0123456789';
+
+    // Standard special characters that are generally safe in most contexts,
+    // including JSON
+    const specialCharacters = '@';
+    // const specialCharacters = r"*.!@#$%^(){}:;',.?/~`_+-=|";
+
+    const extendedSpecial = r'~`$^*+=<>?';
+
+    final allCharacters = upperCaseLetters +
+        lowerCaseLetters +
+        digits +
+        specialCharacters +
+        (extendedSpecialCharacters ? extendedSpecial : '');
+
+    // Ensure the password length is at least 8 characters
+    if (length < 8) {
+      throw ArgumentError('Password length must be at least 8 characters.');
+    }
+
+    // Random number generator
+    final random = Random.secure();
+
+    // Pick one character from each category to ensure password strength
+    final password = <String>[
+      upperCaseLetters[random.nextInt(upperCaseLetters.length)],
+      lowerCaseLetters[random.nextInt(lowerCaseLetters.length)],
+      digits[random.nextInt(digits.length)],
+      specialCharacters[random.nextInt(specialCharacters.length)],
+      if (extendedSpecialCharacters)
+        extendedSpecial[random.nextInt(extendedSpecial.length)],
+    ];
 
-  final allCharacters = upperCaseLetters +
-      lowerCaseLetters +
-      digits +
-      specialCharacters +
-      (extendedSpecialCharacters ? extendedSpecial : '');
+    // Fill the rest of the password length with random characters from the pool
+    for (var i = 4; i < length; i++) {
+      password.add(allCharacters[random.nextInt(allCharacters.length)]);
+    }
path: packages/komodo_defi_types/lib/src/utils/security_utils.dart
user: login: coderabbitai[bot]
body: @takenagain Thank you for implementing the fix! This addresses the off-by-one error in the password generation when extended special characters are used. The dynamic start index will ensure the password length always matches what was requested.

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Avoid%20off-by-one%20bugs%20in%20password%20generators!%20%F0%9F%90%87%20%40coderabbitai%20caught%20a%20loop%20start%20index%20error%20causing%20longer%20passwords%20when%20using%20extended%20special%20chars.%20Fix%20ensures%20exact%20length%20every%20time!%F0%9F%94%90%20Try%20it!&url=https%3A%2F%2Fgithub.com%2FKomodoPlatform%2Fkomodo-defi-sdk-flutter%2Fpull%2F58%23discussion_r2100588609)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@takenagain Thank you for implementing the fix! This addresses the off-by-one error in the password generation when extended special characters are used. The dynamic start index will ensure the password length always matches what was requested.

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Avoid%20off-by-one%20bugs%20in%20password%20generators!%20%F0%9F%90%87%20%40coderabbitai%20caught%20a%20loop%20start%20index%20error%20causing%20longer%20passwords%20when%20using%20extended%20special%20chars.%20Fix%20ensures%20exact%20length%20every%20time!%F0%9F%94%90%20Try%20it!&url=https%3A%2F%2Fgithub.com%2FKomodoPlatform%2Fkomodo-defi-sdk-flutter%2Fpull%2F58%23discussion_r2100588609)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T15:36:19Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-21T15:24:49Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Auto reviews are disabled on base/target branches other than the default branch.
> 
> 
> 
> Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

The changes introduce a comprehensive password validation and generation system. Password generation now ensures compliance with strict security requirements, including checks for length, character diversity, and repetition. The password validation logic is encapsulated in a new method, and an extensive suite of unit tests is added to verify both validation and generation functionalities.

## Changes

| File(s)                                                                                  | Change Summary                                                                                                                                                                                                                                                     |
|------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `.../kdf_startup_config.dart`                                                            | Removed the `generatePassword` and `_validateRPCPassword` static methods from `KdfStartupConfig`. Updated password generation to use `SecurityUtils.generatePasswordSecure(32)` directly, removing the previous custom validation loop.                            |
| `.../security_utils.dart`                                                                | Added the `PasswordValidationError` enum. Implemented robust password validation in `checkPasswordRequirements`. Enhanced `generatePasswordSecure` to guarantee passwords meet all requirements. Improved internal password generation logic for character diversity. |
| `.../security_utils_test.dart`                                                           | Added a comprehensive test suite covering password validation and generation, including edge cases, Unicode support, and randomness checks.                                                                                                                        |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Caller
    participant SecurityUtils

    Caller->>SecurityUtils: generatePasswordSecure(length)
    loop Until valid password
        SecurityUtils->>SecurityUtils: _generateSecurePassword(length)
        SecurityUtils->>SecurityUtils: checkPasswordRequirements(password)
        alt If password invalid
            SecurityUtils-->>SecurityUtils: Retry generation
        end
    end
    SecurityUtils-->>Caller: Return valid password
```

## Poem

> In the warren of code, we burrowed deep,  
> To guard our secrets, no password sheep.  
> With tests and checks for every case,  
> No "password" sneaks, nor three in place.  
> Secure and strong, our carrots stored—  
> The safest vault a bunny’s adored!  
> 🥕🔐

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKNwSPbABsvkCiQBHbGlcSHFcLzpIACIAM3gADwAKCm4GMG40REQAd3wKWmIyZQIKAEoubG5aag9M7LyCyAk0L3ga8XwsAkhmagZYSABpABEAMR4s3Pz6bnw2hllo9Fpaf2zpdCwkBw94btg6qcb6UnIqUoVmOfIMUPxYsMPhlkV8Ky9qWPzmAHJkADWr1ohCU8TAiFoALAsS8eBofH8c0Q6nysg0MGevEEkWYkByWSe1CeRwaM0gZxK+QU3no+wRzHUNDuXnk2No2AYpOmBWQTDu2gw+yIT38HiRJFq9AGzjQYkoyH26D8+ByGIAypzBvUebRkM5xSQRPL6HIScMSPIRpRJNR4F1IGNsBgxPasElRmMypAOR4erhniQEkhxBgRTqTjx5vBFhi4KhbOh4MxkD0qh0/VjjuSWm0Om7IF58EQY2F8JAyA5/BTihcohGZnyWNxWfj1IMAx5PZMyU05gtZAAaHj+KR3YXmykXAsPFAYXPtHu6xAacyWADCLDYd2QDicLjczy6raYrHY/AOqG43l8/iCIRQyFiFBY5uwiEoMVwaABZDQREFQdlhyWByzvYJEBoeg0D8EgJHgEgckgZ9X07SB30/aImCUKhVHUbQNGWABJLB1huD9hywxRlDw794CArYcAIPooMgAR8FwYc5QBDBVUiWhSHoNDRwQpDwIfTAhIoeAiFIfxoO2F1/G3b9bzg0SR3wLlsjjZ52NCBhPmk+IogDYl1B9fBNl40J/DAETEKuRlcFTWBiTcqR0C8fw0FoeQBBIMhYPgxCokkolQmE9THNPPoMDpZASFiWJRHETzj3kECgrQPAWDtBhgtE/Vq0yDDaAxAA5ctYmwChOz4WgkAYd8US6YcUroARuOHalsMNEKkNi9hkBySg6hfeClDpA5Mx88qjH0YxwCgMh6FnHKCCKc4pSc9guF4fhhFSyRNjNPrcLUTRtF0MBDBMKB40VfUsA2wgpx2oa7i4KgkL3PoXDY+RzpUS6tB0RaltMAxMgYAF/2kAB6IFmDeAB9MF4FR580DYRoAQRtoBARxAKAYBH+XiIgkdoWJUcg5xcCqVGKZkjQajqjgDGibmDAsSAAEEiK2kooj+5x5FnGUw2kQ89mufJ7keAADGG4dIDhkbRjHUdwWRuERzWQXRpLMd1/WV3Z3AlfxQlfKm3SPCV96aCsbMCmt+nxAKtgA0ULY6RcyBDi8fW+CV1GFwzGwrHXV3e1oa3RurZT8CkegULxNClaGGn1W/Oqqk3DBKetwypgxddWi8VNyyd2tajj3UkjKa3vgapKcq8UJo/XJdI2dgsk8NFs5SiHJ2x9eB/DERgq5ryAlfVURavUWQAFVxGrjRnZIRuTiX5r/CSABmAAmFuHb78kB4dXjCy6OS50M7AlFc8b1Pwd9mlado7QdIt8DcAihWDAVZTJZnjr0EgoRED6wYPAeIBVTwtiDKvf2sF9bhALAwaSCJ4BoEquWDihw+BFhLAVXqXRcAvl8LCVUjA3LSxGmNXovkSCriMLzSw/Mu4lDdPPNCSgy7Ti6MgWcQY5h1SiNSa8AgFggKwTLAwUAbAkBRmnew34vZQN9vQRe1CJw7z3jMZurcXx4jLtkBeOdYh5wZoXLoJc5wL0JsTUm5NHEyWprTemBduDM08UQNmDMlbKMgKo9RostGlh9qBPR7F5iQAjj/KOMdjEFCSHnaSYZ7CkxbshcxjBPhWOzrnfOjNuBFycUqJWriSZkxZlTAENM6blKZo04JdVQnc2iAtKGqt4aICRsCfAxt4g6z1ojOp7i8DwGrsTZeuDZCo1mVvS2nMelcIFkLHe9AxYA0low0giBZboWqLUOc1DFCck2DBZB/hDigJOlfJokc/5YDYFLJAeJwoo0aiZN+LzTj106FgMhpYeiVlqh4ZBbRMBcjbAGJ4qA3mgoxPzSA5AkJkEcAvdJtAABqKT3kAFEKAvgoNbVAdtTJgRILwaQ55YGiAQaWBsrziWguQtoOE6xhz7Gfo1HJAxRAAifNSSIYYAzDgZR+F0HhZxoQcAISC2SRTRHZbQaIw5Golk4vfJODAsgkGHFUUORqKL2Dgfg3wMoqDygoFxeKk9EBV1VBOAMYoFCgOXmlPYShxxGttW5e1CIVyYkdkYt2tAD7QutrEv2vExL0slFBVsO9kCauQO+CcMFeC2hoAvVGO9Y3+HxfG6BcT0Ljl8GhTVUDoH6h8N/PM7zGC4JtGgYcSJPjwJyXWkSn9kCNWngZKuiLQJ4Bbb/NFpzw4lsWbvaNFbdGPhAYwrkQlyxEGwLKO4gUn5wlalgdaoRIhZHuOQBhsoHUFNfJKAYsEghTyiEamgRA0SQCSGaygFqTUGt/canVMl1DDmZfA1o17Q2UCdWtbgoKq7yCDMyKaVqWWQbtXKMNZQMREVCGQNu2llSMiFMwXFkqiBItnAADig1hhUgNRTupyZgCs5LqQIOaPaT4UFL71tQPyVVnJWJmniBQSC9gSCRFdDkroMKQ30b4BnCscpBhvpIB+lww5OxYHiD4D1zxlKChwhOvwklXyYYdU+QpaFTxqHILMfA8xYPIX2Ih+wsBsDJTaP254lmw1lhAWAsz8UWDkB0qc1FBYE16JFbDfFqjn3KWGtbDCyA15Cj6mANABJk4ae8M4CsCRZXHuekJZ4St/MKmtgM0ggXGG0EiL0bw4gwB9TmPSOjVmFDktSqyXDoRQ5txTAw0V4q+AkeTORsglHYDtXyGoVYQVlWquFMBvVI5GXyuHEWQ1xr0E/tJvtyIuAw1getRhhTVmXOCd9c8iUH0rthoG7BRmFBQHmiYL12BXQhUiiVviolrbQVkopdbakSteLkCpY8cdcWxVLg4QAeSkFQHw2m/NHNuasFUAh3yDejdO/MDpwpQurLsoFTZi4/EC4Ik2DnJOHzQf2GM/kkr5A8AFCc/g3sOY4WuAWvCREff9M8IRRl3liMeBIhW0i+CyPkewdQCETlhP5kt+gOK8QA+jUDmdbpQf5EThPFo0lMAuS4JVqhgpEDluHErAg+B1SgS6fbxk2RhQjBA1bN3OxhQABlVSAY/ErX3Huwxr24Oa41oeF7u5RGGdUF2vDrie5QWPVufXNT9anm9YbY/hUh3JqlWAlYfiZ7rFZm8LYhNXFAAAsooVlUT8o6KrYvRZq8N5zJXFG+OpaSBJE6xR6VkAADeCTfDIdWnQJP6GU9p/E5AAAvNy6uJAAC++SegCrhEoInbaAFALJ4YkF9ZCdsEbU+7AL6VIrjVxrzRreYsL1LV36vGh4cJcCDf5LO5MkGI5Karei3TWC66coG7saUrITUhRZAGE7/g26hAYwM4s6xhhK57Sx7LRLeyVp+zzpn4D74pD53CFgzaj4T5OZT4JAoaz7J657QZL6r6xCtAfhb7Ww9A7p7o0B7AuhHoziPDgQvrSiL6zzvr5Aq7oIEb5AIqTZkZ4gj6wCribLKL9LcSDLDJ/KjLaxmyIw0CQQIyrJDLl4ryV5GE6whCdKcBcw8x8yCzCx1h7KOD/QSyPBSzHKHioBYphAPh6a8FXIchEb3IkCPIoieTOgWT6GBxtxAoH5crhT1o3y6bOiuhdA/ziCbDYiTRRBmgd4V7rzV5Kx8aE5RYOhRFNio7Kjjz75UDSz8CPCIBcgYDOD2iID8p8EvwTjw7ja9D7BTYKHkFzabZyoIqzi6rqBtFnLR4fjoK7bB4eAnZnZoYQbBp54KguZoRBjPzHr1HmgraAHqqarLBKiYBAxZDCgvasHlio7N7DrQLHQOiKqwBerUgozVi3bZ7PLtBK5BpdZLE861RCi+YeAowSY4LK6/GlFYCUAUr4iPLNZdzwAoKPi7CJTBiQSXzlGHovweB0B1Z/ojQTywKjyTHfgqo7b7Cc4+RioubT5PKeQZYxjUR/HrHYl/YVgoxCBIDDiLYvqpHNG1rIasIUA0noKwpBgcn4BcmSb3jyqElIoABelAhA1RSKQg+AlJ4ml8UJPhkEVOYmmcbkBO8c8pHYeQ3qJhfqKAAaXsl2axS+Oow4IEcys0B6rxHO3Kcy6KhYyY6gUQNUCpCp8gURE4R+NY20+hIWIIeIWagWZOmYHgqK1IDWkQxUl0VAAM+w14gc48vsU6OCWQCAYYHC7gsRSRupgcNxsQwZRpk4IK1InqqoiUUBPRihyAqZrkrG1GMqL4gRmR0aUu5o4kkEUQihLmu+OJTaakSWr6ohuhyA36Ue8xO2QeR2lq4xLkLm4GNqLJ4mOGkAAA6vCZsTQTPnssnruU+J8CKKgH+HInQBjh4BTrGROfvlVkvspqxnSahh+JoBGhWfqVPHiLUdGeFjIPIAPDkmRoicibGeFFMJQKGCKBEbKdIOGu4PIFcXEZmMSM+QOUuOaPWkIX/oHPDugiCJihxN6nRAcC8RNh6R8bMp5N8YGnaYwehc8M7BxqmCEP6TIr2ZyBOJHJTugKEHIbikkNRt6OFEZHVkkAAIwAAMil3obZyOqOVcj5AFQW0KuO+OcRBY4UJhJUhO5ZuZgwdJYRMKqcygdWs4vEFAfQXgw4eJ/64UjJfUs8H49+nCdhQukutOYuogEuoKg5MuUia08u2AcipYSuGRquUAFUjkUR9gN+haNKtAluEgGp9AfQ+wpiziKs6hxymhWsJsEy5sCMURhh1eCy+RVePeFhGJlspc1ufROSRAL4VQiAnMkAugC8vw+KBlZRIQiAvwxuSK0F4gyJDuIQBVBJMB8uJREBDojRf40k+A9+fVUASsg1plIKBY5R41pmU1SJTWs1kE81xqPRiRB1q1TRLRm1yhPMqhhgBg90ICa0sOuUDhH0W4e0Zmv0zh4sjGwMtEYMN0d0y0Tk6gqM7QiAqMDko0tArSDMkNkMUAAAnLQAAOyY2Y0CDKUABsp8sQ8lSgp8ONp8ilAALLEAFETdRgIPJTTaIPJQAKw41U1E10AkCY3Hzo3vXQ2xSw3w2I3RTI2oyrSQ1AA== -->

<!-- internal state end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=KomodoPlatform/komodo-defi-sdk-flutter&utm_content=58):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: takenagain
created_at: 2025-05-21T15:25:00Z
body: @coderabbitai review
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2025-05-21T15:25:08Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>✅ Actions performed</summary>

Review triggered.

> Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

</details>
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-21T15:30:57Z
body: Visit the preview URL for this PR (updated for commit b793ea1):

[https://komodo-defi-sdk--pr58-bugfix-rpc-password-wnwwwfrd.web.app](https://komodo-defi-sdk--pr58-bugfix-rpc-password-wnwwwfrd.web.app)

<sub>(expires Wed, 28 May 2025 15:41:35 GMT)</sub>

<sub>🔥 via [Firebase Hosting GitHub Action](https://github.com/marketplace/actions/deploy-to-firebase-hosting) 🌎</sub>

<sub>Sign: 7f9f5ac39928f333b6e8fcefb7138575e24ed347</sub>
author_association: CONTRIBUTOR