code wiki / _hdl_build / nx_cms_roles_exceed_gate.nx
nx_cms_roles_exceed_gate.nx source
↩ module page · 127 lines · 7211 B
1// nx_cms_roles_exceed_gate.nx -- W-RE-004 head-to-head EXCEED (roles-capabilities, TENANT-ISOLATION axis).
2// Runs BOTH authorizers on the SAME role x route matrix:
3// OURS = c0_authz_step_step (tenant-scoped FSM -- clientA scope != clientB scope)
4// INCUMBENT = a flat-role baseline that authorizes ANY authenticated client to ANY client-scope route
5// (ignores tenant) -- the CWE-639 authorization-bypass / IDOR failure mode that
6// WordPress subscriber-role + membership setups are repeatedly bitten by.
7// Ground truth: a client-scope route is allowed ONLY for its own tenant; admin routes only for admin;
8// anon only for public. Verdict COMPUTED by nx_cms_exceed. Writes CMSEXCEED feature=roles-capabilities
9// axis=tenant-isolation on a measured AHEAD + referee-clean + anti-false-green. license_tier: ORIGINAL
10import "nx_cms_exceed.nx"
11import "c0_authz_step.nx"
12import "nx_syscalls.nx"
13
14func rx_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
15func rx_num(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=48+(m%10);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 }
16func rx_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var k: i64=0; while s[k]!=(0 as u8){dst[o]=s[k];o=o+1;k=k+1} return o }
17func rx_catnum(dst: *u8, off: i64, v: i64) -> i64 {
18 var o: i64=off; let t: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; var k: i64=0
19 if m==0 {t[0]=48 as u8;k=1}; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1}
20 var i: i64=0; while i<k {dst[o]=t[k-1-i]; o=o+1; i=i+1} return o
21}
22func rx_row(id: i64, ok: i64, what: *u8) -> i64 {
23 rx_w("RXROW " as *u8); rx_num(id); rx_w(" " as *u8)
24 if ok==1 { rx_w("PASS " as *u8) } else { rx_w("FAIL " as *u8) }
25 rx_w(what); rx_w("\n" as *u8)
26 return ok
27}
28
29// CORRECT authz (ground truth): client-scope only for its own tenant; admin only for admin; public open.
30func rx_truth_allow(s: i64, e: i64) -> i64 {
31 if e >= 3 { if s == 3 { return 1 } return 0 }
32 if e == 1 { if s == 1 { return 1 } return 0 }
33 if e == 2 { if s == 2 { return 1 } return 0 }
34 return 1
35}
36// INCUMBENT flat-role baseline: any authenticated client (1 or 2) may reach ANY client-scope route
37// (tenant IGNORED = CWE-639). Admin tier still respected; anon still blocked from client scope.
38func rx_incumbent_allow(s: i64, e: i64) -> i64 {
39 if e >= 3 { if s == 3 { return 1 } return 0 }
40 if e == 1 { if s == 1 { return 1 } if s == 2 { return 1 } return 0 }
41 if e == 2 { if s == 1 { return 1 } if s == 2 { return 1 } return 0 }
42 return 1
43}
44
45func main() -> i64 {
46 var pass: i64 = 0
47 var rows: i64 = 0
48
49 // corpus: (state, event) cases
50 let st: *i64 = sys_mmap(8 * 16) as *i64
51 let ev: *i64 = sys_mmap(8 * 16) as *i64
52 var nc: i64 = 0
53 st[nc]=1; ev[nc]=1; nc=nc+1 // clientA own scope -> allow
54 st[nc]=2; ev[nc]=2; nc=nc+1 // clientB own scope -> allow
55 st[nc]=1; ev[nc]=2; nc=nc+1 // clientA -> clientB scope -> DENY (isolation)
56 st[nc]=2; ev[nc]=1; nc=nc+1 // clientB -> clientA scope -> DENY (isolation)
57 st[nc]=1; ev[nc]=3; nc=nc+1 // clientA -> admin -> DENY
58 st[nc]=0; ev[nc]=1; nc=nc+1 // anon -> client scope -> DENY
59 st[nc]=3; ev[nc]=3; nc=nc+1 // admin -> admin -> allow
60
61 var our_correct: i64 = 0
62 var inc_correct: i64 = 0
63 var n_iso: i64 = 0
64 var iso_our: i64 = 0
65 var iso_inc: i64 = 0
66 var i: i64 = 0
67 while i < nc {
68 let s: i64 = st[i]
69 let e: i64 = ev[i]
70 let truth: i64 = rx_truth_allow(s, e)
71 var our_v: i64 = 0; if c0_authz_step_step(s, e) >= 0 { our_v = 1 }
72 let inc_v: i64 = rx_incumbent_allow(s, e)
73 if our_v == truth { our_correct = our_correct + 1 }
74 if inc_v == truth { inc_correct = inc_correct + 1 }
75 // a cross-tenant case = client-scope route requested by the OTHER client (truth=deny)
76 var cross: i64 = 0
77 if e == 1 { if s == 2 { cross = 1 } }
78 if e == 2 { if s == 1 { cross = 1 } }
79 if cross == 1 {
80 n_iso = n_iso + 1
81 if our_v == 0 { iso_our = iso_our + 1 }
82 if inc_v == 0 { iso_inc = iso_inc + 1 }
83 }
84 i = i + 1
85 }
86
87 let verdict: i64 = xcd_verdict(our_correct, inc_correct)
88 rx_w("HEAD-TO-HEAD roles-capabilities tenant-isolation: ours=" as *u8); rx_num(our_correct); rx_w("/" as *u8); rx_num(nc)
89 rx_w(" incumbent=" as *u8); rx_num(inc_correct); rx_w("/" as *u8); rx_num(nc)
90 rx_w(" cross-tenant-blocked ours=" as *u8); rx_num(iso_our); rx_w("/" as *u8); rx_num(n_iso)
91 rx_w(" incumbent=" as *u8); rx_num(iso_inc); rx_w("/" as *u8); rx_num(n_iso)
92 rx_w(" verdict=" as *u8); rx_w(xcd_vname(verdict)); rx_w("\n" as *u8)
93
94 var ok: i64 = 0; if our_correct == nc { ok = 1 }
95 rows=rows+1; pass=pass+rx_row(0, ok, "ours fully correct (tenant-scoped: own-scope allow, cross-tenant deny)" as *u8)
96 ok = 0; if inc_correct < nc { ok = 1 }
97 rows=rows+1; pass=pass+rx_row(1, ok, "incumbent flat-role fails (cross-tenant allowed = CWE-639)" as *u8)
98 ok = 0; if verdict == XCD_AHEAD { ok = 1 }
99 rows=rows+1; pass=pass+rx_row(2, ok, "measured verdict = AHEAD" as *u8)
100 ok = 0; if iso_our == n_iso { if iso_inc == 0 { if n_iso > 0 { ok = 1 } } }
101 rows=rows+1; pass=pass+rx_row(3, ok, "isolation axis: ours blocks all cross-tenant, incumbent blocks 0" as *u8)
102 ok = 0; if xcd_referee_ok(XCD_AHEAD, our_correct, nc) == 1 { ok = 1 }
103 rows=rows+1; pass=pass+rx_row(4, ok, "referee accepts honest AHEAD claim" as *u8)
104 ok = 0; if xcd_referee_ok(XCD_AHEAD, nc - 1, nc) == 0 { ok = 1 }
105 rows=rows+1; pass=pass+rx_row(5, ok, "referee rejects overclaim (anti-false-green)" as *u8)
106
107 rx_w("CMS-ROLES-EXCEED-GATE rows=" as *u8); rx_num(rows); rx_w(" pass=" as *u8); rx_num(pass); rx_w("\n" as *u8)
108
109 if pass == rows {
110 if verdict == XCD_AHEAD {
111 if xcd_referee_ok(XCD_AHEAD, our_correct, nc) == 1 {
112 let line: *u8 = sys_mmap(256)
113 var lo: i64 = rx_cat(line, 0, "CMSEXCEED feature=roles-capabilities axis=tenant-isolation cwe=CWE-639 ours=" as *u8)
114 lo = rx_catnum(line, lo, our_correct); lo = rx_cat(line, lo, "/" as *u8); lo = rx_catnum(line, lo, nc)
115 lo = rx_cat(line, lo, " incumbent=" as *u8); lo = rx_catnum(line, lo, inc_correct); lo = rx_cat(line, lo, "/" as *u8); lo = rx_catnum(line, lo, nc)
116 lo = rx_cat(line, lo, " cross-tenant-blocked=" as *u8); lo = rx_catnum(line, lo, iso_our); lo = rx_cat(line, lo, "/" as *u8); lo = rx_catnum(line, lo, n_iso)
117 lo = rx_cat(line, lo, " verdict=AHEAD\n" as *u8)
118 let gf: i64 = sys_openat_append("knowledge/status/cms_exceed.log" as *u8, 0x1a4)
119 if gf >= 0 { sys_write(gf, line, lo); sys_close(gf) }
120 rx_w("CMS-ROLES-EXCEED-GATE verdict=AHEAD -- measured EXCEED recorded\n" as *u8)
121 sys_exit(0); return 0
122 }
123 }
124 }
125 rx_w("CMS-ROLES-EXCEED-GATE verdict=NOT-RECORDED (no fake-green)\n" as *u8)
126 sys_exit(1); return 1
127}