code wiki / (root) / nx_asset_access_gate.nx

nx_asset_access_gate.nx source

↩ module page · 117 lines · 7157 B

1// nx_asset_access_gate.nx -- KAT + TEETH for nx_asset_access (R6 of the universal org-tooling arc). 2// 3// Proves the asset PDP is DENY-BY-DEFAULT + FAIL-CLOSED over a real content-addressed record's 4// classification field (decoded via nx_asset_record): 5// (a) public -> readable by EVERYONE (ANON..OWNER all ALLOW) 6// (b) professional -> ANON/FAMILY DENY, PRO/OWNER ALLOW (the professional split) 7// (c) private -> only OWNER ALLOW; ANON/FAMILY/PRO DENY (the private split) 8// (d) operator-only -> only OWNER ALLOW (the image-stack "operator-only" law) 9// TEETH (must DENY -- the fail-closed proof a default-allow system would get WRONG): 10// (e) NO classification field -> DENY for EVERY level, including OWNER 11// (f) UNRECOGNIZED classification ("bogus") -> DENY for OWNER (fail-closed, not "allow if unsure") 12// (g) private @ ANON -> DENY (the public internet can never read a private asset) 13// 14// Verdict appended to knowledge/status/asset_access_gate.log (additive law #13). 15// expect_exit: 0 license_tier: ORIGINAL 16import "nx_syscalls.nx" 17import "nx_canon_cid.nx" 18import "nx_asset_record.nx" 19import "nx_asset_access.nx" 20import "nx_gate_verdict.nx" 21 22func g_puts(logfd: i64, s: *u8) -> i64 { 23 var n: i64 = 0 24 while s[n] != (0 as u8) { n = n + 1 } 25 sys_write(1, s, n) 26 if logfd > 0 { sys_write(logfd, s, n) } 27 return 0 28} 29func g_putn(logfd: i64, v: i64) -> i64 { 30 let bb: *u8 = sys_mmap(28) 31 var m: i64 = v 32 if m < 0 { g_puts(logfd, "-\x00" as *u8); m = 0 - m } 33 let t: *u8 = sys_mmap(28) 34 var k: i64 = 0 35 if m == 0 { t[0] = 48 as u8; k = 1 } 36 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 37 var i: i64 = 0 38 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 39 sys_write(1, bb, k) 40 if logfd > 0 { sys_write(logfd, bb, k) } 41 return 0 42} 43func E() -> *u8 { return "\x00" as *u8 } 44func sset(a: *i64, i: i64, s: *u8) -> i64 { a[i] = s as i64; return 0 } 45 46// build a minimal asset record with the given classification (cls=E() -> NO classification field). 47func mk_classified(cls: *u8, out: *u8) -> i64 { 48 let core: *i64 = sys_mmap(8 * 8) as *i64 49 sset(core, 0, "doc\x00" as *u8); sset(core, 1, "Asset\x00" as *u8); sset(core, 2, E()) 50 sset(core, 3, "2026-06-20\x00" as *u8); sset(core, 4, "a-1\x00" as *u8); sset(core, 5, E()); sset(core, 6, E()) 51 let pz: *i64 = sys_mmap(8 * 8) as *i64 52 sset(pz, 0, E()); sset(pz, 1, E()); sset(pz, 2, E()); sset(pz, 3, E()); sset(pz, 4, E()); sset(pz, 5, E()) 53 let mz: *i64 = sys_mmap(8 * 8) as *i64 54 sset(mz, 0, E()); sset(mz, 1, E()); sset(mz, 2, E()); sset(mz, 3, E()) 55 let oz: *i64 = sys_mmap(8 * 8) as *i64 56 sset(oz, 0, E()); sset(oz, 1, E()); sset(oz, 2, E()); sset(oz, 3, "1\x00" as *u8); sset(oz, 4, cls); sset(oz, 5, E()) 57 let keys: *i64 = sys_mmap(8 * 48) as *i64 58 let vals: *i64 = sys_mmap(8 * 48) as *i64 59 let n: i64 = ar_fields(core, pz, mz, oz, keys, vals) 60 return ar_encode(keys, vals, n, out) 61} 62 63// assert aa_allowed(aa_decide(rec,n,level)) == want; logs + returns 1 PASS / 0 FAIL. 64func expect(logfd: i64, label: *u8, rec: *u8, n: i64, level: i64, want: i64) -> i64 { 65 let got: i64 = aa_allowed(aa_decide(rec, n, level)) 66 g_puts(logfd, label); g_puts(logfd, " -> allowed=\x00" as *u8); g_putn(logfd, got); g_puts(logfd, " want=\x00" as *u8); g_putn(logfd, want); g_puts(logfd, ": \x00" as *u8) 67 if got == want { g_puts(logfd, "PASS\n\x00" as *u8); return 1 } 68 g_puts(logfd, "FAIL\n\x00" as *u8); return 0 69} 70 71func main() -> i64 { 72 let logfd: i64 = sys_openat_append("knowledge/status/asset_access_gate.log\x00" as *u8, 0x1a4) 73 g_puts(logfd, "=== ASSET-ACCESS-GATE (R6: deny-by-default, fail-closed asset PDP) ===\n\x00" as *u8) 74 75 var pass: i64 = 0 76 var total: i64 = 0 77 78 let rpub: *u8 = sys_mmap(8192); let npub: i64 = mk_classified("public\x00" as *u8, rpub) 79 let rpro: *u8 = sys_mmap(8192); let npro: i64 = mk_classified("professional\x00" as *u8, rpro) 80 let rpriv: *u8 = sys_mmap(8192); let npriv: i64 = mk_classified("private\x00" as *u8, rpriv) 81 let ropr: *u8 = sys_mmap(8192); let nopr: i64 = mk_classified("operator-only\x00" as *u8, ropr) 82 let rnone: *u8 = sys_mmap(8192); let nnone: i64 = mk_classified(E(), rnone) 83 let rbog: *u8 = sys_mmap(8192); let nbog: i64 = mk_classified("bogus\x00" as *u8, rbog) 84 85 // (a) public -> everyone 86 total = total + 1; pass = pass + expect(logfd, " (a1) public @ ANON\x00" as *u8, rpub, npub, AA_ANON(), 1) 87 total = total + 1; pass = pass + expect(logfd, " (a2) public @ OWNER\x00" as *u8, rpub, npub, AA_OWNER(), 1) 88 // (b) professional split 89 total = total + 1; pass = pass + expect(logfd, " (b1) professional @ ANON\x00" as *u8, rpro, npro, AA_ANON(), 0) 90 total = total + 1; pass = pass + expect(logfd, " (b2) professional @ FAMILY\x00" as *u8, rpro, npro, AA_FAMILY(), 0) 91 total = total + 1; pass = pass + expect(logfd, " (b3) professional @ PRO\x00" as *u8, rpro, npro, AA_PRO(), 1) 92 total = total + 1; pass = pass + expect(logfd, " (b4) professional @ OWNER\x00" as *u8, rpro, npro, AA_OWNER(), 1) 93 // (c) private split -- only the owner 94 total = total + 1; pass = pass + expect(logfd, " (c1) private @ PRO\x00" as *u8, rpriv, npriv, AA_PRO(), 0) 95 total = total + 1; pass = pass + expect(logfd, " (c2) private @ OWNER\x00" as *u8, rpriv, npriv, AA_OWNER(), 1) 96 // (d) operator-only -- only the owner (image-stack law) 97 total = total + 1; pass = pass + expect(logfd, " (d1) operator-only @ FAMILY\x00" as *u8, ropr, nopr, AA_FAMILY(), 0) 98 total = total + 1; pass = pass + expect(logfd, " (d2) operator-only @ OWNER\x00" as *u8, ropr, nopr, AA_OWNER(), 1) 99 // (e) TEETH: no classification -> DENY for everyone, incl OWNER (fail-closed) 100 total = total + 1; pass = pass + expect(logfd, " (e1) NO-class @ ANON (fail-closed)\x00" as *u8, rnone, nnone, AA_ANON(), 0) 101 total = total + 1; pass = pass + expect(logfd, " (e2) NO-class @ OWNER (fail-closed)\x00" as *u8, rnone, nnone, AA_OWNER(), 0) 102 // (f) TEETH: unrecognized class -> DENY for OWNER (not "allow if unsure") 103 total = total + 1; pass = pass + expect(logfd, " (f) bogus-class @ OWNER (fail-closed)\x00" as *u8, rbog, nbog, AA_OWNER(), 0) 104 // (g) TEETH: private @ ANON -> the public internet can never read a private asset 105 total = total + 1; pass = pass + expect(logfd, " (g) private @ ANON (internet locked out)\x00" as *u8, rpriv, npriv, AA_ANON(), 0) 106 107 g_puts(logfd, "ASSET-ACCESS-GATE passed \x00" as *u8); g_putn(logfd, pass); g_puts(logfd, "/\x00" as *u8); g_putn(logfd, total) 108 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 109 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 110 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 111 let ctr__dry: *i64 = gv_ctr() 112 ctr__dry[0] = pass 113 ctr__dry[1] = total 114 let rc__dry: i64 = gv_verdict("ASSET-ACCESS-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 115 sys_exit(rc__dry) 116 return rc__dry 117}