upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/nostr/events.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/nostr/events.rs')
-rw-r--r--src/nostr/events.rs281
1 files changed, 196 insertions, 85 deletions
diff --git a/src/nostr/events.rs b/src/nostr/events.rs
index f83e00c..3ec075d 100644
--- a/src/nostr/events.rs
+++ b/src/nostr/events.rs
@@ -362,7 +362,7 @@ impl RepositoryState {
362/// Validate a repository announcement according to GRASP-01 and GRASP-05 362/// Validate a repository announcement according to GRASP-01 and GRASP-05
363/// 363///
364/// Returns: 364/// Returns:
365/// - Accept: Announcement lists our service (GRASP-01) - unless archive_read_only mode 365/// - Accept: Announcement lists our service (GRASP-01) AND matches repository whitelist (if enabled)
366/// - AcceptArchive: Announcement matches archive config (GRASP-05) 366/// - AcceptArchive: Announcement matches archive config (GRASP-05)
367/// - Reject: Validation failed 367/// - Reject: Validation failed
368/// 368///
@@ -370,11 +370,13 @@ impl RepositoryState {
370/// - ONLY accept announcements matching archive whitelist/all 370/// - ONLY accept announcements matching archive whitelist/all
371/// - REJECT announcements listing our service but not in whitelist (read-only sync mode) 371/// - REJECT announcements listing our service but not in whitelist (read-only sync mode)
372/// 372///
373/// When repository_whitelist is set:
374/// - Announcements must BOTH list our service AND match the repository whitelist
375///
373/// Note: AcceptMaintainer is NOT returned here (requires database access) 376/// Note: AcceptMaintainer is NOT returned here (requires database access)
374pub fn validate_announcement( 377pub fn validate_announcement(
375 event: &Event, 378 event: &Event,
376 domain: &str, 379 config: &crate::config::Config,
377 archive_config: &crate::config::ArchiveConfig,
378) -> crate::nostr::policy::AnnouncementResult { 380) -> crate::nostr::policy::AnnouncementResult {
379 use crate::nostr::policy::AnnouncementResult; 381 use crate::nostr::policy::AnnouncementResult;
380 382
@@ -398,12 +400,33 @@ pub fn validate_announcement(
398 Err(e) => return AnnouncementResult::Reject(format!("Invalid announcement: {}", e)), 400 Err(e) => return AnnouncementResult::Reject(format!("Invalid announcement: {}", e)),
399 }; 401 };
400 402
401 // GRASP-01: Normal mode - accept if announcement lists our service 403 // Get archive and repository configs (fail-secure: reject on config errors)
402 if announcement.lists_service(domain) && !archive_config.read_only { 404 let archive_config = match config.archive_config() {
403 return AnnouncementResult::Accept; 405 Ok(c) => c,
404 } 406 Err(e) => return AnnouncementResult::Reject(format!("Config error: {}", e)),
407 };
408 let repository_config = match config.repository_config() {
409 Ok(c) => c,
410 Err(e) => return AnnouncementResult::Reject(format!("Config error: {}", e)),
411 };
405 412
406 let npub = announcement.owner_npub(); 413 let npub = announcement.owner_npub();
414 let lists_service = announcement.lists_service(&config.domain);
415
416 // GRASP-01: Normal mode - accept if announcement lists our service AND matches repository whitelist (if enabled)
417 if lists_service && !archive_config.read_only {
418 // Check repository whitelist if enabled
419 if repository_config.enabled() {
420 if !repository_config.matches(&npub, &announcement.identifier) {
421 return AnnouncementResult::Reject(format!(
422 "Announcement lists service but does not match repository whitelist. \
423 Repository {}/{} not in whitelist",
424 npub, announcement.identifier
425 ));
426 }
427 }
428 return AnnouncementResult::Accept;
429 }
407 430
408 // GRASP-05: Archive mode - accept if announcement matches whitelist 431 // GRASP-05: Archive mode - accept if announcement matches whitelist
409 if archive_config.matches(&npub, &announcement.identifier) { 432 if archive_config.matches(&npub, &announcement.identifier) {
@@ -561,7 +584,7 @@ mod tests {
561 584
562 #[test] 585 #[test]
563 fn test_validate_announcement_success() { 586 fn test_validate_announcement_success() {
564 use crate::config::ArchiveConfig; 587 use crate::config::Config;
565 use crate::nostr::policy::AnnouncementResult; 588 use crate::nostr::policy::AnnouncementResult;
566 589
567 let keys = create_test_keys(); 590 let keys = create_test_keys();
@@ -572,13 +595,17 @@ mod tests {
572 vec!["wss://gitnostr.com"], 595 vec!["wss://gitnostr.com"],
573 ); 596 );
574 597
575 let result = validate_announcement(&event, "gitnostr.com", &ArchiveConfig::default()); 598 let config = Config {
599 domain: "gitnostr.com".to_string(),
600 ..Config::for_testing()
601 };
602 let result = validate_announcement(&event, &config);
576 assert!(matches!(result, AnnouncementResult::Accept)); 603 assert!(matches!(result, AnnouncementResult::Accept));
577 } 604 }
578 605
579 #[test] 606 #[test]
580 fn test_validate_announcement_missing_clone() { 607 fn test_validate_announcement_missing_clone() {
581 use crate::config::ArchiveConfig; 608 use crate::config::Config;
582 use crate::nostr::policy::AnnouncementResult; 609 use crate::nostr::policy::AnnouncementResult;
583 610
584 let keys = create_test_keys(); 611 let keys = create_test_keys();
@@ -589,7 +616,11 @@ mod tests {
589 vec!["wss://gitnostr.com"], 616 vec!["wss://gitnostr.com"],
590 ); 617 );
591 618
592 let result = validate_announcement(&event, "gitnostr.com", &ArchiveConfig::default()); 619 let config = Config {
620 domain: "gitnostr.com".to_string(),
621 ..Config::for_testing()
622 };
623 let result = validate_announcement(&event, &config);
593 if let AnnouncementResult::Reject(reason) = result { 624 if let AnnouncementResult::Reject(reason) = result {
594 assert!(reason.contains("clone")); 625 assert!(reason.contains("clone"));
595 } else { 626 } else {
@@ -599,7 +630,7 @@ mod tests {
599 630
600 #[test] 631 #[test]
601 fn test_validate_announcement_missing_relay() { 632 fn test_validate_announcement_missing_relay() {
602 use crate::config::ArchiveConfig; 633 use crate::config::Config;
603 use crate::nostr::policy::AnnouncementResult; 634 use crate::nostr::policy::AnnouncementResult;
604 635
605 let keys = create_test_keys(); 636 let keys = create_test_keys();
@@ -610,7 +641,11 @@ mod tests {
610 vec![], // No relays 641 vec![], // No relays
611 ); 642 );
612 643
613 let result = validate_announcement(&event, "gitnostr.com", &ArchiveConfig::default()); 644 let config = Config {
645 domain: "gitnostr.com".to_string(),
646 ..Config::for_testing()
647 };
648 let result = validate_announcement(&event, &config);
614 if let AnnouncementResult::Reject(reason) = result { 649 if let AnnouncementResult::Reject(reason) = result {
615 assert!(reason.contains("relays")); 650 assert!(reason.contains("relays"));
616 } else { 651 } else {
@@ -620,7 +655,7 @@ mod tests {
620 655
621 #[test] 656 #[test]
622 fn test_validate_announcement_wrong_domain() { 657 fn test_validate_announcement_wrong_domain() {
623 use crate::config::ArchiveConfig; 658 use crate::config::Config;
624 use crate::nostr::policy::AnnouncementResult; 659 use crate::nostr::policy::AnnouncementResult;
625 660
626 let keys = create_test_keys(); 661 let keys = create_test_keys();
@@ -631,7 +666,11 @@ mod tests {
631 vec!["wss://other-service.com"], 666 vec!["wss://other-service.com"],
632 ); 667 );
633 668
634 let result = validate_announcement(&event, "gitnostr.com", &ArchiveConfig::default()); 669 let config = Config {
670 domain: "gitnostr.com".to_string(),
671 ..Config::for_testing()
672 };
673 let result = validate_announcement(&event, &config);
635 assert!(matches!(result, AnnouncementResult::Reject(_))); 674 assert!(matches!(result, AnnouncementResult::Reject(_)));
636 } 675 }
637 676
@@ -855,7 +894,7 @@ mod tests {
855 894
856 #[test] 895 #[test]
857 fn test_validate_announcement_with_trailing_slash_in_relay() { 896 fn test_validate_announcement_with_trailing_slash_in_relay() {
858 use crate::config::ArchiveConfig; 897 use crate::config::Config;
859 use crate::nostr::policy::AnnouncementResult; 898 use crate::nostr::policy::AnnouncementResult;
860 899
861 let keys = create_test_keys(); 900 let keys = create_test_keys();
@@ -867,14 +906,17 @@ mod tests {
867 ); 906 );
868 907
869 // Should accept despite trailing slash mismatch 908 // Should accept despite trailing slash mismatch
870 let result = 909 let config = Config {
871 validate_announcement(&event, "git.shakespeare.diy", &ArchiveConfig::default()); 910 domain: "git.shakespeare.diy".to_string(),
911 ..Config::for_testing()
912 };
913 let result = validate_announcement(&event, &config);
872 assert!(matches!(result, AnnouncementResult::Accept)); 914 assert!(matches!(result, AnnouncementResult::Accept));
873 } 915 }
874 916
875 #[test] 917 #[test]
876 fn test_validate_announcement_with_trailing_slash_in_clone_url() { 918 fn test_validate_announcement_with_trailing_slash_in_clone_url() {
877 use crate::config::ArchiveConfig; 919 use crate::config::Config;
878 use crate::nostr::policy::AnnouncementResult; 920 use crate::nostr::policy::AnnouncementResult;
879 921
880 let keys = create_test_keys(); 922 let keys = create_test_keys();
@@ -886,14 +928,17 @@ mod tests {
886 ); 928 );
887 929
888 // Should accept despite trailing slash mismatch 930 // Should accept despite trailing slash mismatch
889 let result = 931 let config = Config {
890 validate_announcement(&event, "git.shakespeare.diy", &ArchiveConfig::default()); 932 domain: "git.shakespeare.diy".to_string(),
933 ..Config::for_testing()
934 };
935 let result = validate_announcement(&event, &config);
891 assert!(matches!(result, AnnouncementResult::Accept)); 936 assert!(matches!(result, AnnouncementResult::Accept));
892 } 937 }
893 938
894 #[test] 939 #[test]
895 fn test_validate_announcement_with_trailing_slash_in_both() { 940 fn test_validate_announcement_with_trailing_slash_in_both() {
896 use crate::config::ArchiveConfig; 941 use crate::config::Config;
897 use crate::nostr::policy::AnnouncementResult; 942 use crate::nostr::policy::AnnouncementResult;
898 943
899 let keys = create_test_keys(); 944 let keys = create_test_keys();
@@ -905,14 +950,17 @@ mod tests {
905 ); 950 );
906 951
907 // Should accept with trailing slashes in both 952 // Should accept with trailing slashes in both
908 let result = 953 let config = Config {
909 validate_announcement(&event, "git.shakespeare.diy", &ArchiveConfig::default()); 954 domain: "git.shakespeare.diy".to_string(),
955 ..Config::for_testing()
956 };
957 let result = validate_announcement(&event, &config);
910 assert!(matches!(result, AnnouncementResult::Accept)); 958 assert!(matches!(result, AnnouncementResult::Accept));
911 } 959 }
912 960
913 #[test] 961 #[test]
914 fn test_validate_announcement_domain_with_trailing_slash() { 962 fn test_validate_announcement_domain_with_trailing_slash() {
915 use crate::config::ArchiveConfig; 963 use crate::config::Config;
916 use crate::nostr::policy::AnnouncementResult; 964 use crate::nostr::policy::AnnouncementResult;
917 965
918 let keys = create_test_keys(); 966 let keys = create_test_keys();
@@ -924,7 +972,11 @@ mod tests {
924 ); 972 );
925 973
926 // Should accept even when domain parameter has trailing slash 974 // Should accept even when domain parameter has trailing slash
927 let result = validate_announcement(&event, "gitnostr.com/", &ArchiveConfig::default()); 975 let config = Config {
976 domain: "gitnostr.com/".to_string(),
977 ..Config::for_testing()
978 };
979 let result = validate_announcement(&event, &config);
928 assert!(matches!(result, AnnouncementResult::Accept)); 980 assert!(matches!(result, AnnouncementResult::Accept));
929 } 981 }
930 982
@@ -964,7 +1016,7 @@ mod tests {
964 1016
965 #[test] 1017 #[test]
966 fn test_validate_announcement_archive_mode_npub() { 1018 fn test_validate_announcement_archive_mode_npub() {
967 use crate::config::{ArchiveConfig, ArchiveWhitelistEntry}; 1019 use crate::config::Config;
968 use crate::nostr::policy::AnnouncementResult; 1020 use crate::nostr::policy::AnnouncementResult;
969 1021
970 let keys = create_test_keys(); 1022 let keys = create_test_keys();
@@ -978,20 +1030,21 @@ mod tests {
978 vec!["wss://other-service.com"], 1030 vec!["wss://other-service.com"],
979 ); 1031 );
980 1032
981 // Create archive config that whitelists this npub 1033 // Create config that whitelists this npub
982 let archive_config = ArchiveConfig { 1034 let config = Config {
983 archive_all: false, 1035 domain: "gitnostr.com".to_string(),
984 whitelist: vec![ArchiveWhitelistEntry::Pubkey(npub)], 1036 archive_whitelist: npub,
985 read_only: false, 1037 archive_read_only: Some(false),
1038 ..Config::for_testing()
986 }; 1039 };
987 1040
988 let result = validate_announcement(&event, "gitnostr.com", &archive_config); 1041 let result = validate_announcement(&event, &config);
989 assert!(matches!(result, AnnouncementResult::AcceptArchive)); 1042 assert!(matches!(result, AnnouncementResult::AcceptArchive));
990 } 1043 }
991 1044
992 #[test] 1045 #[test]
993 fn test_validate_announcement_archive_mode_identifier() { 1046 fn test_validate_announcement_archive_mode_identifier() {
994 use crate::config::{ArchiveConfig, ArchiveWhitelistEntry}; 1047 use crate::config::Config;
995 use crate::nostr::policy::AnnouncementResult; 1048 use crate::nostr::policy::AnnouncementResult;
996 1049
997 let keys = create_test_keys(); 1050 let keys = create_test_keys();
@@ -1004,20 +1057,21 @@ mod tests {
1004 vec!["wss://other-service.com"], 1057 vec!["wss://other-service.com"],
1005 ); 1058 );
1006 1059
1007 // Create archive config that whitelists this identifier 1060 // Create config that whitelists this identifier
1008 let archive_config = ArchiveConfig { 1061 let config = Config {
1009 archive_all: false, 1062 domain: "gitnostr.com".to_string(),
1010 whitelist: vec![ArchiveWhitelistEntry::Identifier("bitcoin-core".into())], 1063 archive_whitelist: "bitcoin-core".to_string(),
1011 read_only: false, 1064 archive_read_only: Some(false),
1065 ..Config::for_testing()
1012 }; 1066 };
1013 1067
1014 let result = validate_announcement(&event, "gitnostr.com", &archive_config); 1068 let result = validate_announcement(&event, &config);
1015 assert!(matches!(result, AnnouncementResult::AcceptArchive)); 1069 assert!(matches!(result, AnnouncementResult::AcceptArchive));
1016 } 1070 }
1017 1071
1018 #[test] 1072 #[test]
1019 fn test_validate_announcement_archive_mode_repository() { 1073 fn test_validate_announcement_archive_mode_repository() {
1020 use crate::config::{ArchiveConfig, ArchiveWhitelistEntry}; 1074 use crate::config::Config;
1021 use crate::nostr::policy::AnnouncementResult; 1075 use crate::nostr::policy::AnnouncementResult;
1022 1076
1023 let keys = create_test_keys(); 1077 let keys = create_test_keys();
@@ -1031,23 +1085,21 @@ mod tests {
1031 vec!["wss://other-service.com"], 1085 vec!["wss://other-service.com"],
1032 ); 1086 );
1033 1087
1034 // Create archive config that whitelists this specific repo 1088 // Create config that whitelists this specific repo
1035 let archive_config = ArchiveConfig { 1089 let config = Config {
1036 archive_all: false, 1090 domain: "gitnostr.com".to_string(),
1037 whitelist: vec![ArchiveWhitelistEntry::Repository { 1091 archive_whitelist: format!("{}/linux", npub),
1038 npub, 1092 archive_read_only: Some(false),
1039 identifier: "linux".into(), 1093 ..Config::for_testing()
1040 }],
1041 read_only: false,
1042 }; 1094 };
1043 1095
1044 let result = validate_announcement(&event, "gitnostr.com", &archive_config); 1096 let result = validate_announcement(&event, &config);
1045 assert!(matches!(result, AnnouncementResult::AcceptArchive)); 1097 assert!(matches!(result, AnnouncementResult::AcceptArchive));
1046 } 1098 }
1047 1099
1048 #[test] 1100 #[test]
1049 fn test_validate_announcement_archive_all() { 1101 fn test_validate_announcement_archive_all() {
1050 use crate::config::ArchiveConfig; 1102 use crate::config::Config;
1051 use crate::nostr::policy::AnnouncementResult; 1103 use crate::nostr::policy::AnnouncementResult;
1052 1104
1053 let keys = create_test_keys(); 1105 let keys = create_test_keys();
@@ -1060,20 +1112,21 @@ mod tests {
1060 vec!["wss://other-service.com"], 1112 vec!["wss://other-service.com"],
1061 ); 1113 );
1062 1114
1063 // Create archive config with archive_all enabled 1115 // Config with archive_all enabled
1064 let archive_config = ArchiveConfig { 1116 let config = Config {
1117 domain: "gitnostr.com".to_string(),
1065 archive_all: true, 1118 archive_all: true,
1066 whitelist: Vec::new(), 1119 archive_read_only: Some(false),
1067 read_only: false, 1120 ..Config::for_testing()
1068 }; 1121 };
1069 1122
1070 let result = validate_announcement(&event, "gitnostr.com", &archive_config); 1123 let result = validate_announcement(&event, &config);
1071 assert!(matches!(result, AnnouncementResult::AcceptArchive)); 1124 assert!(matches!(result, AnnouncementResult::AcceptArchive));
1072 } 1125 }
1073 1126
1074 #[test] 1127 #[test]
1075 fn test_validate_announcement_reject_not_in_whitelist() { 1128 fn test_validate_announcement_reject_not_in_whitelist() {
1076 use crate::config::{ArchiveConfig, ArchiveWhitelistEntry}; 1129 use crate::config::Config;
1077 use crate::nostr::policy::AnnouncementResult; 1130 use crate::nostr::policy::AnnouncementResult;
1078 1131
1079 let keys = create_test_keys(); 1132 let keys = create_test_keys();
@@ -1086,20 +1139,21 @@ mod tests {
1086 vec!["wss://other-service.com"], 1139 vec!["wss://other-service.com"],
1087 ); 1140 );
1088 1141
1089 // Create archive config that whitelists different identifier 1142 // Config that whitelists different identifier
1090 let archive_config = ArchiveConfig { 1143 let config = Config {
1091 archive_all: false, 1144 domain: "gitnostr.com".to_string(),
1092 whitelist: vec![ArchiveWhitelistEntry::Identifier("bitcoin-core".into())], 1145 archive_whitelist: "bitcoin-core".to_string(),
1093 read_only: false, 1146 archive_read_only: Some(false),
1147 ..Config::for_testing()
1094 }; 1148 };
1095 1149
1096 let result = validate_announcement(&event, "gitnostr.com", &archive_config); 1150 let result = validate_announcement(&event, &config);
1097 assert!(matches!(result, AnnouncementResult::Reject(_))); 1151 assert!(matches!(result, AnnouncementResult::Reject(_)));
1098 } 1152 }
1099 1153
1100 #[test] 1154 #[test]
1101 fn test_validate_announcement_grasp01_takes_precedence() { 1155 fn test_validate_announcement_grasp01_takes_precedence() {
1102 use crate::config::{ArchiveConfig, ArchiveWhitelistEntry}; 1156 use crate::config::Config;
1103 use crate::nostr::policy::AnnouncementResult; 1157 use crate::nostr::policy::AnnouncementResult;
1104 1158
1105 let keys = create_test_keys(); 1159 let keys = create_test_keys();
@@ -1113,19 +1167,20 @@ mod tests {
1113 ); 1167 );
1114 1168
1115 // With archive_read_only=false, GRASP-01 Accept takes precedence 1169 // With archive_read_only=false, GRASP-01 Accept takes precedence
1116 let archive_config = ArchiveConfig { 1170 let config = Config {
1171 domain: "gitnostr.com".to_string(),
1117 archive_all: true, 1172 archive_all: true,
1118 whitelist: Vec::new(), 1173 archive_read_only: Some(false),
1119 read_only: false, 1174 ..Config::for_testing()
1120 }; 1175 };
1121 1176
1122 let result = validate_announcement(&event, "gitnostr.com", &archive_config); 1177 let result = validate_announcement(&event, &config);
1123 assert!(matches!(result, AnnouncementResult::Accept)); 1178 assert!(matches!(result, AnnouncementResult::Accept));
1124 } 1179 }
1125 1180
1126 #[test] 1181 #[test]
1127 fn test_archive_read_only_rejects_non_whitelisted() { 1182 fn test_archive_read_only_rejects_non_whitelisted() {
1128 use crate::config::{ArchiveConfig, ArchiveWhitelistEntry}; 1183 use crate::config::Config;
1129 use crate::nostr::policy::AnnouncementResult; 1184 use crate::nostr::policy::AnnouncementResult;
1130 1185
1131 let keys = create_test_keys(); 1186 let keys = create_test_keys();
@@ -1140,19 +1195,20 @@ mod tests {
1140 1195
1141 // With archive_read_only=true and whitelist that doesn't include this repo, 1196 // With archive_read_only=true and whitelist that doesn't include this repo,
1142 // should reject even though it lists our service 1197 // should reject even though it lists our service
1143 let archive_config = ArchiveConfig { 1198 let config = Config {
1144 archive_all: false, 1199 domain: "gitnostr.com".to_string(),
1145 whitelist: vec![ArchiveWhitelistEntry::Identifier("bitcoin-core".into())], 1200 archive_whitelist: "bitcoin-core".to_string(),
1146 read_only: true, 1201 archive_read_only: Some(true),
1202 ..Config::for_testing()
1147 }; 1203 };
1148 1204
1149 let result = validate_announcement(&event, "gitnostr.com", &archive_config); 1205 let result = validate_announcement(&event, &config);
1150 assert!(matches!(result, AnnouncementResult::Reject(_))); 1206 assert!(matches!(result, AnnouncementResult::Reject(_)));
1151 } 1207 }
1152 1208
1153 #[test] 1209 #[test]
1154 fn test_archive_read_only_accepts_whitelisted() { 1210 fn test_archive_read_only_accepts_whitelisted() {
1155 use crate::config::{ArchiveConfig, ArchiveWhitelistEntry}; 1211 use crate::config::Config;
1156 use crate::nostr::policy::AnnouncementResult; 1212 use crate::nostr::policy::AnnouncementResult;
1157 1213
1158 let keys = create_test_keys(); 1214 let keys = create_test_keys();
@@ -1168,19 +1224,20 @@ mod tests {
1168 1224
1169 // With archive_read_only=true and whitelist that DOES include this repo, 1225 // With archive_read_only=true and whitelist that DOES include this repo,
1170 // should accept as AcceptArchive 1226 // should accept as AcceptArchive
1171 let archive_config = ArchiveConfig { 1227 let config = Config {
1172 archive_all: false, 1228 domain: "gitnostr.com".to_string(),
1173 whitelist: vec![ArchiveWhitelistEntry::Pubkey(npub)], 1229 archive_whitelist: npub,
1174 read_only: true, 1230 archive_read_only: Some(true),
1231 ..Config::for_testing()
1175 }; 1232 };
1176 1233
1177 let result = validate_announcement(&event, "gitnostr.com", &archive_config); 1234 let result = validate_announcement(&event, &config);
1178 assert!(matches!(result, AnnouncementResult::AcceptArchive)); 1235 assert!(matches!(result, AnnouncementResult::AcceptArchive));
1179 } 1236 }
1180 1237
1181 #[test] 1238 #[test]
1182 fn test_archive_read_only_with_archive_all() { 1239 fn test_archive_read_only_with_archive_all() {
1183 use crate::config::ArchiveConfig; 1240 use crate::config::Config;
1184 use crate::nostr::policy::AnnouncementResult; 1241 use crate::nostr::policy::AnnouncementResult;
1185 1242
1186 let keys = create_test_keys(); 1243 let keys = create_test_keys();
@@ -1195,13 +1252,67 @@ mod tests {
1195 1252
1196 // With archive_read_only=true and archive_all=true, 1253 // With archive_read_only=true and archive_all=true,
1197 // should accept as AcceptArchive 1254 // should accept as AcceptArchive
1198 let archive_config = ArchiveConfig { 1255 let config = Config {
1256 domain: "gitnostr.com".to_string(),
1199 archive_all: true, 1257 archive_all: true,
1200 whitelist: Vec::new(), 1258 archive_read_only: Some(true),
1201 read_only: true, 1259 ..Config::for_testing()
1202 }; 1260 };
1203 1261
1204 let result = validate_announcement(&event, "gitnostr.com", &archive_config); 1262 let result = validate_announcement(&event, &config);
1205 assert!(matches!(result, AnnouncementResult::AcceptArchive)); 1263 assert!(matches!(result, AnnouncementResult::AcceptArchive));
1206 } 1264 }
1265
1266 #[test]
1267 fn test_repository_whitelist_accepts_matching() {
1268 use crate::config::Config;
1269 use crate::nostr::policy::AnnouncementResult;
1270
1271 let keys = create_test_keys();
1272 let npub = keys.public_key().to_bech32().unwrap();
1273
1274 // Create announcement that lists our service
1275 let event = create_announcement_event(
1276 &keys,
1277 "test-repo",
1278 vec!["https://gitnostr.com/alice/test-repo.git"],
1279 vec!["wss://gitnostr.com"],
1280 );
1281
1282 // Config with repository whitelist that includes this repo
1283 let config = Config {
1284 domain: "gitnostr.com".to_string(),
1285 repository_whitelist: npub,
1286 ..Config::for_testing()
1287 };
1288
1289 let result = validate_announcement(&event, &config);
1290 assert!(matches!(result, AnnouncementResult::Accept));
1291 }
1292
1293 #[test]
1294 fn test_repository_whitelist_rejects_non_matching() {
1295 use crate::config::Config;
1296 use crate::nostr::policy::AnnouncementResult;
1297
1298 let keys = create_test_keys();
1299
1300 // Create announcement that lists our service
1301 let event = create_announcement_event(
1302 &keys,
1303 "test-repo",
1304 vec!["https://gitnostr.com/alice/test-repo.git"],
1305 vec!["wss://gitnostr.com"],
1306 );
1307
1308 // Config with repository whitelist that does NOT include this repo
1309 let config = Config {
1310 domain: "gitnostr.com".to_string(),
1311 repository_whitelist: "bitcoin-core".to_string(),
1312 ..Config::for_testing()
1313 };
1314
1315 let result = validate_announcement(&event, &config);
1316 assert!(matches!(result, AnnouncementResult::Reject(_)));
1317 }
1207} 1318}