@@ -252,7 +252,7 @@ int fossil_io_validate_sanitize_string_ctx(const char *input,
252252 return FOSSIL_SAN_MODIFIED ;
253253 }
254254
255- size_t in_len = strnlen (input , 4096 ); /* cap scanning to 4k */
255+ size_t in_len = fossil_io_cstring_length_safe (input , 4096 ); /* cap scanning to 4k */
256256 size_t out_i = 0 ;
257257 int flags = FOSSIL_SAN_OK ;
258258
@@ -385,7 +385,7 @@ int fossil_io_validate_is_suspicious_user(const char *input) {
385385 // 1. Too long or too short
386386 if (len < 3 || len > 32 ) return 1 ;
387387
388- // 2. Check digit runs
388+ // 2. Count digits, letters, and digit runs
389389 int digit_run = 0 , max_digit_run = 0 , digit_count = 0 , alpha_count = 0 ;
390390 for (size_t i = 0 ; i < len ; i ++ ) {
391391 if (isdigit ((unsigned char )input [i ])) {
@@ -397,10 +397,13 @@ int fossil_io_validate_is_suspicious_user(const char *input) {
397397 if (isalpha ((unsigned char )input [i ])) alpha_count ++ ;
398398 }
399399 }
400+
401+ // 3. Check for long digit runs or too few letters
400402 if (max_digit_run >= 5 ) return 1 ; // suspicious long digit tail
401403 if ((float )digit_count / len > 0.5 ) return 1 ; // mostly digits
404+ if ((float )alpha_count / len < 0.3 ) return 1 ; // too few letters
402405
403- // 3 . Suspicious keywords
406+ // 4 . Suspicious keywords
404407 const char * bad_keywords [] = {"bot" , "test" , "fake" , "spam" , "zzz" , "null" , "admin" };
405408 size_t nkeys = sizeof (bad_keywords ) / sizeof (bad_keywords [0 ]);
406409 for (size_t i = 0 ; i < nkeys ; i ++ ) {
@@ -409,7 +412,7 @@ int fossil_io_validate_is_suspicious_user(const char *input) {
409412 }
410413 }
411414
412- // 4 . Very high entropy (simple Shannon estimate)
415+ // 5 . Very high entropy (simple Shannon estimate)
413416 int freq [256 ] = {0 };
414417 for (size_t i = 0 ; i < len ; i ++ ) freq [(unsigned char )input [i ]]++ ;
415418 double entropy = 0.0 ;
0 commit comments