diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-10 02:40:16 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-10 02:42:38 +0000 |
| commit | 6dbe167a1c8f9a59538e76f6322424eee93994df (patch) | |
| tree | e0491f8be02da3f48c3c03482b942e773d2365a4 /src | |
| parent | 1b6b669b9b82d1f81b887a32055f19c53d3bb8bf (diff) | |
fix: normalize URLs with trailing slashes in announcement validation
Announcements were being rejected when clone URLs or relay URLs had
trailing slashes that didn't match. Added URL normalization to strip
trailing slashes before comparison, allowing announcements to be
accepted regardless of trailing slash presence.
- Add normalize_url_for_comparison() helper
- Update has_clone_url() and has_relay() to normalize before matching
- Add comprehensive tests for trailing slash scenarios
Fixes issue in work/active-issues/clone-relays-mismatch-validation.md
Diffstat (limited to 'src')
| -rw-r--r-- | src/nostr/events.rs | 113 |
1 files changed, 111 insertions, 2 deletions
diff --git a/src/nostr/events.rs b/src/nostr/events.rs index 66808cc..9d43ca3 100644 --- a/src/nostr/events.rs +++ b/src/nostr/events.rs | |||
| @@ -143,14 +143,29 @@ impl RepositoryAnnouncement { | |||
| 143 | }) | 143 | }) |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | /// Normalize a URL by removing trailing slashes for consistent comparison | ||
| 147 | /// | ||
| 148 | /// See test_validate_announcement_with_trailing_slash_in_relay for why we need this | ||
| 149 | fn normalize_url_for_comparison(url: &str) -> &str { | ||
| 150 | url.trim_end_matches('/') | ||
| 151 | } | ||
| 152 | |||
| 146 | /// Check if this announcement lists the given domain in clone URLs | 153 | /// Check if this announcement lists the given domain in clone URLs |
| 147 | pub fn has_clone_url(&self, domain: &str) -> bool { | 154 | pub fn has_clone_url(&self, domain: &str) -> bool { |
| 148 | self.clone_urls.iter().any(|url| url.contains(domain)) | 155 | let normalized_domain = Self::normalize_url_for_comparison(domain); |
| 156 | self.clone_urls.iter().any(|url| { | ||
| 157 | let normalized_url = Self::normalize_url_for_comparison(url); | ||
| 158 | normalized_url.contains(normalized_domain) | ||
| 159 | }) | ||
| 149 | } | 160 | } |
| 150 | 161 | ||
| 151 | /// Check if this announcement lists the given relay | 162 | /// Check if this announcement lists the given relay |
| 152 | pub fn has_relay(&self, relay: &str) -> bool { | 163 | pub fn has_relay(&self, relay: &str) -> bool { |
| 153 | self.relays.iter().any(|r| r.contains(relay)) | 164 | let normalized_relay = Self::normalize_url_for_comparison(relay); |
| 165 | self.relays.iter().any(|r| { | ||
| 166 | let normalized_r = Self::normalize_url_for_comparison(r); | ||
| 167 | normalized_r.contains(normalized_relay) | ||
| 168 | }) | ||
| 154 | } | 169 | } |
| 155 | 170 | ||
| 156 | /// Check if this announcement lists the service (both clone and relay) | 171 | /// Check if this announcement lists the service (both clone and relay) |
| @@ -787,4 +802,98 @@ mod tests { | |||
| 787 | // HEAD points to develop but only main branch exists in state | 802 | // HEAD points to develop but only main branch exists in state |
| 788 | assert!(!state.head_commit_available()); | 803 | assert!(!state.head_commit_available()); |
| 789 | } | 804 | } |
| 805 | |||
| 806 | #[test] | ||
| 807 | fn test_validate_announcement_with_trailing_slash_in_relay() { | ||
| 808 | let keys = create_test_keys(); | ||
| 809 | let event = create_announcement_event( | ||
| 810 | &keys, | ||
| 811 | "test-repo", | ||
| 812 | vec!["https://git.shakespeare.diy/alice/test-repo.git"], | ||
| 813 | vec!["wss://git.shakespeare.diy/"], // Trailing slash in relay | ||
| 814 | ); | ||
| 815 | |||
| 816 | // Should accept despite trailing slash mismatch | ||
| 817 | let result = validate_announcement(&event, "git.shakespeare.diy"); | ||
| 818 | assert!(result.is_ok()); | ||
| 819 | } | ||
| 820 | |||
| 821 | #[test] | ||
| 822 | fn test_validate_announcement_with_trailing_slash_in_clone_url() { | ||
| 823 | let keys = create_test_keys(); | ||
| 824 | let event = create_announcement_event( | ||
| 825 | &keys, | ||
| 826 | "test-repo", | ||
| 827 | vec!["https://git.shakespeare.diy/"], // Trailing slash in clone URL | ||
| 828 | vec!["wss://git.shakespeare.diy"], | ||
| 829 | ); | ||
| 830 | |||
| 831 | // Should accept despite trailing slash mismatch | ||
| 832 | let result = validate_announcement(&event, "git.shakespeare.diy"); | ||
| 833 | assert!(result.is_ok()); | ||
| 834 | } | ||
| 835 | |||
| 836 | #[test] | ||
| 837 | fn test_validate_announcement_with_trailing_slash_in_both() { | ||
| 838 | let keys = create_test_keys(); | ||
| 839 | let event = create_announcement_event( | ||
| 840 | &keys, | ||
| 841 | "test-repo", | ||
| 842 | vec!["https://git.shakespeare.diy/alice/test-repo.git/"], // Trailing slash | ||
| 843 | vec!["wss://git.shakespeare.diy/"], // Trailing slash | ||
| 844 | ); | ||
| 845 | |||
| 846 | // Should accept with trailing slashes in both | ||
| 847 | let result = validate_announcement(&event, "git.shakespeare.diy"); | ||
| 848 | assert!(result.is_ok()); | ||
| 849 | } | ||
| 850 | |||
| 851 | #[test] | ||
| 852 | fn test_validate_announcement_domain_with_trailing_slash() { | ||
| 853 | let keys = create_test_keys(); | ||
| 854 | let event = create_announcement_event( | ||
| 855 | &keys, | ||
| 856 | "test-repo", | ||
| 857 | vec!["https://gitnostr.com/alice/test-repo.git"], | ||
| 858 | vec!["wss://gitnostr.com"], | ||
| 859 | ); | ||
| 860 | |||
| 861 | // Should accept even when domain parameter has trailing slash | ||
| 862 | let result = validate_announcement(&event, "gitnostr.com/"); | ||
| 863 | assert!(result.is_ok()); | ||
| 864 | } | ||
| 865 | |||
| 866 | #[test] | ||
| 867 | fn test_has_clone_url_with_trailing_slashes() { | ||
| 868 | let keys = create_test_keys(); | ||
| 869 | let event = create_announcement_event( | ||
| 870 | &keys, | ||
| 871 | "test-repo", | ||
| 872 | vec!["https://example.com/repo.git/"], | ||
| 873 | vec!["wss://example.com"], | ||
| 874 | ); | ||
| 875 | |||
| 876 | let announcement = RepositoryAnnouncement::from_event(event).unwrap(); | ||
| 877 | |||
| 878 | // Should match with or without trailing slash | ||
| 879 | assert!(announcement.has_clone_url("example.com")); | ||
| 880 | assert!(announcement.has_clone_url("example.com/")); | ||
| 881 | } | ||
| 882 | |||
| 883 | #[test] | ||
| 884 | fn test_has_relay_with_trailing_slashes() { | ||
| 885 | let keys = create_test_keys(); | ||
| 886 | let event = create_announcement_event( | ||
| 887 | &keys, | ||
| 888 | "test-repo", | ||
| 889 | vec!["https://example.com/repo.git"], | ||
| 890 | vec!["wss://example.com/"], | ||
| 891 | ); | ||
| 892 | |||
| 893 | let announcement = RepositoryAnnouncement::from_event(event).unwrap(); | ||
| 894 | |||
| 895 | // Should match with or without trailing slash | ||
| 896 | assert!(announcement.has_relay("example.com")); | ||
| 897 | assert!(announcement.has_relay("example.com/")); | ||
| 898 | } | ||
| 790 | } | 899 | } |