code wiki / _hdl_build / nx_cms_blocks_gate.nx

nx_cms_blocks_gate.nx source

↩ module page · 449 lines · 19481 B

1// nx_cms_blocks_gate.nx -- CMS C11 GATE (re-runnable, evidence-driven): BLOCK EDITOR + UPLOADS at 2// the wire (the Gutenberg + media-library class). Proves: blocks render through the TEAM-AUTHORED 3// _pe_blockr (TEMPLATE_TABLE, emit12) in document order; rich blocks sanitized ON WRITE; image 4// uploads are REAL multipart (nx_multipart) and pass only when magic bytes MATCH the extension, 5// size is within the site's upload.conf cap (config DATA), and the name is traversal-proof; served 6// uploads come back BYTE-IDENTICAL with the right Content-Type; block ops are session+CSRF guarded. 7// Appends "CMSGATE row=nx_cms_blocks ... verdict=PASS|FAIL". Exit 0 iff all rows pass. 8// license_tier: ORIGINAL 9import "nx_cms_store.nx" 10import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 11import "nx_sha256.nx" 12import "nx_syscalls.nx" 13import "_hdl_build/nx_kill_portable.nx" 14 15const KB_PORT: i64 = 8085 16const KB_SITE: *u8 = "/tmp/_cms_blocks_site" 17 18func kb_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 kb_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 20func kb_cat(dst: *u8, off: i64, s: *u8) -> i64 { 21 var o: i64 = off 22 var k: i64 = 0 23 while s[k] != (0 as u8) { dst[o] = s[k]; o = o + 1; k = k + 1 } 24 return o 25} 26func kb_catn(dst: *u8, off: i64, s: *u8, n: i64) -> i64 { 27 var o: i64 = off 28 var k: i64 = 0 29 while k < n { dst[o] = s[k]; o = o + 1; k = k + 1 } 30 return o 31} 32func kb_index(hay: *u8, n: i64, needle: *u8) -> i64 { 33 let nl: i64 = kb_len(needle) 34 var i: i64 = 0 35 while i + nl <= n { 36 var q: i64 = 0 37 var ok: i64 = 1 38 while q < nl { if (hay[i+q] as i64) != (needle[q] as i64) { ok = 0; q = nl } q = q + 1 } 39 if ok == 1 { return i } 40 i = i + 1 41 } 42 return 0 - 1 43} 44func kb_has(hay: *u8, n: i64, needle: *u8) -> i64 { if kb_index(hay, n, needle) >= 0 { return 1 } return 0 } 45 46func kb_http(req: *u8, rl: i64, resp: *u8, cap: i64) -> i64 { 47 let fd: i64 = sys_socket(2, 1, 0) 48 if fd < 0 { return 0 - 1 } 49 let addr: *u8 = sys_mmap(16) 50 addr[0] = 2 as u8; addr[1] = 0 as u8 51 addr[2] = ((KB_PORT >> 8) & 0xff) as u8 52 addr[3] = (KB_PORT & 0xff) as u8 53 addr[4] = 127 as u8; addr[5] = 0 as u8; addr[6] = 0 as u8; addr[7] = 1 as u8 54 var zi: i64 = 8 55 while zi < 16 { addr[zi] = 0 as u8; zi = zi + 1 } 56 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(fd); return 0 - 1 } 57 var off: i64 = 0 58 var go: i64 = 1 59 while go == 1 { 60 go = 0 61 if off < rl { 62 let w: i64 = sys_write(fd, (req + off) as *u8, rl - off) 63 if w > 0 { off = off + w; go = 1 } 64 } 65 } 66 sys_set_socket_timeout(fd, 10) 67 var total: i64 = 0 68 go = 1 69 while go == 1 { 70 go = 0 71 if total < cap { 72 let r: i64 = sys_read(fd, (resp + total) as *u8, cap - total) 73 if r > 0 { total = total + r; go = 1 } 74 } 75 } 76 sys_close(fd) 77 return total 78} 79 80func kb_post(req: *u8, path: *u8, cookie: *u8, body: *u8) -> i64 { 81 var o: i64 = kb_cat(req, 0, "POST " as *u8) 82 o = kb_cat(req, o, path) 83 o = kb_cat(req, o, " HTTP/1.1\r\nHost: gate\r\nContent-Type: application/x-www-form-urlencoded\r\n" as *u8) 84 if (cookie[0] as i64) != 0 { 85 o = kb_cat(req, o, "Cookie: nsess=" as *u8) 86 o = kb_catn(req, o, cookie, 32) 87 o = kb_cat(req, o, "\r\n" as *u8) 88 } 89 o = kb_cat(req, o, "Content-Length: " as *u8) 90 let bl: i64 = kb_len(body) 91 let t: *u8 = sys_mmap(24) 92 var m: i64 = bl 93 var k: i64 = 0 94 if m == 0 { t[0] = 48 as u8; k = 1 } 95 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 96 var i: i64 = 0 97 while i < k { req[o] = t[k-1-i]; o = o + 1; i = i + 1 } 98 o = kb_cat(req, o, "\r\n\r\n" as *u8) 99 o = kb_cat(req, o, body) 100 return o 101} 102// multipart POST: binary-safe body of blen bytes with the gate's fixed boundary 103func kb_post_mp(req: *u8, path: *u8, cookie: *u8, body: *u8, blen: i64) -> i64 { 104 var o: i64 = kb_cat(req, 0, "POST " as *u8) 105 o = kb_cat(req, o, path) 106 o = kb_cat(req, o, " HTTP/1.1\r\nHost: gate\r\nContent-Type: multipart/form-data; boundary=----nxgate\r\n" as *u8) 107 if (cookie[0] as i64) != 0 { 108 o = kb_cat(req, o, "Cookie: nsess=" as *u8) 109 o = kb_catn(req, o, cookie, 32) 110 o = kb_cat(req, o, "\r\n" as *u8) 111 } 112 o = kb_cat(req, o, "Content-Length: " as *u8) 113 let t: *u8 = sys_mmap(24) 114 var m: i64 = blen 115 var k: i64 = 0 116 if m == 0 { t[0] = 48 as u8; k = 1 } 117 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 118 var i: i64 = 0 119 while i < k { req[o] = t[k-1-i]; o = o + 1; i = i + 1 } 120 o = kb_cat(req, o, "\r\n\r\n" as *u8) 121 o = kb_catn(req, o, body, blen) 122 return o 123} 124func kb_get(req: *u8, path: *u8, cookie: *u8) -> i64 { 125 var o: i64 = kb_cat(req, 0, "GET " as *u8) 126 o = kb_cat(req, o, path) 127 o = kb_cat(req, o, " HTTP/1.1\r\nHost: gate\r\n" as *u8) 128 if (cookie[0] as i64) != 0 { 129 o = kb_cat(req, o, "Cookie: nsess=" as *u8) 130 o = kb_catn(req, o, cookie, 32) 131 o = kb_cat(req, o, "\r\n" as *u8) 132 } 133 o = kb_cat(req, o, "\r\n" as *u8) 134 return o 135} 136 137// build a multipart body: csrf part + file part (fname, payload). returns body length. 138func kb_mp_body(out: *u8, csrf: *u8, fname: *u8, pay: *u8, plen: i64) -> i64 { 139 var o: i64 = kb_cat(out, 0, "------nxgate\r\nContent-Disposition: form-data; name=\"csrf\"\r\n\r\n" as *u8) 140 o = kb_catn(out, o, csrf, 32) 141 o = kb_cat(out, o, "\r\n------nxgate\r\nContent-Disposition: form-data; name=\"file\"; filename=\"" as *u8) 142 o = kb_cat(out, o, fname) 143 o = kb_cat(out, o, "\"\r\nContent-Type: application/octet-stream\r\n\r\n" as *u8) 144 o = kb_catn(out, o, pay, plen) 145 o = kb_cat(out, o, "\r\n------nxgate--\r\n" as *u8) 146 return o 147} 148 149func kb_row(id: i64, ok: i64, what: *u8) -> i64 { 150 kb_w("BLOCKROW " as *u8) 151 let d: *u8 = sys_mmap(8); d[0] = (48 + id/10) as u8; d[1] = (48 + id%10) as u8; d[2] = 32 as u8; d[3] = 0 as u8 152 kb_w(d) 153 if ok == 1 { kb_w("PASS " as *u8) } 154 if ok == 0 { kb_w("FAIL " as *u8) } 155 kb_w(what); kb_w("\n" as *u8) 156 return ok 157} 158 159func kb_mkdir(path: *u8, mode: i64) -> i64 { 160 let nb: *i64 = sys_mmap(8) as *i64 161 nb[0] = 258 162 return __syscall(nb[0], 0 - 100, path, mode, 0, 0, 0) 163} 164func kb_writefile(path: *u8, buf: *u8, n: i64) -> i64 { 165 let fd: i64 = sys_openat_wr(path, 0x1a4) 166 if fd < 0 { return 0 } 167 sys_write(fd, buf, n) 168 sys_close(fd) 169 return 1 170} 171 172func main() -> i64 { 173 // ---- fixture site: template WITH {{blocks_html}}, andelinwest content, small upload cap ---- 174 kb_mkdir(KB_SITE, 0x1ed) 175 let tpl: *u8 = "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>{{hero_title}}</title></head><body><h1>{{hero_title}}</h1><main>{{blocks_html}}</main></body></html>" as *u8 176 kb_writefile("/tmp/_cms_blocks_site/template.html" as *u8, tpl, kb_len(tpl)) 177 cst_copy("knowledge/cms/andelinwest/content.txt" as *u8, "/tmp/_cms_blocks_site/content.txt" as *u8) 178 sys_renameat("/tmp/_cms_blocks_site/draft.txt" as *u8, "/tmp/_cms_blocks_site/draft.stale" as *u8) 179 let conf: *u8 = "max_bytes=1000\n" as *u8 180 kb_writefile("/tmp/_cms_blocks_site/upload.conf" as *u8, conf, kb_len(conf)) 181 let dig: *u8 = sys_mmap(32) 182 sha256_digest("blocks-gate-pw-1" as *u8, 16, dig) 183 let hexs: *u8 = sys_mmap(80) 184 let hxc: *u8 = "0123456789abcdef" as *u8 185 var i: i64 = 0 186 while i < 32 { 187 hexs[i*2] = hxc[((dig[i] as i64) >> 4) & 15] 188 hexs[i*2+1] = hxc[(dig[i] as i64) & 15] 189 i = i + 1 190 } 191 kb_writefile("/tmp/_cms_blocks_site/admin_pw.sha256" as *u8, hexs, 64) 192 193 // ---- rebuild + boot the admin ---- 194 let bpid: i64 = sys_fork() 195 if bpid == 0 { 196 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 197 if dn >= 0 { sys_dup3(dn, 1, 0); sys_dup3(dn, 2, 0) } 198 let bargv: *i64 = sys_mmap(32) as *i64 199 bargv[0] = "_offc/nx_sov_build_run.elf" as *u8 as i64 200 bargv[1] = "nx_cms_admin" as *u8 as i64 201 bargv[2] = 0 202 let benvp: *i64 = sys_mmap(16) as *i64 203 benvp[0] = 0 204 sys_execve("_offc/nx_sov_build_run.elf" as *u8, bargv, benvp); sys_exit(127) 205 } 206 let bst: *i64 = sys_mmap(16) as *i64 207 sys_wait4(bpid, bst, 0) 208 // CALLER-GAP FIX 2026-08-01 (seq1267 class): the runner emits buildroot/_build/<t>.sov.elf under 209 // the no-/tmp doctrine while this probed /tmp. Proven by control: _build copy 208962B, /tmp absent. 210 var bchk: i64 = sys_openat_rd("buildroot/_build/nx_cms_admin.sov.elf" as *u8) 211 var badm: *u8 = "buildroot/_build/nx_cms_admin.sov.elf" as *u8 212 if bchk < 0 { bchk = sys_openat_rd("/tmp/nx_cms_admin.sov.elf" as *u8); badm = "/tmp/nx_cms_admin.sov.elf" as *u8 } 213 if bchk < 0 { kb_w("CMSGATE row=nx_cms_blocks ADMIN-BUILD-MISSING (probed buildroot/_build AND /tmp)\n" as *u8); sys_exit(1) } 214 sys_close(bchk) 215 let pid: i64 = sys_fork() 216 if pid == 0 { 217 let lg: i64 = sys_openat_wr("/tmp/_cms_admin_blocks_gate.log" as *u8, 0x1a4) 218 if lg >= 0 { sys_dup3(lg, 1, 0); sys_dup3(lg, 2, 0) } 219 let argvv: *i64 = sys_mmap(8*6) as *i64 220 argvv[0] = badm as i64 221 argvv[1] = "8085" as *u8 as i64 222 argvv[2] = KB_SITE as i64 223 argvv[3] = "99" as *u8 as i64 224 argvv[4] = 0 225 let envp: *i64 = sys_mmap(16) as *i64 226 envp[0] = 0 227 sys_execve(badm, argvv, envp) 228 sys_exit(127) 229 } 230 231 let req: *u8 = sys_mmap(262144) 232 let resp: *u8 = sys_mmap(524288) 233 let nocookie: *u8 = sys_mmap(8) 234 nocookie[0] = 0 as u8 235 let sess: *u8 = sys_mmap(64) 236 sess[0] = 0 as u8 237 let csrf: *u8 = sys_mmap(64) 238 csrf[0] = 0 as u8 239 240 // K0: boot + login + csrf 241 var rn: i64 = 0 - 1 242 var tries: i64 = 0 243 while tries < 50 { 244 let rl0: i64 = kb_get(req, "/" as *u8, nocookie) 245 rn = kb_http(req, rl0, resp, 524287) 246 if rn > 0 { tries = 50 } 247 if rn <= 0 { sys_sleep_ms(100); tries = tries + 1 } 248 } 249 var pass: i64 = 0 250 var rows: i64 = 0 251 var ok: i64 = 0 252 var rl: i64 = kb_post(req, "/admin/login" as *u8, nocookie, "pw=blocks-gate-pw-1" as *u8) 253 rn = kb_http(req, rl, resp, 524287) 254 if rn > 0 { if kb_has(resp, rn, "303" as *u8) == 1 { 255 let sp: i64 = kb_index(resp, rn, "nsess=" as *u8) 256 if sp >= 0 { 257 var m2: i64 = 0 258 while m2 < 32 { sess[m2] = resp[sp+6+m2]; m2 = m2 + 1 } 259 sess[32] = 0 as u8 260 rl = kb_get(req, "/admin" as *u8, sess) 261 rn = kb_http(req, rl, resp, 524287) 262 let cp: i64 = kb_index(resp, rn, "name=\"csrf\" value=\"" as *u8) 263 if cp >= 0 { 264 var m3: i64 = 0 265 while m3 < 32 { csrf[m3] = resp[cp+19+m3]; m3 = m3 + 1 } 266 csrf[32] = 0 as u8 267 ok = 1 268 } 269 } 270 } } 271 rows = rows + 1; pass = pass + kb_row(0, ok, "boot + login + csrf" as *u8) 272 273 // K1: blocks editor reachable (session) 274 rl = kb_get(req, "/admin/blocks" as *u8, sess) 275 rn = kb_http(req, rl, resp, 524287) 276 ok = 0 277 if rn > 0 { if kb_has(resp, rn, "Block Editor" as *u8) == 1 { ok = 1 } } 278 rows = rows + 1; pass = pass + kb_row(1, ok, "block editor page (session)" as *u8) 279 280 // K2: add h2 block -> live page renders <h2> via the TEAM renderer 281 let body2: *u8 = sys_mmap(4096) 282 var b2: i64 = kb_cat(body2, 0, "csrf=" as *u8) 283 b2 = kb_catn(body2, b2, csrf, 32) 284 b2 = kb_cat(body2, b2, "&op=add&kind=2&payload=Our+Services" as *u8) 285 body2[b2] = 0 as u8 286 rl = kb_post(req, "/admin/blocks" as *u8, sess, body2) 287 rn = kb_http(req, rl, resp, 524287) 288 ok = 0 289 if rn > 0 { if kb_has(resp, rn, "303" as *u8) == 1 { 290 let rl2: i64 = kb_get(req, "/" as *u8, nocookie) 291 let rn2: i64 = kb_http(req, rl2, resp, 524287) 292 if rn2 > 0 { if kb_has(resp, rn2, "<h2>Our Services</h2>" as *u8) == 1 { ok = 1 } } 293 } } 294 rows = rows + 1; pass = pass + kb_row(2, ok, "h2 block live via team renderer" as *u8) 295 296 // K3: rich block sanitized on write (script gone, <b> kept) 297 let body3: *u8 = sys_mmap(4096) 298 var b3: i64 = kb_cat(body3, 0, "csrf=" as *u8) 299 b3 = kb_catn(body3, b3, csrf, 32) 300 b3 = kb_cat(body3, b3, "&op=add&kind=1&payload=%3Cscript%3Ealert%283%29%3C%2Fscript%3E%3Cb%3Ebold+stays%3C%2Fb%3E" as *u8) 301 body3[b3] = 0 as u8 302 rl = kb_post(req, "/admin/blocks" as *u8, sess, body3) 303 rn = kb_http(req, rl, resp, 524287) 304 ok = 0 305 if rn > 0 { if kb_has(resp, rn, "303" as *u8) == 1 { 306 let rl3: i64 = kb_get(req, "/" as *u8, nocookie) 307 let rn3: i64 = kb_http(req, rl3, resp, 524287) 308 if rn3 > 0 { if kb_has(resp, rn3, "<b>bold stays</b>" as *u8) == 1 { if kb_has(resp, rn3, "<script" as *u8) == 0 { ok = 1 } } } 309 } } 310 rows = rows + 1; pass = pass + kb_row(3, ok, "rich block sanitized on write" as *u8) 311 312 // K4: REAL multipart upload (valid PNG magic) -> served back byte-identical + image/png 313 let png: *u8 = sys_mmap(256) 314 png[0] = 0x89 as u8; png[1] = 0x50 as u8; png[2] = 0x4e as u8; png[3] = 0x47 as u8 315 var pi2: i64 = 4 316 while pi2 < 100 { png[pi2] = ((pi2 * 7 + 3) & 0xff) as u8; pi2 = pi2 + 1 } 317 let mpb: *u8 = sys_mmap(8192) 318 var mpn: i64 = kb_mp_body(mpb, csrf, "logo.png" as *u8, png, 100) 319 rl = kb_post_mp(req, "/admin/upload" as *u8, sess, mpb, mpn) 320 rn = kb_http(req, rl, resp, 524287) 321 ok = 0 322 if rn > 0 { if kb_has(resp, rn, "303" as *u8) == 1 { 323 let rl4: i64 = kb_get(req, "/uploads/logo.png" as *u8, nocookie) 324 let rn4: i64 = kb_http(req, rl4, resp, 524287) 325 if rn4 > 0 { if kb_has(resp, rn4, "200 OK" as *u8) == 1 { if kb_has(resp, rn4, "image/png" as *u8) == 1 { 326 let bs: i64 = kb_index(resp, rn4, "\r\n\r\n" as *u8) 327 if bs >= 0 { if rn4 - bs - 4 == 100 { 328 var eq: i64 = 1 329 var q4: i64 = 0 330 while q4 < 100 { if resp[bs+4+q4] != png[q4] { eq = 0; q4 = 100 } q4 = q4 + 1 } 331 if eq == 1 { ok = 1 } 332 } } 333 } } } 334 } } 335 rows = rows + 1; pass = pass + kb_row(4, ok, "multipart upload + byte-identical serve" as *u8) 336 337 // K5: fake .png (bad magic) refused 400, nothing served 338 let fake: *u8 = sys_mmap(128) 339 var fi: i64 = 0 340 while fi < 50 { fake[fi] = (65 + (fi % 20)) as u8; fi = fi + 1 } 341 mpn = kb_mp_body(mpb, csrf, "evil.png" as *u8, fake, 50) 342 rl = kb_post_mp(req, "/admin/upload" as *u8, sess, mpb, mpn) 343 rn = kb_http(req, rl, resp, 524287) 344 ok = 0 345 if rn > 0 { if kb_has(resp, rn, "400" as *u8) == 1 { 346 let rl5: i64 = kb_get(req, "/uploads/evil.png" as *u8, nocookie) 347 let rn5: i64 = kb_http(req, rl5, resp, 524287) 348 if rn5 > 0 { if kb_has(resp, rn5, "404" as *u8) == 1 { ok = 1 } } 349 } } 350 rows = rows + 1; pass = pass + kb_row(5, ok, "magic-mismatch refused, nothing stored" as *u8) 351 352 // K6: oversize (valid magic, 1500B > conf 1000) refused 413 353 let big: *u8 = sys_mmap(2048) 354 big[0] = 0x89 as u8; big[1] = 0x50 as u8; big[2] = 0x4e as u8; big[3] = 0x47 as u8 355 var gi: i64 = 4 356 while gi < 1500 { big[gi] = (gi & 0xff) as u8; gi = gi + 1 } 357 mpn = kb_mp_body(mpb, csrf, "big.png" as *u8, big, 1500) 358 rl = kb_post_mp(req, "/admin/upload" as *u8, sess, mpb, mpn) 359 rn = kb_http(req, rl, resp, 524287) 360 ok = 0 361 if rn > 0 { if kb_has(resp, rn, "413" as *u8) == 1 { ok = 1 } } 362 rows = rows + 1; pass = pass + kb_row(6, ok, "size cap from upload.conf enforced 413" as *u8) 363 364 // K7: traversal filename refused 400 365 mpn = kb_mp_body(mpb, csrf, "../x.png" as *u8, png, 100) 366 rl = kb_post_mp(req, "/admin/upload" as *u8, sess, mpb, mpn) 367 rn = kb_http(req, rl, resp, 524287) 368 ok = 0 369 if rn > 0 { if kb_has(resp, rn, "400" as *u8) == 1 { ok = 1 } } 370 rows = rows + 1; pass = pass + kb_row(7, ok, "traversal filename refused 400" as *u8) 371 372 // K8: img block (src|alt) renders attr-escaped via the team renderer 373 let body8: *u8 = sys_mmap(4096) 374 var b8: i64 = kb_cat(body8, 0, "csrf=" as *u8) 375 b8 = kb_catn(body8, b8, csrf, 32) 376 b8 = kb_cat(body8, b8, "&op=add&kind=5&payload=%2Fuploads%2Flogo.png%7CTeam+logo" as *u8) 377 body8[b8] = 0 as u8 378 rl = kb_post(req, "/admin/blocks" as *u8, sess, body8) 379 rn = kb_http(req, rl, resp, 524287) 380 ok = 0 381 if rn > 0 { if kb_has(resp, rn, "303" as *u8) == 1 { 382 let rl8: i64 = kb_get(req, "/" as *u8, nocookie) 383 let rn8: i64 = kb_http(req, rl8, resp, 524287) 384 if rn8 > 0 { if kb_has(resp, rn8, "<img src=\"/uploads/logo.png\" alt=\"Team logo\">" as *u8) == 1 { ok = 1 } } 385 } } 386 rows = rows + 1; pass = pass + kb_row(8, ok, "img block renders src|alt" as *u8) 387 388 // K9: delete block 0 -> h2 gone, img remains (order + targeting proven) 389 let body9: *u8 = sys_mmap(4096) 390 var b9: i64 = kb_cat(body9, 0, "csrf=" as *u8) 391 b9 = kb_catn(body9, b9, csrf, 32) 392 b9 = kb_cat(body9, b9, "&op=del&idx=0" as *u8) 393 body9[b9] = 0 as u8 394 rl = kb_post(req, "/admin/blocks" as *u8, sess, body9) 395 rn = kb_http(req, rl, resp, 524287) 396 ok = 0 397 if rn > 0 { if kb_has(resp, rn, "303" as *u8) == 1 { 398 let rl9: i64 = kb_get(req, "/" as *u8, nocookie) 399 let rn9: i64 = kb_http(req, rl9, resp, 524287) 400 if rn9 > 0 { if kb_has(resp, rn9, "<h2>Our Services</h2>" as *u8) == 0 { if kb_has(resp, rn9, "Team logo" as *u8) == 1 { ok = 1 } } } 401 } } 402 rows = rows + 1; pass = pass + kb_row(9, ok, "delete targets block 0, others intact" as *u8) 403 404 // K10: unauth block op refused 405 rl = kb_post(req, "/admin/blocks" as *u8, nocookie, "csrf=00000000000000000000000000000000&op=add&kind=2&payload=PWNED" as *u8) 406 rn = kb_http(req, rl, resp, 524287) 407 ok = 0 408 if rn > 0 { if kb_has(resp, rn, "403" as *u8) == 1 { ok = 1 } } 409 rows = rows + 1; pass = pass + kb_row(10, ok, "unauth block op refused 403" as *u8) 410 411 // ---- teardown ---- 412 nxk_kill(pid, 9) 413 let stbuf: *i64 = sys_mmap(16) as *i64 414 sys_wait4(pid, stbuf, 0) 415 416 var verdict: *u8 = "FAIL" as *u8 417 if pass == rows { verdict = "PASS" as *u8 } 418 let line: *u8 = sys_mmap(512) 419 var lo: i64 = kb_cat(line, 0, "CMSGATE row=nx_cms_blocks rows=" as *u8) 420 let t2: *u8 = sys_mmap(24) 421 var mm: i64 = rows 422 var kk: i64 = 0 423 if mm == 0 { t2[0] = 48 as u8; kk = 1 } 424 while mm > 0 { t2[kk] = (48 + (mm % 10)) as u8; mm = mm / 10; kk = kk + 1 } 425 var ii: i64 = 0 426 while ii < kk { line[lo] = t2[kk-1-ii]; lo = lo + 1; ii = ii + 1 } 427 lo = kb_cat(line, lo, " pass=" as *u8) 428 mm = pass; kk = 0 429 if mm == 0 { t2[0] = 48 as u8; kk = 1 } 430 while mm > 0 { t2[kk] = (48 + (mm % 10)) as u8; mm = mm / 10; kk = kk + 1 } 431 ii = 0 432 while ii < kk { line[lo] = t2[kk-1-ii]; lo = lo + 1; ii = ii + 1 } 433 lo = kb_cat(line, lo, " verdict=" as *u8) 434 lo = kb_cat(line, lo, verdict) 435 lo = kb_cat(line, lo, " epoch=" as *u8) 436 mm = sys_now_realtime_sec(); kk = 0 437 if mm == 0 { t2[0] = 48 as u8; kk = 1 } 438 while mm > 0 { t2[kk] = (48 + (mm % 10)) as u8; mm = mm / 10; kk = kk + 1 } 439 ii = 0 440 while ii < kk { line[lo] = t2[kk-1-ii]; lo = lo + 1; ii = ii + 1 } 441 line[lo] = 10 as u8 442 lo = lo + 1 443 sys_write(1, line, lo) 444 let gf: i64 = sys_openat_append("knowledge/status/cms_gate.log" as *u8, 0x1a4) 445 if gf >= 0 { sys_write(gf, line, lo); sys_close(gf) } 446 if pass == rows { sys_exit(0) } 447 sys_exit(1) 448 return 1 449}