mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 23:18:24 +00:00
feat(config): add repository blacklist to block specific repos/npubs/identifiers
Adds NGIT_REPOSITORY_BLACKLIST option for blocking repositories, taking precedence over all whitelists (archive and repository) to enable moderation without affecting curation policy. Key features: - Three blacklist formats: <npub>, <npub>/<identifier>, <identifier> - Blacklist checked first before any other validation - Overrides archive whitelist and repository whitelist - Specific rejection reasons based on match type (npub/identifier/both) - Not flagged in NIP-11 curation (operational, not policy) Implementation: - Add BlacklistConfig struct with check() method returning detailed reasons - Add NGIT_REPOSITORY_BLACKLIST config option and blacklist_config() method - Update validate_announcement() to check blacklist first with specific reasons - 12 new unit tests covering all blacklist behavior and precedence Configuration synced across all four sources: - src/config.rs: Core implementation with BlacklistConfig - .env.example: Comprehensive documentation with examples - docs/reference/configuration.md: Complete reference documentation - nix/module.nix: NixOS module option with environment mapping Testing: - 12 new tests for blacklist functionality (config + validation) - All 332 library tests passing - All 38 integration tests passing Use cases: - Block spam/malware repos by identifier - Block abusive users by npub - Block specific problematic repos by npub/identifier - Temporary blocks for investigation
This commit is contained in:
+28
-1
@@ -227,4 +227,31 @@
|
||||
# NGIT_REPOSITORY_WHITELIST=bitcoin-core,linux,rust
|
||||
# Note: Cannot be used with NGIT_ARCHIVE_READ_ONLY=true (mutually exclusive)
|
||||
# Note: When set, NIP-11 curation field will indicate curated repository acceptance
|
||||
# NGIT_REPOSITORY_WHITELIST=
|
||||
# NGIT_REPOSITORY_WHITELIST=
|
||||
|
||||
# ============================================================================
|
||||
# REPOSITORY BLACKLIST
|
||||
# ============================================================================
|
||||
|
||||
# Blacklist specific repos/pubkeys/identifiers to reject
|
||||
# Comma-separated list supporting three formats (same as whitelist formats):
|
||||
# <npub> - Block all repos from this pubkey
|
||||
# <npub>/<identifier> - Block specific repo
|
||||
# <identifier> - Block repos with this identifier (any pubkey)
|
||||
#
|
||||
# Blacklist takes precedence over ALL whitelists:
|
||||
# - Blacklisted repos are rejected even if they match archive or repository whitelists
|
||||
# - Blacklisted repos are rejected even if they list our service
|
||||
#
|
||||
# Rejection reasons indicate the match type:
|
||||
# - "Repository owner <npub> is blacklisted" (npub format)
|
||||
# - "Repository <npub>/<identifier> is blacklisted" (npub/identifier format)
|
||||
# - "Repository identifier <identifier> is blacklisted" (identifier format)
|
||||
#
|
||||
# CLI: --repository-blacklist <list>
|
||||
# Default: (empty - no repositories are blacklisted)
|
||||
# Examples:
|
||||
# NGIT_REPOSITORY_BLACKLIST=npub1spam...
|
||||
# NGIT_REPOSITORY_BLACKLIST=npub1alice.../bad-repo
|
||||
# NGIT_REPOSITORY_BLACKLIST=malware-repo,spam-repo
|
||||
# NGIT_REPOSITORY_BLACKLIST=
|
||||
@@ -36,7 +36,7 @@ Unlike the reference implementation ([ngit-relay](https://gitworkshop.dev/npub15
|
||||
- **Pure Rust Implementation**: Single binary, no external dependencies beyond Git itself
|
||||
- **Integrated Authorization**: Push validation happens inline during the Git receive-pack operation
|
||||
- **GRASP-01 Compliant**: Core service requirements for Git hosting with Nostr authorization
|
||||
- **Repository Whitelist**: Optional curation via pubkey/identifier whitelist (GRASP-01 mode)
|
||||
- **Repository Whitelist/Blacklist**: Optional curation via pubkey/identifier whitelist (GRASP-01 mode) and blacklist (overrides all whitelists)
|
||||
- **GRASP-02 Proactive Sync**: Sophisticated relay-to-relay event and git data synchronization
|
||||
- **NIP-77 Negentropy**: Efficient set reconciliation with automatic fallback to REQ+EOSE
|
||||
- **Live & Historic Sync**: Real-time event streaming plus catch-up for past events
|
||||
|
||||
@@ -744,6 +744,95 @@ NGIT_REPOSITORY_WHITELIST=bitcoin-core,npub1alice...
|
||||
|
||||
---
|
||||
|
||||
### Repository Blacklist
|
||||
|
||||
#### `NGIT_REPOSITORY_BLACKLIST`
|
||||
|
||||
**Description:** Blacklist specific repositories/pubkeys/identifiers to reject
|
||||
**Type:** Comma-separated list
|
||||
**Default:** Empty (no repositories are blacklisted)
|
||||
**Required:** No
|
||||
|
||||
**Format:** Same as whitelist formats:
|
||||
- `npub1...` - Block all repos from this pubkey
|
||||
- `npub1.../identifier` - Block specific repo
|
||||
- `identifier` - Block repos with this identifier (any pubkey)
|
||||
|
||||
**Precedence:** Blacklist takes precedence over **ALL** whitelists:
|
||||
- Blacklisted repos are rejected even if they match archive or repository whitelists
|
||||
- Blacklisted repos are rejected even if they list our service
|
||||
- Blacklist is checked **first** before any other validation
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Block all repos from specific pubkey
|
||||
NGIT_REPOSITORY_BLACKLIST=npub1spam...
|
||||
|
||||
# Block specific repo
|
||||
NGIT_REPOSITORY_BLACKLIST=npub1alice.../malware-repo
|
||||
|
||||
# Block repos with specific identifiers
|
||||
NGIT_REPOSITORY_BLACKLIST=malware,spam,phishing
|
||||
|
||||
# Combined blacklist
|
||||
NGIT_REPOSITORY_BLACKLIST=npub1spam...,npub1alice.../bad-repo,malware
|
||||
```
|
||||
|
||||
**Rejection Reasons:**
|
||||
|
||||
The blacklist provides specific rejection reasons based on the match type:
|
||||
|
||||
- **Npub format:** `"Repository owner <npub> is blacklisted"`
|
||||
- **Npub/identifier format:** `"Repository <npub>/<identifier> is blacklisted"`
|
||||
- **Identifier format:** `"Repository identifier <identifier> is blacklisted"`
|
||||
|
||||
These reasons help operators understand why a repository was rejected without needing to flag it in curation metadata.
|
||||
|
||||
**Behavior:**
|
||||
|
||||
Blacklist is checked **before** all other validation:
|
||||
1. Check blacklist → Reject if matched
|
||||
2. Check if lists service → Accept if matches repository whitelist (if enabled)
|
||||
3. Check archive config → Accept if matches archive whitelist (if enabled)
|
||||
4. Reject otherwise
|
||||
|
||||
**Use Cases:**
|
||||
|
||||
```bash
|
||||
# Block spam/malware repos
|
||||
NGIT_REPOSITORY_BLACKLIST=malware,spam,phishing
|
||||
|
||||
# Block abusive users
|
||||
NGIT_REPOSITORY_BLACKLIST=npub1spammer...,npub1abuser...
|
||||
|
||||
# Block specific problematic repos
|
||||
NGIT_REPOSITORY_BLACKLIST=npub1alice.../copyright-violation,npub1bob.../illegal-content
|
||||
|
||||
# Temporary block for investigation
|
||||
NGIT_REPOSITORY_BLACKLIST=npub1suspicious.../repo-under-review
|
||||
```
|
||||
|
||||
**Comparison with Whitelists:**
|
||||
|
||||
| Configuration | Blacklisted? | Matches Whitelist? | Lists Service? | Result |
|
||||
|---------------|--------------|-------------------|----------------|---------|
|
||||
| Blacklist only | Yes | N/A | N/A | ❌ Reject (blacklisted) |
|
||||
| Blacklist only | No | N/A | Yes | ✅ Accept (GRASP-01) |
|
||||
| Blacklist + Repository whitelist | Yes | Yes | Yes | ❌ Reject (blacklist wins) |
|
||||
| Blacklist + Archive whitelist | Yes | Yes | No | ❌ Reject (blacklist wins) |
|
||||
| Blacklist + Both whitelists | Yes | Yes | Yes | ❌ Reject (blacklist wins) |
|
||||
| Blacklist only | No | N/A | No | ❌ Reject (no whitelist match) |
|
||||
|
||||
**NIP-11 Impact:**
|
||||
|
||||
Blacklist does **not** affect NIP-11 metadata:
|
||||
- No `curation` field changes (blacklist is operational, not curation policy)
|
||||
- Blacklist is transparent to clients (rejected with specific reason)
|
||||
- Operators can use blacklist without advertising curation
|
||||
|
||||
---
|
||||
|
||||
### Logging Configuration
|
||||
|
||||
#### `RUST_LOG`
|
||||
|
||||
@@ -224,6 +224,19 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
repositoryBlacklist = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [ "npub1spam..." "npub1alice.../bad-repo" "malware" ];
|
||||
description = ''
|
||||
Repository blacklist for blocking specific repositories/pubkeys/identifiers.
|
||||
Blacklist takes precedence over ALL whitelists (archive and repository).
|
||||
Formats: <npub>, <npub>/<identifier>, <identifier>
|
||||
Blacklisted repos are rejected with specific reasons (npub/identifier/both).
|
||||
Does not affect NIP-11 curation field (operational, not curation policy).
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "ngit-grasp-${name}";
|
||||
@@ -267,6 +280,7 @@ let
|
||||
NGIT_ARCHIVE_ALL = toString cfg.archiveAll;
|
||||
NGIT_ARCHIVE_WHITELIST = concatStringsSep "," cfg.archiveWhitelist;
|
||||
NGIT_REPOSITORY_WHITELIST = concatStringsSep "," cfg.repositoryWhitelist;
|
||||
NGIT_REPOSITORY_BLACKLIST = concatStringsSep "," cfg.repositoryBlacklist;
|
||||
RUST_LOG = cfg.logLevel;
|
||||
} // optionalAttrs (cfg.relayName != null) {
|
||||
NGIT_RELAY_NAME = cfg.relayName;
|
||||
|
||||
+143
@@ -195,6 +195,55 @@ impl Default for RepositoryConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Repository blacklist configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BlacklistConfig {
|
||||
/// Blacklist entries for blocking specific repositories
|
||||
///
|
||||
/// If empty, no repositories are blacklisted.
|
||||
/// Blacklist takes precedence over both archive and repository whitelists.
|
||||
pub blacklist: Vec<WhitelistEntry>,
|
||||
}
|
||||
|
||||
impl BlacklistConfig {
|
||||
/// Check if repository blacklist is enabled (non-empty blacklist)
|
||||
pub fn enabled(&self) -> bool {
|
||||
!self.blacklist.is_empty()
|
||||
}
|
||||
|
||||
/// Check if an announcement matches the repository blacklist
|
||||
///
|
||||
/// Returns Some(reason) if blacklisted, None if not blacklisted.
|
||||
/// The reason indicates what type of match occurred (npub, npub/identifier, or identifier).
|
||||
pub fn check(&self, npub: &str, identifier: &str) -> Option<String> {
|
||||
for entry in &self.blacklist {
|
||||
if entry.matches(npub, identifier) {
|
||||
let reason = match entry {
|
||||
WhitelistEntry::Pubkey(_) => {
|
||||
format!("Repository owner {} is blacklisted", npub)
|
||||
}
|
||||
WhitelistEntry::Repository { .. } => {
|
||||
format!("Repository {}/{} is blacklisted", npub, identifier)
|
||||
}
|
||||
WhitelistEntry::Identifier(_) => {
|
||||
format!("Repository identifier {} is blacklisted", identifier)
|
||||
}
|
||||
};
|
||||
return Some(reason);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BlacklistConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
blacklist: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Database backend type for the relay
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default, ValueEnum)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
@@ -373,6 +422,12 @@ pub struct Config {
|
||||
/// When set, only announcements matching the whitelist AND listing the service are accepted
|
||||
#[arg(long, env = "NGIT_REPOSITORY_WHITELIST", default_value = "")]
|
||||
pub repository_whitelist: String,
|
||||
|
||||
/// Repository blacklist: comma-separated list of npub/identifier/npub/identifier entries to reject
|
||||
/// Formats: "npub1...", "npub1.../identifier", "identifier"
|
||||
/// Blacklist takes precedence over all whitelists (archive and repository)
|
||||
#[arg(long, env = "NGIT_REPOSITORY_BLACKLIST", default_value = "")]
|
||||
pub repository_blacklist: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -549,6 +604,14 @@ impl Config {
|
||||
RepositoryConfig { whitelist }
|
||||
}
|
||||
|
||||
/// Get parsed repository blacklist configuration
|
||||
///
|
||||
/// This method assumes config has been validated - call Config::validate() first!
|
||||
pub fn blacklist_config(&self) -> BlacklistConfig {
|
||||
let blacklist = WhitelistEntry::parse_whitelist(&self.repository_blacklist);
|
||||
BlacklistConfig { blacklist }
|
||||
}
|
||||
|
||||
/// Create config for testing
|
||||
#[cfg(test)]
|
||||
pub fn for_testing() -> Self {
|
||||
@@ -583,6 +646,7 @@ impl Config {
|
||||
archive_whitelist: String::new(),
|
||||
archive_read_only: None,
|
||||
repository_whitelist: String::new(),
|
||||
repository_blacklist: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1105,4 +1169,83 @@ mod tests {
|
||||
.to_string()
|
||||
.contains("relay_owner_nsec not set"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_config_parsing() {
|
||||
let keys = Keys::generate();
|
||||
let test_npub = keys.public_key().to_bech32().unwrap();
|
||||
let config = Config {
|
||||
repository_blacklist: format!("{},bitcoin-core", test_npub),
|
||||
..Config::for_testing()
|
||||
};
|
||||
let blacklist_config = config.blacklist_config();
|
||||
assert_eq!(blacklist_config.blacklist.len(), 2);
|
||||
assert!(blacklist_config.enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_config_empty() {
|
||||
let config = Config::for_testing();
|
||||
let blacklist_config = config.blacklist_config();
|
||||
assert!(blacklist_config.blacklist.is_empty());
|
||||
assert!(!blacklist_config.enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_check_npub() {
|
||||
let keys = Keys::generate();
|
||||
let test_npub = keys.public_key().to_bech32().unwrap();
|
||||
let config = BlacklistConfig {
|
||||
blacklist: vec![WhitelistEntry::Pubkey(test_npub.clone())],
|
||||
};
|
||||
|
||||
let result = config.check(&test_npub, "any-repo");
|
||||
assert!(result.is_some());
|
||||
let reason = result.unwrap();
|
||||
assert!(reason.contains("owner"));
|
||||
assert!(reason.contains(&test_npub));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_check_identifier() {
|
||||
let config = BlacklistConfig {
|
||||
blacklist: vec![WhitelistEntry::Identifier("banned-repo".to_string())],
|
||||
};
|
||||
|
||||
let result = config.check("npub1alice", "banned-repo");
|
||||
assert!(result.is_some());
|
||||
let reason = result.unwrap();
|
||||
assert!(reason.contains("identifier"));
|
||||
assert!(reason.contains("banned-repo"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_check_repository() {
|
||||
let keys = Keys::generate();
|
||||
let test_npub = keys.public_key().to_bech32().unwrap();
|
||||
let config = BlacklistConfig {
|
||||
blacklist: vec![WhitelistEntry::Repository {
|
||||
npub: test_npub.clone(),
|
||||
identifier: "specific-repo".to_string(),
|
||||
}],
|
||||
};
|
||||
|
||||
let result = config.check(&test_npub, "specific-repo");
|
||||
assert!(result.is_some());
|
||||
let reason = result.unwrap();
|
||||
assert!(reason.contains(&test_npub));
|
||||
assert!(reason.contains("specific-repo"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_check_not_blacklisted() {
|
||||
let keys = Keys::generate();
|
||||
let test_npub = keys.public_key().to_bech32().unwrap();
|
||||
let config = BlacklistConfig {
|
||||
blacklist: vec![WhitelistEntry::Identifier("banned-repo".to_string())],
|
||||
};
|
||||
|
||||
let result = config.check(&test_npub, "allowed-repo");
|
||||
assert!(result.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,6 +366,9 @@ impl RepositoryState {
|
||||
/// - AcceptArchive: Announcement matches archive config (GRASP-05)
|
||||
/// - Reject: Validation failed
|
||||
///
|
||||
/// Blacklist takes precedence over all whitelists:
|
||||
/// - If blacklisted, always reject with specific reason (npub/identifier/npub+identifier)
|
||||
///
|
||||
/// When archive_read_only is true:
|
||||
/// - ONLY accept announcements matching archive whitelist/all
|
||||
/// - REJECT announcements listing our service but not in whitelist (read-only sync mode)
|
||||
@@ -403,10 +406,16 @@ pub fn validate_announcement(
|
||||
// Get validated configs (config.validate() must be called at startup)
|
||||
let archive_config = config.archive_config();
|
||||
let repository_config = config.repository_config();
|
||||
let blacklist_config = config.blacklist_config();
|
||||
|
||||
let npub = announcement.owner_npub();
|
||||
let lists_service = announcement.lists_service(&config.domain);
|
||||
|
||||
// Check blacklist FIRST - it overrides everything
|
||||
if let Some(reason) = blacklist_config.check(&npub, &announcement.identifier) {
|
||||
return AnnouncementResult::Reject(reason);
|
||||
}
|
||||
|
||||
// GRASP-01: Normal mode - accept if announcement lists our service AND matches repository whitelist (if enabled)
|
||||
if lists_service && !archive_config.read_only {
|
||||
// Check repository whitelist if enabled
|
||||
@@ -1309,4 +1318,182 @@ mod tests {
|
||||
let result = validate_announcement(&event, &config);
|
||||
assert!(matches!(result, AnnouncementResult::Reject(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_rejects_npub() {
|
||||
use crate::config::Config;
|
||||
use crate::nostr::policy::AnnouncementResult;
|
||||
|
||||
let keys = create_test_keys();
|
||||
let npub = keys.public_key().to_bech32().unwrap();
|
||||
|
||||
// Create announcement that lists our service
|
||||
let event = create_announcement_event(
|
||||
&keys,
|
||||
"test-repo",
|
||||
vec!["https://gitnostr.com/alice/test-repo.git"],
|
||||
vec!["wss://gitnostr.com"],
|
||||
);
|
||||
|
||||
// Config with blacklist for this npub
|
||||
let config = Config {
|
||||
domain: "gitnostr.com".to_string(),
|
||||
repository_blacklist: npub.clone(),
|
||||
..Config::for_testing()
|
||||
};
|
||||
|
||||
let result = validate_announcement(&event, &config);
|
||||
if let AnnouncementResult::Reject(reason) = result {
|
||||
assert!(reason.contains("owner"));
|
||||
assert!(reason.contains(&npub));
|
||||
} else {
|
||||
panic!("Expected Reject, got {:?}", result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_rejects_identifier() {
|
||||
use crate::config::Config;
|
||||
use crate::nostr::policy::AnnouncementResult;
|
||||
|
||||
let keys = create_test_keys();
|
||||
|
||||
// Create announcement that lists our service
|
||||
let event = create_announcement_event(
|
||||
&keys,
|
||||
"banned-repo",
|
||||
vec!["https://gitnostr.com/alice/banned-repo.git"],
|
||||
vec!["wss://gitnostr.com"],
|
||||
);
|
||||
|
||||
// Config with blacklist for this identifier
|
||||
let config = Config {
|
||||
domain: "gitnostr.com".to_string(),
|
||||
repository_blacklist: "banned-repo".to_string(),
|
||||
..Config::for_testing()
|
||||
};
|
||||
|
||||
let result = validate_announcement(&event, &config);
|
||||
if let AnnouncementResult::Reject(reason) = result {
|
||||
assert!(reason.contains("identifier"));
|
||||
assert!(reason.contains("banned-repo"));
|
||||
} else {
|
||||
panic!("Expected Reject, got {:?}", result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_rejects_specific_repository() {
|
||||
use crate::config::Config;
|
||||
use crate::nostr::policy::AnnouncementResult;
|
||||
|
||||
let keys = create_test_keys();
|
||||
let npub = keys.public_key().to_bech32().unwrap();
|
||||
|
||||
// Create announcement that lists our service
|
||||
let event = create_announcement_event(
|
||||
&keys,
|
||||
"specific-repo",
|
||||
vec!["https://gitnostr.com/alice/specific-repo.git"],
|
||||
vec!["wss://gitnostr.com"],
|
||||
);
|
||||
|
||||
// Config with blacklist for this specific repo
|
||||
let config = Config {
|
||||
domain: "gitnostr.com".to_string(),
|
||||
repository_blacklist: format!("{}/specific-repo", npub),
|
||||
..Config::for_testing()
|
||||
};
|
||||
|
||||
let result = validate_announcement(&event, &config);
|
||||
if let AnnouncementResult::Reject(reason) = result {
|
||||
assert!(reason.contains(&npub));
|
||||
assert!(reason.contains("specific-repo"));
|
||||
} else {
|
||||
panic!("Expected Reject, got {:?}", result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_overrides_repository_whitelist() {
|
||||
use crate::config::Config;
|
||||
use crate::nostr::policy::AnnouncementResult;
|
||||
|
||||
let keys = create_test_keys();
|
||||
let npub = keys.public_key().to_bech32().unwrap();
|
||||
|
||||
// Create announcement that lists our service
|
||||
let event = create_announcement_event(
|
||||
&keys,
|
||||
"test-repo",
|
||||
vec!["https://gitnostr.com/alice/test-repo.git"],
|
||||
vec!["wss://gitnostr.com"],
|
||||
);
|
||||
|
||||
// Config with both whitelist and blacklist - blacklist should win
|
||||
let config = Config {
|
||||
domain: "gitnostr.com".to_string(),
|
||||
repository_whitelist: npub.clone(),
|
||||
repository_blacklist: npub.clone(),
|
||||
..Config::for_testing()
|
||||
};
|
||||
|
||||
let result = validate_announcement(&event, &config);
|
||||
assert!(matches!(result, AnnouncementResult::Reject(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_overrides_archive_whitelist() {
|
||||
use crate::config::Config;
|
||||
use crate::nostr::policy::AnnouncementResult;
|
||||
|
||||
let keys = create_test_keys();
|
||||
let npub = keys.public_key().to_bech32().unwrap();
|
||||
|
||||
// Create announcement that does NOT list our service
|
||||
let event = create_announcement_event(
|
||||
&keys,
|
||||
"test-repo",
|
||||
vec!["https://other-service.com/alice/test-repo.git"],
|
||||
vec!["wss://other-service.com"],
|
||||
);
|
||||
|
||||
// Config with archive whitelist and blacklist - blacklist should win
|
||||
let config = Config {
|
||||
domain: "gitnostr.com".to_string(),
|
||||
archive_whitelist: npub.clone(),
|
||||
archive_read_only: Some(false),
|
||||
repository_blacklist: npub.clone(),
|
||||
..Config::for_testing()
|
||||
};
|
||||
|
||||
let result = validate_announcement(&event, &config);
|
||||
assert!(matches!(result, AnnouncementResult::Reject(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blacklist_allows_non_blacklisted() {
|
||||
use crate::config::Config;
|
||||
use crate::nostr::policy::AnnouncementResult;
|
||||
|
||||
let keys = create_test_keys();
|
||||
|
||||
// Create announcement that lists our service
|
||||
let event = create_announcement_event(
|
||||
&keys,
|
||||
"allowed-repo",
|
||||
vec!["https://gitnostr.com/alice/allowed-repo.git"],
|
||||
vec!["wss://gitnostr.com"],
|
||||
);
|
||||
|
||||
// Config with blacklist for different identifier
|
||||
let config = Config {
|
||||
domain: "gitnostr.com".to_string(),
|
||||
repository_blacklist: "banned-repo".to_string(),
|
||||
..Config::for_testing()
|
||||
};
|
||||
|
||||
let result = validate_announcement(&event, &config);
|
||||
assert!(matches!(result, AnnouncementResult::Accept));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user