package policy import ( "encoding/json" "testing" "git.mleku.dev/mleku/nostr/encoders/event" "git.mleku.dev/mleku/nostr/encoders/hex" ) // TestPrivilegedOnlyBug tests the reported bug where privileged flag // doesn't work when read_allow is missing func TestPrivilegedOnlyBug(t *testing.T) { aliceSigner, alicePubkey := generateTestKeypair(t) _, bobPubkey := generateTestKeypair(t) _, charliePubkey := generateTestKeypair(t) // Create policy with ONLY privileged, no read_allow policyJSON := map[string]interface{}{ "rules": map[string]interface{}{ "4": map[string]interface{}{ "description": "DM - privileged only", "privileged": true, }, }, } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } t.Logf("Policy JSON: %s", policyBytes) policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Verify the rule was loaded correctly rule, hasRule := policy.rules[4] if !hasRule { t.Fatal("Rule for kind 4 was not loaded") } t.Logf("Loaded rule: %+v", rule) t.Logf("Privileged flag: %v", rule.Privileged) t.Logf("ReadAllow: %v", rule.ReadAllow) t.Logf("readAllowBin: %v", rule.readAllowBin) if !rule.Privileged { t.Fatal("BUG: Privileged flag was not set to true!") } // Create a kind 4 (DM) event from Alice to Bob ev := createTestEvent(t, aliceSigner, "Secret message from Alice to Bob", 4) addPTag(ev, bobPubkey) // Bob is recipient t.Logf("Event author: %s", hex.Enc(ev.Pubkey)) t.Logf("Event p-tags: %v", ev.Tags.GetAll([]byte("p"))) // Test 1: Alice (author) should be able to read t.Run("alice_author_can_read", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", ev, alicePubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("BUG! Author should be able to read their own privileged event") } }) // Test 2: Bob (in p-tag) should be able to read t.Run("bob_recipient_can_read", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", ev, bobPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("BUG! Recipient (in p-tag) should be able to read privileged event") } }) // Test 3: Charlie (third party) should NOT be able to read t.Run("charlie_third_party_denied", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", ev, charliePubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("BUG! Third party should NOT be able to read privileged event") } }) // Test 4: Unauthenticated user should NOT be able to read t.Run("unauthenticated_denied", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", ev, nil, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("BUG! Unauthenticated user should NOT be able to read privileged event") } }) } // TestPrivilegedWithReadAllowBug tests the scenario where read_allow is present // and checks if the OR logic works correctly func TestPrivilegedWithReadAllowBug(t *testing.T) { aliceSigner, alicePubkey := generateTestKeypair(t) _, bobPubkey := generateTestKeypair(t) _, charliePubkey := generateTestKeypair(t) _, davePubkey := generateTestKeypair(t) // Create policy with privileged AND read_allow // Expected: OR logic - user can read if in read_allow OR party to event policyJSON := map[string]interface{}{ "rules": map[string]interface{}{ "4": map[string]interface{}{ "description": "DM - privileged with read_allow", "privileged": true, "read_allow": []string{hex.Enc(davePubkey)}, // Dave is in read_allow }, }, } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } t.Logf("Policy JSON: %s", policyBytes) policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Create a kind 4 (DM) event from Alice to Bob (Dave is NOT in p-tag) ev := createTestEvent(t, aliceSigner, "Secret message from Alice to Bob", 4) addPTag(ev, bobPubkey) // Bob is recipient, Dave is NOT in p-tag t.Logf("Event author: %s", hex.Enc(ev.Pubkey)) t.Logf("Event p-tags: %v", ev.Tags.GetAll([]byte("p"))) t.Logf("Dave (in read_allow, NOT in p-tag): %s", hex.Enc(davePubkey)) // Test 1: Alice (author, party) should be able to read via privileged t.Run("alice_party_can_read", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", ev, alicePubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("BUG! Author should be able to read (privileged)") } }) // Test 2: Bob (in p-tag, party) should be able to read via privileged t.Run("bob_party_can_read", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", ev, bobPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("BUG! Recipient (in p-tag) should be able to read (privileged)") } }) // Test 3: Dave (in read_allow, NOT party) should be able to read via OR logic t.Run("dave_read_allow_can_read", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", ev, davePubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("BUG! User in read_allow should be able to read (OR logic)") } }) // Test 4: Charlie (NOT in read_allow, NOT party) should NOT be able to read t.Run("charlie_denied", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", ev, charliePubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("BUG! Third party should NOT be able to read") } }) } // TestNoReadAllowNoPrivileged tests what happens when a rule has neither // read_allow nor privileged - should default to allow func TestNoReadAllowNoPrivileged(t *testing.T) { aliceSigner, _ := generateTestKeypair(t) _, charliePubkey := generateTestKeypair(t) // Create policy with a rule that has no read_allow and no privileged policyJSON := map[string]interface{}{ "default_policy": "allow", "rules": map[string]interface{}{ "1": map[string]interface{}{ "description": "Regular text note - no restrictions", }, }, } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } t.Logf("Policy JSON: %s", policyBytes) policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Check that privileged is false for this rule rule := policy.rules[1] t.Logf("Privileged: %v, ReadAllow: %v", rule.Privileged, rule.ReadAllow) // Create a kind 1 event ev := createTestEvent(t, aliceSigner, "Hello world", 1) // Test: Third party should be able to read (no restrictions) t.Run("charlie_can_read_unrestricted", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", ev, charliePubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("Third party should be able to read unrestricted events") } }) // Test: Unauthenticated should also be able to read t.Run("unauthenticated_can_read_unrestricted", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", ev, nil, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("Unauthenticated user should be able to read unrestricted events") } }) } // TestPrivilegedWithBinaryEncodedPTags tests that privileged access works correctly // when p-tags are stored in binary-optimized format (as happens after JSON unmarshaling). // This is the real-world scenario where events come from network JSON. func TestPrivilegedWithBinaryEncodedPTags(t *testing.T) { _, alicePubkey := generateTestKeypair(t) _, bobPubkey := generateTestKeypair(t) _, charliePubkey := generateTestKeypair(t) // Create policy with privileged flag policyJSON := map[string]interface{}{ "rules": map[string]interface{}{ "4": map[string]interface{}{ "description": "DM - privileged only", "privileged": true, }, }, } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Create event JSON with p-tag (simulating real network event) // When this JSON is unmarshaled, the p-tag value will be converted to binary format eventJSON := `{ "id": "0000000000000000000000000000000000000000000000000000000000000001", "pubkey": "` + hex.Enc(alicePubkey) + `", "created_at": 1234567890, "kind": 4, "tags": [["p", "` + hex.Enc(bobPubkey) + `"]], "content": "Secret message", "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }` var ev event.E if err := json.Unmarshal([]byte(eventJSON), &ev); err != nil { t.Fatalf("Failed to unmarshal event: %v", err) } // Verify the p-tag is stored in binary format pTags := ev.Tags.GetAll([]byte("p")) if len(pTags) == 0 { t.Fatal("Event should have p-tag") } pTag := pTags[0] binValue := pTag.ValueBinary() t.Logf("P-tag Value() length: %d", len(pTag.Value())) t.Logf("P-tag ValueBinary(): %v (len=%d)", binValue != nil, len(binValue)) if binValue == nil { t.Log("Warning: P-tag is NOT in binary format (test may not exercise the binary code path)") } else { t.Log("P-tag IS in binary format - testing binary-encoded path") } // Test: Bob (in p-tag) should be able to read even with binary-encoded tag t.Run("bob_binary_ptag_can_read", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", &ev, bobPubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("BUG! Recipient (in binary-encoded p-tag) should be able to read privileged event") } }) // Test: Alice (author) should be able to read t.Run("alice_author_can_read", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", &ev, alicePubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if !allowed { t.Error("Author should be able to read their own privileged event") } }) // Test: Charlie (third party) should NOT be able to read t.Run("charlie_denied", func(t *testing.T) { allowed, err := policy.CheckPolicy("read", &ev, charliePubkey, "127.0.0.1") if err != nil { t.Fatalf("Unexpected error: %v", err) } if allowed { t.Error("Third party should NOT be able to read privileged event") } }) }