Our Personal Data Server from scratch! tranquil.farm
pds rust database fun oauth atproto
221
fork

Configure Feed

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

fix(lexicon): wildcard mime type handling and case insensitivity

authored by

nekomimi.pet and committed by tangled.org 531ca7c1 add1ff17

+110 -9
+110 -9
crates/tranquil-lexicon/src/validate.rs
··· 322 322 323 323 if let Some(ref accept) = lex_blob.accept { 324 324 let mime_type = obj.get("mimeType").and_then(|v| v.as_str()).unwrap_or(""); 325 - let matched = accept 326 - .iter() 327 - .any(|pattern| match pattern.strip_suffix("/*") { 328 - Some(prefix) => { 329 - mime_type.starts_with(prefix) 330 - && mime_type.as_bytes().get(prefix.len()) == Some(&b'/') 331 - } 332 - None => mime_type == pattern, 333 - }); 325 + let matched = accept.iter().any(|pattern| mime_type_matches_accept_pattern(mime_type, pattern)); 334 326 if !mime_type.is_empty() && !matched { 335 327 return Err(LexValidationError::field( 336 328 path, ··· 350 342 } 351 343 352 344 Ok(()) 345 + } 346 + 347 + fn mime_type_matches_accept_pattern(mime_type: &str, pattern: &str) -> bool { 348 + let normalized_mime = normalize_mime_for_match(mime_type); 349 + let normalized = normalize_mime_for_match(pattern); 350 + 351 + if normalized == "*/*" || normalized == "*" { 352 + return true; 353 + } 354 + 355 + match normalized.strip_suffix("/*") { 356 + Some(prefix) => { 357 + !prefix.is_empty() 358 + && normalized_mime.starts_with(prefix) 359 + && normalized_mime.len() > prefix.len() 360 + && normalized_mime.as_bytes()[prefix.len()] == b'/' 361 + } 362 + None => normalized_mime == normalized, 363 + } 364 + } 365 + 366 + fn normalize_mime_for_match(value: &str) -> String { 367 + value 368 + .split(';') 369 + .next() 370 + .unwrap_or("") 371 + .trim() 372 + .to_ascii_lowercase() 353 373 } 354 374 355 375 fn validate_bytes( ··· 731 751 validate_record(&registry, "com.test.withreply", &bad_embed).is_err(), 732 752 "union with bare NSID ref must validate the matched schema" 733 753 ); 754 + } 755 + 756 + #[test] 757 + fn test_blob_accept_wildcard_allows_any_mime() { 758 + let lex_blob = LexBlob { 759 + accept: Some(vec!["*/*".to_string()]), 760 + max_size: None, 761 + }; 762 + let blob = json!({ 763 + "$type": "blob", 764 + "ref": { "$link": "bafyreiabcdef" }, 765 + "mimeType": "application/gzip", 766 + "size": 123 767 + }); 768 + 769 + assert!(validate_blob_ref(&lex_blob, &blob, "root/entries/0/node/blob").is_ok()); 770 + } 771 + 772 + #[test] 773 + fn test_blob_accept_prefix_wildcard_matches_subtypes() { 774 + let lex_blob = LexBlob { 775 + accept: Some(vec!["image/*".to_string()]), 776 + max_size: None, 777 + }; 778 + let blob = json!({ 779 + "$type": "blob", 780 + "ref": { "$link": "bafyreiabcdef" }, 781 + "mimeType": "image/png", 782 + "size": 123 783 + }); 784 + 785 + assert!(validate_blob_ref(&lex_blob, &blob, "blob").is_ok()); 786 + } 787 + 788 + #[test] 789 + fn test_blob_accept_exact_type_rejects_different_mime() { 790 + let lex_blob = LexBlob { 791 + accept: Some(vec!["image/png".to_string()]), 792 + max_size: None, 793 + }; 794 + let blob = json!({ 795 + "$type": "blob", 796 + "ref": { "$link": "bafyreiabcdef" }, 797 + "mimeType": "application/gzip", 798 + "size": 123 799 + }); 800 + 801 + let err = validate_blob_ref(&lex_blob, &blob, "blob").unwrap_err(); 802 + assert!(matches!(err, LexValidationError::InvalidField { .. })); 803 + } 804 + 805 + #[test] 806 + fn test_blob_accept_exact_type_ignores_params_and_case() { 807 + let lex_blob = LexBlob { 808 + accept: Some(vec!["text/html".to_string()]), 809 + max_size: None, 810 + }; 811 + let blob = json!({ 812 + "$type": "blob", 813 + "ref": { "$link": "bafyreiabcdef" }, 814 + "mimeType": "Text/HTML; charset=utf-8", 815 + "size": 123 816 + }); 817 + 818 + assert!(validate_blob_ref(&lex_blob, &blob, "blob").is_ok()); 819 + } 820 + 821 + #[test] 822 + fn test_blob_accept_prefix_ignores_params_and_case() { 823 + let lex_blob = LexBlob { 824 + accept: Some(vec!["text/*".to_string()]), 825 + max_size: None, 826 + }; 827 + let blob = json!({ 828 + "$type": "blob", 829 + "ref": { "$link": "bafyreiabcdef" }, 830 + "mimeType": "TEXT/HTML; charset=UTF-8", 831 + "size": 123 832 + }); 833 + 834 + assert!(validate_blob_ref(&lex_blob, &blob, "blob").is_ok()); 734 835 } 735 836 736 837 #[test]