Highly ambitious ATProtocol AppView service and sdks
138
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix TID validation

TID format validator was rejecting valid TIDs like `3m3zm7eurxk26`.

It seems like syntax format assumed in the file is wrong, so I amended it to use https://atproto.com/specs/tid#tid-syntax. In particular, see the "reference regex" in the spec, which is `/^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/` and doesn't match old code.

This is vibecoded so be cautious (though both Claude and ChatGPT seem happy with the result). I've also asked Claude to cross-check the new tests against `atproto/packages/syntax/src/tid.ts`.

authored by danabra.mov and committed by

Tangled ac8aa7be 32726d63

+24 -11
+24 -11
crates/slices-lexicon/src/validation/primitive/string.rs
··· 577 577 578 578 /// Validates TID (Timestamp Identifier) format 579 579 /// 580 - /// TID format: 13-character base32-encoded timestamp + random bits 581 - /// Uses Crockford base32 alphabet: 0123456789ABCDEFGHJKMNPQRSTVWXYZ (case-insensitive) 580 + /// TID format: 13-character base32-sortable encoded timestamp + random bits 581 + /// Uses ATProto base32-sortable alphabet: 234567abcdefghijklmnopqrstuvwxyz (lowercase only) 582 582 pub fn is_valid_tid(&self, value: &str) -> bool { 583 583 use regex::Regex; 584 584 ··· 586 586 return false; 587 587 } 588 588 589 - // TID uses Crockford base32 (case-insensitive, excludes I, L, O, U) 590 - let tid_regex = Regex::new(r"^[0-9A-HJKMNP-TV-Z]{13}$").unwrap(); 591 - let uppercase_value = value.to_uppercase(); 589 + // TID uses base32-sortable (s32) - lowercase only 590 + // First character must be from limited set (ensures top bit is 0) 591 + // Remaining 12 characters from full base32-sortable alphabet 592 + let tid_regex = Regex::new(r"^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$").unwrap(); 592 593 593 - tid_regex.is_match(&uppercase_value) 594 + tid_regex.is_match(value) 594 595 } 595 596 596 597 /// Validates Record Key format ··· 1096 1097 1097 1098 let validator = StringValidator; 1098 1099 1099 - // Valid TIDs (13 characters, Crockford base32) 1100 - assert!(validator.validate_data(&json!("3JZFKJT0000ZZ"), &schema, &ctx).is_ok()); 1101 - assert!(validator.validate_data(&json!("3jzfkjt0000zz"), &schema, &ctx).is_ok()); // case insensitive 1100 + // Valid TIDs (base32-sortable, 13 chars, lowercase) 1101 + assert!(validator.validate_data(&json!("3m3zm7eurxk26"), &schema, &ctx).is_ok()); 1102 + assert!(validator.validate_data(&json!("2222222222222"), &schema, &ctx).is_ok()); // minimum TID 1103 + assert!(validator.validate_data(&json!("a222222222222"), &schema, &ctx).is_ok()); // leading 'a' (lower bound) 1104 + assert!(validator.validate_data(&json!("j234567abcdef"), &schema, &ctx).is_ok()); // leading 'j' (upper bound) 1105 + 1102 1106 1103 - // Invalid TIDs 1107 + // Invalid TIDs - uppercase not allowed (charset is lowercase only) 1108 + assert!(validator.validate_data(&json!("3m3zM7eurxk26"), &schema, &ctx).is_err()); // mixed case 1109 + 1110 + // Invalid TIDs - wrong length 1104 1111 assert!(validator.validate_data(&json!("too-short"), &schema, &ctx).is_err()); 1105 1112 assert!(validator.validate_data(&json!("too-long-string"), &schema, &ctx).is_err()); 1113 + 1114 + // Invalid TIDs - invalid characters (hyphen/punct rejected; digits 0,1,8,9 not allowed) 1106 1115 assert!(validator.validate_data(&json!("invalid-chars!"), &schema, &ctx).is_err()); 1107 - assert!(validator.validate_data(&json!("invalid-ILOU0"), &schema, &ctx).is_err()); // invalid chars (I, L, O, U) 1116 + assert!(validator.validate_data(&json!("xyz1234567890"), &schema, &ctx).is_err()); // has 0,1,8,9 1117 + 1118 + // Invalid TIDs - first character must be one of 234567abcdefghij 1119 + assert!(validator.validate_data(&json!("k222222222222"), &schema, &ctx).is_err()); // leading 'k' forbidden 1120 + assert!(validator.validate_data(&json!("z234567abcdef"), &schema, &ctx).is_err()); // leading 'z' forbidden 1108 1121 } 1109 1122 1110 1123 #[test]