code wiki / _hdl_build / nx_adnet_submit_gate.nx

nx_adnet_submit_gate.nx source

↩ module page · 133 lines · 7129 B

1// nx_adnet_submit_gate.nx -- GATE for the self-serve intake surface (nx_adnet_submit). Proves: the GET form 2// renders zero-JS with all fields; a VALID POST stages a row (read-back verified) and shows confirmation; a 3// malicious POST (external img / javascript: click / bad slug) is REJECTED, re-populates the form ESCAPED, 4// and stages NOTHING (load-bearing negative control on the live journal); reflected XSS in a field stays 5// escaped. expect_exit: 0 license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "_hdl_build/nx_adnet_submit.nx" 8import "nx_gate_verdict.nx" 9 10func fg_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 11func fg_putn(v: i64) -> i64 { var m: i64 = v; if m < 0 { fg_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 } 12 13func fg_has(hay: *u8, n: i64, needle: *u8) -> i64 { 14 var nl: i64 = 0 15 while needle[nl] != (0 as u8) { nl = nl + 1 } 16 if nl == 0 { return 0 } 17 var i: i64 = 0 18 while i + nl <= n { 19 var j: i64 = 0 20 var eq: i64 = 1 21 while j < nl { if hay[i + j] != needle[j] { eq = 0; break } j = j + 1 } 22 if eq == 1 { return 1 } 23 i = i + 1 24 } 25 return 0 26} 27 28func fg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 29 30func fg_readn(path: *u8, box: *i64) -> *u8 { 31 box[0] = 0 32 return sys_read_file(path, box) 33} 34 35func main() -> i64 { 36 fg_puts("=== nx_adnet_submit_gate ===\n" as *u8) 37 var pass: i64 = 0 38 var fail: i64 = 0 39 let out: *u8 = sys_mmap(65536) 40 let subs: *u8 = "/tmp/adnet_submit_test.log" as *u8 41 let f0: i64 = sys_openat_wr(subs, 0x1A4) 42 if f0 >= 0 { sys_close(f0) } 43 44 // T1 GET form zero-JS with all fields 45 let w0: i64 = asf_form_page(out, 65536) 46 var t1: i64 = 1 47 if w0 <= 0 { t1 = 0 } 48 if fg_has(out, w0, "name=\"slug\"" as *u8) == 0 { t1 = 0 } 49 if fg_has(out, w0, "name=\"business\"" as *u8) == 0 { t1 = 0 } 50 if fg_has(out, w0, "name=\"img\"" as *u8) == 0 { t1 = 0 } 51 if fg_has(out, w0, "name=\"click\"" as *u8) == 0 { t1 = 0 } 52 if fg_has(out, w0, "name=\"section\"" as *u8) == 0 { t1 = 0 } 53 if fg_has(out, w0, "<script" as *u8) == 1 { t1 = 0 } 54 // empty-form fields must render EMPTY (value="") -- guards the ""-literal alias-pool trap that leaked a 55 // stray pooled vhost string ("andelinwest.com") into every field. slug field value must be immediately "". 56 if fg_has(out, w0, "name=\"slug\" value=\"\"" as *u8) == 0 { t1 = 0 } 57 if fg_has(out, w0, "andelinwest" as *u8) == 1 { t1 = 0 } 58 if t1 == 1 { pass = pass + 1 } else { fail = fail + 1; fg_puts("T1 FAIL form render / empty-field alias leak\n" as *u8) } 59 60 // T2 valid POST stages + confirms 61 let good: *u8 = "slug=acme-spring&business=Acme+Co&img=%2Fsynth%2Facme.png&click=https%3A%2F%2Facme.example&section=home" as *u8 62 let w1: i64 = asf_post(good, fg_slen(good), subs, out, 65536) 63 var t2: i64 = 1 64 if w1 <= 0 { t2 = 0 } 65 if fg_has(out, w1, "Submitted for review" as *u8) == 0 { t2 = 0 } 66 if fg_has(out, w1, "acme-spring" as *u8) == 0 { t2 = 0 } 67 let box: *i64 = sys_mmap(8) as *i64 68 let back1: *u8 = fg_readn(subs, box) 69 if (back1 as i64) == 0 { t2 = 0 } else { 70 if fg_has(back1, box[0], "acme-spring" as *u8) == 0 { t2 = 0 } 71 if fg_has(back1, box[0], "Acme Co" as *u8) == 0 { t2 = 0 } 72 if fg_has(back1, box[0], "/synth/acme.png" as *u8) == 0 { t2 = 0 } 73 } 74 if t2 == 1 { pass = pass + 1 } else { fail = fail + 1; fg_puts("T2 FAIL valid submit\n" as *u8) } 75 76 // T3 external img REJECTED + nothing staged 77 let badimg: *u8 = "slug=evilimg&business=Bad&img=https%3A%2F%2Fevil.example%2Fx.png&click=https%3A%2F%2Fok.example&section=home" as *u8 78 let w2: i64 = asf_post(badimg, fg_slen(badimg), subs, out, 65536) 79 var t3: i64 = 1 80 if fg_has(out, w2, "Rejected" as *u8) == 0 { t3 = 0 } 81 let back2: *u8 = fg_readn(subs, box) 82 if (back2 as i64) != 0 { if fg_has(back2, box[0], "evilimg" as *u8) == 1 { t3 = 0 } } 83 if t3 == 1 { pass = pass + 1 } else { fail = fail + 1; fg_puts("T3 FAIL external img not rejected\n" as *u8) } 84 85 // T4 javascript: click REJECTED + nothing staged 86 let badclick: *u8 = "slug=evilclick&business=Bad&img=%2Fsynth%2Fb.png&click=javascript%3Aalert(1)&section=home" as *u8 87 let w3: i64 = asf_post(badclick, fg_slen(badclick), subs, out, 65536) 88 var t4: i64 = 1 89 if fg_has(out, w3, "Rejected" as *u8) == 0 { t4 = 0 } 90 let back3: *u8 = fg_readn(subs, box) 91 if (back3 as i64) != 0 { if fg_has(back3, box[0], "evilclick" as *u8) == 1 { t4 = 0 } } 92 if t4 == 1 { pass = pass + 1 } else { fail = fail + 1; fg_puts("T4 FAIL javascript click not rejected\n" as *u8) } 93 94 // T5 bad slug (uppercase/space) REJECTED 95 let badslug: *u8 = "slug=Bad+Slug&business=Co&img=%2Fsynth%2Fc.png&click=%2Fx&section=home" as *u8 96 let w4: i64 = asf_post(badslug, fg_slen(badslug), subs, out, 65536) 97 var t5: i64 = 1 98 if fg_has(out, w4, "Rejected" as *u8) == 0 { t5 = 0 } 99 if t5 == 1 { pass = pass + 1 } else { fail = fail + 1; fg_puts("T5 FAIL bad slug not rejected\n" as *u8) } 100 101 // T6 reflected XSS in business field stays escaped in the re-populated form (rejected via external img, 102 // so the form re-renders with the submitted business value -> must be escaped, raw tag absent). 103 let xss: *u8 = "slug=okslug&business=%3Cscript%3Ealert(1)%3C%2Fscript%3E&img=https%3A%2F%2Fevil%2Fx.png&click=%2Fx&section=home" as *u8 104 let w5b: i64 = asf_post(xss, fg_slen(xss), subs, out, 65536) 105 var t6: i64 = 1 106 if fg_has(out, w5b, "&lt;script&gt;" as *u8) == 0 { t6 = 0 } 107 if fg_has(out, w5b, "<script>alert" as *u8) == 1 { t6 = 0 } 108 if t6 == 1 { pass = pass + 1 } else { fail = fail + 1; fg_puts("T6 FAIL reflected xss not escaped\n" as *u8) } 109 110 fg_puts("pass=" as *u8) 111 fg_putn(pass) 112 fg_puts(" fail=" as *u8) 113 fg_putn(fail) 114 fg_puts("\n" as *u8) 115 116 let log: *u8 = sys_mmap(256) 117 var lo: i64 = 0 118 if fail == 0 { lo = ad_cat(log, lo, "ADNETSUBMIT authored=organ verdict=GREEN" as *u8) } else { lo = ad_cat(log, lo, "ADNETSUBMIT authored=organ verdict=RED" as *u8) } 119 log[lo] = 10 as u8 120 lo = lo + 1 121 let fd: i64 = sys_openat_wr("knowledge/status/adnet_submit.log" as *u8, 0x1A4) 122 if fd >= 0 { sys_write(fd, log, lo); sys_close(fd) } 123 124 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 125 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 126 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 127 let ctr__dry: *i64 = gv_ctr() 128 ctr__dry[0] = pass 129 ctr__dry[1] = pass + fail 130 let rc__dry: i64 = gv_verdict("ADNET-SUBMIT-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 131 sys_exit(rc__dry) 132 return rc__dry 133}