code wiki / _hdl_build / _wiki_edit_gate.nx

_wiki_edit_gate.nx source

↩ module page · 258 lines · 12876 B

1// _wiki_edit_gate.nx -- unit gate PROVING the wiki R1 auth-gated page EDIT/SAVE 2// backbone: an AUTHED save persists + renders + is an immutable revision, and 3// an UNAUTH save is REFUSED with the store UNCHANGED (the liar-kill). 4// 5// NO mocks, NO live socket: drives the REAL save core (nx_wiki_page_save) + 6// REAL doc store + the REAL reused auth (armed NxAuthContext: persistent key 7// bundle + OPAQUE account store + argon2id KSF -- the exact production auth the 8// wiki login uses) + the REAL append-only seg_store. Mirrors the proven shapes 9// in nx_wiki_login_live_gate.nx (armed ctx + register + login -> real token) and 10// _wiki_engine_gate.nx (doc-store serve as ground truth). 11// 12// Rows: 13// save_ok : AUTHED nx_wiki_page_save(slug,"new content X",VALID token) 14// -> ground truth = seg_store wikicur:<slug> reads back 15// "new content X" AND the doc store serves "new content X". 16// unauth_refused : nx_wiki_page_save(slug,"evil",EMPTY/INVALID token) 17// -> returns REFUSED AND seg_store wikicur:<slug> is STILL 18// "new content X" (the evil write never landed). 19// rev_ok : a wikirev:<slug>:* revision row exists for the authed save. 20// readback_ok : the wikicur:<slug> readback equaled the authed content. 21// 22// JUDGED by the printed WIKIEDIT marker (verdict=GREEN only if EVERY assertion 23// holds), NOT by $?. Per the native-codegen desync landmine, every assertion is 24// judged on a GROUND-TRUTH observable -- the BYTES read back out of the 25// seg_store / served from the doc store -- never a bare returned status code. 26// 27// A UNIQUE slug per run (epoch-suffixed) keeps the shared append-only 28// knowledge/store/wikipage- prefix from letting a PRIOR run's bytes answer for 29// this one (the store is additive; latest-put wins per slug). 30// 31// Import set: nx_syscalls + wiki/nx_wiki_edit_handler (transitively pulls the 32// save core -> the reused auth flow -> uas/opaque/bip39, base64, html_escape, 33// index_builder, seg_store). Each base module splices exactly ONCE (path-dedup) 34// to avoid the double-import nxasm rc6 trap. 35// license_tier: ORIGINAL 36import "nx_syscalls.nx" 37import "wiki/nx_wiki_edit_handler.nx" 38import "wiki/nx_wiki_doc_handler.nx" 39 40// ----- tiny io helpers (write to a fd) ----- 41func gw(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 } 42func gn(fd: i64, v: i64) -> i64 { 43 let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) } 44 let t: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 } 45 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 46 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 47 sys_write(fd, bb, k); return 0 48} 49// write to BOTH stdout and the durable log fd (lfd<0 = stdout only) 50func w2(lfd: i64, s: *u8) -> i64 { gw(1, s); if lfd >= 0 { gw(lfd, s) } return 0 } 51func n2(lfd: i64, v: i64) -> i64 { gn(1, v); if lfd >= 0 { gn(lfd, v) } return 0 } 52 53// counted byte-equality 54func bytes_eq(a: *u8, an: i64, b: *u8, bn: i64) -> i64 { 55 if an != bn { return 0 } 56 var i: i64 = 0 57 while i < an { if a[i] != b[i] { return 0 } i = i + 1 } 58 return 1 59} 60 61// does NUL-terminated haystack contain counted-length needle? (substring) 62func contains(hay: *u8, hay_n: i64, needle: *u8, needle_n: i64) -> i64 { 63 if needle_n <= 0 { return 0 } 64 if hay_n < needle_n { return 0 } 65 var i: i64 = 0 66 while i + needle_n <= hay_n { 67 var k: i64 = 0 68 var hit: i64 = 1 69 while k < needle_n { if hay[i + k] != needle[k] { hit = 0; k = needle_n } else { k = k + 1 } } 70 if hit == 1 { return 1 } 71 i = i + 1 72 } 73 return 0 74} 75 76// Serve "/wiki/<slug>" through the REAL doc handler and confirm the response is 77// HTTP 200 AND contains `need` (ground truth = bytes served). Returns 1/0. 78func serves_with(store: *NxWikiDocStore, slug: *u8, slug_n: i64, 79 need: *u8, need_n: i64) -> i64 { 80 let url: *u8 = sys_mmap(NX_WPS_URL_CAP) 81 let url_n: i64 = nx_wps_url(slug, slug_n, url) 82 let resp: *u8 = sys_mmap(1048576) 83 let rn: *i64 = sys_mmap(8) as *i64 84 rn[0] = 0 85 nx_wiki_doc_handle(store, 0 as *NxArtifactStore, url, url_n, resp, 1048576, rn) 86 let n: i64 = rn[0] 87 if n <= 0 { return 0 } 88 if contains(resp, n, "HTTP/1.1 200 OK" as *u8, 15) != 1 { return 0 } 89 if contains(resp, n, need, need_n) != 1 { return 0 } 90 return 1 91} 92 93// Does a wikirev:<slug>:* revision row exist (any epoch)? Ground truth: scan the 94// seg_store for the cur key's sibling rev prefix by walking ss_scan over a 95// REBUILT exact rev key is impossible (epoch unknown), so we read the rev via a 96// known-epoch key the caller passes (the epoch used for the authed save). 97func rev_exists(slug: *u8, slug_n: i64, epoch: i64) -> i64 { 98 let revk: *u8 = sys_mmap(NX_WPS_KEY_CAP) 99 nx_wps_rev_key(slug, slug_n, epoch, revk) 100 let h: *i64 = ss_open(nx_wps_prefix()) 101 if (h as i64) == 0 { return 0 } 102 let pp: *i64 = sys_mmap(16) as *i64 103 let pl: *i64 = sys_mmap(16) as *i64 104 if ss_hget(h, revk, pp, pl) != 1 { return 0 } 105 // value must start with "author=" and carry the content tail 106 let v: *u8 = pp[0] as *u8 107 let vn: i64 = pl[0] 108 if contains(v, vn, "author=" as *u8, 7) != 1 { return 0 } 109 return 1 110} 111 112func main() -> i64 { 113 gw(1, "WIKIEDIT-GATE: start (authed save -> persist+render+revision; unauth -> refuse, store unchanged)\n" as *u8) 114 115 // ===== armed auth context (REAL key bundle + account store; /tmp = fresh) ===== 116 let keys_path: *u8 = "/tmp/nishi_wiki_edit_gate_keys.log" as *u8 117 let store_path: *u8 = "/tmp/nishi_wiki_edit_gate_store.log" as *u8 118 // truncate any prior account store so register is clean each run 119 let fz: i64 = sys_openat_wr(store_path, 0x180) 120 if fz >= 0 { sys_close(fz) } 121 122 let oprf_seed: *u8 = sys_mmap(32) 123 let akp: *u8 = sys_mmap(32) 124 let akb: *u8 = sys_mmap(33) 125 let edp: *u8 = sys_mmap(32) 126 let edb: *u8 = sys_mmap(32) 127 if nx_uas_server_keys_load_or_init(keys_path, oprf_seed, akp, akb, edp, edb) != NX_UAS_OK { 128 gw(1, "WIKIEDIT save_ok=0 unauth_refused=0 rev_ok=0 readback_ok=0 verdict=RED (keys init failed)\n" as *u8) 129 sys_exit(1); return 1 130 } 131 let realm: *u8 = "Nishi Wiki" as *u8 132 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext 133 nx_auth_context_init(ctx, realm, 10, realm, 10, 134 store_path as i64, oprf_seed, edp, edb, 135 900, 8192, 1, 1, 5, 1) 136 137 // register the admin, then LOGIN to mint a REAL session token 138 let handle: *u8 = "elderwesto" as *u8 139 let pw: *u8 = "correct horse+battery!" as *u8 // 22 bytes 140 let mn: *u8 = sys_mmap(512) 141 let mn_n: *i64 = sys_mmap(16) as *i64 142 nx_modern_auth_register(ctx, handle, 10, pw, 22, mn, 512, mn_n) 143 144 let tok: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES + 8) 145 let tok_n: *i64 = sys_mmap(8) as *i64 146 tok_n[0] = 0 147 let login_rc: i64 = nx_modern_auth_login(ctx, handle, 10, pw, 22, 148 tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok_n) 149 // ground-truth token validity: a freshly minted token must validate NOW 150 let now_s: i64 = sys_now_realtime_sec() 151 var token_valid: i64 = 0 152 if login_rc == NX_MAUTH_OK { 153 if tok_n[0] == NX_MAUTH_SESSION_TOKEN_BYTES { 154 if nx_modern_auth_validate_session(ctx, tok, tok_n[0], now_s, 155 (0 as i64) as *u8, 0, (0 as i64) as *i64) == NX_MAUTH_OK { 156 token_valid = 1 157 } 158 } 159 } 160 gw(1, " minted+validated real session token: "); gn(1, token_valid); gw(1, "\n" as *u8) 161 162 // ===== seed a fresh doc store + a UNIQUE slug for this run ===== 163 let doc_store: *NxWikiDocStore = sys_mmap(256) as *NxWikiDocStore 164 if nx_wiki_doc_store_init(doc_store, 100, 65536, 65536, 4194304) != NX_WIB_OK { 165 gw(1, "WIKIEDIT save_ok=0 unauth_refused=0 rev_ok=0 readback_ok=0 verdict=RED (doc store init failed)\n" as *u8) 166 sys_exit(1); return 1 167 } 168 // slug = "edit-gate-<epoch>" (unique per run; keeps the shared store clean) 169 let slug: *u8 = sys_mmap(64) 170 var so: i64 = 0 171 let sp: *u8 = "edit-gate-" as *u8 172 while sp[so] != (0 as u8) { slug[so] = sp[so]; so = so + 1 } 173 // append epoch decimal 174 var ev: i64 = now_s 175 let et: *u8 = sys_mmap(28); var ek: i64 = 0 176 if ev == 0 { et[0] = 48 as u8; ek = 1 } 177 while ev > 0 { et[ek] = (48 + (ev % 10)) as u8; ev = ev / 10; ek = ek + 1 } 178 var ei: i64 = 0 179 while ei < ek { slug[so + ei] = et[ek - 1 - ei]; ei = ei + 1 } 180 let slug_n: i64 = so + ek 181 182 // seed the page into the doc store (url /wiki/<slug>) with placeholder body 183 let url: *u8 = sys_mmap(NX_WPS_URL_CAP) 184 let url_n: i64 = nx_wps_url(slug, slug_n, url) 185 nx_wiki_doc_store_add(doc_store, slug, slug_n, url, url_n, "# seed\nold body\n" as *u8, 16) 186 187 // ===== ROW save_ok + readback_ok: AUTHED save -> persist + render ===== 188 let content: *u8 = "new content X" as *u8 189 let content_n: i64 = 13 190 nx_wiki_page_save(doc_store, ctx, slug, slug_n, content, content_n, 191 tok, tok_n[0], now_s) 192 193 // ground truth #1: read wikicur:<slug> straight back out of the seg_store 194 let rb: *u8 = sys_mmap(NX_WPS_MAX_CONTENT_LEN + 16) 195 let rb_n: i64 = nx_wiki_page_load_current(slug, slug_n, rb, NX_WPS_MAX_CONTENT_LEN) 196 var readback_ok: i64 = 0 197 if rb_n >= 0 { if bytes_eq(rb, rb_n, content, content_n) == 1 { readback_ok = 1 } } 198 gw(1, " seg_store wikicur readback len="); gn(1, rb_n); gw(1, " eq='new content X': "); gn(1, readback_ok); gw(1, "\n" as *u8) 199 200 // ground truth #2: the doc store now SERVES "new content X" 201 var render_ok: i64 = serves_with(doc_store, slug, slug_n, content, content_n) 202 gw(1, " doc store serves 'new content X': "); gn(1, render_ok); gw(1, "\n" as *u8) 203 204 var save_ok: i64 = 0 205 if readback_ok == 1 { if render_ok == 1 { save_ok = 1 } } 206 207 // ===== ROW rev_ok: an immutable revision row exists for the authed save ===== 208 var rev_ok: i64 = rev_exists(slug, slug_n, now_s) 209 gw(1, " wikirev:<slug>:<epoch> exists: "); gn(1, rev_ok); gw(1, "\n" as *u8) 210 211 // ===== ROW unauth_refused: UNAUTH save -> REFUSED + store UNCHANGED ===== 212 // empty token (token_n=0) is the simplest forged credential. 213 let evil: *u8 = "evil" as *u8 214 let bad_tok: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES + 8) 215 let unauth_rc: i64 = nx_wiki_page_save(doc_store, ctx, slug, slug_n, 216 evil, 4, bad_tok, 0, now_s) 217 // ground truth: the verdict is REFUSED ... 218 var refused_code: i64 = 0 219 if unauth_rc == 0 - NX_WPS_REFUSED { refused_code = 1 } 220 // ... AND (the load-bearing check) the persisted current bytes are STILL 221 // "new content X" -- the evil write never touched the store. 222 let rb2: *u8 = sys_mmap(NX_WPS_MAX_CONTENT_LEN + 16) 223 let rb2_n: i64 = nx_wiki_page_load_current(slug, slug_n, rb2, NX_WPS_MAX_CONTENT_LEN) 224 var unchanged: i64 = 0 225 if rb2_n >= 0 { if bytes_eq(rb2, rb2_n, content, content_n) == 1 { unchanged = 1 } } 226 // ALSO confirm a second invalid token (garbage 152 bytes) is refused 227 let garb: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES + 8) 228 var gi: i64 = 0 229 while gi < NX_MAUTH_SESSION_TOKEN_BYTES { garb[gi] = 65 as u8; gi = gi + 1 } 230 let unauth_rc2: i64 = nx_wiki_page_save(doc_store, ctx, slug, slug_n, 231 evil, 4, garb, NX_MAUTH_SESSION_TOKEN_BYTES, now_s) 232 var refused_code2: i64 = 0 233 if unauth_rc2 == 0 - NX_WPS_REFUSED { refused_code2 = 1 } 234 var unauth_refused: i64 = 0 235 if refused_code == 1 { if refused_code2 == 1 { if unchanged == 1 { unauth_refused = 1 } } } 236 gw(1, " unauth refused(empty)="); gn(1, refused_code) 237 gw(1, " refused(garbage)="); gn(1, refused_code2) 238 gw(1, " store_unchanged="); gn(1, unchanged); gw(1, "\n" as *u8) 239 240 // ===== verdict + marker line ===== 241 var green: i64 = 1 242 if token_valid != 1 { green = 0 } 243 if save_ok != 1 { green = 0 } 244 if unauth_refused != 1 { green = 0 } 245 if rev_ok != 1 { green = 0 } 246 if readback_ok != 1 { green = 0 } 247 248 let lfd: i64 = sys_openat_append("knowledge/status/wiki_edit_gate.log" as *u8, 420) 249 w2(lfd, "WIKIEDIT save_ok=" as *u8); n2(lfd, save_ok) 250 w2(lfd, " unauth_refused=" as *u8); n2(lfd, unauth_refused) 251 w2(lfd, " rev_ok=" as *u8); n2(lfd, rev_ok) 252 w2(lfd, " readback_ok=" as *u8); n2(lfd, readback_ok) 253 if green == 1 { w2(lfd, " verdict=GREEN\n" as *u8) } else { w2(lfd, " verdict=RED\n" as *u8) } 254 if lfd >= 0 { sys_close(lfd) } 255 256 if green == 1 { sys_exit(0); return 0 } 257 sys_exit(1); return 1 258}