policy_test.go raw
1 package policy
2
3 import (
4 "context"
5 "encoding/json"
6 "os"
7 "path/filepath"
8 "strings"
9 "testing"
10 "time"
11
12 "next.orly.dev/pkg/lol/chk"
13 "next.orly.dev/pkg/nostr/interfaces/signer/p8k"
14 "next.orly.dev/pkg/nostr/encoders/event"
15 "next.orly.dev/pkg/nostr/encoders/hex"
16 "next.orly.dev/pkg/nostr/encoders/tag"
17 )
18
19 // Helper function to create int64 pointer
20 func int64Ptr(i int64) *int64 {
21 return &i
22 }
23
24 // Helper function to generate a keypair for testing
25 func generateTestKeypair(t *testing.T) (signer *p8k.Signer, pubkey []byte) {
26 signer = p8k.MustNew()
27 if err := signer.Generate(); chk.E(err) {
28 t.Fatalf("Failed to generate test keypair: %v", err)
29 }
30 pubkey = signer.Pub()
31 return
32 }
33
34 // Helper function to generate a keypair for benchmarks
35 func generateTestKeypairB(b *testing.B) (signer *p8k.Signer, pubkey []byte) {
36 signer = p8k.MustNew()
37 if err := signer.Generate(); chk.E(err) {
38 b.Fatalf("Failed to generate test keypair: %v", err)
39 }
40 pubkey = signer.Pub()
41 return
42 }
43
44 // Helper function to create a real test event with proper signing
45 func createTestEvent(t *testing.T, signer *p8k.Signer, content string, kind uint16) *event.E {
46 ev := event.New()
47 ev.CreatedAt = time.Now().Unix()
48 ev.Kind = kind
49 ev.Content = []byte(content)
50 ev.Tags = tag.NewS()
51
52 // Sign the event properly
53 if err := ev.Sign(signer); chk.E(err) {
54 t.Fatalf("Failed to sign test event: %v", err)
55 }
56
57 return ev
58 }
59
60 // Helper function to create a test event with a specific pubkey (for unauthorized tests)
61 func createTestEventWithPubkey(t *testing.T, signer *p8k.Signer, content string, kind uint16) *event.E {
62 ev := event.New()
63 ev.CreatedAt = time.Now().Unix()
64 ev.Kind = kind
65 ev.Content = []byte(content)
66 ev.Tags = tag.NewS()
67
68 // Sign the event properly
69 if err := ev.Sign(signer); chk.E(err) {
70 t.Fatalf("Failed to sign test event: %v", err)
71 }
72
73 return ev
74 }
75
76 // Helper function to add p tag with hex-encoded pubkey to event
77 func addPTag(ev *event.E, pubkey []byte) {
78 pTag := tag.NewFromAny("p", hex.Enc(pubkey))
79 ev.Tags.Append(pTag)
80 }
81
82 // Helper function to add other tags to event
83 func addTag(ev *event.E, key, value string) {
84 tagItem := tag.NewFromAny(key, value)
85 ev.Tags.Append(tagItem)
86 }
87
88 func TestNew(t *testing.T) {
89 tests := []struct {
90 name string
91 policyJSON []byte
92 expectError bool
93 expectRules int
94 }{
95 {
96 name: "empty JSON",
97 policyJSON: []byte("{}"),
98 expectError: false,
99 expectRules: 0,
100 },
101 {
102 name: "valid policy JSON",
103 policyJSON: []byte(`{"kind":{"whitelist":[1,3,5]},"rules":{"1":{"description":"test"}}}`),
104 expectError: false,
105 expectRules: 1,
106 },
107 {
108 name: "invalid JSON",
109 policyJSON: []byte(`{"invalid": json}`),
110 expectError: true,
111 expectRules: 0,
112 },
113 {
114 name: "nil JSON",
115 policyJSON: nil,
116 expectError: false,
117 expectRules: 0,
118 },
119 }
120
121 for _, tt := range tests {
122 t.Run(tt.name, func(t *testing.T) {
123 policy, err := New(tt.policyJSON)
124 if tt.expectError {
125 if err == nil {
126 t.Errorf("Expected error but got none")
127 }
128 return
129 }
130 if err != nil {
131 t.Errorf("Unexpected error: %v", err)
132 return
133 }
134 if policy == nil {
135 t.Errorf("Expected policy but got nil")
136 return
137 }
138 if len(policy.rules) != tt.expectRules {
139 t.Errorf("Expected %d rules, got %d", tt.expectRules, len(policy.rules))
140 }
141 })
142 }
143 }
144
145 func TestCheckKindsPolicy(t *testing.T) {
146 tests := []struct {
147 name string
148 policy *P
149 access string // "read" or "write"
150 kind uint16
151 expected bool
152 }{
153 {
154 name: "no whitelist or blacklist - allow (no rules at all)",
155 policy: &P{
156 Kind: Kinds{},
157 rules: map[int]Rule{}, // No rules defined
158 },
159 access: "write",
160 kind: 1,
161 expected: true, // Should be allowed (no rules = allow all kinds)
162 },
163 {
164 name: "no whitelist or blacklist - deny (has other rules)",
165 policy: &P{
166 Kind: Kinds{},
167 rules: map[int]Rule{
168 2: {Description: "Rule for kind 2"},
169 },
170 },
171 access: "write",
172 kind: 1,
173 expected: false, // Should be denied (implicit whitelist, no rule for kind 1)
174 },
175 {
176 name: "no whitelist or blacklist - allow (has rule)",
177 policy: &P{
178 Kind: Kinds{},
179 rules: map[int]Rule{
180 1: {Description: "Rule for kind 1"},
181 },
182 },
183 access: "write",
184 kind: 1,
185 expected: true, // Should be allowed (has rule)
186 },
187 {
188 name: "no whitelist or blacklist - allow (has global rule)",
189 policy: &P{
190 Kind: Kinds{},
191 Global: Rule{
192 AccessControl: AccessControl{
193 WriteAllow: []string{"test"}, // Global rule exists
194 },
195 },
196 rules: map[int]Rule{}, // No specific rules
197 },
198 access: "write",
199 kind: 1,
200 expected: true, // Should be allowed (global rule exists)
201 },
202 {
203 name: "whitelist - kind allowed",
204 policy: &P{
205 Kind: Kinds{
206 Whitelist: []int{1, 3, 5},
207 },
208 },
209 access: "write",
210 kind: 1,
211 expected: true,
212 },
213 {
214 name: "whitelist - kind not allowed",
215 policy: &P{
216 Kind: Kinds{
217 Whitelist: []int{1, 3, 5},
218 },
219 },
220 access: "write",
221 kind: 2,
222 expected: false,
223 },
224 {
225 name: "blacklist - kind not blacklisted (no rule)",
226 policy: &P{
227 Kind: Kinds{
228 Blacklist: []int{2, 4, 6},
229 },
230 rules: map[int]Rule{
231 3: {Description: "Rule for kind 3"}, // Has at least one rule
232 },
233 },
234 access: "write",
235 kind: 1,
236 expected: false, // Should be denied (not blacklisted but no rule for kind 1)
237 },
238 {
239 name: "blacklist - kind not blacklisted (has rule)",
240 policy: &P{
241 Kind: Kinds{
242 Blacklist: []int{2, 4, 6},
243 },
244 rules: map[int]Rule{
245 1: {Description: "Rule for kind 1"},
246 },
247 },
248 access: "write",
249 kind: 1,
250 expected: true, // Should be allowed (not blacklisted and has rule)
251 },
252 {
253 name: "blacklist - kind blacklisted",
254 policy: &P{
255 Kind: Kinds{
256 Blacklist: []int{2, 4, 6},
257 },
258 },
259 access: "write",
260 kind: 2,
261 expected: false,
262 },
263 {
264 name: "whitelist overrides blacklist",
265 policy: &P{
266 Kind: Kinds{
267 Whitelist: []int{1, 3, 5},
268 Blacklist: []int{1, 2, 3},
269 },
270 },
271 access: "write",
272 kind: 1,
273 expected: true,
274 },
275 // Tests for new permissive flags
276 {
277 name: "read_allow_permissive - allows read for non-whitelisted kind",
278 policy: &P{
279 Kind: Kinds{
280 Whitelist: []int{1, 3, 5},
281 },
282 Global: Rule{
283 AccessControl: AccessControl{
284 ReadAllowPermissive: true,
285 },
286 },
287 },
288 access: "read",
289 kind: 2,
290 expected: true, // Should be allowed (read permissive overrides whitelist)
291 },
292 {
293 name: "read_allow_permissive - write still blocked for non-whitelisted kind",
294 policy: &P{
295 Kind: Kinds{
296 Whitelist: []int{1, 3, 5},
297 },
298 Global: Rule{
299 AccessControl: AccessControl{
300 ReadAllowPermissive: true,
301 },
302 },
303 },
304 access: "write",
305 kind: 2,
306 expected: false, // Should be denied (only read is permissive)
307 },
308 {
309 name: "write_allow_permissive - allows write for non-whitelisted kind",
310 policy: &P{
311 Kind: Kinds{
312 Whitelist: []int{1, 3, 5},
313 },
314 Global: Rule{
315 AccessControl: AccessControl{
316 WriteAllowPermissive: true,
317 },
318 },
319 },
320 access: "write",
321 kind: 2,
322 expected: true, // Should be allowed (write permissive overrides whitelist)
323 },
324 {
325 name: "write_allow_permissive - read still blocked for non-whitelisted kind",
326 policy: &P{
327 Kind: Kinds{
328 Whitelist: []int{1, 3, 5},
329 },
330 Global: Rule{
331 AccessControl: AccessControl{
332 WriteAllowPermissive: true,
333 },
334 },
335 },
336 access: "read",
337 kind: 2,
338 expected: false, // Should be denied (only write is permissive)
339 },
340 {
341 name: "blacklist - permissive flags do NOT override blacklist",
342 policy: &P{
343 Kind: Kinds{
344 Blacklist: []int{2, 4, 6},
345 },
346 Global: Rule{
347 AccessControl: AccessControl{
348 ReadAllowPermissive: true,
349 WriteAllowPermissive: true,
350 },
351 },
352 },
353 access: "write",
354 kind: 2,
355 expected: false, // Should be denied (blacklist always applies)
356 },
357 }
358
359 for _, tt := range tests {
360 t.Run(tt.name, func(t *testing.T) {
361 result := tt.policy.checkKindsPolicy(tt.access, tt.kind)
362 if result != tt.expected {
363 t.Errorf("Expected %v, got %v", tt.expected, result)
364 }
365 })
366 }
367 }
368
369 func TestCheckRulePolicy(t *testing.T) {
370 // Generate real keypairs for testing
371 eventSigner, eventPubkey := generateTestKeypair(t)
372 _, pTagPubkey := generateTestKeypair(t)
373 _, unauthorizedPubkey := generateTestKeypair(t)
374
375 // Create real test event with proper signing
376 testEvent := createTestEvent(t, eventSigner, "test content", 1)
377 // Add p tag with hex-encoded pubkey
378 addPTag(testEvent, pTagPubkey)
379 addTag(testEvent, "expiration", "1234567890")
380
381 tests := []struct {
382 name string
383 access string
384 event *event.E
385 rule Rule
386 loggedInPubkey []byte
387 expected bool
388 }{
389 {
390 name: "write access - no restrictions",
391 access: "write",
392 event: testEvent,
393 rule: Rule{
394 Description: "no restrictions",
395 },
396 loggedInPubkey: eventPubkey,
397 expected: true,
398 },
399 {
400 name: "write access - pubkey allowed",
401 access: "write",
402 event: testEvent,
403 rule: Rule{
404 Description: "pubkey allowed",
405 AccessControl: AccessControl{
406 WriteAllow: []string{hex.Enc(testEvent.Pubkey)},
407 },
408 },
409 loggedInPubkey: eventPubkey,
410 expected: true,
411 },
412 {
413 name: "write access - pubkey not allowed",
414 access: "write",
415 event: testEvent,
416 rule: Rule{
417 Description: "pubkey not allowed",
418 AccessControl: AccessControl{
419 WriteAllow: []string{hex.Enc(pTagPubkey)}, // Different pubkey
420 },
421 },
422 loggedInPubkey: eventPubkey,
423 expected: false,
424 },
425 {
426 name: "size limit - within limit",
427 access: "write",
428 event: testEvent,
429 rule: Rule{
430 Description: "size limit",
431 Constraints: Constraints{
432 SizeLimit: int64Ptr(10000),
433 },
434 },
435 loggedInPubkey: eventPubkey,
436 expected: true,
437 },
438 {
439 name: "size limit - exceeds limit",
440 access: "write",
441 event: testEvent,
442 rule: Rule{
443 Description: "size limit exceeded",
444 Constraints: Constraints{
445 SizeLimit: int64Ptr(10),
446 },
447 },
448 loggedInPubkey: eventPubkey,
449 expected: false,
450 },
451 {
452 name: "content limit - within limit",
453 access: "write",
454 event: testEvent,
455 rule: Rule{
456 Description: "content limit",
457 Constraints: Constraints{
458 ContentLimit: int64Ptr(1000),
459 },
460 },
461 loggedInPubkey: eventPubkey,
462 expected: true,
463 },
464 {
465 name: "content limit - exceeds limit",
466 access: "write",
467 event: testEvent,
468 rule: Rule{
469 Description: "content limit exceeded",
470 Constraints: Constraints{
471 ContentLimit: int64Ptr(5),
472 },
473 },
474 loggedInPubkey: eventPubkey,
475 expected: false,
476 },
477 {
478 name: "required tags - has required tag",
479 access: "write",
480 event: testEvent,
481 rule: Rule{
482 Description: "required tags",
483 TagValidationConfig: TagValidationConfig{
484 MustHaveTags: []string{"p"},
485 },
486 },
487 loggedInPubkey: eventPubkey,
488 expected: true,
489 },
490 {
491 name: "required tags - missing required tag",
492 access: "write",
493 event: testEvent,
494 rule: Rule{
495 Description: "required tags missing",
496 TagValidationConfig: TagValidationConfig{
497 MustHaveTags: []string{"e"},
498 },
499 },
500 loggedInPubkey: eventPubkey,
501 expected: false,
502 },
503 {
504 name: "privileged write - event authored by logged in user (privileged doesn't affect write)",
505 access: "write",
506 event: testEvent,
507 rule: Rule{
508 Description: "privileged event",
509 Constraints: Constraints{
510 Privileged: true,
511 },
512 },
513 loggedInPubkey: testEvent.Pubkey,
514 expected: true, // Privileged doesn't restrict write, uses default (allow)
515 },
516 {
517 name: "privileged write - event contains logged in user in p tag (privileged doesn't affect write)",
518 access: "write",
519 event: testEvent,
520 rule: Rule{
521 Description: "privileged event with p tag",
522 Constraints: Constraints{
523 Privileged: true,
524 },
525 },
526 loggedInPubkey: pTagPubkey,
527 expected: true, // Privileged doesn't restrict write, uses default (allow)
528 },
529 {
530 name: "privileged write - not authenticated (privileged doesn't affect write)",
531 access: "write",
532 event: testEvent,
533 rule: Rule{
534 Description: "privileged event not authenticated",
535 Constraints: Constraints{
536 Privileged: true,
537 },
538 },
539 loggedInPubkey: nil,
540 expected: true, // Privileged doesn't restrict write, uses default (allow)
541 },
542 {
543 name: "privileged write - authenticated but not authorized (privileged doesn't affect write)",
544 access: "write",
545 event: testEvent,
546 rule: Rule{
547 Description: "privileged event unauthorized user",
548 Constraints: Constraints{
549 Privileged: true,
550 },
551 },
552 loggedInPubkey: unauthorizedPubkey,
553 expected: true, // Privileged doesn't restrict write, uses default (allow)
554 },
555 {
556 name: "privileged read - event authored by logged in user",
557 access: "read",
558 event: testEvent,
559 rule: Rule{
560 Description: "privileged event read access",
561 Constraints: Constraints{
562 Privileged: true,
563 },
564 },
565 loggedInPubkey: testEvent.Pubkey,
566 expected: true,
567 },
568 {
569 name: "privileged read - event contains logged in user in p tag",
570 access: "read",
571 event: testEvent,
572 rule: Rule{
573 Description: "privileged event read access with p tag",
574 Constraints: Constraints{
575 Privileged: true,
576 },
577 },
578 loggedInPubkey: pTagPubkey,
579 expected: true,
580 },
581 {
582 name: "privileged read - not authenticated",
583 access: "read",
584 event: testEvent,
585 rule: Rule{
586 Description: "privileged event read access not authenticated",
587 Constraints: Constraints{
588 Privileged: true,
589 },
590 },
591 loggedInPubkey: nil,
592 expected: false,
593 },
594 {
595 name: "privileged read - authenticated but not authorized (different pubkey, not in p tags)",
596 access: "read",
597 event: testEvent,
598 rule: Rule{
599 Description: "privileged event read access unauthorized user",
600 Constraints: Constraints{
601 Privileged: true,
602 },
603 },
604 loggedInPubkey: unauthorizedPubkey,
605 expected: false,
606 },
607 }
608
609 for _, tt := range tests {
610 t.Run(tt.name, func(t *testing.T) {
611 policy := &P{}
612 result, err := policy.checkRulePolicy(tt.access, tt.event, tt.rule, tt.loggedInPubkey)
613 if err != nil {
614 t.Errorf("Unexpected error: %v", err)
615 return
616 }
617 if result != tt.expected {
618 t.Errorf("Expected %v, got %v", tt.expected, result)
619 }
620 })
621 }
622 }
623
624 func TestCheckPolicy(t *testing.T) {
625 // Generate real keypair for testing
626 eventSigner, eventPubkey := generateTestKeypair(t)
627
628 // Create real test event with proper signing
629 testEvent := createTestEvent(t, eventSigner, "test content", 1)
630
631 tests := []struct {
632 name string
633 access string
634 event *event.E
635 policy *P
636 loggedInPubkey []byte
637 ipAddress string
638 expected bool
639 expectError bool
640 }{
641 {
642 name: "no policy rules - allow",
643 access: "write",
644 event: testEvent,
645 policy: &P{
646 Kind: Kinds{},
647 rules: map[int]Rule{},
648 },
649 loggedInPubkey: eventPubkey,
650 ipAddress: "127.0.0.1",
651 expected: true,
652 expectError: false,
653 },
654 {
655 name: "kinds policy blocks - deny",
656 access: "write",
657 event: testEvent,
658 policy: &P{
659 Kind: Kinds{
660 Whitelist: []int{3, 5},
661 },
662 rules: map[int]Rule{},
663 },
664 loggedInPubkey: eventPubkey,
665 ipAddress: "127.0.0.1",
666 expected: false,
667 expectError: false,
668 },
669 {
670 name: "rule blocks - deny",
671 access: "write",
672 event: testEvent,
673 policy: &P{
674 Kind: Kinds{},
675 rules: map[int]Rule{
676 1: {
677 Description: "block test",
678 AccessControl: AccessControl{
679 WriteDeny: []string{hex.Enc(testEvent.Pubkey)},
680 },
681 },
682 },
683 },
684 loggedInPubkey: eventPubkey,
685 ipAddress: "127.0.0.1",
686 expected: false,
687 expectError: false,
688 },
689 }
690
691 for _, tt := range tests {
692 t.Run(tt.name, func(t *testing.T) {
693 result, err := tt.policy.CheckPolicy(tt.access, tt.event, tt.loggedInPubkey, tt.ipAddress)
694 if tt.expectError {
695 if err == nil {
696 t.Errorf("Expected error but got none")
697 }
698 return
699 }
700 if err != nil {
701 t.Errorf("Unexpected error: %v", err)
702 return
703 }
704 if result != tt.expected {
705 t.Errorf("Expected %v, got %v", tt.expected, result)
706 }
707 })
708 }
709 }
710
711 func TestLoadFromFile(t *testing.T) {
712 // Create temporary directory
713 tempDir := t.TempDir()
714 configPath := filepath.Join(tempDir, "policy.json")
715
716 tests := []struct {
717 name string
718 configData string
719 expectError bool
720 expectRules int
721 }{
722 {
723 name: "valid policy file",
724 configData: `{"kind":{"whitelist":[1,3,5]},"rules":{"1":{"description":"test"}}}`,
725 expectError: false,
726 expectRules: 1,
727 },
728 {
729 name: "empty policy file",
730 configData: `{}`,
731 expectError: false,
732 expectRules: 0,
733 },
734 {
735 name: "invalid JSON",
736 configData: `{"invalid": json}`,
737 expectError: true,
738 expectRules: 0,
739 },
740 }
741
742 for _, tt := range tests {
743 t.Run(tt.name, func(t *testing.T) {
744 // Write test config file
745 if tt.configData != "" {
746 err := os.WriteFile(configPath, []byte(tt.configData), 0644)
747 if err != nil {
748 t.Fatalf("Failed to write test config file: %v", err)
749 }
750 }
751
752 policy := &P{}
753 err := policy.LoadFromFile(configPath)
754 if tt.expectError {
755 if err == nil {
756 t.Errorf("Expected error but got none")
757 }
758 return
759 }
760 if err != nil {
761 t.Errorf("Unexpected error: %v", err)
762 return
763 }
764 if len(policy.rules) != tt.expectRules {
765 t.Errorf("Expected %d rules, got %d", tt.expectRules, len(policy.rules))
766 }
767 })
768 }
769
770 // Test file not found
771 t.Run("file not found", func(t *testing.T) {
772 policy := &P{}
773 err := policy.LoadFromFile("/nonexistent/policy.json")
774 if err == nil {
775 t.Errorf("Expected error for nonexistent file but got none")
776 }
777 })
778 }
779
780 func TestPolicyEventSerialization(t *testing.T) {
781 // Generate real keypair for testing
782 eventSigner, eventPubkey := generateTestKeypair(t)
783
784 // Create real test event with proper signing
785 testEvent := createTestEvent(t, eventSigner, "test content", 1)
786
787 // Create policy event
788 policyEvent := &PolicyEvent{
789 E: testEvent,
790 LoggedInPubkey: hex.Enc(eventPubkey),
791 IPAddress: "127.0.0.1",
792 }
793
794 // Test JSON serialization
795 jsonData, err := json.Marshal(policyEvent)
796 if err != nil {
797 t.Fatalf("Failed to marshal policy event: %v", err)
798 }
799
800 // Verify the JSON contains expected fields
801 jsonStr := string(jsonData)
802 t.Logf("Generated JSON: %s", jsonStr)
803
804 if !strings.Contains(jsonStr, hex.Enc(eventPubkey)) {
805 t.Error("JSON should contain logged_in_pubkey field")
806 }
807 if !strings.Contains(jsonStr, "127.0.0.1") {
808 t.Error("JSON should contain ip_address field")
809 }
810 if !strings.Contains(jsonStr, hex.Enc(testEvent.ID)) {
811 t.Error("JSON should contain event id field (hex encoded)")
812 }
813
814 // Test with nil event
815 nilPolicyEvent := &PolicyEvent{
816 E: nil,
817 LoggedInPubkey: "test-logged-in-pubkey",
818 IPAddress: "127.0.0.1",
819 }
820
821 jsonData2, err := json.Marshal(nilPolicyEvent)
822 if err != nil {
823 t.Fatalf("Failed to marshal nil policy event: %v", err)
824 }
825
826 jsonStr2 := string(jsonData2)
827 if !strings.Contains(jsonStr2, "test-logged-in-pubkey") {
828 t.Error("JSON should contain logged_in_pubkey field even with nil event")
829 }
830 if !strings.Contains(jsonStr2, "127.0.0.1") {
831 t.Error("JSON should contain ip_address field even with nil event")
832 }
833 }
834
835 func TestPolicyResponseSerialization(t *testing.T) {
836 // Test JSON serialization
837 response := &PolicyResponse{
838 ID: "test-id",
839 Action: "accept",
840 Msg: "test message",
841 }
842
843 jsonData, err := json.Marshal(response)
844 if err != nil {
845 t.Fatalf("Failed to marshal policy response: %v", err)
846 }
847
848 // Test JSON deserialization
849 var deserializedResponse PolicyResponse
850 err = json.Unmarshal(jsonData, &deserializedResponse)
851 if err != nil {
852 t.Fatalf("Failed to unmarshal policy response: %v", err)
853 }
854
855 // Verify fields
856 if deserializedResponse.ID != response.ID {
857 t.Errorf("Expected ID %s, got %s", response.ID, deserializedResponse.ID)
858 }
859 if deserializedResponse.Action != response.Action {
860 t.Errorf("Expected Action %s, got %s", response.Action, deserializedResponse.Action)
861 }
862 if deserializedResponse.Msg != response.Msg {
863 t.Errorf("Expected Msg %s, got %s", response.Msg, deserializedResponse.Msg)
864 }
865 }
866
867 func TestNewWithManager(t *testing.T) {
868 ctx := context.Background()
869 appName := "test-app"
870
871 // Test with disabled policy (doesn't require policy.json file)
872 t.Run("disabled policy", func(t *testing.T) {
873 enabled := false
874 policy := NewWithManager(ctx, appName, enabled, "")
875
876 if policy == nil {
877 t.Fatal("Expected policy but got nil")
878 }
879
880 if policy.manager == nil {
881 t.Fatal("Expected policy manager but got nil")
882 }
883
884 if policy.manager.IsEnabled() {
885 t.Error("Expected policy manager to be disabled")
886 }
887
888 if policy.manager.IsRunning() {
889 t.Error("Expected policy manager to not be running")
890 }
891
892 // Verify default policy was set
893 if policy.DefaultPolicy != "allow" {
894 t.Errorf("Expected default_policy='allow', got '%s'", policy.DefaultPolicy)
895 }
896
897 // Clean up
898 policy.manager.Shutdown()
899 })
900
901 // Test with enabled policy and valid config file
902 t.Run("enabled policy with valid config", func(t *testing.T) {
903 // Create a temporary config directory with a valid policy.json
904 tmpDir := t.TempDir()
905 configDir := filepath.Join(tmpDir, "test-policy-enabled")
906 if err := os.MkdirAll(configDir, 0755); err != nil {
907 t.Fatalf("Failed to create config dir: %v", err)
908 }
909
910 // Write a minimal valid policy.json
911 policyJSON := `{
912 "default_policy": "allow",
913 "kind": {
914 "whitelist": [1, 3, 4]
915 },
916 "rules": {
917 "1": {
918 "description": "Text notes"
919 }
920 }
921 }`
922 policyPath := filepath.Join(configDir, "policy.json")
923 if err := os.WriteFile(policyPath, []byte(policyJSON), 0644); err != nil {
924 t.Fatalf("Failed to write policy.json: %v", err)
925 }
926
927 // Create policy manager manually to use custom config path
928 ctx, cancel := context.WithCancel(context.Background())
929 defer cancel()
930
931 manager := &PolicyManager{
932 ctx: ctx,
933 cancel: cancel,
934 configDir: configDir,
935 scriptPath: filepath.Join(configDir, "policy.sh"),
936 enabled: true,
937 runners: make(map[string]*ScriptRunner),
938 }
939
940 policy := &P{
941 DefaultPolicy: "allow",
942 manager: manager,
943 }
944
945 // Load policy from our test file
946 if err := policy.LoadFromFile(policyPath); err != nil {
947 t.Fatalf("Failed to load policy: %v", err)
948 }
949
950 if policy.manager == nil {
951 t.Fatal("Expected policy manager but got nil")
952 }
953
954 if !policy.manager.IsEnabled() {
955 t.Error("Expected policy manager to be enabled")
956 }
957
958 // Verify policy was loaded correctly
959 if len(policy.Kind.Whitelist) != 3 {
960 t.Errorf("Expected 3 whitelisted kinds, got %d", len(policy.Kind.Whitelist))
961 }
962
963 if policy.DefaultPolicy != "allow" {
964 t.Errorf("Expected default_policy='allow', got '%s'", policy.DefaultPolicy)
965 }
966
967 // Clean up
968 policy.manager.Shutdown()
969 })
970 }
971
972 func TestPolicyManagerLifecycle(t *testing.T) {
973 // Test basic manager initialization without script execution
974 ctx, cancel := context.WithCancel(context.Background())
975 defer cancel()
976
977 manager := &PolicyManager{
978 ctx: ctx,
979 cancel: cancel,
980 configDir: "/tmp",
981 scriptPath: "/tmp/policy.sh",
982 enabled: true,
983 runners: make(map[string]*ScriptRunner),
984 }
985
986 // Test manager state
987 if !manager.IsEnabled() {
988 t.Error("Expected policy manager to be enabled")
989 }
990
991 if manager.IsRunning() {
992 t.Error("Expected policy manager to not be running initially")
993 }
994
995 // Test getting or creating a runner for a non-existent script
996 runner := manager.getOrCreateRunner("/tmp/policy.sh")
997 if runner == nil {
998 t.Fatal("Expected runner to be created")
999 }
1000
1001 // Test starting with non-existent script (should fail gracefully)
1002 err := runner.Start()
1003 if err == nil {
1004 t.Error("Expected error when starting script with non-existent file")
1005 }
1006
1007 // Test stopping when not running (should fail gracefully)
1008 err = runner.Stop()
1009 if err == nil {
1010 t.Error("Expected error when stopping script that's not running")
1011 }
1012 }
1013
1014 func TestPolicyManagerProcessEvent(t *testing.T) {
1015 // Test processing event when runner is not running (should fail gracefully)
1016 ctx, cancel := context.WithCancel(context.Background())
1017 defer cancel()
1018
1019 manager := &PolicyManager{
1020 ctx: ctx,
1021 cancel: cancel,
1022 configDir: "/tmp",
1023 scriptPath: "/tmp/policy.sh",
1024 enabled: true,
1025 runners: make(map[string]*ScriptRunner),
1026 }
1027
1028 // Generate real keypair for testing
1029 eventSigner, eventPubkey := generateTestKeypair(t)
1030
1031 // Create real test event with proper signing
1032 testEvent := createTestEvent(t, eventSigner, "test content", 1)
1033
1034 // Create policy event
1035 policyEvent := &PolicyEvent{
1036 E: testEvent,
1037 LoggedInPubkey: hex.Enc(eventPubkey),
1038 IPAddress: "127.0.0.1",
1039 }
1040
1041 // Get or create a runner
1042 runner := manager.getOrCreateRunner("/tmp/policy.sh")
1043
1044 // Process event when not running (should fail gracefully)
1045 _, err := runner.ProcessEvent(policyEvent)
1046 if err == nil {
1047 t.Error("Expected error when processing event with non-running script")
1048 }
1049 }
1050
1051 func TestEdgeCasesEmptyPolicy(t *testing.T) {
1052 policy := &P{}
1053
1054 // Generate real keypair for testing
1055 eventSigner, eventPubkey := generateTestKeypair(t)
1056
1057 // Create real test event with proper signing
1058 testEvent := createTestEvent(t, eventSigner, "test content", 1)
1059
1060 // Should allow all events when policy is empty
1061 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1062 if err != nil {
1063 t.Errorf("Unexpected error: %v", err)
1064 }
1065 if !allowed {
1066 t.Error("Expected event to be allowed with empty policy")
1067 }
1068 }
1069
1070 func TestEdgeCasesNilEvent(t *testing.T) {
1071 policy := &P{}
1072
1073 // Should handle nil event gracefully
1074 allowed, err := policy.CheckPolicy("write", nil, []byte("test-pubkey"), "127.0.0.1")
1075 if err == nil {
1076 t.Error("Expected error when event is nil")
1077 }
1078 if allowed {
1079 t.Error("Expected event to be blocked when nil")
1080 }
1081
1082 // Verify the error message
1083 if err != nil && !strings.Contains(err.Error(), "event cannot be nil") {
1084 t.Errorf("Expected error message to contain 'event cannot be nil', got: %v", err)
1085 }
1086 }
1087
1088 func TestEdgeCasesLargeEvent(t *testing.T) {
1089 // Create large content
1090 largeContent := strings.Repeat("a", 100000) // 100KB content
1091
1092 policy := &P{
1093 Kind: Kinds{},
1094 rules: map[int]Rule{
1095 1: {
1096 Description: "size limit test",
1097 Constraints: Constraints{
1098 SizeLimit: int64Ptr(50000), // 50KB limit
1099 ContentLimit: int64Ptr(10000), // 10KB content limit
1100 },
1101 },
1102 },
1103 }
1104
1105 // Generate real keypair for testing
1106 eventSigner, eventPubkey := generateTestKeypair(t)
1107
1108 // Create real test event with large content
1109 testEvent := createTestEvent(t, eventSigner, largeContent, 1)
1110
1111 // Should block large event
1112 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1113 if err != nil {
1114 t.Errorf("Unexpected error: %v", err)
1115 }
1116 if allowed {
1117 t.Error("Expected large event to be blocked")
1118 }
1119 }
1120
1121 func TestEdgeCasesWhitelistBlacklistConflict(t *testing.T) {
1122 policy := &P{
1123 Kind: Kinds{
1124 Whitelist: []int{1, 3, 5},
1125 Blacklist: []int{1, 2, 3}, // Overlap with whitelist
1126 },
1127 }
1128
1129 // Test kind in both whitelist and blacklist - whitelist should win
1130 allowed := policy.checkKindsPolicy("write", 1)
1131 if !allowed {
1132 t.Error("Expected whitelist to override blacklist")
1133 }
1134
1135 // Test kind in blacklist but not whitelist
1136 allowed = policy.checkKindsPolicy("write", 2)
1137 if allowed {
1138 t.Error("Expected kind in blacklist but not whitelist to be blocked")
1139 }
1140
1141 // Test kind in whitelist but not blacklist
1142 allowed = policy.checkKindsPolicy("write", 5)
1143 if !allowed {
1144 t.Error("Expected kind in whitelist to be allowed")
1145 }
1146 }
1147
1148 func TestEdgeCasesManagerWithInvalidScript(t *testing.T) {
1149 // Create temporary directory
1150 tempDir := t.TempDir()
1151 scriptPath := filepath.Join(tempDir, "policy.sh")
1152
1153 // Create invalid script (not executable, wrong shebang, etc.)
1154 scriptContent := `invalid script content`
1155 err := os.WriteFile(scriptPath, []byte(scriptContent), 0644) // Not executable
1156 if err != nil {
1157 t.Fatalf("Failed to create invalid script: %v", err)
1158 }
1159
1160 ctx, cancel := context.WithCancel(context.Background())
1161 defer cancel()
1162
1163 manager := &PolicyManager{
1164 ctx: ctx,
1165 cancel: cancel,
1166 configDir: tempDir,
1167 scriptPath: scriptPath,
1168 enabled: true,
1169 runners: make(map[string]*ScriptRunner),
1170 }
1171
1172 // Get runner and try to start with invalid script
1173 runner := manager.getOrCreateRunner(scriptPath)
1174 err = runner.Start()
1175 if err == nil {
1176 t.Error("Expected error when starting invalid script")
1177 }
1178 }
1179
1180 func TestEdgeCasesManagerDoubleStart(t *testing.T) {
1181 // Test double start without actually starting (simpler test)
1182 ctx, cancel := context.WithCancel(context.Background())
1183 defer cancel()
1184
1185 manager := &PolicyManager{
1186 ctx: ctx,
1187 cancel: cancel,
1188 configDir: "/tmp",
1189 scriptPath: "/tmp/policy.sh",
1190 enabled: true,
1191 runners: make(map[string]*ScriptRunner),
1192 }
1193
1194 // Get runner
1195 runner := manager.getOrCreateRunner("/tmp/policy.sh")
1196
1197 // Try to start with non-existent script - should fail
1198 err := runner.Start()
1199 if err == nil {
1200 t.Error("Expected error when starting script with non-existent file")
1201 }
1202
1203 // Try to start again - should still fail
1204 err = runner.Start()
1205 if err == nil {
1206 t.Error("Expected error when starting script twice")
1207 }
1208 }
1209
1210 func TestCheckGlobalRulePolicy(t *testing.T) {
1211 // Generate real keypairs for testing
1212 eventSigner, _ := generateTestKeypair(t)
1213 _, loggedInPubkey := generateTestKeypair(t)
1214
1215 tests := []struct {
1216 name string
1217 globalRule Rule
1218 event *event.E
1219 loggedInPubkey []byte
1220 expected bool
1221 }{
1222 {
1223 name: "global rule with write allow - submitter allowed",
1224 globalRule: Rule{
1225 AccessControl: AccessControl{
1226 WriteAllow: []string{hex.Enc(loggedInPubkey)}, // Allow the submitter
1227 },
1228 },
1229 event: createTestEvent(t, eventSigner, "test content", 1),
1230 loggedInPubkey: loggedInPubkey,
1231 expected: true,
1232 },
1233 {
1234 name: "global rule with write deny - submitter denied",
1235 globalRule: Rule{
1236 AccessControl: AccessControl{
1237 WriteDeny: []string{hex.Enc(loggedInPubkey)}, // Deny the submitter
1238 },
1239 },
1240 event: createTestEvent(t, eventSigner, "test content", 1),
1241 loggedInPubkey: loggedInPubkey,
1242 expected: false,
1243 },
1244 {
1245 name: "global rule with size limit - event too large",
1246 globalRule: Rule{
1247 Constraints: Constraints{
1248 SizeLimit: func() *int64 { v := int64(10); return &v }(),
1249 },
1250 },
1251 event: createTestEvent(t, eventSigner, "this is a very long content that exceeds the size limit", 1),
1252 loggedInPubkey: loggedInPubkey,
1253 expected: false,
1254 },
1255 {
1256 name: "global rule with max age of event - event too old",
1257 globalRule: Rule{
1258 Constraints: Constraints{
1259 MaxAgeOfEvent: func() *int64 { v := int64(3600); return &v }(), // 1 hour
1260 },
1261 },
1262 event: func() *event.E {
1263 ev := createTestEvent(t, eventSigner, "test content", 1)
1264 ev.CreatedAt = time.Now().Unix() - 7200 // 2 hours ago
1265 return ev
1266 }(),
1267 loggedInPubkey: loggedInPubkey,
1268 expected: false,
1269 },
1270 {
1271 name: "global rule with max age event in future - event too far in future",
1272 globalRule: Rule{
1273 Constraints: Constraints{
1274 MaxAgeEventInFuture: func() *int64 { v := int64(3600); return &v }(), // 1 hour
1275 },
1276 },
1277 event: func() *event.E {
1278 ev := createTestEvent(t, eventSigner, "test content", 1)
1279 ev.CreatedAt = time.Now().Unix() + 7200 // 2 hours in future
1280 return ev
1281 }(),
1282 loggedInPubkey: loggedInPubkey,
1283 expected: false,
1284 },
1285 }
1286
1287 for _, tt := range tests {
1288 t.Run(tt.name, func(t *testing.T) {
1289 policy := &P{
1290 Global: tt.globalRule,
1291 }
1292
1293 result := policy.checkGlobalRulePolicy("write", tt.event, tt.loggedInPubkey)
1294 if result != tt.expected {
1295 t.Errorf("Expected %v, got %v", tt.expected, result)
1296 }
1297 })
1298 }
1299 }
1300
1301 func TestCheckPolicyWithGlobalRule(t *testing.T) {
1302 // Generate real keypairs for testing
1303 eventSigner, eventPubkey := generateTestKeypair(t)
1304 _, loggedInPubkey := generateTestKeypair(t)
1305
1306 // Test that global rule is applied first
1307 policy := &P{
1308 Global: Rule{
1309 AccessControl: AccessControl{
1310 WriteDeny: []string{hex.Enc(eventPubkey)}, // Deny event pubkey globally
1311 },
1312 },
1313 Kind: Kinds{
1314 Whitelist: []int{1}, // Allow kind 1
1315 },
1316 rules: map[int]Rule{
1317 1: {
1318 AccessControl: AccessControl{
1319 WriteAllow: []string{hex.Enc(eventPubkey)}, // Allow event pubkey for kind 1
1320 },
1321 },
1322 },
1323 }
1324
1325 event := createTestEvent(t, eventSigner, "test content", 1)
1326
1327 // Global rule should deny this event even though kind-specific rule would allow it
1328 allowed, err := policy.CheckPolicy("write", event, loggedInPubkey, "127.0.0.1")
1329 if err != nil {
1330 t.Fatalf("CheckPolicy failed: %v", err)
1331 }
1332
1333 if allowed {
1334 t.Error("Expected event to be denied by global rule, but it was allowed")
1335 }
1336 }
1337
1338 func TestMaxAgeChecks(t *testing.T) {
1339 // Generate real keypairs for testing
1340 eventSigner, _ := generateTestKeypair(t)
1341 _, loggedInPubkey := generateTestKeypair(t)
1342
1343 tests := []struct {
1344 name string
1345 rule Rule
1346 event *event.E
1347 loggedInPubkey []byte
1348 expected bool
1349 }{
1350 {
1351 name: "max age of event - event within allowed age",
1352 rule: Rule{
1353 Constraints: Constraints{
1354 MaxAgeOfEvent: func() *int64 { v := int64(3600); return &v }(), // 1 hour
1355 },
1356 },
1357 event: func() *event.E {
1358 ev := createTestEvent(t, eventSigner, "test content", 1)
1359 ev.CreatedAt = time.Now().Unix() - 1800 // 30 minutes ago
1360 return ev
1361 }(),
1362 loggedInPubkey: loggedInPubkey,
1363 expected: true,
1364 },
1365 {
1366 name: "max age of event - event too old",
1367 rule: Rule{
1368 Constraints: Constraints{
1369 MaxAgeOfEvent: func() *int64 { v := int64(3600); return &v }(), // 1 hour
1370 },
1371 },
1372 event: func() *event.E {
1373 ev := createTestEvent(t, eventSigner, "test content", 1)
1374 ev.CreatedAt = time.Now().Unix() - 7200 // 2 hours ago
1375 return ev
1376 }(),
1377 loggedInPubkey: loggedInPubkey,
1378 expected: false,
1379 },
1380 {
1381 name: "max age event in future - event within allowed future time",
1382 rule: Rule{
1383 Constraints: Constraints{
1384 MaxAgeEventInFuture: func() *int64 { v := int64(3600); return &v }(), // 1 hour
1385 },
1386 },
1387 event: func() *event.E {
1388 ev := createTestEvent(t, eventSigner, "test content", 1)
1389 ev.CreatedAt = time.Now().Unix() + 1800 // 30 minutes in future
1390 return ev
1391 }(),
1392 loggedInPubkey: loggedInPubkey,
1393 expected: true,
1394 },
1395 {
1396 name: "max age event in future - event too far in future",
1397 rule: Rule{
1398 Constraints: Constraints{
1399 MaxAgeEventInFuture: func() *int64 { v := int64(3600); return &v }(), // 1 hour
1400 },
1401 },
1402 event: func() *event.E {
1403 ev := createTestEvent(t, eventSigner, "test content", 1)
1404 ev.CreatedAt = time.Now().Unix() + 7200 // 2 hours in future
1405 return ev
1406 }(),
1407 loggedInPubkey: loggedInPubkey,
1408 expected: false,
1409 },
1410 {
1411 name: "both age checks - event within both limits",
1412 rule: Rule{
1413 Constraints: Constraints{
1414 MaxAgeOfEvent: func() *int64 { v := int64(3600); return &v }(), // 1 hour
1415 MaxAgeEventInFuture: func() *int64 { v := int64(1800); return &v }(), // 30 minutes
1416 },
1417 },
1418 event: func() *event.E {
1419 ev := createTestEvent(t, eventSigner, "test content", 1)
1420 ev.CreatedAt = time.Now().Unix() + 900 // 15 minutes in future
1421 return ev
1422 }(),
1423 loggedInPubkey: loggedInPubkey,
1424 expected: true,
1425 },
1426 }
1427
1428 for _, tt := range tests {
1429 t.Run(tt.name, func(t *testing.T) {
1430 policy := &P{}
1431
1432 allowed, err := policy.checkRulePolicy("write", tt.event, tt.rule, tt.loggedInPubkey)
1433 if err != nil {
1434 t.Fatalf("checkRulePolicy failed: %v", err)
1435 }
1436
1437 if allowed != tt.expected {
1438 t.Errorf("Expected %v, got %v", tt.expected, allowed)
1439 }
1440 })
1441 }
1442 }
1443
1444 func TestScriptPolicyDisabledFallsBackToDefault(t *testing.T) {
1445 // Generate real keypair for testing
1446 eventSigner, eventPubkey := generateTestKeypair(t)
1447
1448 // Create a policy with a script rule but policy is disabled, default policy is "allow"
1449 policy := &P{
1450 DefaultPolicy: "allow",
1451 rules: map[int]Rule{
1452 1: {
1453 Description: "script rule",
1454 Script: "policy.sh",
1455 },
1456 },
1457 manager: &PolicyManager{
1458 enabled: false, // Policy is disabled
1459 runners: make(map[string]*ScriptRunner),
1460 },
1461 }
1462
1463 // Create real test event with proper signing
1464 testEvent := createTestEvent(t, eventSigner, "test content", 1)
1465
1466 // Should allow the event when policy is disabled (falls back to default "allow")
1467 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1468 if err != nil {
1469 t.Errorf("Unexpected error: %v", err)
1470 }
1471 if !allowed {
1472 t.Error("Expected event to be allowed when policy is disabled (should fall back to default policy 'allow')")
1473 }
1474
1475 // Test with default policy "deny"
1476 policy.DefaultPolicy = "deny"
1477 allowed2, err2 := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1478 if err2 != nil {
1479 t.Errorf("Unexpected error: %v", err2)
1480 }
1481 if allowed2 {
1482 t.Error("Expected event to be denied when policy is disabled and default policy is 'deny'")
1483 }
1484 }
1485
1486 func TestDefaultPolicyAllow(t *testing.T) {
1487 // Generate real keypair for testing
1488 eventSigner, eventPubkey := generateTestKeypair(t)
1489
1490 // Test default policy "allow" behavior
1491 policy := &P{
1492 DefaultPolicy: "allow",
1493 Kind: Kinds{},
1494 rules: map[int]Rule{}, // No specific rules
1495 }
1496
1497 // Create real test event with proper signing
1498 testEvent := createTestEvent(t, eventSigner, "test content", 1)
1499
1500 // Should allow the event with default policy "allow"
1501 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1502 if err != nil {
1503 t.Errorf("Unexpected error: %v", err)
1504 }
1505 if !allowed {
1506 t.Error("Expected event to be allowed with default_policy 'allow'")
1507 }
1508 }
1509
1510 func TestDefaultPolicyDeny(t *testing.T) {
1511 // Generate real keypair for testing
1512 eventSigner, eventPubkey := generateTestKeypair(t)
1513
1514 // Test default policy "deny" behavior
1515 policy := &P{
1516 DefaultPolicy: "deny",
1517 Kind: Kinds{},
1518 rules: map[int]Rule{}, // No specific rules
1519 }
1520
1521 // Create real test event with proper signing
1522 testEvent := createTestEvent(t, eventSigner, "test content", 1)
1523
1524 // Should deny the event with default policy "deny"
1525 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1526 if err != nil {
1527 t.Errorf("Unexpected error: %v", err)
1528 }
1529 if allowed {
1530 t.Error("Expected event to be denied with default_policy 'deny'")
1531 }
1532 }
1533
1534 func TestDefaultPolicyEmpty(t *testing.T) {
1535 // Generate real keypair for testing
1536 eventSigner, eventPubkey := generateTestKeypair(t)
1537
1538 // Test empty default policy (should default to "allow")
1539 policy := &P{
1540 DefaultPolicy: "",
1541 Kind: Kinds{},
1542 rules: map[int]Rule{}, // No specific rules
1543 }
1544
1545 // Create real test event with proper signing
1546 testEvent := createTestEvent(t, eventSigner, "test content", 1)
1547
1548 // Should allow the event with empty default policy (defaults to "allow")
1549 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1550 if err != nil {
1551 t.Errorf("Unexpected error: %v", err)
1552 }
1553 if !allowed {
1554 t.Error("Expected event to be allowed with empty default_policy (should default to 'allow')")
1555 }
1556 }
1557
1558 func TestDefaultPolicyInvalid(t *testing.T) {
1559 // Generate real keypair for testing
1560 eventSigner, eventPubkey := generateTestKeypair(t)
1561
1562 // Test invalid default policy (should default to "allow")
1563 policy := &P{
1564 DefaultPolicy: "invalid",
1565 Kind: Kinds{},
1566 rules: map[int]Rule{}, // No specific rules
1567 }
1568
1569 // Create real test event with proper signing
1570 testEvent := createTestEvent(t, eventSigner, "test content", 1)
1571
1572 // Should allow the event with invalid default policy (defaults to "allow")
1573 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1574 if err != nil {
1575 t.Errorf("Unexpected error: %v", err)
1576 }
1577 if !allowed {
1578 t.Error("Expected event to be allowed with invalid default_policy (should default to 'allow')")
1579 }
1580 }
1581
1582 func TestDefaultPolicyWithSpecificRule(t *testing.T) {
1583 // Generate real keypair for testing
1584 eventSigner, eventPubkey := generateTestKeypair(t)
1585
1586 // Test that specific rules override default policy
1587 policy := &P{
1588 DefaultPolicy: "deny", // Default is deny
1589 Kind: Kinds{},
1590 rules: map[int]Rule{
1591 1: {
1592 Description: "allow kind 1",
1593 AccessControl: AccessControl{
1594 WriteAllow: []string{}, // Allow all for kind 1
1595 },
1596 },
1597 },
1598 }
1599
1600 // Create real test event with proper signing for kind 1 (has specific rule)
1601 testEvent := createTestEvent(t, eventSigner, "test content", 1)
1602
1603 // Should allow the event because specific rule allows it, despite default policy being "deny"
1604 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1605 if err != nil {
1606 t.Errorf("Unexpected error: %v", err)
1607 }
1608 if !allowed {
1609 t.Error("Expected event to be allowed by specific rule, despite default_policy 'deny'")
1610 }
1611
1612 // Create real test event with proper signing for kind 2 (no specific rule exists)
1613 testEvent2 := createTestEvent(t, eventSigner, "test content", 2)
1614
1615 // Should deny the event because no specific rule and default policy is "deny"
1616 allowed2, err2 := policy.CheckPolicy("write", testEvent2, eventPubkey, "127.0.0.1")
1617 if err2 != nil {
1618 t.Errorf("Unexpected error: %v", err2)
1619 }
1620 if allowed2 {
1621 t.Error("Expected event to be denied with default_policy 'deny' for kind without specific rule")
1622 }
1623 }
1624
1625 func TestNewPolicyDefaultsToAllow(t *testing.T) {
1626 // Test that New() function sets default policy to "allow"
1627 policy, err := New([]byte(`{}`))
1628 if err != nil {
1629 t.Fatalf("Failed to create policy: %v", err)
1630 }
1631
1632 if policy.DefaultPolicy != "allow" {
1633 t.Errorf("Expected default policy to be 'allow', got '%s'", policy.DefaultPolicy)
1634 }
1635 }
1636
1637 func TestNewPolicyWithDefaultPolicyJSON(t *testing.T) {
1638 // Test loading default policy from JSON
1639 jsonConfig := `{"default_policy": "deny"}`
1640 policy, err := New([]byte(jsonConfig))
1641 if err != nil {
1642 t.Fatalf("Failed to create policy: %v", err)
1643 }
1644
1645 if policy.DefaultPolicy != "deny" {
1646 t.Errorf("Expected default policy to be 'deny', got '%s'", policy.DefaultPolicy)
1647 }
1648 }
1649
1650 func TestScriptProcessingDisabledFallsBackToDefault(t *testing.T) {
1651 // Generate real keypair for testing
1652 eventSigner, eventPubkey := generateTestKeypair(t)
1653
1654 // Test that when policy is disabled, it falls back to default policy
1655 policy := &P{
1656 DefaultPolicy: "allow",
1657 rules: map[int]Rule{
1658 1: {
1659 Description: "script rule",
1660 Script: "policy.sh",
1661 },
1662 },
1663 manager: &PolicyManager{
1664 enabled: false, // Policy is disabled
1665 runners: make(map[string]*ScriptRunner),
1666 },
1667 }
1668
1669 // Create real test event with proper signing
1670 testEvent := createTestEvent(t, eventSigner, "test content", 1)
1671
1672 // Should allow the event when policy is disabled (falls back to default "allow")
1673 allowed, err := policy.checkScriptPolicy("write", testEvent, "policy.sh", eventPubkey, "127.0.0.1")
1674 if err != nil {
1675 t.Errorf("Unexpected error: %v", err)
1676 }
1677 if !allowed {
1678 t.Error("Expected event to be allowed when policy is disabled (should fall back to default policy 'allow')")
1679 }
1680
1681 // Test with default policy "deny"
1682 policy.DefaultPolicy = "deny"
1683 allowed2, err2 := policy.checkScriptPolicy("write", testEvent, "policy.sh", eventPubkey, "127.0.0.1")
1684 if err2 != nil {
1685 t.Errorf("Unexpected error: %v", err2)
1686 }
1687 if allowed2 {
1688 t.Error("Expected event to be denied when policy is disabled and default policy is 'deny'")
1689 }
1690 }
1691
1692 func TestDefaultPolicyLogicWithRules(t *testing.T) {
1693 // Generate real keypairs for testing
1694 testSigner, _ := generateTestKeypair(t)
1695 _, deniedPubkey := generateTestKeypair(t) // Only need pubkey for denied user
1696 _, loggedInPubkey := generateTestKeypair(t)
1697
1698 // Test that default policy logic works correctly with rules
1699
1700 // Test 1: default_policy "deny" - should only allow if rule explicitly allows
1701 policy1 := &P{
1702 DefaultPolicy: "deny",
1703 Kind: Kinds{
1704 Whitelist: []int{1, 2, 3}, // Allow kinds 1, 2, 3
1705 },
1706 rules: map[int]Rule{
1707 1: {
1708 Description: "allow all for kind 1",
1709 AccessControl: AccessControl{
1710 WriteAllow: []string{}, // Empty means allow all
1711 },
1712 },
1713 2: {
1714 Description: "deny specific pubkey for kind 2",
1715 AccessControl: AccessControl{
1716 WriteDeny: []string{hex.Enc(deniedPubkey)},
1717 },
1718 },
1719 // No rule for kind 3
1720 },
1721 }
1722
1723 // Kind 1: has rule that allows all - should be allowed
1724 event1 := createTestEvent(t, testSigner, "content", 1)
1725 allowed1, err1 := policy1.CheckPolicy("write", event1, loggedInPubkey, "127.0.0.1")
1726 if err1 != nil {
1727 t.Errorf("Unexpected error for kind 1: %v", err1)
1728 }
1729 if !allowed1 {
1730 t.Error("Expected kind 1 to be allowed (rule allows all)")
1731 }
1732
1733 // Kind 2: has rule that denies specific pubkey - should be allowed for other pubkeys
1734 event2 := createTestEvent(t, testSigner, "content", 2)
1735 allowed2, err2 := policy1.CheckPolicy("write", event2, loggedInPubkey, "127.0.0.1")
1736 if err2 != nil {
1737 t.Errorf("Unexpected error for kind 2: %v", err2)
1738 }
1739 if !allowed2 {
1740 t.Error("Expected kind 2 to be allowed for non-denied pubkey")
1741 }
1742
1743 // Kind 2: submitter in deny list should be denied
1744 event2Denied := createTestEvent(t, testSigner, "content", 2) // Event can be from anyone
1745 allowed2Denied, err2Denied := policy1.CheckPolicy("write", event2Denied, deniedPubkey, "127.0.0.1") // But submitted by denied user
1746 if err2Denied != nil {
1747 t.Errorf("Unexpected error for kind 2 denied: %v", err2Denied)
1748 }
1749 if allowed2Denied {
1750 t.Error("Expected kind 2 to be denied when submitter is in deny list")
1751 }
1752
1753 // Kind 3: whitelisted but no rule - should follow default policy (deny)
1754 event3 := createTestEvent(t, testSigner, "content", 3)
1755 allowed3, err3 := policy1.CheckPolicy("write", event3, loggedInPubkey, "127.0.0.1")
1756 if err3 != nil {
1757 t.Errorf("Unexpected error for kind 3: %v", err3)
1758 }
1759 if allowed3 {
1760 t.Error("Expected kind 3 to be denied (no rule, default policy is deny)")
1761 }
1762
1763 // Test 2: default_policy "allow" - should allow unless rule explicitly denies
1764 policy2 := &P{
1765 DefaultPolicy: "allow",
1766 Kind: Kinds{
1767 Whitelist: []int{1, 2, 3}, // Allow kinds 1, 2, 3
1768 },
1769 rules: map[int]Rule{
1770 1: {
1771 Description: "deny specific pubkey for kind 1",
1772 AccessControl: AccessControl{
1773 WriteDeny: []string{hex.Enc(deniedPubkey)},
1774 },
1775 },
1776 // No rules for kind 2, 3
1777 },
1778 }
1779
1780 // Kind 1: has rule that denies specific pubkey - should be allowed for other pubkeys
1781 event1Allow := createTestEvent(t, testSigner, "content", 1)
1782 allowed1Allow, err1Allow := policy2.CheckPolicy("write", event1Allow, loggedInPubkey, "127.0.0.1")
1783 if err1Allow != nil {
1784 t.Errorf("Unexpected error for kind 1 allow: %v", err1Allow)
1785 }
1786 if !allowed1Allow {
1787 t.Error("Expected kind 1 to be allowed for non-denied pubkey")
1788 }
1789
1790 // Kind 1: denied pubkey should be denied when they try to submit
1791 event1Deny := createTestEvent(t, testSigner, "content", 1) // Event can be authored by anyone
1792 allowed1Deny, err1Deny := policy2.CheckPolicy("write", event1Deny, deniedPubkey, "127.0.0.1") // But denied user cannot submit
1793 if err1Deny != nil {
1794 t.Errorf("Unexpected error for kind 1 deny: %v", err1Deny)
1795 }
1796 if allowed1Deny {
1797 t.Error("Expected kind 1 to be denied for denied pubkey")
1798 }
1799
1800 // Kind 2: whitelisted but no rule - should follow default policy (allow)
1801 event2Allow := createTestEvent(t, testSigner, "content", 2)
1802 allowed2Allow, err2Allow := policy2.CheckPolicy("write", event2Allow, loggedInPubkey, "127.0.0.1")
1803 if err2Allow != nil {
1804 t.Errorf("Unexpected error for kind 2 allow: %v", err2Allow)
1805 }
1806 if !allowed2Allow {
1807 t.Error("Expected kind 2 to be allowed (no rule, default policy is allow)")
1808 }
1809 }
1810
1811 func TestRuleScriptLoading(t *testing.T) {
1812 // This test validates that a policy script loads for a specific Rule
1813 // and properly processes events
1814
1815 // Create temporary directory for test files
1816 tempDir := t.TempDir()
1817 scriptPath := filepath.Join(tempDir, "test-rule-script.sh")
1818
1819 // Create a test script that accepts events with "allowed" in content
1820 scriptContent := `#!/bin/bash
1821 while IFS= read -r line; do
1822 if echo "$line" | grep -q 'allowed'; then
1823 echo '{"action":"accept","msg":"Content approved"}'
1824 else
1825 echo '{"action":"reject","msg":"Content not allowed"}'
1826 fi
1827 done
1828 `
1829 err := os.WriteFile(scriptPath, []byte(scriptContent), 0755)
1830 if err != nil {
1831 t.Fatalf("Failed to create test script: %v", err)
1832 }
1833
1834 // Create policy manager with script support
1835 ctx, cancel := context.WithCancel(context.Background())
1836 defer cancel()
1837
1838 manager := &PolicyManager{
1839 ctx: ctx,
1840 cancel: cancel,
1841 configDir: tempDir,
1842 scriptPath: filepath.Join(tempDir, "default-policy.sh"), // Different from rule script
1843 enabled: true,
1844 runners: make(map[string]*ScriptRunner),
1845 }
1846
1847 // Create policy with a rule that uses the script
1848 policy := &P{
1849 DefaultPolicy: "deny",
1850 manager: manager,
1851 rules: map[int]Rule{
1852 4678: {
1853 Description: "Test rule with custom script",
1854 Script: scriptPath, // Rule-specific script path
1855 },
1856 },
1857 }
1858
1859 // Generate test keypairs
1860 eventSigner, eventPubkey := generateTestKeypair(t)
1861
1862 // Pre-start the script before running tests
1863 runner := manager.getOrCreateRunner(scriptPath)
1864 err = runner.Start()
1865 if err != nil {
1866 t.Fatalf("Failed to start script: %v", err)
1867 }
1868
1869 // Wait for script to be ready
1870 time.Sleep(200 * time.Millisecond)
1871
1872 if !runner.IsRunning() {
1873 t.Fatal("Script should be running after Start()")
1874 }
1875
1876 // Test sending a warmup event to ensure script is responsive
1877 signer := p8k.MustNew()
1878 signer.Generate()
1879 warmupEv := event.New()
1880 warmupEv.CreatedAt = time.Now().Unix()
1881 warmupEv.Kind = 4678
1882 warmupEv.Content = []byte("warmup")
1883 warmupEv.Tags = tag.NewS()
1884 warmupEv.Sign(signer)
1885
1886 warmupEvent := &PolicyEvent{
1887 E: warmupEv,
1888 IPAddress: "127.0.0.1",
1889 }
1890
1891 // Send warmup event to verify script is responding
1892 _, err = runner.ProcessEvent(warmupEvent)
1893 if err != nil {
1894 t.Fatalf("Script not responding to warmup event: %v", err)
1895 }
1896
1897 t.Log("Script is ready and responding")
1898
1899 // Test 1: Event with "allowed" content should be accepted
1900 t.Run("script_accepts_allowed_content", func(t *testing.T) {
1901 testEvent := createTestEvent(t, eventSigner, "this is allowed content", 4678)
1902
1903 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1904 if err != nil {
1905 t.Logf("Policy check failed: %v", err)
1906 // Check if script exists
1907 if _, statErr := os.Stat(scriptPath); statErr != nil {
1908 t.Errorf("Script file error: %v", statErr)
1909 }
1910 t.Fatalf("Unexpected error during policy check: %v", err)
1911 }
1912 if !allowed {
1913 t.Error("Expected event with 'allowed' content to be accepted by script")
1914 t.Logf("Event content: %s", string(testEvent.Content))
1915 }
1916
1917 // Verify the script runner was created and is running
1918 manager.mutex.RLock()
1919 runner, exists := manager.runners[scriptPath]
1920 manager.mutex.RUnlock()
1921
1922 if !exists {
1923 t.Fatal("Expected script runner to be created for rule script path")
1924 }
1925 if !runner.IsRunning() {
1926 t.Error("Expected script runner to be running after processing event")
1927 }
1928 })
1929
1930 // Test 2: Event without "allowed" content should be rejected
1931 t.Run("script_rejects_disallowed_content", func(t *testing.T) {
1932 testEvent := createTestEvent(t, eventSigner, "this is not permitted", 4678)
1933
1934 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1935 if err != nil {
1936 t.Errorf("Unexpected error: %v", err)
1937 }
1938 if allowed {
1939 t.Error("Expected event without 'allowed' content to be rejected by script")
1940 }
1941 })
1942
1943 // Test 3: Verify script path is correct (rule-specific, not default)
1944 t.Run("script_path_is_rule_specific", func(t *testing.T) {
1945 manager.mutex.RLock()
1946 runner, exists := manager.runners[scriptPath]
1947 _, defaultExists := manager.runners[manager.scriptPath]
1948 manager.mutex.RUnlock()
1949
1950 if !exists {
1951 t.Fatal("Expected rule-specific script runner to exist")
1952 }
1953 if defaultExists {
1954 t.Error("Default script runner should not be created when only rule-specific scripts are used")
1955 }
1956
1957 // Verify the runner is using the correct script path
1958 if runner.scriptPath != scriptPath {
1959 t.Errorf("Expected runner to use script path %s, got %s", scriptPath, runner.scriptPath)
1960 }
1961 })
1962
1963 // Test 4: Multiple events should use the same script instance
1964 t.Run("script_reused_for_multiple_events", func(t *testing.T) {
1965 // Get initial runner
1966 manager.mutex.RLock()
1967 initialRunner, _ := manager.runners[scriptPath]
1968 initialRunnerCount := len(manager.runners)
1969 manager.mutex.RUnlock()
1970
1971 // Process multiple events
1972 for i := 0; i < 5; i++ {
1973 content := "this is allowed message " + string(rune('0'+i))
1974 testEvent := createTestEvent(t, eventSigner, content, 4678)
1975 _, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
1976 if err != nil {
1977 t.Errorf("Unexpected error on event %d: %v", i, err)
1978 }
1979 }
1980
1981 // Verify same runner is used
1982 manager.mutex.RLock()
1983 currentRunner, _ := manager.runners[scriptPath]
1984 currentRunnerCount := len(manager.runners)
1985 manager.mutex.RUnlock()
1986
1987 if currentRunner != initialRunner {
1988 t.Error("Expected same runner instance to be reused for multiple events")
1989 }
1990 if currentRunnerCount != initialRunnerCount {
1991 t.Errorf("Expected runner count to stay at %d, got %d", initialRunnerCount, currentRunnerCount)
1992 }
1993 })
1994
1995 // Test 5: Different kind without script should use default policy
1996 t.Run("different_kind_uses_default_policy", func(t *testing.T) {
1997 testEvent := createTestEvent(t, eventSigner, "any content", 1) // Kind 1 has no rule
1998
1999 allowed, err := policy.CheckPolicy("write", testEvent, eventPubkey, "127.0.0.1")
2000 if err != nil {
2001 t.Errorf("Unexpected error: %v", err)
2002 }
2003 // Should be denied by default policy (deny)
2004 if allowed {
2005 t.Error("Expected event of kind without rule to be denied by default policy")
2006 }
2007 })
2008
2009 // Cleanup: Stop the script
2010 manager.mutex.RLock()
2011 runner, exists := manager.runners[scriptPath]
2012 manager.mutex.RUnlock()
2013 if exists && runner.IsRunning() {
2014 runner.Stop()
2015 }
2016 }
2017
2018 func TestPolicyFilterProcessing(t *testing.T) {
2019 // Test policy filter processing using the provided filter JSON specification
2020 filterJSON := []byte(`{
2021 "kind": {
2022 "whitelist": [4678, 10306, 30520, 30919]
2023 },
2024 "rules": {
2025 "4678": {
2026 "description": "Zenotp message events",
2027 "script": "/home/orly/.config/ORLY/validate4678.js",
2028 "privileged": true
2029 },
2030 "10306": {
2031 "description": "End user whitelist changes",
2032 "read_allow": [
2033 "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
2034 ],
2035 "privileged": true
2036 },
2037 "30520": {
2038 "description": "Zenotp events",
2039 "write_allow": [
2040 "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
2041 ],
2042 "privileged": true
2043 },
2044 "30919": {
2045 "description": "Zenotp events",
2046 "write_allow": [
2047 "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
2048 ],
2049 "privileged": true
2050 }
2051 }
2052 }`)
2053
2054 // Create policy from JSON
2055 policy, err := New(filterJSON)
2056 if err != nil {
2057 t.Fatalf("Failed to create policy from JSON: %v", err)
2058 }
2059
2060 // Verify whitelist is set correctly
2061 if len(policy.Kind.Whitelist) != 4 {
2062 t.Errorf("Expected whitelist to have 4 kinds, got %d", len(policy.Kind.Whitelist))
2063 }
2064 expectedKinds := map[int]bool{4678: true, 10306: true, 30520: true, 30919: true}
2065 for _, kind := range policy.Kind.Whitelist {
2066 if !expectedKinds[kind] {
2067 t.Errorf("Unexpected kind in whitelist: %d", kind)
2068 }
2069 }
2070
2071 // Verify rules are loaded correctly
2072 if len(policy.rules) != 4 {
2073 t.Errorf("Expected 4 rules, got %d", len(policy.rules))
2074 }
2075
2076 // Verify rule 4678 (script-based)
2077 rule4678, ok := policy.rules[4678]
2078 if !ok {
2079 t.Fatal("Rule 4678 not found")
2080 }
2081 if rule4678.Description != "Zenotp message events" {
2082 t.Errorf("Expected description 'Zenotp message events', got '%s'", rule4678.Description)
2083 }
2084 if rule4678.Script != "/home/orly/.config/ORLY/validate4678.js" {
2085 t.Errorf("Expected script path '/home/orly/.config/ORLY/validate4678.js', got '%s'", rule4678.Script)
2086 }
2087 if !rule4678.Privileged {
2088 t.Error("Expected rule 4678 to be privileged")
2089 }
2090
2091 // Verify rule 10306 (read_allow)
2092 rule10306, ok := policy.rules[10306]
2093 if !ok {
2094 t.Fatal("Rule 10306 not found")
2095 }
2096 if rule10306.Description != "End user whitelist changes" {
2097 t.Errorf("Expected description 'End user whitelist changes', got '%s'", rule10306.Description)
2098 }
2099 if len(rule10306.ReadAllow) != 1 {
2100 t.Errorf("Expected 1 read_allow pubkey, got %d", len(rule10306.ReadAllow))
2101 }
2102 if rule10306.ReadAllow[0] != "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5" {
2103 t.Errorf("Expected read_allow pubkey '04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5', got '%s'", rule10306.ReadAllow[0])
2104 }
2105 if !rule10306.Privileged {
2106 t.Error("Expected rule 10306 to be privileged")
2107 }
2108
2109 // Verify rule 30520 (write_allow)
2110 rule30520, ok := policy.rules[30520]
2111 if !ok {
2112 t.Fatal("Rule 30520 not found")
2113 }
2114 if rule30520.Description != "Zenotp events" {
2115 t.Errorf("Expected description 'Zenotp events', got '%s'", rule30520.Description)
2116 }
2117 if len(rule30520.WriteAllow) != 1 {
2118 t.Errorf("Expected 1 write_allow pubkey, got %d", len(rule30520.WriteAllow))
2119 }
2120 if rule30520.WriteAllow[0] != "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5" {
2121 t.Errorf("Expected write_allow pubkey '04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5', got '%s'", rule30520.WriteAllow[0])
2122 }
2123 if !rule30520.Privileged {
2124 t.Error("Expected rule 30520 to be privileged")
2125 }
2126
2127 // Verify rule 30919 (write_allow)
2128 rule30919, ok := policy.rules[30919]
2129 if !ok {
2130 t.Fatal("Rule 30919 not found")
2131 }
2132 if rule30919.Description != "Zenotp events" {
2133 t.Errorf("Expected description 'Zenotp events', got '%s'", rule30919.Description)
2134 }
2135 if len(rule30919.WriteAllow) != 1 {
2136 t.Errorf("Expected 1 write_allow pubkey, got %d", len(rule30919.WriteAllow))
2137 }
2138 if rule30919.WriteAllow[0] != "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5" {
2139 t.Errorf("Expected write_allow pubkey '04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5', got '%s'", rule30919.WriteAllow[0])
2140 }
2141 if !rule30919.Privileged {
2142 t.Error("Expected rule 30919 to be privileged")
2143 }
2144
2145 // Test kind whitelist filtering
2146 t.Run("kind whitelist filtering", func(t *testing.T) {
2147 eventSigner, eventPubkey := generateTestKeypair(t)
2148 _, loggedInPubkey := generateTestKeypair(t)
2149
2150 // Test whitelisted kind (should pass kind check, but may fail other checks)
2151 whitelistedEvent := createTestEvent(t, eventSigner, "test content", 4678)
2152 addPTag(whitelistedEvent, loggedInPubkey)
2153 allowed, err := policy.CheckPolicy("write", whitelistedEvent, loggedInPubkey, "127.0.0.1")
2154 if err != nil {
2155 t.Errorf("Unexpected error: %v", err)
2156 }
2157 // Should be allowed because script isn't running (falls back to default "allow")
2158 // and privileged check passes (loggedInPubkey is in p tag)
2159 if !allowed {
2160 t.Error("Expected whitelisted kind to be allowed when script not running and privileged check passes")
2161 }
2162
2163 // Test non-whitelisted kind (should be denied)
2164 nonWhitelistedEvent := createTestEvent(t, eventSigner, "test content", 1)
2165 allowed, err = policy.CheckPolicy("write", nonWhitelistedEvent, eventPubkey, "127.0.0.1")
2166 if err != nil {
2167 t.Errorf("Unexpected error: %v", err)
2168 }
2169 if allowed {
2170 t.Error("Expected non-whitelisted kind to be denied")
2171 }
2172 })
2173
2174 // Test read access for kind 10306
2175 t.Run("read access for kind 10306", func(t *testing.T) {
2176 allowedPubkeyHex := "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
2177 allowedPubkeyBytes, err := hex.Dec(allowedPubkeyHex)
2178 if err != nil {
2179 t.Fatalf("Failed to decode allowed pubkey: %v", err)
2180 }
2181
2182 // Create event with allowed pubkey using a temporary signer
2183 // Note: We can't actually sign with just the pubkey, so we'll create the event
2184 // with a random signer and then manually set the pubkey for testing
2185 tempSigner, _ := generateTestKeypair(t)
2186 event10306 := createTestEvent(t, tempSigner, "test content", 10306)
2187 event10306.Pubkey = allowedPubkeyBytes
2188 // Re-sign won't work without private key, but policy checks use the pubkey field
2189 // So we'll test with the pubkey set correctly
2190
2191 // Test read access with allowed pubkey (logged in user matches event pubkey)
2192 allowed, err := policy.CheckPolicy("read", event10306, allowedPubkeyBytes, "127.0.0.1")
2193 if err != nil {
2194 t.Errorf("Unexpected error: %v", err)
2195 }
2196 if !allowed {
2197 t.Error("Expected event to be allowed for read access with allowed pubkey")
2198 }
2199
2200 // Test read access with non-allowed pubkey
2201 _, unauthorizedPubkey := generateTestKeypair(t)
2202 allowed, err = policy.CheckPolicy("read", event10306, unauthorizedPubkey, "127.0.0.1")
2203 if err != nil {
2204 t.Errorf("Unexpected error: %v", err)
2205 }
2206 if allowed {
2207 t.Error("Expected event to be denied for read access with non-allowed pubkey")
2208 }
2209
2210 // Test read access without authentication (privileged check)
2211 allowed, err = policy.CheckPolicy("read", event10306, nil, "127.0.0.1")
2212 if err != nil {
2213 t.Errorf("Unexpected error: %v", err)
2214 }
2215 if allowed {
2216 t.Error("Expected event to be denied for read access without authentication (privileged)")
2217 }
2218 })
2219
2220 // Test write access for kind 30520
2221 t.Run("write access for kind 30520", func(t *testing.T) {
2222 allowedPubkeyHex := "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
2223 allowedPubkeyBytes, err := hex.Dec(allowedPubkeyHex)
2224 if err != nil {
2225 t.Fatalf("Failed to decode allowed pubkey: %v", err)
2226 }
2227
2228 // Create event with allowed pubkey using a temporary signer
2229 // We'll set the pubkey manually for testing since we don't have the private key
2230 tempSigner, _ := generateTestKeypair(t)
2231 event30520 := createTestEvent(t, tempSigner, "test content", 30520)
2232 event30520.Pubkey = allowedPubkeyBytes
2233
2234 // Test write access with allowed pubkey (event pubkey matches write_allow)
2235 allowed, err := policy.CheckPolicy("write", event30520, allowedPubkeyBytes, "127.0.0.1")
2236 if err != nil {
2237 t.Errorf("Unexpected error: %v", err)
2238 }
2239 if !allowed {
2240 t.Error("Expected event to be allowed for write access with allowed pubkey")
2241 }
2242
2243 // Test write access with non-allowed pubkey
2244 unauthorizedSigner, unauthorizedPubkey := generateTestKeypair(t)
2245 unauthorizedEvent := createTestEvent(t, unauthorizedSigner, "test content", 30520)
2246 allowed, err = policy.CheckPolicy("write", unauthorizedEvent, unauthorizedPubkey, "127.0.0.1")
2247 if err != nil {
2248 t.Errorf("Unexpected error: %v", err)
2249 }
2250 if allowed {
2251 t.Error("Expected event to be denied for write access with non-allowed pubkey")
2252 }
2253
2254 // Test write access without authentication (privileged check)
2255 allowed, err = policy.CheckPolicy("write", event30520, nil, "127.0.0.1")
2256 if err != nil {
2257 t.Errorf("Unexpected error: %v", err)
2258 }
2259 if allowed {
2260 t.Error("Expected event to be denied for write access without authentication (privileged)")
2261 }
2262 })
2263
2264 // Test write access for kind 30919
2265 t.Run("write access for kind 30919", func(t *testing.T) {
2266 allowedPubkeyHex := "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
2267 allowedPubkeyBytes, err := hex.Dec(allowedPubkeyHex)
2268 if err != nil {
2269 t.Fatalf("Failed to decode allowed pubkey: %v", err)
2270 }
2271
2272 // Create event with allowed pubkey using a temporary signer
2273 // We'll set the pubkey manually for testing since we don't have the private key
2274 tempSigner, _ := generateTestKeypair(t)
2275 event30919 := createTestEvent(t, tempSigner, "test content", 30919)
2276 event30919.Pubkey = allowedPubkeyBytes
2277
2278 // Test write access with allowed pubkey (event pubkey matches write_allow)
2279 allowed, err := policy.CheckPolicy("write", event30919, allowedPubkeyBytes, "127.0.0.1")
2280 if err != nil {
2281 t.Errorf("Unexpected error: %v", err)
2282 }
2283 if !allowed {
2284 t.Error("Expected event to be allowed for write access with allowed pubkey")
2285 }
2286
2287 // Test write access with non-allowed pubkey
2288 unauthorizedSigner, unauthorizedPubkey := generateTestKeypair(t)
2289 unauthorizedEvent := createTestEvent(t, unauthorizedSigner, "test content", 30919)
2290 allowed, err = policy.CheckPolicy("write", unauthorizedEvent, unauthorizedPubkey, "127.0.0.1")
2291 if err != nil {
2292 t.Errorf("Unexpected error: %v", err)
2293 }
2294 if allowed {
2295 t.Error("Expected event to be denied for write access with non-allowed pubkey")
2296 }
2297
2298 // Test write access without authentication (privileged check)
2299 allowed, err = policy.CheckPolicy("write", event30919, nil, "127.0.0.1")
2300 if err != nil {
2301 t.Errorf("Unexpected error: %v", err)
2302 }
2303 if allowed {
2304 t.Error("Expected event to be denied for write access without authentication (privileged)")
2305 }
2306 })
2307
2308 // Test privileged flag behavior with p tags
2309 t.Run("privileged flag with p tags", func(t *testing.T) {
2310 eventSigner, _ := generateTestKeypair(t)
2311 _, loggedInPubkey := generateTestKeypair(t)
2312 allowedPubkeyHex := "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
2313 allowedPubkeyBytes, err := hex.Dec(allowedPubkeyHex)
2314 if err != nil {
2315 t.Fatalf("Failed to decode allowed pubkey: %v", err)
2316 }
2317
2318 // Create event with allowed pubkey and logged-in pubkey in p tag
2319 event30520 := createTestEvent(t, eventSigner, "test content", 30520)
2320 event30520.Pubkey = allowedPubkeyBytes
2321 addPTag(event30520, loggedInPubkey)
2322
2323 // Test that event is DENIED when submitter (logged-in pubkey) is not in write_allow
2324 // Even though the submitter is in p-tag, write_allow is about who can submit
2325 allowed, err := policy.CheckPolicy("write", event30520, loggedInPubkey, "127.0.0.1")
2326 if err != nil {
2327 t.Errorf("Unexpected error: %v", err)
2328 }
2329 if allowed {
2330 t.Error("Expected event to be denied when submitter (logged-in pubkey) is not in write_allow")
2331 }
2332
2333 // Test that event is denied when submitter is not in write_allow (even without p-tag)
2334 event30520NoPTag := createTestEvent(t, eventSigner, "test content", 30520)
2335 event30520NoPTag.Pubkey = allowedPubkeyBytes
2336 allowed, err = policy.CheckPolicy("write", event30520NoPTag, loggedInPubkey, "127.0.0.1")
2337 if err != nil {
2338 t.Errorf("Unexpected error: %v", err)
2339 }
2340 if allowed {
2341 t.Error("Expected event to be denied when submitter is not in write_allow")
2342 }
2343 })
2344
2345 // Test script-based rule (kind 4678)
2346 t.Run("script-based rule for kind 4678", func(t *testing.T) {
2347 eventSigner, _ := generateTestKeypair(t)
2348 _, loggedInPubkey := generateTestKeypair(t)
2349
2350 event4678 := createTestEvent(t, eventSigner, "test content", 4678)
2351 addPTag(event4678, loggedInPubkey)
2352
2353 // Test with script not running (should fall back to default policy)
2354 allowed, err := policy.CheckPolicy("write", event4678, loggedInPubkey, "127.0.0.1")
2355 if err != nil {
2356 t.Errorf("Unexpected error: %v", err)
2357 }
2358 // Should allow because default policy is "allow" and script is not running
2359 // and privileged check passes (loggedInPubkey is in p tag)
2360 if !allowed {
2361 t.Error("Expected event to be allowed when script is not running (falls back to default 'allow') and privileged check passes")
2362 }
2363
2364 // Test without authentication (privileged doesn't affect write operations)
2365 allowed, err = policy.CheckPolicy("write", event4678, nil, "127.0.0.1")
2366 if err != nil {
2367 t.Errorf("Unexpected error: %v", err)
2368 }
2369 // Should be allowed because privileged doesn't affect write operations
2370 // Falls back to default policy which is "allow"
2371 if !allowed {
2372 t.Error("Expected event to be allowed without authentication (privileged doesn't affect write)")
2373 }
2374 })
2375 }
2376