From 61d3b8bdf283d93066c878fde0fd7297b1642ad8 Mon Sep 17 00:00:00 2001 From: AbelOsaretin Date: Wed, 24 Jun 2026 13:25:06 +0100 Subject: [PATCH 1/4] feat(validation): add Stellar address validation utility - Add Validation class with stellarSecretKey and stellarPublicKey methods - Validate key format: prefix, length, and base32 character set - Return null for valid keys, error message string for invalid ones --- lib/utils/validation.dart | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 lib/utils/validation.dart diff --git a/lib/utils/validation.dart b/lib/utils/validation.dart new file mode 100644 index 0000000..104f22e --- /dev/null +++ b/lib/utils/validation.dart @@ -0,0 +1,26 @@ +class Validation { + /// Returns null if valid, or an error message if invalid. + static String? stellarSecretKey(String key) { + final trimmed = key.trim(); + if (trimmed.isEmpty) return 'Secret key is required'; + if (!trimmed.startsWith('S')) return "Secret key must start with 'S'"; + if (trimmed.length != 56) { + return 'Secret key must be exactly 56 characters (got ${trimmed.length})'; + } + final base32 = RegExp(r'^[A-Z2-7]+$'); + if (!base32.hasMatch(trimmed)) { + return 'Secret key contains invalid characters (must be A-Z and 2-7)'; + } + return null; + } + + static String? stellarPublicKey(String key) { + final trimmed = key.trim(); + if (trimmed.isEmpty) return 'Address is required'; + if (!trimmed.startsWith('G')) return "Stellar address must start with 'G'"; + if (trimmed.length != 56) { + return 'Stellar address must be exactly 56 characters (got ${trimmed.length})'; + } + return null; + } +} From e6d51383aecccafb294aa79493f0eb23dec383e0 Mon Sep 17 00:00:00 2001 From: AbelOsaretin Date: Wed, 24 Jun 2026 13:25:10 +0100 Subject: [PATCH 2/4] feat(wallet): add input validation to wallet connection - Integrate Validation.stellarSecretKey in _onConnect method - Show SnackBar with error message for invalid keys - Prevent invalid keys from being passed to WalletProvider.connect() --- lib/screens/wallet_screen.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/screens/wallet_screen.dart b/lib/screens/wallet_screen.dart index df5b680..fc37223 100644 --- a/lib/screens/wallet_screen.dart +++ b/lib/screens/wallet_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import '../providers/wallet_provider.dart'; +import '../utils/validation.dart'; import '../widgets/transaction_button.dart'; class WalletScreen extends StatefulWidget { @@ -24,9 +25,14 @@ class _WalletScreenState extends State { void _onConnect(WalletProvider provider) { final key = _secretKeyController.text.trim(); - if (key.isEmpty) return; + final error = Validation.stellarSecretKey(key); + if (error != null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(error), backgroundColor: Colors.red), + ); + return; + } provider.connect(key); - // Clear the field immediately so the key is not held in memory longer than needed _secretKeyController.clear(); } From d297e4cd5fe17a5c1a97f3392f7f0b2e8684a7b1 Mon Sep 17 00:00:00 2001 From: AbelOsaretin Date: Wed, 24 Jun 2026 13:25:13 +0100 Subject: [PATCH 3/4] test(validation): add unit tests for Stellar address validation - Test valid secret key format (prefix, length, base32 chars) - Test valid public key format - Test edge cases: empty string, wrong prefix, wrong length, invalid chars - Test trimming of whitespace in input --- test/utils/validation_test.dart | 51 +++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 test/utils/validation_test.dart diff --git a/test/utils/validation_test.dart b/test/utils/validation_test.dart new file mode 100644 index 0000000..17d72b8 --- /dev/null +++ b/test/utils/validation_test.dart @@ -0,0 +1,51 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:nodus_protocol/utils/validation.dart'; + +void main() { + group('Validation.stellarSecretKey', () { + test('valid 56-char S-key returns null', () { + const key = 'SCZANGBA5AKIA7ORYH4RO2EQVN3ISYIKXT6EU7EOSQ37NQBP5M4OBK3'; + expect(Validation.stellarSecretKey(key), isNull); + }); + + test('empty string fails', () { + expect(Validation.stellarSecretKey(''), isNotNull); + }); + + test('wrong prefix fails', () { + expect(Validation.stellarSecretKey('GAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'), isNotNull); + }); + + test('wrong length fails', () { + expect(Validation.stellarSecretKey('SSHORT'), isNotNull); + }); + + test('invalid base32 chars fail', () { + expect(Validation.stellarSecretKey('S${'1' * 55}'), isNotNull); + }); + + test('valid key with spaces trims and validates', () { + const key = ' SCZANGBA5AKIA7ORYH4RO2EQVN3ISYIKXT6EU7EOSQ37NQBP5M4OBK3 '; + expect(Validation.stellarSecretKey(key), isNull); + }); + }); + + group('Validation.stellarPublicKey', () { + test('valid 56-char G-key returns null', () { + const key = 'GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXJH'; + expect(Validation.stellarPublicKey(key), isNull); + }); + + test('empty string fails', () { + expect(Validation.stellarPublicKey(''), isNotNull); + }); + + test('wrong prefix fails', () { + expect(Validation.stellarPublicKey('SAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'), isNotNull); + }); + + test('wrong length fails', () { + expect(Validation.stellarPublicKey('GSHORT'), isNotNull); + }); + }); +} From 1226667c2c9959dd2bc0f40734ecdac914c350b4 Mon Sep 17 00:00:00 2001 From: AbelOsaretin Date: Wed, 24 Jun 2026 14:49:16 +0100 Subject: [PATCH 4/4] fix: correct test fixture character counts in validation_test.dart - S-key test fixture was 55 chars, now 56 chars - G-key test fixture was 59 chars, now 56 chars - Both test keys now match the 56-character requirement for Stellar keys --- test/utils/validation_test.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/utils/validation_test.dart b/test/utils/validation_test.dart index 17d72b8..bea58ba 100644 --- a/test/utils/validation_test.dart +++ b/test/utils/validation_test.dart @@ -4,7 +4,7 @@ import 'package:nodus_protocol/utils/validation.dart'; void main() { group('Validation.stellarSecretKey', () { test('valid 56-char S-key returns null', () { - const key = 'SCZANGBA5AKIA7ORYH4RO2EQVN3ISYIKXT6EU7EOSQ37NQBP5M4OBK3'; + const key = 'SCZANGBA5AKIA7ORYH4RO2EQVN3ISYIKXT6EU7EOSQ37NQBP5M4OBK3A'; expect(Validation.stellarSecretKey(key), isNull); }); @@ -25,14 +25,14 @@ void main() { }); test('valid key with spaces trims and validates', () { - const key = ' SCZANGBA5AKIA7ORYH4RO2EQVN3ISYIKXT6EU7EOSQ37NQBP5M4OBK3 '; + const key = ' SCZANGBA5AKIA7ORYH4RO2EQVN3ISYIKXT6EU7EOSQ37NQBP5M4OBK3A '; expect(Validation.stellarSecretKey(key), isNull); }); }); group('Validation.stellarPublicKey', () { test('valid 56-char G-key returns null', () { - const key = 'GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXJH'; + const key = 'GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXJH'; expect(Validation.stellarPublicKey(key), isNull); });