code wiki / _hdl_build / nx_hr_serve3_slot_gate.nx

nx_hr_serve3_slot_gate.nx source

↩ module page · 216 lines · 9690 B

1// nx_hr_serve3_slot_gate.nx -- GATE for hr_serve3_slot (universal ad-slot injection at the file server). 2// Proves on REAL files in a hermetic /tmp docroot: (T1) html gets the slot immediately before the LAST 3// </body> with a CORRECT Content-Length; (T2) non-html untouched; (T3) html without </body> untouched; 4// (T4) nx-ad-optout page untouched; (T5) slot_n==0 -> BYTE-IDENTICAL to hr_serve3 (the do-no-harm law); 5// (T6) canonical 301 path identical in both; (T7) helper detectors FIRE on planted vectors. 6// expect_exit: 0 license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "_hdl_build/nx_host_router.nx" 9import "nx_gate_verdict.nx" 10 11func hg_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 12func hg_putn(v: i64) -> i64 { var m: i64 = v; if m < 0 { hg_puts("-" as *u8); m = 0 - m } let t: *u8 = sys_mmap(24); var k: i64 = 0; 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 } let o: *u8 = sys_mmap(24); var i: i64 = 0; while i < k { o[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, o, k); return 0 } 13func hg_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64 = off; var i: i64 = 0; while s[i] != (0 as u8) { dst[o] = s[i]; o = o + 1; i = i + 1 } return o } 14 15func hg_has(hay: *u8, n: i64, needle: *u8) -> i64 { 16 var nl: i64 = 0 17 while needle[nl] != (0 as u8) { nl = nl + 1 } 18 if nl == 0 { return 0 } 19 var i: i64 = 0 20 while i + nl <= n { 21 var j: i64 = 0 22 var eq: i64 = 1 23 while j < nl { if hay[i + j] != needle[j] { eq = 0; break } j = j + 1 } 24 if eq == 1 { return 1 } 25 i = i + 1 26 } 27 return 0 28} 29 30func hg_memeq(a: *u8, b: *u8, n: i64) -> i64 { 31 var i: i64 = 0 32 while i < n { if a[i] != b[i] { return 0 } i = i + 1 } 33 return 1 34} 35 36func hg_wfile(path: *u8, content: *u8) -> i64 { 37 let fd: i64 = sys_openat_wr(path, 0x1A4) 38 if fd < 0 { return 0 } 39 var n: i64 = 0 40 while content[n] != (0 as u8) { n = n + 1 } 41 sys_write(fd, content, n) 42 sys_close(fd) 43 return 1 44} 45 46// parse "Content-Length: <N>" out of resp; then verify bytes after CRLFCRLF == N. 1 = coherent. 47func hg_cl_coherent(resp: *u8, n: i64) -> i64 { 48 var cl: i64 = 0 - 1 49 var i: i64 = 0 50 while i + 16 <= n { 51 if resp[i] == (67 as u8) { 52 if hg_memeq(((resp as i64 + i) as *u8), "Content-Length: " as *u8, 16) == 1 { 53 var v: i64 = 0 54 var k: i64 = i + 16 55 while k < n { let c: u8 = resp[k]; if c < (48 as u8) { break } if c > (57 as u8) { break } v = v * 10 + ((c as i64) - 48); k = k + 1 } 56 cl = v 57 break 58 } 59 } 60 i = i + 1 61 } 62 if cl < 0 { return 0 } 63 var hoff: i64 = 0 - 1 64 i = 0 65 while i + 4 <= n { 66 if resp[i] == (13 as u8) { 67 if resp[i + 1] == (10 as u8) { if resp[i + 2] == (13 as u8) { if resp[i + 3] == (10 as u8) { hoff = i + 4; break } } } 68 } 69 i = i + 1 70 } 71 if hoff < 0 { return 0 } 72 if n - hoff == cl { return 1 } 73 return 0 74} 75 76func hg_req(dst: *u8, path: *u8) -> i64 { 77 var o: i64 = 0 78 o = hg_cat(dst, o, "GET " as *u8) 79 o = hg_cat(dst, o, path) 80 o = hg_cat(dst, o, " HTTP/1.1\r\nHost: nishifamily.com\r\nAccept: text/html\r\n\r\n" as *u8) 81 return o 82} 83 84func main() -> i64 { 85 hg_puts("=== nx_hr_serve3_slot_gate ===\n" as *u8) 86 var pass: i64 = 0 87 var fail: i64 = 0 88 89 sys_mkdir("/tmp/hrslot_root" as *u8, 0x1ED) 90 var wok: i64 = 0 91 wok = wok + hg_wfile("/tmp/hrslot_root/index.html" as *u8, "<html><head><title>T</title></head><body><p>Hello</p></body></html>" as *u8) 92 wok = wok + hg_wfile("/tmp/hrslot_root/style.css" as *u8, "body{color:red}" as *u8) 93 wok = wok + hg_wfile("/tmp/hrslot_root/noclose.html" as *u8, "<html><body><p>no close tag here" as *u8) 94 wok = wok + hg_wfile("/tmp/hrslot_root/optout.html" as *u8, "<html><body><span>nx-ad-optout</span><p>quiet page</p></body></html>" as *u8) 95 if wok == 4 { pass = pass + 1 } else { fail = fail + 1; hg_puts("SETUP FAIL files\n" as *u8) } 96 97 let cfg: *u8 = sys_mmap(256) 98 var cfgn: i64 = 0 99 cfgn = hg_cat(cfg, cfgn, "nishifamily.com /tmp/hrslot_root" as *u8) 100 cfg[cfgn] = 10 as u8 101 cfgn = cfgn + 1 102 103 let req: *u8 = sys_mmap(1024) 104 let outA: *u8 = sys_mmap(262144) 105 let outB: *u8 = sys_mmap(262144) 106 let nA: *i64 = (sys_mmap(8)) as *i64 107 let nB: *i64 = (sys_mmap(8)) as *i64 108 let inj: *i64 = (sys_mmap(8)) as *i64 109 let slot: *u8 = "<aside class=nx-ad-slot>AD HERE</aside>" as *u8 110 let slotn: i64 = hr_slen(slot) 111 112 // T1 inject into html + correct Content-Length + slot sits right before </body> 113 let rq1: i64 = hg_req(req, "/" as *u8) 114 let v1: i64 = hr_serve3_slot(cfg, cfgn, req, rq1, outB, 262144, nB, slot, slotn, inj) 115 var t1: i64 = 1 116 if v1 != HR_S2_OK { t1 = 0 } 117 if inj[0] != 1 { t1 = 0 } 118 if hg_has(outB, nB[0], "AD HERE</aside></body>" as *u8) == 0 { t1 = 0 } 119 if hg_cl_coherent(outB, nB[0]) == 0 { t1 = 0 } 120 if t1 == 1 { pass = pass + 1 } else { fail = fail + 1; hg_puts("T1 FAIL html inject\n" as *u8) } 121 122 // T2 css untouched (no injection, still OK, CL coherent) 123 let rq2: i64 = hg_req(req, "/style.css" as *u8) 124 let v2: i64 = hr_serve3_slot(cfg, cfgn, req, rq2, outB, 262144, nB, slot, slotn, inj) 125 var t2: i64 = 1 126 if v2 != HR_S2_OK { t2 = 0 } 127 if inj[0] != 0 { t2 = 0 } 128 if hg_has(outB, nB[0], "nx-ad-slot" as *u8) == 1 { t2 = 0 } 129 if hg_cl_coherent(outB, nB[0]) == 0 { t2 = 0 } 130 if t2 == 1 { pass = pass + 1 } else { fail = fail + 1; hg_puts("T2 FAIL css untouched\n" as *u8) } 131 132 // T3 html without </body> untouched 133 let rq3: i64 = hg_req(req, "/noclose" as *u8) 134 let v3: i64 = hr_serve3_slot(cfg, cfgn, req, rq3, outB, 262144, nB, slot, slotn, inj) 135 var t3: i64 = 1 136 if v3 != HR_S2_OK { t3 = 0 } 137 if inj[0] != 0 { t3 = 0 } 138 if hg_has(outB, nB[0], "AD HERE" as *u8) == 1 { t3 = 0 } 139 if t3 == 1 { pass = pass + 1 } else { fail = fail + 1; hg_puts("T3 FAIL noclose untouched\n" as *u8) } 140 141 // T4 optout page untouched 142 let rq4: i64 = hg_req(req, "/optout" as *u8) 143 let v4: i64 = hr_serve3_slot(cfg, cfgn, req, rq4, outB, 262144, nB, slot, slotn, inj) 144 var t4: i64 = 1 145 if v4 != HR_S2_OK { t4 = 0 } 146 if inj[0] != 0 { t4 = 0 } 147 if hg_has(outB, nB[0], "AD HERE" as *u8) == 1 { t4 = 0 } 148 if t4 == 1 { pass = pass + 1 } else { fail = fail + 1; hg_puts("T4 FAIL optout untouched\n" as *u8) } 149 150 // T5 do-no-harm: slot_n==0 -> BYTE-IDENTICAL to hr_serve3 (html + css + optout) 151 var t5: i64 = 1 152 let rq5: i64 = hg_req(req, "/" as *u8) 153 let va: i64 = hr_serve3(cfg, cfgn, req, rq5, outA, 262144, nA) 154 let vb: i64 = hr_serve3_slot(cfg, cfgn, req, rq5, outB, 262144, nB, slot, 0, inj) 155 if va != vb { t5 = 0 } 156 if nA[0] != nB[0] { t5 = 0 } 157 if hg_memeq(outA, outB, nA[0]) == 0 { t5 = 0 } 158 if inj[0] != 0 { t5 = 0 } 159 let rq5b: i64 = hg_req(req, "/style.css" as *u8) 160 let va2: i64 = hr_serve3(cfg, cfgn, req, rq5b, outA, 262144, nA) 161 let vb2: i64 = hr_serve3_slot(cfg, cfgn, req, rq5b, outB, 262144, nB, slot, 0, inj) 162 if va2 != vb2 { t5 = 0 } 163 if nA[0] != nB[0] { t5 = 0 } 164 if hg_memeq(outA, outB, nA[0]) == 0 { t5 = 0 } 165 if t5 == 1 { pass = pass + 1 } else { fail = fail + 1; hg_puts("T5 FAIL byte-identical control\n" as *u8) } 166 167 // T6 canonical 301 identical in both (slot never touches redirects) 168 var t6: i64 = 1 169 let rq6: i64 = hg_req(req, "/index.html" as *u8) 170 let vc: i64 = hr_serve3(cfg, cfgn, req, rq6, outA, 262144, nA) 171 let vd: i64 = hr_serve3_slot(cfg, cfgn, req, rq6, outB, 262144, nB, slot, slotn, inj) 172 if vc != HR_S2_REDIR { t6 = 0 } 173 if vd != HR_S2_REDIR { t6 = 0 } 174 if nA[0] != nB[0] { t6 = 0 } 175 if hg_memeq(outA, outB, nA[0]) == 0 { t6 = 0 } 176 if inj[0] != 0 { t6 = 0 } 177 if t6 == 1 { pass = pass + 1 } else { fail = fail + 1; hg_puts("T6 FAIL redirect identical\n" as *u8) } 178 179 // T7 detectors fire (anti-false-green) 180 var t7: i64 = 1 181 let ob: *u8 = "abc nx-ad-optout xyz" as *u8 182 if hr_has_optout(ob, 20) == 0 { t7 = 0 } 183 let cb: *u8 = "no close body here at all" as *u8 184 let fcb: i64 = hr_find_close_body(cb, 25) 185 if fcb >= 0 { t7 = 0 } 186 let hb: *u8 = "<html><body>x</body>" as *u8 187 let fcb2: i64 = hr_find_close_body(hb, 20) 188 if fcb2 != 13 { t7 = 0 } 189 if hr_ct_is_html("text/css" as *u8) == 1 { t7 = 0 } 190 if hr_ct_is_html("text/html; charset=utf-8" as *u8) == 0 { t7 = 0 } 191 if t7 == 1 { pass = pass + 1 } else { fail = fail + 1; hg_puts("T7 FAIL detectors\n" as *u8) } 192 193 hg_puts("pass=" as *u8) 194 hg_putn(pass) 195 hg_puts(" fail=" as *u8) 196 hg_putn(fail) 197 hg_puts("\n" as *u8) 198 199 let log: *u8 = sys_mmap(256) 200 var lo: i64 = 0 201 if fail == 0 { lo = hg_cat(log, lo, "HRSLOTGATE authored=organ verdict=GREEN" as *u8) } else { lo = hg_cat(log, lo, "HRSLOTGATE authored=organ verdict=RED" as *u8) } 202 log[lo] = 10 as u8 203 lo = lo + 1 204 let fd: i64 = sys_openat_wr("knowledge/status/hr_serve3_slot.log" as *u8, 0x1A4) 205 if fd >= 0 { sys_write(fd, log, lo); sys_close(fd) } 206 207 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 208 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 209 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 210 let ctr__dry: *i64 = gv_ctr() 211 ctr__dry[0] = pass 212 ctr__dry[1] = pass + fail 213 let rc__dry: i64 = gv_verdict("HR-SERVE3-SLOT-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 214 sys_exit(rc__dry) 215 return rc__dry 216}