code wiki / _hdl_build / nx_rebac_gate.nx
nx_rebac_gate.nx source
↩ module page · 122 lines · 11119 B
1// nx_rebac_gate.nx -- ADVERSARIAL GATE for the sovereign ReBAC plane (nx_rebac). The CRITIC + ADVERSARY: a
2// legit-access/attack matrix where every escalation, revocation-bypass, cross-scope leak, block-bypass, and
3// userset-cycle MUST resolve correctly (deny-by-default). Green ONLY if all ~28 checks pass. license_tier: ORIGINAL
4// expect_exit: 0
5import "nx_rebac.nx"
6
7func ck(label: *u8, got: i64, want: i64, okp: *i64) -> i64 {
8 rb_p(" REBAC " as *u8); rb_p(label)
9 if got == 1 { rb_p(" -> ALLOW" as *u8) } else { rb_p(" -> DENY " as *u8) }
10 if got == want { rb_p(" ok\n" as *u8); return 0 }
11 rb_p(" *** WRONG (wanted " as *u8); rb_pn(want); rb_p(") ***\n" as *u8)
12 okp[0] = 0
13 return 0
14}
15
16func main() -> i64 {
17 rb_p("=== NX-REBAC ADVERSARIAL GATE (Zanzibar/ReBAC: tuple check + userset rewrite + tombstone + block + HR override) ===\n" as *u8)
18 let okp: *i64 = sys_mmap(8) as *i64; okp[0] = 1
19
20 // fresh store prefix (own the isolation; sys_now_us keeps commits time-ordered for latest-wins)
21 let prefix: *u8 = sys_mmap(128); var po: i64 = 0
22 po = rb_cat(prefix, po, "/tmp/rebac_" as *u8); po = rb_catn(prefix, po, sys_now_us()); po = rb_cat(prefix, po, "_" as *u8); prefix[po] = 0 as u8
23
24 // ---- SETUP (grants) ----
25 // group:elders owned by alice, bob is a member; doc:budget owned by alice, SHARED with elders#member (userset);
26 // tenant:andelinwest admin=alice member=bob; alice<->bob mutual friends.
27 rb_put(prefix, "group:elders" as *u8, "owner" as *u8, "user:alice" as *u8, "system" as *u8, 1)
28 rb_put(prefix, "group:elders" as *u8, "member" as *u8, "user:bob" as *u8, "user:alice" as *u8, 1)
29 rb_put(prefix, "doc:budget" as *u8, "owner" as *u8, "user:alice" as *u8, "system" as *u8, 1)
30 rb_put(prefix, "doc:budget" as *u8, "viewer" as *u8, "group:elders#member" as *u8, "user:alice" as *u8, 1)
31 rb_put(prefix, "tenant:andelinwest" as *u8, "admin" as *u8, "user:alice" as *u8, "system" as *u8, 1)
32 rb_put(prefix, "tenant:andelinwest" as *u8, "member" as *u8, "user:bob" as *u8, "user:alice" as *u8, 1)
33 rb_put(prefix, "user:alice" as *u8, "friend" as *u8, "user:bob" as *u8, "user:bob" as *u8, 1)
34 rb_put(prefix, "user:bob" as *u8, "friend" as *u8, "user:alice" as *u8, "user:alice" as *u8, 1)
35
36 // ---- ALLOW (legitimate access) ----
37 ck("A1 alice manage doc:budget (owner)" as *u8, rb_check(prefix, "user:alice" as *u8, "manage" as *u8, "doc:budget" as *u8, 0), 1, okp)
38 ck("A2 bob read doc:budget (member->elders#member->viewer USERSET)" as *u8, rb_check(prefix, "user:bob" as *u8, "read" as *u8, "doc:budget" as *u8, 0), 1, okp)
39 ck("A3 alice read doc:budget (owner->read rewrite)" as *u8, rb_check(prefix, "user:alice" as *u8, "read" as *u8, "doc:budget" as *u8, 0), 1, okp)
40 ck("A4 bob view group:elders (member)" as *u8, rb_check(prefix, "user:bob" as *u8, "view" as *u8, "group:elders" as *u8, 0), 1, okp)
41 ck("A5 alice manage group:elders (owner)" as *u8, rb_check(prefix, "user:alice" as *u8, "manage" as *u8, "group:elders" as *u8, 0), 1, okp)
42 ck("A6 bob read tenant:andelinwest (member)" as *u8, rb_check(prefix, "user:bob" as *u8, "read" as *u8, "tenant:andelinwest" as *u8, 0), 1, okp)
43 ck("A7 alice friend bob (mutual)" as *u8, rb_check(prefix, "user:alice" as *u8, "friend" as *u8, "user:bob" as *u8, 0), 1, okp)
44
45 // ENUMERATORS (the surface lists a caller's circle from these): alice's friends include bob; bob's groups include elders
46 let el: *i64 = sys_mmap(8 * 64) as *i64
47 let enf: i64 = rb_list_subjects(prefix, "user:alice" as *u8, "friend" as *u8, el, 64)
48 var friend_listed: i64 = 0
49 if enf >= 1 { if rb_in_list(el, enf, "user:bob" as *u8) == 1 { friend_listed = 1 } }
50 ck("E1 rb_list_subjects: alice friends include bob" as *u8, friend_listed, 1, okp)
51 let eg: *i64 = sys_mmap(8 * 64) as *i64
52 let eng: i64 = rb_list_objects_for_sub(prefix, "user:bob" as *u8, "member" as *u8, eg, 64)
53 var group_listed: i64 = 0
54 if eng >= 1 { if rb_in_list(eg, eng, "group:elders" as *u8) == 1 { group_listed = 1 } }
55 ck("E2 rb_list_objects_for_sub: bob groups include elders" as *u8, group_listed, 1, okp)
56
57 // ---- DENY (adversary: no grant / wrong relation / escalation) ----
58 ck("D1 carol read doc:budget (not a member)" as *u8, rb_check(prefix, "user:carol" as *u8, "read" as *u8, "doc:budget" as *u8, 0), 0, okp)
59 ck("D2 bob WRITE doc:budget (only viewer, not editor)" as *u8, rb_check(prefix, "user:bob" as *u8, "write" as *u8, "doc:budget" as *u8, 0), 0, okp)
60 ck("D3 mallory read tenant:andelinwest (no grant)" as *u8, rb_check(prefix, "user:mallory" as *u8, "read" as *u8, "tenant:andelinwest" as *u8, 0), 0, okp)
61 ck("D4 bob MANAGE group:elders (member != admin ESCALATION)" as *u8, rb_check(prefix, "user:bob" as *u8, "manage" as *u8, "group:elders" as *u8, 0), 0, okp)
62 ck("D5 carol friend alice (not friends)" as *u8, rb_check(prefix, "user:carol" as *u8, "friend" as *u8, "user:alice" as *u8, 0), 0, okp)
63
64 // ---- REVOCATION (tombstone must WIN; scoped, not global) ----
65 // T1 defriend: tombstone both directions -> no longer friends
66 rb_put(prefix, "user:alice" as *u8, "friend" as *u8, "user:bob" as *u8, "user:alice" as *u8, 0)
67 rb_put(prefix, "user:bob" as *u8, "friend" as *u8, "user:alice" as *u8, "user:bob" as *u8, 0)
68 ck("T1 defriended: alice friend bob (tombstone wins)" as *u8, rb_check(prefix, "user:alice" as *u8, "friend" as *u8, "user:bob" as *u8, 0), 0, okp)
69 // T2 leave group: bob leaves elders -> loses doc VIA the group, but KEEPS the independent tenant membership
70 rb_put(prefix, "group:elders" as *u8, "member" as *u8, "user:bob" as *u8, "user:bob" as *u8, 0)
71 ck("T2a left-group: bob read doc:budget (userset re-eval DENIES)" as *u8, rb_check(prefix, "user:bob" as *u8, "read" as *u8, "doc:budget" as *u8, 0), 0, okp)
72 ck("T2b scoped: bob read tenant:andelinwest STILL allowed" as *u8, rb_check(prefix, "user:bob" as *u8, "read" as *u8, "tenant:andelinwest" as *u8, 0), 1, okp)
73 // T3 re-grant then unshare: re-join proves re-grant works; unshare the doc-userset kills it again
74 rb_put(prefix, "group:elders" as *u8, "member" as *u8, "user:bob" as *u8, "user:alice" as *u8, 1)
75 ck("T3a re-joined: bob read doc:budget allowed again (re-grant)" as *u8, rb_check(prefix, "user:bob" as *u8, "read" as *u8, "doc:budget" as *u8, 0), 1, okp)
76 rb_put(prefix, "doc:budget" as *u8, "viewer" as *u8, "group:elders#member" as *u8, "user:alice" as *u8, 0)
77 ck("T3b unshared: bob read doc:budget DENIED (userset tombstone)" as *u8, rb_check(prefix, "user:bob" as *u8, "read" as *u8, "doc:budget" as *u8, 0), 0, okp)
78
79 // ---- BLOCK (consent axis: blocks OUTRANK any relation) ----
80 rb_put(prefix, "user:bob" as *u8, "blocked" as *u8, "user:mallory" as *u8, "user:bob" as *u8, 1)
81 ck("B1 mallory friend bob after BLOCK (blocks outrank)" as *u8, rb_check(prefix, "user:mallory" as *u8, "friend" as *u8, "user:bob" as *u8, 0), 0, okp)
82 ck("B2 mallory view bob after BLOCK" as *u8, rb_check(prefix, "user:mallory" as *u8, "view" as *u8, "user:bob" as *u8, 0), 0, okp)
83
84 // ---- HR OPERATOR OVERRIDE (the tie to Nishi HR level) ----
85 ck("H1 mallory read tenant via HR operator lvl 3 (god-in-realm)" as *u8, rb_check_hr(prefix, "user:mallory" as *u8, "read" as *u8, "tenant:andelinwest" as *u8, 3), 1, okp)
86 ck("H2 mallory read tenant via HR member lvl 1 (no tuple -> deny)" as *u8, rb_check_hr(prefix, "user:mallory" as *u8, "read" as *u8, "tenant:andelinwest" as *u8, 1), 0, okp)
87
88 // ---- WRITE AUTHORITY (a member cannot grant; unclaimed object bootstraps; then closes) ----
89 ck("W1 bob may_grant doc:budget (viewer, not manage -> NO)" as *u8, rb_may_grant(prefix, "user:bob" as *u8, "doc:budget" as *u8), 0, okp)
90 ck("W2 alice may_grant doc:budget (owner -> YES)" as *u8, rb_may_grant(prefix, "user:alice" as *u8, "doc:budget" as *u8), 1, okp)
91 ck("W3a carol may_grant group:new (UNCLAIMED -> bootstrap YES)" as *u8, rb_may_grant(prefix, "user:carol" as *u8, "group:new" as *u8), 1, okp)
92 rb_put(prefix, "group:new" as *u8, "owner" as *u8, "user:carol" as *u8, "user:carol" as *u8, 1)
93 ck("W3b mallory may_grant group:new (now CLAIMED by carol -> NO)" as *u8, rb_may_grant(prefix, "user:mallory" as *u8, "group:new" as *u8), 0, okp)
94
95 // ---- NESTED USERSET (positive, multi-level): leads subgroup nested in elders, doc shared with elders ----
96 rb_put(prefix, "group:leads" as *u8, "member" as *u8, "user:carol" as *u8, "user:carol" as *u8, 1)
97 rb_put(prefix, "group:elders" as *u8, "member" as *u8, "group:leads#member" as *u8, "user:alice" as *u8, 1)
98 rb_put(prefix, "doc:plan" as *u8, "owner" as *u8, "user:alice" as *u8, "system" as *u8, 1)
99 rb_put(prefix, "doc:plan" as *u8, "viewer" as *u8, "group:elders#member" as *u8, "user:alice" as *u8, 1)
100 ck("U1 carol read doc:plan (carol->leads->elders->doc NESTED userset)" as *u8, rb_check(prefix, "user:carol" as *u8, "read" as *u8, "doc:plan" as *u8, 0), 1, okp)
101
102 // ---- CYCLE TERMINATION (adversary DoS: mutual group membership must NOT hang) ----
103 rb_put(prefix, "group:ca" as *u8, "member" as *u8, "group:cb#member" as *u8, "user:alice" as *u8, 1)
104 rb_put(prefix, "group:cb" as *u8, "member" as *u8, "group:ca#member" as *u8, "user:alice" as *u8, 1)
105 ck("C1 cyclic groups TERMINATE (mallory read group:ca -> bounded DENY, no hang)" as *u8, rb_check(prefix, "user:mallory" as *u8, "read" as *u8, "group:ca" as *u8, 0), 0, okp)
106
107 // ---- CROSS-SURFACE (rb_check2, two stores): a GROUP in store2 grants an OBJECT owned in store1 ----
108 let p2: *u8 = sys_mmap(128); var p2o: i64 = 0
109 p2o = rb_cat(p2, p2o, "/tmp/rebac2_" as *u8); p2o = rb_catn(p2, p2o, sys_now_us()); p2o = rb_cat(p2, p2o, "_" as *u8); p2[p2o] = 0 as u8
110 rb_put(p2, "group:choir" as *u8, "member" as *u8, "user:zoe" as *u8, "user:lead" as *u8, 1) // store2 = the "relate" surface
111 rb_put(prefix, "doc:hymnal" as *u8, "owner" as *u8, "user:lead" as *u8, "system" as *u8, 1) // store1 = the "office" surface
112 rb_put(prefix, "doc:hymnal" as *u8, "viewer" as *u8, "group:choir#member" as *u8, "user:lead" as *u8, 1)
113 ck("X1 CROSS-SURFACE: zoe (store2 group member) reads doc in store1" as *u8, rb_check2(prefix, p2, "user:zoe" as *u8, "read" as *u8, "doc:hymnal" as *u8, 0), 1, okp)
114 ck("X2 non-member frank DENIED cross-surface" as *u8, rb_check2(prefix, p2, "user:frank" as *u8, "read" as *u8, "doc:hymnal" as *u8, 0), 0, okp)
115 rb_put(p2, "group:choir" as *u8, "member" as *u8, "user:zoe" as *u8, "user:lead" as *u8, 0) // revoke in store2
116 ck("X3 revoke store2 group membership -> zoe LOSES the store1 doc" as *u8, rb_check2(prefix, p2, "user:zoe" as *u8, "read" as *u8, "doc:hymnal" as *u8, 0), 0, okp)
117
118 rb_p("NX-REBAC-GATE " as *u8)
119 if okp[0] == 1 { rb_p("verdict=GREEN (28 checks: userset rewrites + nested groups + tombstone revocation + scoped-not-global + blocks-outrank + HR operator override + write-authority-no-escalation + cycle-termination)\n" as *u8); return 0 }
120 rb_p("verdict=RED\n" as *u8)
121 return 1
122}