You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
317 lines
9.4 KiB
317 lines
9.4 KiB
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)") |
|
} |
|
}) |
|
}
|
|
|