privileged_only_test.go raw

   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