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}