What changed, and why it matters
This commit is a small code cleanup in a wallet app's connection settings form. It moves three address-format checkers to the top of the file, fixes a bug where the app was stripping 'http://' or 'https://' from addresses incorrectly, and now also cleans scanned QR-code addresses the same way typed addresses are cleaned. The changes are defensive: they make address validation more consistent and reduce the chance that a user-entered or scanned URL with a protocol prefix is rejected or mishandled.
No urgent action required. Reviewers should verify that the new regex patterns still reject malformed or malicious addresses (e.g., embedded newlines, unusual Unicode homoglyphs, overlong ports) and that `_cleanAddress` handles only the intended prefixes. Consider adding unit tests for address cleaning and validation, including QR-code inputs.
Security signals we found
Input validation regexes were duplicated; refactor centralizes them, reducing risk of inconsistent validation
Previous protocol stripping used a string literal instead of a RegExp, so 'http://'/'https://' prefixes were not actually removed
QR-code scanned addresses are now cleaned before validation, matching the path for typed addresses
No new network calls, permissions, or dependencies introduced
Evidence from the diff
The refactor extracts RegExp patterns for IPv4, domain, and v3 onion addresses into file-level finals. It corrects _cleanAddress so replaceAll receives a real RegExp rather than a literal string (previously r'https?:\/\/' was treated as a raw string literal, not a regex, so the protocol strip did not work). It also applies _cleanAddress to QR-scanned input before validation, and removes duplicate inline regex declarations from _onAddressChange. _isValidConnectionAddress now validates the already-cleaned value instead of cleaning it again. These are correctness/consistency improvements, not a redesign of the validation logic.
Changed components
lib/widgets/connection_settings_form.dartConnectionSettingsForm widget_cleanAddress helper_isValidConnectionAddress helper_scanQrCode method_onAddressChange methodInspect captured patch +12 / −16
diff --git a/lib/widgets/connection_settings_form.dart b/lib/widgets/connection_settings_form.dart
index cbe5212..f4e93b5 100644
--- a/lib/widgets/connection_settings_form.dart
+++ b/lib/widgets/connection_settings_form.dart
@@ -13,6 +13,14 @@ import 'package:skylight_wallet/services/tor_service.dart';
const isDemoMode = String.fromEnvironment('DEMO_MODE') == 'true';
+final ipAddressRegex = RegExp(
+ r'(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)){3}(?::\d{1,5})?$',
+);
+final domainAddressRegex = RegExp(
+ r'(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63}(?::\d{1,5})?$',
+);
+final onionAddressRegex = RegExp(r'[a-z2-7]{56}.onion(:\d{1,5})?$');
+
/// Shared form widget used by both ConnectionSetupScreen and the connection settings dialog
class ConnectionSettingsForm extends StatefulWidget {
final String saveButtonLabel;
@@ -67,19 +75,15 @@ class _ConnectionSettingsFormState extends State<ConnectionSettingsForm> {
}
String _cleanAddress(String value) {
- return value.trim().replaceAll(r'https?:\/\/', '');
+ return value.trim().replaceAll(RegExp(r'https?:\/\/'), '');
}
bool _isValidConnectionAddress(String value) {
final connectionUrlRegex = RegExp(
- [
- r'(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)){3}(?::\d{1,5})?$',
- r'[a-z2-7]{56}.onion(:\d{1,5})?$',
- r'(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63}(?::\d{1,5})?$',
- ].join('|'),
+ [ipAddressRegex.pattern, onionAddressRegex.pattern, domainAddressRegex.pattern].join('|'),
);
- return connectionUrlRegex.hasMatch(value.replaceAll(r'https?:\/\/', ''));
+ return connectionUrlRegex.hasMatch(value);
}
Future<void> _scanQrCode() async {
@@ -88,7 +92,7 @@ class _ConnectionSettingsFormState extends State<ConnectionSettingsForm> {
final result = await Navigator.pushNamed(context, '/scan_qr');
if (result != null && result is String) {
- final scannedAddress = result.trim();
+ final scannedAddress = _cleanAddress(result);
if (_isValidConnectionAddress(scannedAddress)) {
_addressController.text = scannedAddress;
_onAddressChange(scannedAddress);
@@ -105,14 +109,6 @@ class _ConnectionSettingsFormState extends State<ConnectionSettingsForm> {
void _onAddressChange(String value) {
value = _cleanAddress(value);
- final ipAddressRegex = RegExp(
- r'(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)){3}$',
- );
- final onionAddressRegex = RegExp(r'[a-z2-7]{56}.onion(:\d{1,5})?$');
- final domainAddressRegex = RegExp(
- r'(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63}(?::\d{1,5})?$',
- );
-
var useTor = false;
var useSsl = false;
Why this scored 29/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.