1 package policy
2 3 import (
4 "encoding/json"
5 "testing"
6 7 "next.orly.dev/pkg/nostr/encoders/event"
8 "next.orly.dev/pkg/nostr/encoders/hex"
9 )
10 11 // TestPrivilegedOnlyBug tests the reported bug where privileged flag
12 // doesn't work when read_allow is missing
13 func TestPrivilegedOnlyBug(t *testing.T) {
14 aliceSigner, alicePubkey := generateTestKeypair(t)
15 _, bobPubkey := generateTestKeypair(t)
16 _, charliePubkey := generateTestKeypair(t)
17 18 // Create policy with ONLY privileged, no read_allow
19 policyJSON := map[string]interface{}{
20 "rules": map[string]interface{}{
21 "4": map[string]interface{}{
22 "description": "DM - privileged only",
23 "privileged": true,
24 },
25 },
26 }
27 28 policyBytes, err := json.Marshal(policyJSON)
29 if err != nil {
30 t.Fatalf("Failed to marshal policy: %v", err)
31 }
32 t.Logf("Policy JSON: %s", policyBytes)
33 34 policy, err := New(policyBytes)
35 if err != nil {
36 t.Fatalf("Failed to create policy: %v", err)
37 }
38 39 // Verify the rule was loaded correctly
40 rule, hasRule := policy.rules[4]
41 if !hasRule {
42 t.Fatal("Rule for kind 4 was not loaded")
43 }
44 t.Logf("Loaded rule: %+v", rule)
45 t.Logf("Privileged flag: %v", rule.Privileged)
46 t.Logf("ReadAllow: %v", rule.ReadAllow)
47 t.Logf("readAllowBin: %v", rule.readAllowBin)
48 49 if !rule.Privileged {
50 t.Fatal("BUG: Privileged flag was not set to true!")
51 }
52 53 // Create a kind 4 (DM) event from Alice to Bob
54 ev := createTestEvent(t, aliceSigner, "Secret message from Alice to Bob", 4)
55 addPTag(ev, bobPubkey) // Bob is recipient
56 57 t.Logf("Event author: %s", hex.Enc(ev.Pubkey))
58 t.Logf("Event p-tags: %v", ev.Tags.GetAll([]byte("p")))
59 60 // Test 1: Alice (author) should be able to read
61 t.Run("alice_author_can_read", func(t *testing.T) {
62 allowed, err := policy.CheckPolicy("read", ev, alicePubkey, "127.0.0.1")
63 if err != nil {
64 t.Fatalf("Unexpected error: %v", err)
65 }
66 if !allowed {
67 t.Error("BUG! Author should be able to read their own privileged event")
68 }
69 })
70 71 // Test 2: Bob (in p-tag) should be able to read
72 t.Run("bob_recipient_can_read", func(t *testing.T) {
73 allowed, err := policy.CheckPolicy("read", ev, bobPubkey, "127.0.0.1")
74 if err != nil {
75 t.Fatalf("Unexpected error: %v", err)
76 }
77 if !allowed {
78 t.Error("BUG! Recipient (in p-tag) should be able to read privileged event")
79 }
80 })
81 82 // Test 3: Charlie (third party) should NOT be able to read
83 t.Run("charlie_third_party_denied", func(t *testing.T) {
84 allowed, err := policy.CheckPolicy("read", ev, charliePubkey, "127.0.0.1")
85 if err != nil {
86 t.Fatalf("Unexpected error: %v", err)
87 }
88 if allowed {
89 t.Error("BUG! Third party should NOT be able to read privileged event")
90 }
91 })
92 93 // Test 4: Unauthenticated user should NOT be able to read
94 t.Run("unauthenticated_denied", func(t *testing.T) {
95 allowed, err := policy.CheckPolicy("read", ev, nil, "127.0.0.1")
96 if err != nil {
97 t.Fatalf("Unexpected error: %v", err)
98 }
99 if allowed {
100 t.Error("BUG! Unauthenticated user should NOT be able to read privileged event")
101 }
102 })
103 }
104 105 // TestPrivilegedWithReadAllowBug tests the scenario where read_allow is present
106 // and checks if the OR logic works correctly
107 func TestPrivilegedWithReadAllowBug(t *testing.T) {
108 aliceSigner, alicePubkey := generateTestKeypair(t)
109 _, bobPubkey := generateTestKeypair(t)
110 _, charliePubkey := generateTestKeypair(t)
111 _, davePubkey := generateTestKeypair(t)
112 113 // Create policy with privileged AND read_allow
114 // Expected: OR logic - user can read if in read_allow OR party to event
115 policyJSON := map[string]interface{}{
116 "rules": map[string]interface{}{
117 "4": map[string]interface{}{
118 "description": "DM - privileged with read_allow",
119 "privileged": true,
120 "read_allow": []string{hex.Enc(davePubkey)}, // Dave is in read_allow
121 },
122 },
123 }
124 125 policyBytes, err := json.Marshal(policyJSON)
126 if err != nil {
127 t.Fatalf("Failed to marshal policy: %v", err)
128 }
129 t.Logf("Policy JSON: %s", policyBytes)
130 131 policy, err := New(policyBytes)
132 if err != nil {
133 t.Fatalf("Failed to create policy: %v", err)
134 }
135 136 // Create a kind 4 (DM) event from Alice to Bob (Dave is NOT in p-tag)
137 ev := createTestEvent(t, aliceSigner, "Secret message from Alice to Bob", 4)
138 addPTag(ev, bobPubkey) // Bob is recipient, Dave is NOT in p-tag
139 140 t.Logf("Event author: %s", hex.Enc(ev.Pubkey))
141 t.Logf("Event p-tags: %v", ev.Tags.GetAll([]byte("p")))
142 t.Logf("Dave (in read_allow, NOT in p-tag): %s", hex.Enc(davePubkey))
143 144 // Test 1: Alice (author, party) should be able to read via privileged
145 t.Run("alice_party_can_read", func(t *testing.T) {
146 allowed, err := policy.CheckPolicy("read", ev, alicePubkey, "127.0.0.1")
147 if err != nil {
148 t.Fatalf("Unexpected error: %v", err)
149 }
150 if !allowed {
151 t.Error("BUG! Author should be able to read (privileged)")
152 }
153 })
154 155 // Test 2: Bob (in p-tag, party) should be able to read via privileged
156 t.Run("bob_party_can_read", func(t *testing.T) {
157 allowed, err := policy.CheckPolicy("read", ev, bobPubkey, "127.0.0.1")
158 if err != nil {
159 t.Fatalf("Unexpected error: %v", err)
160 }
161 if !allowed {
162 t.Error("BUG! Recipient (in p-tag) should be able to read (privileged)")
163 }
164 })
165 166 // Test 3: Dave (in read_allow, NOT party) should be able to read via OR logic
167 t.Run("dave_read_allow_can_read", func(t *testing.T) {
168 allowed, err := policy.CheckPolicy("read", ev, davePubkey, "127.0.0.1")
169 if err != nil {
170 t.Fatalf("Unexpected error: %v", err)
171 }
172 if !allowed {
173 t.Error("BUG! User in read_allow should be able to read (OR logic)")
174 }
175 })
176 177 // Test 4: Charlie (NOT in read_allow, NOT party) should NOT be able to read
178 t.Run("charlie_denied", func(t *testing.T) {
179 allowed, err := policy.CheckPolicy("read", ev, charliePubkey, "127.0.0.1")
180 if err != nil {
181 t.Fatalf("Unexpected error: %v", err)
182 }
183 if allowed {
184 t.Error("BUG! Third party should NOT be able to read")
185 }
186 })
187 }
188 189 // TestNoReadAllowNoPrivileged tests what happens when a rule has neither
190 // read_allow nor privileged - should default to allow
191 func TestNoReadAllowNoPrivileged(t *testing.T) {
192 aliceSigner, _ := generateTestKeypair(t)
193 _, charliePubkey := generateTestKeypair(t)
194 195 // Create policy with a rule that has no read_allow and no privileged
196 policyJSON := map[string]interface{}{
197 "default_policy": "allow",
198 "rules": map[string]interface{}{
199 "1": map[string]interface{}{
200 "description": "Regular text note - no restrictions",
201 },
202 },
203 }
204 205 policyBytes, err := json.Marshal(policyJSON)
206 if err != nil {
207 t.Fatalf("Failed to marshal policy: %v", err)
208 }
209 t.Logf("Policy JSON: %s", policyBytes)
210 211 policy, err := New(policyBytes)
212 if err != nil {
213 t.Fatalf("Failed to create policy: %v", err)
214 }
215 216 // Check that privileged is false for this rule
217 rule := policy.rules[1]
218 t.Logf("Privileged: %v, ReadAllow: %v", rule.Privileged, rule.ReadAllow)
219 220 // Create a kind 1 event
221 ev := createTestEvent(t, aliceSigner, "Hello world", 1)
222 223 // Test: Third party should be able to read (no restrictions)
224 t.Run("charlie_can_read_unrestricted", func(t *testing.T) {
225 allowed, err := policy.CheckPolicy("read", ev, charliePubkey, "127.0.0.1")
226 if err != nil {
227 t.Fatalf("Unexpected error: %v", err)
228 }
229 if !allowed {
230 t.Error("Third party should be able to read unrestricted events")
231 }
232 })
233 234 // Test: Unauthenticated should also be able to read
235 t.Run("unauthenticated_can_read_unrestricted", func(t *testing.T) {
236 allowed, err := policy.CheckPolicy("read", ev, nil, "127.0.0.1")
237 if err != nil {
238 t.Fatalf("Unexpected error: %v", err)
239 }
240 if !allowed {
241 t.Error("Unauthenticated user should be able to read unrestricted events")
242 }
243 })
244 }
245 246 // TestPrivilegedWithBinaryEncodedPTags tests that privileged access works correctly
247 // when p-tags are stored in binary-optimized format (as happens after JSON unmarshaling).
248 // This is the real-world scenario where events come from network JSON.
249 func TestPrivilegedWithBinaryEncodedPTags(t *testing.T) {
250 _, alicePubkey := generateTestKeypair(t)
251 _, bobPubkey := generateTestKeypair(t)
252 _, charliePubkey := generateTestKeypair(t)
253 254 // Create policy with privileged flag
255 policyJSON := map[string]interface{}{
256 "rules": map[string]interface{}{
257 "4": map[string]interface{}{
258 "description": "DM - privileged only",
259 "privileged": true,
260 },
261 },
262 }
263 264 policyBytes, err := json.Marshal(policyJSON)
265 if err != nil {
266 t.Fatalf("Failed to marshal policy: %v", err)
267 }
268 269 policy, err := New(policyBytes)
270 if err != nil {
271 t.Fatalf("Failed to create policy: %v", err)
272 }
273 274 // Create event JSON with p-tag (simulating real network event)
275 // When this JSON is unmarshaled, the p-tag value will be converted to binary format
276 eventJSON := `{
277 "id": "0000000000000000000000000000000000000000000000000000000000000001",
278 "pubkey": "` + hex.Enc(alicePubkey) + `",
279 "created_at": 1234567890,
280 "kind": 4,
281 "tags": [["p", "` + hex.Enc(bobPubkey) + `"]],
282 "content": "Secret message",
283 "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
284 }`
285 286 var ev event.E
287 if err := json.Unmarshal([]byte(eventJSON), &ev); err != nil {
288 t.Fatalf("Failed to unmarshal event: %v", err)
289 }
290 291 // Verify the p-tag is stored in binary format
292 pTags := ev.Tags.GetAll([]byte("p"))
293 if len(pTags) == 0 {
294 t.Fatal("Event should have p-tag")
295 }
296 pTag := pTags[0]
297 binValue := pTag.ValueBinary()
298 t.Logf("P-tag Value() length: %d", len(pTag.Value()))
299 t.Logf("P-tag ValueBinary(): %v (len=%d)", binValue != nil, len(binValue))
300 if binValue == nil {
301 t.Log("Warning: P-tag is NOT in binary format (test may not exercise the binary code path)")
302 } else {
303 t.Log("P-tag IS in binary format - testing binary-encoded path")
304 }
305 306 // Test: Bob (in p-tag) should be able to read even with binary-encoded tag
307 t.Run("bob_binary_ptag_can_read", func(t *testing.T) {
308 allowed, err := policy.CheckPolicy("read", &ev, bobPubkey, "127.0.0.1")
309 if err != nil {
310 t.Fatalf("Unexpected error: %v", err)
311 }
312 if !allowed {
313 t.Error("BUG! Recipient (in binary-encoded p-tag) should be able to read privileged event")
314 }
315 })
316 317 // Test: Alice (author) should be able to read
318 t.Run("alice_author_can_read", func(t *testing.T) {
319 allowed, err := policy.CheckPolicy("read", &ev, alicePubkey, "127.0.0.1")
320 if err != nil {
321 t.Fatalf("Unexpected error: %v", err)
322 }
323 if !allowed {
324 t.Error("Author should be able to read their own privileged event")
325 }
326 })
327 328 // Test: Charlie (third party) should NOT be able to read
329 t.Run("charlie_denied", func(t *testing.T) {
330 allowed, err := policy.CheckPolicy("read", &ev, charliePubkey, "127.0.0.1")
331 if err != nil {
332 t.Fatalf("Unexpected error: %v", err)
333 }
334 if allowed {
335 t.Error("Third party should NOT be able to read privileged event")
336 }
337 })
338 }
339