kind_whitelist_test.go raw

   1  package policy
   2  
   3  import (
   4  	"testing"
   5  
   6  	"next.orly.dev/pkg/nostr/encoders/hex"
   7  )
   8  
   9  // TestKindWhitelistComprehensive verifies that kind whitelisting properly rejects
  10  // unlisted kinds in all scenarios: explicit whitelist, implicit whitelist (rules), and combinations
  11  func TestKindWhitelistComprehensive(t *testing.T) {
  12  	testSigner, testPubkey := generateTestKeypair(t)
  13  
  14  	t.Run("Explicit Whitelist - kind IN whitelist, HAS rule", func(t *testing.T) {
  15  		policy := &P{
  16  			DefaultPolicy: "allow", // Changed to allow so rules without constraints allow by default
  17  			Kind: Kinds{
  18  				Whitelist: []int{1, 3, 5}, // Explicit whitelist
  19  			},
  20  			rules: map[int]Rule{
  21  				1: {Description: "Rule for kind 1"},
  22  				3: {Description: "Rule for kind 3"},
  23  				5: {Description: "Rule for kind 5"},
  24  			},
  25  		}
  26  
  27  		event := createTestEvent(t, testSigner, "test", 1)
  28  		allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1")
  29  		if err != nil {
  30  			t.Fatalf("Unexpected error: %v", err)
  31  		}
  32  		if !allowed {
  33  			t.Error("Kind 1 should be ALLOWED (in whitelist, has rule, passes rule check)")
  34  		}
  35  	})
  36  
  37  	t.Run("Explicit Whitelist - kind IN whitelist, NO rule", func(t *testing.T) {
  38  		policy := &P{
  39  			DefaultPolicy: "allow",
  40  			Kind: Kinds{
  41  				Whitelist: []int{1, 3, 5}, // Explicit whitelist
  42  			},
  43  			rules: map[int]Rule{
  44  				1: {Description: "Rule for kind 1"},
  45  				// Kind 3 has no rule
  46  			},
  47  		}
  48  
  49  		event := createTestEvent(t, testSigner, "test", 3)
  50  		allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1")
  51  		if err != nil {
  52  			t.Fatalf("Unexpected error: %v", err)
  53  		}
  54  		if !allowed {
  55  			t.Error("Kind 3 should be ALLOWED (in whitelist, no rule, default policy is allow)")
  56  		}
  57  	})
  58  
  59  	t.Run("Explicit Whitelist - kind NOT in whitelist, HAS rule", func(t *testing.T) {
  60  		policy := &P{
  61  			DefaultPolicy: "allow",
  62  			Kind: Kinds{
  63  				Whitelist: []int{1, 3, 5}, // Explicit whitelist - kind 10 NOT included
  64  			},
  65  			rules: map[int]Rule{
  66  				1:  {Description: "Rule for kind 1"},
  67  				10: {Description: "Rule for kind 10"}, // Has rule but not in whitelist!
  68  			},
  69  		}
  70  
  71  		event := createTestEvent(t, testSigner, "test", 10)
  72  		allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1")
  73  		if err != nil {
  74  			t.Fatalf("Unexpected error: %v", err)
  75  		}
  76  		if allowed {
  77  			t.Error("Kind 10 should be REJECTED (NOT in whitelist, even though it has a rule)")
  78  		}
  79  	})
  80  
  81  	t.Run("Explicit Whitelist - kind NOT in whitelist, NO rule", func(t *testing.T) {
  82  		policy := &P{
  83  			DefaultPolicy: "allow",
  84  			Kind: Kinds{
  85  				Whitelist: []int{1, 3, 5}, // Explicit whitelist
  86  			},
  87  			rules: map[int]Rule{
  88  				1: {Description: "Rule for kind 1"},
  89  			},
  90  		}
  91  
  92  		event := createTestEvent(t, testSigner, "test", 99)
  93  		allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1")
  94  		if err != nil {
  95  			t.Fatalf("Unexpected error: %v", err)
  96  		}
  97  		if allowed {
  98  			t.Error("Kind 99 should be REJECTED (NOT in whitelist)")
  99  		}
 100  	})
 101  
 102  	t.Run("Implicit Whitelist (rules) - kind HAS rule", func(t *testing.T) {
 103  		policy := &P{
 104  			DefaultPolicy: "allow", // Changed to allow so rules without constraints allow by default
 105  			// No explicit whitelist
 106  			rules: map[int]Rule{
 107  				1: {Description: "Rule for kind 1"},
 108  				3: {Description: "Rule for kind 3"},
 109  			},
 110  		}
 111  
 112  		event := createTestEvent(t, testSigner, "test", 1)
 113  		allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1")
 114  		if err != nil {
 115  			t.Fatalf("Unexpected error: %v", err)
 116  		}
 117  		if !allowed {
 118  			t.Error("Kind 1 should be ALLOWED (has rule, implicit whitelist)")
 119  		}
 120  	})
 121  
 122  	t.Run("Implicit Whitelist (rules) - kind NO rule", func(t *testing.T) {
 123  		policy := &P{
 124  			// DefaultPolicy not set (empty) - uses implicit whitelist when rules exist
 125  			// No explicit whitelist
 126  			rules: map[int]Rule{
 127  				1: {Description: "Rule for kind 1"},
 128  				3: {Description: "Rule for kind 3"},
 129  			},
 130  		}
 131  
 132  		event := createTestEvent(t, testSigner, "test", 99)
 133  		allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1")
 134  		if err != nil {
 135  			t.Fatalf("Unexpected error: %v", err)
 136  		}
 137  		if allowed {
 138  			t.Error("Kind 99 should be REJECTED (no rule, implicit whitelist mode)")
 139  		}
 140  	})
 141  
 142  	t.Run("Explicit Whitelist + Global Rule - kind NOT in whitelist", func(t *testing.T) {
 143  		policy := &P{
 144  			DefaultPolicy: "allow",
 145  			Kind: Kinds{
 146  				Whitelist: []int{1, 3, 5}, // Explicit whitelist
 147  			},
 148  			Global: Rule{
 149  				Description: "Global rule applies to all kinds",
 150  				AccessControl: AccessControl{
 151  					WriteAllow: []string{hex.Enc(testPubkey)},
 152  				},
 153  			},
 154  			rules: map[int]Rule{
 155  				1: {Description: "Rule for kind 1"},
 156  			},
 157  		}
 158  
 159  		// Even with global rule, kind not in whitelist should be rejected
 160  		event := createTestEvent(t, testSigner, "test", 99)
 161  		allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1")
 162  		if err != nil {
 163  			t.Fatalf("Unexpected error: %v", err)
 164  		}
 165  		if allowed {
 166  			t.Error("Kind 99 should be REJECTED (NOT in whitelist, even with global rule)")
 167  		}
 168  	})
 169  
 170  	t.Run("Blacklist + Rules - kind in blacklist but has rule", func(t *testing.T) {
 171  		policy := &P{
 172  			DefaultPolicy: "allow",
 173  			Kind: Kinds{
 174  				Blacklist: []int{10, 20}, // Blacklist
 175  			},
 176  			rules: map[int]Rule{
 177  				1:  {Description: "Rule for kind 1"},
 178  				10: {Description: "Rule for kind 10"}, // Has rule but blacklisted!
 179  			},
 180  		}
 181  
 182  		// Kind 10 is blacklisted, should be rejected
 183  		event := createTestEvent(t, testSigner, "test", 10)
 184  		allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1")
 185  		if err != nil {
 186  			t.Fatalf("Unexpected error: %v", err)
 187  		}
 188  		if allowed {
 189  			t.Error("Kind 10 should be REJECTED (in blacklist)")
 190  		}
 191  	})
 192  
 193  	t.Run("Blacklist + Rules - kind NOT in blacklist but NO rule (implicit whitelist)", func(t *testing.T) {
 194  		policy := &P{
 195  			DefaultPolicy: "allow",
 196  			Kind: Kinds{
 197  				Blacklist: []int{10, 20}, // Blacklist
 198  			},
 199  			rules: map[int]Rule{
 200  				1: {Description: "Rule for kind 1"},
 201  				3: {Description: "Rule for kind 3"},
 202  			},
 203  		}
 204  
 205  		// Kind 99 is not blacklisted but has no rule
 206  		// With blacklist present + rules, implicit whitelist applies
 207  		event := createTestEvent(t, testSigner, "test", 99)
 208  		allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1")
 209  		if err != nil {
 210  			t.Fatalf("Unexpected error: %v", err)
 211  		}
 212  		if allowed {
 213  			t.Error("Kind 99 should be REJECTED (not in blacklist but no rule, implicit whitelist)")
 214  		}
 215  	})
 216  
 217  	t.Run("Whitelist takes precedence over Blacklist", func(t *testing.T) {
 218  		policy := &P{
 219  			DefaultPolicy: "allow",
 220  			Kind: Kinds{
 221  				Whitelist: []int{1, 3, 5, 10}, // Whitelist includes 10
 222  				Blacklist: []int{10, 20},      // Blacklist also includes 10
 223  			},
 224  			rules: map[int]Rule{
 225  				1:  {Description: "Rule for kind 1"},
 226  				10: {Description: "Rule for kind 10"},
 227  			},
 228  		}
 229  
 230  		// Kind 10 is in BOTH whitelist and blacklist - whitelist should win
 231  		event := createTestEvent(t, testSigner, "test", 10)
 232  		allowed, err := policy.CheckPolicy("write", event, testPubkey, "127.0.0.1")
 233  		if err != nil {
 234  			t.Fatalf("Unexpected error: %v", err)
 235  		}
 236  		if !allowed {
 237  			t.Error("Kind 10 should be ALLOWED (whitelist takes precedence over blacklist)")
 238  		}
 239  	})
 240  }
 241  
 242  // TestKindWhitelistRealWorld tests real-world scenarios from the documentation
 243  func TestKindWhitelistRealWorld(t *testing.T) {
 244  	testSigner, testPubkey := generateTestKeypair(t)
 245  	_, otherPubkey := generateTestKeypair(t)
 246  
 247  	t.Run("Real World: Only allow kinds 1, 3, 30023", func(t *testing.T) {
 248  		policy := &P{
 249  			DefaultPolicy: "allow", // Allow by default for kinds in whitelist
 250  			Kind: Kinds{
 251  				Whitelist: []int{1, 3, 30023},
 252  			},
 253  			rules: map[int]Rule{
 254  				1: {
 255  					Description: "Text notes",
 256  					// No WriteAllow = anyone authenticated can write
 257  				},
 258  				3: {
 259  					Description: "Contact lists",
 260  					// No WriteAllow = anyone authenticated can write
 261  				},
 262  				30023: {
 263  					Description: "Long-form content",
 264  					AccessControl: AccessControl{
 265  						WriteAllow: []string{hex.Enc(testPubkey)}, // Only specific user can write
 266  					},
 267  				},
 268  			},
 269  		}
 270  
 271  		// Test kind 1 (allowed)
 272  		event1 := createTestEvent(t, testSigner, "Hello world", 1)
 273  		allowed, err := policy.CheckPolicy("write", event1, testPubkey, "127.0.0.1")
 274  		if err != nil {
 275  			t.Fatalf("Unexpected error: %v", err)
 276  		}
 277  		if !allowed {
 278  			t.Error("Kind 1 should be ALLOWED")
 279  		}
 280  
 281  		// Test kind 4 (NOT in whitelist, should be rejected)
 282  		event4 := createTestEvent(t, testSigner, "DM", 4)
 283  		allowed, err = policy.CheckPolicy("write", event4, testPubkey, "127.0.0.1")
 284  		if err != nil {
 285  			t.Fatalf("Unexpected error: %v", err)
 286  		}
 287  		if allowed {
 288  			t.Error("Kind 4 should be REJECTED (not in whitelist)")
 289  		}
 290  
 291  		// Test kind 30023 by authorized user (allowed)
 292  		event30023Auth := createTestEvent(t, testSigner, "Article", 30023)
 293  		allowed, err = policy.CheckPolicy("write", event30023Auth, testPubkey, "127.0.0.1")
 294  		if err != nil {
 295  			t.Fatalf("Unexpected error: %v", err)
 296  		}
 297  		if !allowed {
 298  			t.Error("Kind 30023 should be ALLOWED for authorized user")
 299  		}
 300  
 301  		// Test kind 30023 by unauthorized user (should fail rule check)
 302  		event30023Unauth := createTestEvent(t, testSigner, "Article", 30023)
 303  		allowed, err = policy.CheckPolicy("write", event30023Unauth, otherPubkey, "127.0.0.1")
 304  		if err != nil {
 305  			t.Fatalf("Unexpected error: %v", err)
 306  		}
 307  		if allowed {
 308  			t.Error("Kind 30023 should be REJECTED for unauthorized user")
 309  		}
 310  
 311  		// Test kind 9735 (NOT in whitelist, should be rejected even with valid signature)
 312  		event9735 := createTestEvent(t, testSigner, "Zap", 9735)
 313  		allowed, err = policy.CheckPolicy("write", event9735, testPubkey, "127.0.0.1")
 314  		if err != nil {
 315  			t.Fatalf("Unexpected error: %v", err)
 316  		}
 317  		if allowed {
 318  			t.Error("Kind 9735 should be REJECTED (not in whitelist)")
 319  		}
 320  	})
 321  }
 322