package policy import ( "testing" "git.mleku.dev/mleku/nostr/encoders/hex" ) // TestKindWhitelistComprehensive verifies that kind whitelisting properly rejects // unlisted kinds in all scenarios: explicit whitelist, implicit whitelist (rules), and combinations func TestKindWhitelistComprehensive(t *testing.T) { testSigner, testPubkey := generateTestKeypair(t) t.Run("Explicit Whitelist - kind IN whitelist, HAS rule", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", // Changed to allow so rules without constraints allow by default Kind: Kinds{ Whitelist: []int{1, 3, 5}, // Explicit whitelist }, rules: map[int]Rule{ 1: {Description: "Rule for kind 1"}, 3: {Description: "Rule for kind 3"}, 5: {Description: "Rule for kind 5"}, }, } event := createTestEvent(t, testSigner, "test", 1) allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("Kind 1 should be ALLOWED (in whitelist, has rule, passes rule check)") } }) t.Run("Explicit Whitelist - kind IN whitelist, NO rule", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", Kind: Kinds{ Whitelist: []int{1, 3, 5}, // Explicit whitelist }, rules: map[int]Rule{ 1: {Description: "Rule for kind 1"}, // Kind 3 has no rule }, } event := createTestEvent(t, testSigner, "test", 3) allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("Kind 3 should be ALLOWED (in whitelist, no rule, default policy is allow)") } }) t.Run("Explicit Whitelist - kind NOT in whitelist, HAS rule", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", Kind: Kinds{ Whitelist: []int{1, 3, 5}, // Explicit whitelist - kind 10 NOT included }, rules: map[int]Rule{ 1: {Description: "Rule for kind 1"}, 10: {Description: "Rule for kind 10"}, // Has rule but not in whitelist! }, } event := createTestEvent(t, testSigner, "test", 10) allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("Kind 10 should be REJECTED (NOT in whitelist, even though it has a rule)") } }) t.Run("Explicit Whitelist - kind NOT in whitelist, NO rule", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", Kind: Kinds{ Whitelist: []int{1, 3, 5}, // Explicit whitelist }, rules: map[int]Rule{ 1: {Description: "Rule for kind 1"}, }, } event := createTestEvent(t, testSigner, "test", 99) allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("Kind 99 should be REJECTED (NOT in whitelist)") } }) t.Run("Implicit Whitelist (rules) - kind HAS rule", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", // Changed to allow so rules without constraints allow by default // No explicit whitelist rules: map[int]Rule{ 1: {Description: "Rule for kind 1"}, 3: {Description: "Rule for kind 3"}, }, } event := createTestEvent(t, testSigner, "test", 1) allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("Kind 1 should be ALLOWED (has rule, implicit whitelist)") } }) t.Run("Implicit Whitelist (rules) - kind NO rule", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", // No explicit whitelist rules: map[int]Rule{ 1: {Description: "Rule for kind 1"}, 3: {Description: "Rule for kind 3"}, }, } event := createTestEvent(t, testSigner, "test", 99) allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("Kind 99 should be REJECTED (no rule, implicit whitelist mode)") } }) t.Run("Explicit Whitelist + Global Rule - kind NOT in whitelist", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", Kind: Kinds{ Whitelist: []int{1, 3, 5}, // Explicit whitelist }, Global: Rule{ Description: "Global rule applies to all kinds", WriteAllow: []string{hex.Enc(testPubkey)}, }, rules: map[int]Rule{ 1: {Description: "Rule for kind 1"}, }, } // Even with global rule, kind not in whitelist should be rejected event := createTestEvent(t, testSigner, "test", 99) allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("Kind 99 should be REJECTED (NOT in whitelist, even with global rule)") } }) t.Run("Blacklist + Rules - kind in blacklist but has rule", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", Kind: Kinds{ Blacklist: []int{10, 20}, // Blacklist }, rules: map[int]Rule{ 1: {Description: "Rule for kind 1"}, 10: {Description: "Rule for kind 10"}, // Has rule but blacklisted! }, } // Kind 10 is blacklisted, should be rejected event := createTestEvent(t, testSigner, "test", 10) allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("Kind 10 should be REJECTED (in blacklist)") } }) t.Run("Blacklist + Rules - kind NOT in blacklist but NO rule (implicit whitelist)", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", Kind: Kinds{ Blacklist: []int{10, 20}, // Blacklist }, rules: map[int]Rule{ 1: {Description: "Rule for kind 1"}, 3: {Description: "Rule for kind 3"}, }, } // Kind 99 is not blacklisted but has no rule // With blacklist present + rules, implicit whitelist applies event := createTestEvent(t, testSigner, "test", 99) allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("Kind 99 should be REJECTED (not in blacklist but no rule, implicit whitelist)") } }) t.Run("Whitelist takes precedence over Blacklist", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", Kind: Kinds{ Whitelist: []int{1, 3, 5, 10}, // Whitelist includes 10 Blacklist: []int{10, 20}, // Blacklist also includes 10 }, rules: map[int]Rule{ 1: {Description: "Rule for kind 1"}, 10: {Description: "Rule for kind 10"}, }, } // Kind 10 is in BOTH whitelist and blacklist - whitelist should win event := createTestEvent(t, testSigner, "test", 10) allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("Kind 10 should be ALLOWED (whitelist takes precedence over blacklist)") } }) } // TestKindWhitelistRealWorld tests real-world scenarios from the documentation func TestKindWhitelistRealWorld(t *testing.T) { testSigner, testPubkey := generateTestKeypair(t) _, otherPubkey := generateTestKeypair(t) t.Run("Real World: Only allow kinds 1, 3, 30023", func(t *testing.T) { policy := &P{ DefaultPolicy: "allow", // Allow by default for kinds in whitelist Kind: Kinds{ Whitelist: []int{1, 3, 30023}, }, rules: map[int]Rule{ 1: { Description: "Text notes", // No WriteAllow = anyone authenticated can write }, 3: { Description: "Contact lists", // No WriteAllow = anyone authenticated can write }, 30023: { Description: "Long-form content", WriteAllow: []string{hex.Enc(testPubkey)}, // Only specific user can write }, }, } // Test kind 1 (allowed) event1 := createTestEvent(t, testSigner, "Hello world", 1) allowed, err := policy.CheckPolicy("write", event1, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("Kind 1 should be ALLOWED") } // Test kind 4 (NOT in whitelist, should be rejected) event4 := createTestEvent(t, testSigner, "DM", 4) allowed, err = policy.CheckPolicy("write", event4, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("Kind 4 should be REJECTED (not in whitelist)") } // Test kind 30023 by authorized user (allowed) event30023Auth := createTestEvent(t, testSigner, "Article", 30023) allowed, err = policy.CheckPolicy("write", event30023Auth, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("Kind 30023 should be ALLOWED for authorized user") } // Test kind 30023 by unauthorized user (should fail rule check) event30023Unauth := createTestEvent(t, testSigner, "Article", 30023) allowed, err = policy.CheckPolicy("write", event30023Unauth, otherPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("Kind 30023 should be REJECTED for unauthorized user") } // Test kind 9735 (NOT in whitelist, should be rejected even with valid signature) event9735 := createTestEvent(t, testSigner, "Zap", 9735) allowed, err = policy.CheckPolicy("write", event9735, testPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("Kind 9735 should be REJECTED (not in whitelist)") } }) }