access_test.mx raw

   1  package access
   2  
   3  import (
   4  	"testing"
   5  
   6  	"git.smesh.lol/smesh/pkg/nostr/event"
   7  	"git.smesh.lol/smesh/pkg/nostr/kind"
   8  	"git.smesh.lol/smesh/pkg/nostr/tag"
   9  )
  10  
  11  // accEvent builds a bare event: CanSee only reads Kind, Pubkey and the tag
  12  // list, so no signature is needed.
  13  func accEvent(k uint16, pub []byte, tags *tag.S) (ev *event.E) {
  14  	return &event.E{Kind: k, Pubkey: pub, Tags: tags}
  15  }
  16  
  17  func accDash() (s *tag.S) {
  18  	return tag.NewS(tag.NewFromBytesSlice([]byte("-")))
  19  }
  20  
  21  // TestIsMLS pins exactly which kinds the relay treats as MLS: the three MLS
  22  // kinds plus the gift-wrap that can carry a Welcome. GiftWrapWithKind4 is a
  23  // separate kind and must not be swept in.
  24  func TestIsMLS(t *testing.T) {
  25  	// IsMLS compares against kind package pointers; they are nil until
  26  	// ensureKinds runs. Production callers reach here after Ensure.
  27  	kind.Ensure()
  28  	if !IsMLS(443) {
  29  		t.Fatal("MLSKeyPackage (443) must be MLS")
  30  	}
  31  	if !IsMLS(444) {
  32  		t.Fatal("MLSWelcome (444) must be MLS")
  33  	}
  34  	if !IsMLS(445) {
  35  		t.Fatal("MLSGroupEvent (445) must be MLS")
  36  	}
  37  	if !IsMLS(1059) {
  38  		t.Fatal("GiftWrap (1059) must be MLS")
  39  	}
  40  	if IsMLS(1060) {
  41  		t.Fatal("GiftWrapWithKind4 (1060) must not be MLS")
  42  	}
  43  	if IsMLS(1) {
  44  		t.Fatal("kind 1 must not be MLS")
  45  	}
  46  	if IsMLS(0) {
  47  		t.Fatal("kind 0 must not be MLS")
  48  	}
  49  }
  50  
  51  // TestCanSeePrivilege covers the privileged-kind gate with every combination of
  52  // auth and marmotOpen. Only MLS kinds are exempted by marmotOpen; a privileged
  53  // non-MLS kind still requires auth.
  54  func TestCanSeePrivilege(t *testing.T) {
  55  	// Force the kind table before any package pointer is read.
  56  	kind.Ensure()
  57  	pk := []byte("authed-pubkey-32-bytes-long-000")
  58  	evPlain := accEvent(1, pk, nil)
  59  	evPriv := accEvent(4, pk, nil)   // EncryptedDirectMessage
  60  	evMLS := accEvent(443, pk, nil)  // MLSKeyPackage
  61  	evMLS2 := accEvent(1059, pk, nil) // GiftWrap
  62  
  63  	if !CanSee(false, nil, evPlain, false, false) {
  64  		t.Fatal("non-privileged event must be visible unauthenticated")
  65  	}
  66  	if CanSee(false, nil, evPriv, false, false) {
  67  		t.Fatal("privileged event must be hidden from an unauthenticated reader")
  68  	}
  69  	if !CanSee(true, pk, evPriv, false, false) {
  70  		t.Fatal("privileged event must be visible to an authenticated reader")
  71  	}
  72  	if !CanSee(false, nil, evMLS, false, true) {
  73  		t.Fatal("MLS kind must pass with marmotOpen")
  74  	}
  75  	if CanSee(false, nil, evMLS, false, false) {
  76  		t.Fatal("MLS kind must be gated when marmotOpen is off")
  77  	}
  78  	if !CanSee(false, nil, evMLS2, false, true) {
  79  		t.Fatal("GiftWrap must pass with marmotOpen")
  80  	}
  81  	if CanSee(false, nil, evPriv, false, true) {
  82  		t.Fatal("marmotOpen must not exempt a non-MLS privileged kind")
  83  	}
  84  }
  85  
  86  // TestCanSeeNIP70 covers the "-" protected tag: the event reaches only its own
  87  // author, and only when authenticated. Every other combination is denied.
  88  func TestCanSeeNIP70(t *testing.T) {
  89  	kind.Ensure()
  90  	author := []byte("author-pubkey-32-bytes-long-0000")
  91  	other := []byte("other-pubkey-32-bytes-long-00000")
  92  	ev := accEvent(1, author, accDash())
  93  	evBare := accEvent(1, author, tag.NewS(tag.NewFromBytesSlice([]byte("t"), []byte("x"))))
  94  
  95  	if !CanSee(false, nil, ev, false, false) {
  96  		t.Fatal("nip70 off must not filter the protected tag")
  97  	}
  98  	if CanSee(false, nil, ev, true, false) {
  99  		t.Fatal("protected event must be hidden from an unauthenticated reader")
 100  	}
 101  	if !CanSee(true, author, ev, true, false) {
 102  		t.Fatal("protected event must reach its authenticated author")
 103  	}
 104  	if CanSee(true, other, ev, true, false) {
 105  		t.Fatal("protected event must not reach a different authenticated pubkey")
 106  	}
 107  	if CanSee(true, nil, ev, true, false) {
 108  		t.Fatal("protected event must not reach an auth with no pubkey")
 109  	}
 110  	if !CanSee(false, nil, evBare, true, false) {
 111  		t.Fatal("a tagless event must pass nip70 filtering")
 112  	}
 113  
 114  	// Tags present but no "-" tag: still delivered.
 115  	evNoDash := accEvent(1, author, tag.NewS(tag.NewFromBytesSlice([]byte("e"), []byte("x"))))
 116  	if !CanSee(false, nil, evNoDash, true, false) {
 117  		t.Fatal("an event without the '-' tag must pass nip70 filtering")
 118  	}
 119  	if !CanSee(false, nil, accEvent(1, author, nil), true, false) {
 120  		t.Fatal("nil tags must pass nip70 filtering")
 121  	}
 122  }
 123  
 124  // TestWriteExempt pins the write-auth exemptions: NIP-46 connect only when the
 125  // bypass flag is set, and MLS kinds only when marmotOpen.
 126  func TestWriteExempt(t *testing.T) {
 127  	kind.Ensure()
 128  	if !WriteExempt(24133, true, false) {
 129  		t.Fatal("NostrConnect must be exempt when nip46BypassAuth is on")
 130  	}
 131  	if WriteExempt(24133, false, false) {
 132  		t.Fatal("NostrConnect must not be exempt when the bypass is off")
 133  	}
 134  	if WriteExempt(1, true, false) {
 135  		t.Fatal("kind 1 must not be exempt via the NIP-46 bypass")
 136  	}
 137  	if !WriteExempt(443, false, true) {
 138  		t.Fatal("MLSKeyPackage must be exempt when marmotOpen")
 139  	}
 140  	if !WriteExempt(444, false, true) {
 141  		t.Fatal("MLSWelcome must be exempt when marmotOpen")
 142  	}
 143  	if !WriteExempt(445, false, true) {
 144  		t.Fatal("MLSGroupEvent must be exempt when marmotOpen")
 145  	}
 146  	if !WriteExempt(1059, false, true) {
 147  		t.Fatal("GiftWrap must be exempt when marmotOpen")
 148  	}
 149  	if WriteExempt(443, false, false) {
 150  		t.Fatal("MLS must not be exempt when marmotOpen is off")
 151  	}
 152  	if WriteExempt(1060, false, true) {
 153  		t.Fatal("GiftWrapWithKind4 must not be exempt")
 154  	}
 155  	if WriteExempt(1, true, true) {
 156  		t.Fatal("kind 1 must never be write-exempt")
 157  	}
 158  }
 159