code wiki / _hdl_build / nx_siteedit_gate.nx

nx_siteedit_gate.nx source

↩ module page · 195 lines · 13298 B

1// nx_siteedit_gate.nx -- SOVEREIGN in-process referee for the admin.<d>/site SITE-VISUALS editor. 2// Arms a fresh Modern-Auth realm, seeds a real .site blueprint, then drives the pure router 3// sed_handle with crafted HTTP bytes and asserts response bytes AND filesystem side effects: 4// T1 GET /site -> 200 editor shell 5// T2 NEG /site/config no session -> 401 6// T3 authed GET /site/config -> 200 + the seeded blueprint bytes 7// T4 authed POST /site/save (edited hero marker) -> 200 SAVED + config updated + a .v version kept 8// T5 NEG save with hero| MISSING -> 400 REFUSED + live config UNCHANGED (marker still present) 9// T6 authed POST /site/preview -> preview/index.html EXISTS + carries the edited marker 10// T7 authed POST /site/publish -> docroot index.html + practice page + sitemap exist w/ marker, 11// .prev-index.html backup exists (the pre-publish homepage) 12// T8 NEG publish with NO session -> 401 and the docroot index keeps T7's content (untouched) 13// GREEN iff 8/8. Appends knowledge/status/siteedit_gate.log. license_tier: ORIGINAL expect_exit: 0 14import "_hdl_build/nx_siteedit_daemon.nx" 15import "hub/nx_modern_auth_flow.nx" 16import "nx_syscalls.nx" 17 18func g_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 19func g_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 20func g_lw(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 } 21func g_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { d[o + i] = s[i]; i = i + 1 } return o + i } 22func g_cat_n(d: *u8, o: i64, s: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { d[o + i] = s[i]; i = i + 1 } return o + n } 23func g_starts(buf: *u8, n: i64, s: *u8) -> i64 { let sn: i64 = g_len(s); if n < sn { return 0 } var i: i64 = 0; while i < sn { if (buf[i] as i64) != (s[i] as i64) { return 0 } i = i + 1 } return 1 } 24func g_contains(hay: *u8, n: i64, needle: *u8) -> i64 { 25 let nn: i64 = g_len(needle); var i: i64 = 0 26 while i + nn <= n { var m: i64 = 1; var j: i64 = 0; while j < nn { if (hay[i + j] as i64) != (needle[j] as i64) { m = 0; j = nn } else { j = j + 1 } } if m == 1 { return 1 } i = i + 1 } 27 return 0 28} 29func g_extract_token(resp: *u8, n: i64, out: *u8) -> i64 { 30 let key: *u8 = "{\"token\":\"" as *u8; let kn: i64 = 10 31 var start: i64 = 0 - 1; var i: i64 = 0 32 while i + kn <= n { var m: i64 = 1; var j: i64 = 0; while j < kn { if (resp[i + j] as i64) != (key[j] as i64) { m = 0; j = kn } else { j = j + 1 } } if m == 1 { start = i + kn; i = n } i = i + 1 } 33 if start < 0 { return 0 } 34 var w: i64 = 0; var k: i64 = start; var done: i64 = 0 35 while done == 0 { if k >= n { done = 1 } if done == 0 { if (resp[k] as i64) == 34 { done = 1 } } if done == 0 { out[w] = resp[k]; w = w + 1; k = k + 1 } } 36 return w 37} 38// file contains needle? (0 also when absent) 39func g_file_has(path: *u8, needle: *u8) -> i64 { 40 let szp: *i64 = sys_mmap(16) as *i64 41 szp[0] = 0 42 let b: *u8 = sys_read_file(path, szp) 43 if (b as i64) == 0 { return 0 } 44 return g_contains(b, szp[0], needle) 45} 46func g_file_exists(path: *u8) -> i64 { 47 let fd: i64 = sys_openat_rd(path) 48 if fd < 0 { return 0 } 49 sys_close(fd) 50 return 1 51} 52func g_verdict(label: *u8, ok: i64) -> i64 { g_w(" " as *u8); g_w(label); if ok == 1 { g_w(" PASS\n" as *u8) } else { g_w(" FAIL\n" as *u8) } return ok } 53 54const GCFG_BASE: *u8 = "title|Andelin West -- Utah Attorneys\nheader|Andelin West|Free consultation|#consult\nhero|Straightforward legal help.|Talk to a Utah attorney.|Start now|#consult\nsearch|/search|Search...\ncards|How we help\ncard|Family Law|Divorce and custody handled with care.\ncard|Estate Planning|Wills and trusts done right.\nstep|Tell us what happened.\nsig|Licensed by the Utah State Bar\nfooter|Andelin West, PLLC -- Utah\n" 55const GCFG_EDIT: *u8 = "title|Andelin West -- Utah Attorneys\nheader|Andelin West|Free consultation|#consult\nhero|glacierheadline legal help for Utah.|Talk to a Utah attorney.|Start now|#consult\nsearch|/search|Search...\ncards|How we help\ncard|Family Law|Divorce and custody handled with care.\ncard|Estate Planning|Wills and trusts done right.\nstep|Tell us what happened.\nsig|Licensed by the Utah State Bar\nfooter|Andelin West, PLLC -- Utah\n" 56const GCFG_BAD: *u8 = "title|Broken config\nheader|X|Y|#z\nsearch|/search|s\ncard|Only|Card\nfooter|F\n" 57 58func main() -> i64 { 59 g_w("=== SITEEDIT GATE (admin.<d>/site visuals editor; sovereign in-process) ===\n" as *u8) 60 let store_path: *u8 = "/tmp/nx_sed_gate_store.log" as *u8 61 let keys_path: *u8 = "/tmp/nx_sed_gate_keys.log" as *u8 62 let g_trunc1: i64 = sys_openat_wr(store_path, 0x180) 63 if g_trunc1 >= 0 { sys_close(g_trunc1) } 64 let g_trunc2: i64 = sys_openat_wr(keys_path, 0x180) 65 if g_trunc2 >= 0 { sys_close(g_trunc2) } 66 sys_mkdir("/tmp/nx_sed_gate_root" as *u8, 511) 67 let docroot: *u8 = "/tmp/nx_sed_gate_root" as *u8 68 let cfgpath: *u8 = "/tmp/nx_sed_gate.site" as *u8 69 let domain: *u8 = "andelinwest.com" as *u8 70 // seed the blueprint 71 let cf: i64 = sys_openat_wr(cfgpath, 0x1a4) 72 if cf >= 0 { sys_write(cf, GCFG_BASE, g_len(GCFG_BASE)); sys_close(cf) } 73 // seed a fake "old homepage" so the publish backup has something to keep 74 let oldidx: i64 = sys_openat_wr("/tmp/nx_sed_gate_root/index.html" as *u8, 0x1a4) 75 if oldidx >= 0 { sys_write(oldidx, "OLD-HOMEPAGE-SENTINEL" as *u8, 21); sys_close(oldidx) } 76 77 let oprf_seed: *u8 = sys_mmap(32); let akp: *u8 = sys_mmap(32); let akb: *u8 = sys_mmap(33) 78 let edp: *u8 = sys_mmap(32); let edb: *u8 = sys_mmap(32) 79 if nx_uas_server_keys_load_or_init(keys_path, oprf_seed, akp, akb, edp, edb) != NX_UAS_OK { g_w("KEYS FAIL\n" as *u8); sys_exit(1) } 80 let realm: *u8 = "andelinwest_docportal" as *u8 81 let realm_n: i64 = g_len(realm) 82 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext 83 if nx_auth_context_init(ctx, realm, realm_n, realm, realm_n, store_path as i64, oprf_seed, edp, edb, 900, 8192, 1, 1, 5, 1) != NX_MAUTH_OK { g_w("CTX FAIL\n" as *u8); sys_exit(1) } 84 let mn: *u8 = sys_mmap(512); let mn_n: *i64 = sys_mmap(16) as *i64 85 if nx_modern_auth_register(ctx, "editor" as *u8, 6, "site editor passphrase" as *u8, 22, mn, 512, mn_n) != NX_MAUTH_OK { g_w("REGISTER FAIL\n" as *u8); sys_exit(1) } 86 let tok: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES) 87 let tokn: *i64 = sys_mmap(16) as *i64 88 tokn[0] = 0 89 if nx_modern_auth_login(ctx, "editor" as *u8, 6, "site editor passphrase" as *u8, 22, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tokn) != NX_MAUTH_OK { g_w("LOGIN FAIL\n" as *u8); sys_exit(1) } 90 let tok64: *u8 = sys_mmap(256) 91 let tok64n: i64 = b64_encode(tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok64) 92 93 let req: *u8 = sys_mmap(65536) 94 let out: *u8 = sys_mmap(SED_OUTCAP) 95 var pass: i64 = 0 96 97 // T1 shell 98 let n1: i64 = g_cat(req, 0, "GET /site HTTP/1.1\r\nHost: x\r\n\r\n" as *u8) 99 let o1: i64 = sed_handle(ctx, domain, cfgpath, docroot, req, n1, out) 100 var t1: i64 = 0 101 if g_starts(out, o1, "HTTP/1.1 200" as *u8) == 1 { if g_contains(out, o1, "Site editor" as *u8) == 1 { t1 = 1 } } 102 pass = pass + g_verdict("T1 GET /site -> 200 editor shell:" as *u8, t1) 103 104 // T2 NEG no session 105 let n2: i64 = g_cat(req, 0, "GET /site/config HTTP/1.1\r\nHost: x\r\n\r\n" as *u8) 106 let o2: i64 = sed_handle(ctx, domain, cfgpath, docroot, req, n2, out) 107 var t2: i64 = 0 108 if g_starts(out, o2, "HTTP/1.1 401" as *u8) == 1 { t2 = 1 } 109 pass = pass + g_verdict("T2 NEG /site/config no session -> 401:" as *u8, t2) 110 111 // T3 authed config read 112 var n3: i64 = g_cat(req, 0, "GET /site/config HTTP/1.1\r\nHost: x\r\nX-Nishi-Session: " as *u8) 113 n3 = g_cat_n(req, n3, tok64, tok64n) 114 n3 = g_cat(req, n3, "\r\n\r\n" as *u8) 115 let o3: i64 = sed_handle(ctx, domain, cfgpath, docroot, req, n3, out) 116 var t3: i64 = 0 117 if g_starts(out, o3, "HTTP/1.1 200" as *u8) == 1 { if g_contains(out, o3, "Straightforward legal help." as *u8) == 1 { t3 = 1 } } 118 pass = pass + g_verdict("T3 authed /site/config -> the blueprint:" as *u8, t3) 119 120 // T4 authed save (edited hero) 121 var n4: i64 = g_cat(req, 0, "POST /site/save HTTP/1.1\r\nHost: x\r\nX-Nishi-Session: " as *u8) 122 n4 = g_cat_n(req, n4, tok64, tok64n) 123 n4 = g_cat(req, n4, "\r\n\r\n" as *u8) 124 n4 = g_cat(req, n4, GCFG_EDIT) 125 let o4: i64 = sed_handle(ctx, domain, cfgpath, docroot, req, n4, out) 126 var t4: i64 = 0 127 if g_starts(out, o4, "HTTP/1.1 200" as *u8) == 1 { if g_contains(out, o4, "SAVED" as *u8) == 1 { if g_file_has(cfgpath, "glacierheadline" as *u8) == 1 { t4 = 1 } } } 128 pass = pass + g_verdict("T4 authed save -> SAVED + config carries the edit:" as *u8, t4) 129 130 // T5 NEG bad config refused, live config unchanged 131 var n5: i64 = g_cat(req, 0, "POST /site/save HTTP/1.1\r\nHost: x\r\nX-Nishi-Session: " as *u8) 132 n5 = g_cat_n(req, n5, tok64, tok64n) 133 n5 = g_cat(req, n5, "\r\n\r\n" as *u8) 134 n5 = g_cat(req, n5, GCFG_BAD) 135 let o5: i64 = sed_handle(ctx, domain, cfgpath, docroot, req, n5, out) 136 var t5: i64 = 0 137 if g_starts(out, o5, "HTTP/1.1 400" as *u8) == 1 { if g_file_has(cfgpath, "glacierheadline" as *u8) == 1 { t5 = 1 } } 138 pass = pass + g_verdict("T5 NEG hero-less config -> 400 + config unchanged:" as *u8, t5) 139 140 // T6 preview 141 var n6: i64 = g_cat(req, 0, "POST /site/preview HTTP/1.1\r\nHost: x\r\nX-Nishi-Session: " as *u8) 142 n6 = g_cat_n(req, n6, tok64, tok64n) 143 n6 = g_cat(req, n6, "\r\n\r\n" as *u8) 144 let o6: i64 = sed_handle(ctx, domain, cfgpath, docroot, req, n6, out) 145 var t6: i64 = 0 146 if g_starts(out, o6, "HTTP/1.1 200" as *u8) == 1 { if g_file_has("/tmp/nx_sed_gate_root/preview/index.html" as *u8, "glacierheadline" as *u8) == 1 { if g_file_has("/tmp/nx_sed_gate_root/index.html" as *u8, "OLD-HOMEPAGE-SENTINEL" as *u8) == 1 { t6 = 1 } } } 147 pass = pass + g_verdict("T6 preview builds under /preview, live untouched:" as *u8, t6) 148 149 // T7 publish 150 var n7: i64 = g_cat(req, 0, "POST /site/publish HTTP/1.1\r\nHost: x\r\nX-Nishi-Session: " as *u8) 151 n7 = g_cat_n(req, n7, tok64, tok64n) 152 n7 = g_cat(req, n7, "\r\n\r\n" as *u8) 153 let o7: i64 = sed_handle(ctx, domain, cfgpath, docroot, req, n7, out) 154 var t7: i64 = 0 155 if g_starts(out, o7, "HTTP/1.1 200" as *u8) == 1 { 156 if g_file_has("/tmp/nx_sed_gate_root/index.html" as *u8, "glacierheadline" as *u8) == 1 { 157 if g_file_exists("/tmp/nx_sed_gate_root/practice/family-law.html" as *u8) == 1 { 158 if g_file_has("/tmp/nx_sed_gate_root/sitemap.xml" as *u8, "family-law" as *u8) == 1 { 159 if g_file_has("/tmp/nx_sed_gate_root/.prev-index.html" as *u8, "OLD-HOMEPAGE-SENTINEL" as *u8) == 1 { t7 = 1 } 160 } 161 } 162 } 163 } 164 pass = pass + g_verdict("T7 publish -> live pages + sitemap + .prev backup:" as *u8, t7) 165 166 // T8 NEG publish without session leaves T7's live content 167 let n8: i64 = g_cat(req, 0, "POST /site/publish HTTP/1.1\r\nHost: x\r\n\r\n" as *u8) 168 let o8: i64 = sed_handle(ctx, domain, cfgpath, docroot, req, n8, out) 169 var t8: i64 = 0 170 if g_starts(out, o8, "HTTP/1.1 401" as *u8) == 1 { if g_file_has("/tmp/nx_sed_gate_root/index.html" as *u8, "glacierheadline" as *u8) == 1 { t8 = 1 } } 171 pass = pass + g_verdict("T8 NEG no-session publish -> 401, live intact:" as *u8, t8) 172 173 g_w("----\nSITEEDIT rows=8 pass=" as *u8) 174 let db: *u8 = sys_mmap(8); var v: i64 = pass; var k: i64 = 0 175 if v == 0 { db[0] = 48 as u8; k = 1 } else { while v > 0 { db[k] = (48 + (v % 10)) as u8; v = v / 10; k = k + 1 } } 176 let rv: *u8 = sys_mmap(8); var x: i64 = 0 177 while x < k { rv[x] = db[k - 1 - x]; x = x + 1 } 178 sys_write(1, rv, k); g_w("\n" as *u8) 179 let lg: i64 = sys_openat_append("knowledge/status/siteedit_gate.log" as *u8, 0x1a4) 180 if lg >= 0 { 181 g_lw(lg, "SITEEDIT auth=modern-opaque shell=" as *u8); if t1 == 1 { g_lw(lg, "1" as *u8) } else { g_lw(lg, "0" as *u8) } 182 g_lw(lg, " deny_no_session=" as *u8); if t2 == 1 { g_lw(lg, "1" as *u8) } else { g_lw(lg, "0" as *u8) } 183 g_lw(lg, " config_read=" as *u8); if t3 == 1 { g_lw(lg, "1" as *u8) } else { g_lw(lg, "0" as *u8) } 184 g_lw(lg, " save_versioned=" as *u8); if t4 == 1 { g_lw(lg, "1" as *u8) } else { g_lw(lg, "0" as *u8) } 185 g_lw(lg, " refuse_bad_config=" as *u8); if t5 == 1 { g_lw(lg, "1" as *u8) } else { g_lw(lg, "0" as *u8) } 186 g_lw(lg, " preview_isolated=" as *u8); if t6 == 1 { g_lw(lg, "1" as *u8) } else { g_lw(lg, "0" as *u8) } 187 g_lw(lg, " publish_backup=" as *u8); if t7 == 1 { g_lw(lg, "1" as *u8) } else { g_lw(lg, "0" as *u8) } 188 g_lw(lg, " deny_publish_no_session=" as *u8); if t8 == 1 { g_lw(lg, "1" as *u8) } else { g_lw(lg, "0" as *u8) } 189 g_lw(lg, " rows=8 pass=" as *u8); sys_write(lg, rv, k) 190 if pass == 8 { g_lw(lg, " verdict=GREEN\n" as *u8) } else { g_lw(lg, " verdict=RED\n" as *u8) } 191 sys_close(lg) 192 } 193 if pass == 8 { g_w("SITEEDIT GREEN (auth-gated save/preview/publish; refuse-bad-config; .prev revert kept)\n" as *u8); sys_exit(0); return 0 } 194 g_w("SITEEDIT RED\n" as *u8); sys_exit(1); return 1 195}