@@ -102,6 +102,9 @@ extension MiscExtensionsNullable on String? {
102102 if (this .isBlank) {
103103 return 0 ;
104104 }
105+ if (wordsPerMinute <= 0 ) {
106+ throw ArgumentError ('wordsPerMinute must be greater than 0' );
107+ }
105108 var words = this ! .trim ().split (RegExp (r'(\s+)' ));
106109 var magicalNumber = words.length / wordsPerMinute;
107110 return (magicalNumber * 100 ).toInt ();
@@ -311,10 +314,16 @@ extension MiscExtensionsNullable on String? {
311314 if (this .isBlank) {
312315 return false ;
313316 }
314- this ! .substring (0 , 1 );
315- var regex = RegExp (
316- r'(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))' );
317- return regex.hasMatch (this ! );
317+
318+ // Simple IPv6 validation using Uri.tryParse for security
319+ try {
320+ var uri = Uri .tryParse ('http://[${this !}]' );
321+ return uri != null && uri.host.isNotEmpty;
322+ } catch (e) {
323+ // Additional simple regex check for basic IPv6 format
324+ var regex = RegExp (r'^[0-9a-fA-F:]+$' );
325+ return regex.hasMatch (this ! ) && this ! .contains (':' ) && this ! .length <= 39 ;
326+ }
318327 }
319328
320329 /// Checks whether the `String` is a valid URL.
@@ -332,9 +341,23 @@ extension MiscExtensionsNullable on String? {
332341 if (this .isBlank) {
333342 return false ;
334343 }
335- var regex = RegExp (
336- r'[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)' );
337- return regex.hasMatch (this ! );
344+
345+ // Use Uri.tryParse for secure URL validation
346+ try {
347+ var uri = Uri .tryParse (this ! );
348+ if (uri == null ) return false ;
349+
350+ // Check for valid schemes and prevent dangerous ones
351+ var validSchemes = ['http' , 'https' , 'ftp' , 'ftps' ];
352+ if (! validSchemes.contains (uri.scheme.toLowerCase ())) {
353+ return false ;
354+ }
355+
356+ // Must have a valid host
357+ return uri.hasAuthority && uri.host.isNotEmpty;
358+ } catch (e) {
359+ return false ;
360+ }
338361 }
339362
340363 /// Checks whether the `String` is a valid `DateTime` :
@@ -545,6 +568,9 @@ extension MiscExtensionsNullable on String? {
545568 // ignore: omit_local_variable_types
546569 List <Map <String , int >> occurrences = [];
547570 var letters = this ! .split ('' )..sort ();
571+ if (letters.isEmpty) {
572+ return [];
573+ }
548574 var checkingLetter = letters[0 ];
549575 var count = 0 ;
550576 for (var i = 0 ; i < letters.length; i++ ) {
@@ -593,6 +619,9 @@ extension MiscExtensionsNullable on String? {
593619 }
594620 var occurrences = < String , int > {};
595621 var letters = this ! .split ('' )..sort ();
622+ if (letters.isEmpty) {
623+ return this ;
624+ }
596625 var checkingLetter = letters[0 ];
597626 var count = 0 ;
598627
@@ -761,6 +790,9 @@ extension MiscExtensionsNullable on String? {
761790 }
762791
763792 var words = this ! .trim ().split (RegExp (r'(\s+)' ));
793+ if (words.isEmpty) {
794+ return this ;
795+ }
764796 var result = words[0 ].toLowerCase ();
765797 for (var i = 1 ; i < words.length; i++ ) {
766798 result += words[i].substring (0 , 1 ).toUpperCase () +
0 commit comments