code wiki / _hdl_build / nx_cms_api_exceed_gate.nx

nx_cms_api_exceed_gate.nx source

↩ module page · 152 lines · 8108 B

1// nx_cms_api_exceed_gate.nx -- W-RE-004 head-to-head EXCEED (rest-graphql-api, INJECTION-SAFETY axis). 2// Runs BOTH serializers on the SAME corpus of field values (benign + quote-injection): 3// OURS = cms_api_render_obj (nx_json_emit, RFC 8259 escaping) 4// INCUMBENT = naive string-concat JSON (no escaping) -- the CWE-116 failure mode (improper output 5// neutralization -> JSON injection / structure breakout). 6// A serialization is CORRECT iff the output stays well-formed: exactly the expected number of UNESCAPED 7// double-quotes (4 for one key+value object). A value containing a `"` breaks the naive concat (extra 8// unescaped quotes) but ours escapes it. Verdict COMPUTED by nx_cms_exceed. license_tier: ORIGINAL 9import "nx_cms_exceed.nx" 10import "nx_cms_api.nx" 11import "nx_syscalls.nx" 12 13func ax_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 14func ax_num(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=48+(m%10);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 } 15func ax_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var k: i64=0; while s[k]!=(0 as u8){dst[o]=s[k];o=o+1;k=k+1} return o } 16func ax_catnum(dst: *u8, off: i64, v: i64) -> i64 { 17 var o: i64=off; let t: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; var k: i64=0 18 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} 19 var i: i64=0; while i<k {dst[o]=t[k-1-i]; o=o+1; i=i+1} return o 20} 21func ax_row(id: i64, ok: i64, what: *u8) -> i64 { 22 ax_w("AXROW " as *u8); ax_num(id); ax_w(" " as *u8) 23 if ok==1 { ax_w("PASS " as *u8) } else { ax_w("FAIL " as *u8) } 24 ax_w(what); ax_w("\n" as *u8) 25 return ok 26} 27 28// count double-quotes (0x22) that are NOT escaped (not immediately preceded by a backslash) 29func ax_unescaped_quotes(buf: *u8, n: i64) -> i64 { 30 var c: i64 = 0 31 var i: i64 = 0 32 while i < n { 33 if (buf[i] as i64) == 0x22 { 34 if i == 0 { c = c + 1 } else { if (buf[i-1] as i64) != 0x5C { c = c + 1 } } 35 } 36 i = i + 1 37 } 38 return c 39} 40 41// incumbent: naive concat {"key":"<raw value>"} with NO escaping (CWE-116) 42func ax_naive_render(key: *u8, val: *u8, vl: i64, out: *u8) -> i64 { 43 var o: i64 = 0 44 out[o]=123 as u8; o=o+1 // { 45 out[o]=34 as u8; o=o+1 // " 46 var k: i64=0; while key[k]!=(0 as u8){out[o]=key[k];o=o+1;k=k+1} 47 out[o]=34 as u8; o=o+1; out[o]=58 as u8; o=o+1; out[o]=34 as u8; o=o+1 // ":" 48 var j: i64=0; while j<vl{out[o]=val[j];o=o+1;j=j+1} 49 out[o]=34 as u8; o=o+1; out[o]=125 as u8; o=o+1 // "} 50 return o 51} 52 53// build a one-field store @body\n<value>\n@z\nx\n (value may contain quotes) 54func ax_build_store(val: *u8, vl: i64, out: *u8) -> i64 { 55 var o: i64 = ax_cat(out, 0, "@body\n" as *u8) 56 var j: i64=0; while j<vl { out[o]=val[j]; o=o+1; j=j+1 } 57 o = ax_cat(out, o, "\n@z\nx\n" as *u8) 58 return o 59} 60 61func main() -> i64 { 62 var pass: i64 = 0 63 var rows: i64 = 0 64 65 // corpus: parallel arrays of (value ptr, value len). benign first, then quote-injection. 66 let vp: *i64 = sys_mmap(8 * 16) as *i64 67 let vl: *i64 = sys_mmap(8 * 16) as *i64 68 var nc: i64 = 0 69 let b0: *u8 = "Hello world" as *u8; vp[nc]=b0 as i64; vl[nc]=11; nc=nc+1 70 let b1: *u8 = "estate planning" as *u8; vp[nc]=b1 as i64; vl[nc]=15; nc=nc+1 71 // injections (contain unescaped quotes that break naive concat) 72 let j0: *u8 = "x\",\"role\":\"admin" as *u8; vp[nc]=j0 as i64 73 var l0: i64=0; while j0[l0]!=(0 as u8){l0=l0+1}; vl[nc]=l0; nc=nc+1 74 let j1: *u8 = "a\"b" as *u8; vp[nc]=j1 as i64 75 var l1: i64=0; while j1[l1]!=(0 as u8){l1=l1+1}; vl[nc]=l1; nc=nc+1 76 let j2: *u8 = "\"" as *u8; vp[nc]=j2 as i64; vl[nc]=1; nc=nc+1 77 78 let names: *i64 = sys_mmap(32) as *i64 79 names[0] = "body" as *u8 as i64 80 let store: *u8 = sys_mmap(4096) 81 let oout: *u8 = sys_mmap(4096) 82 let nout: *u8 = sys_mmap(4096) 83 84 var our_correct: i64 = 0 85 var inc_correct: i64 = 0 86 var n_inj: i64 = 0 87 var inj_our: i64 = 0 88 var inj_inc: i64 = 0 89 var i: i64 = 0 90 while i < nc { 91 let v: *u8 = (vp[i]) as *u8 92 let ln: i64 = vl[i] 93 let sn: i64 = ax_build_store(v, ln, store) 94 let on: i64 = cms_api_render_obj(store, sn, names, 1, oout, 4096) 95 let inn: i64 = ax_naive_render("body" as *u8, v, ln, nout) 96 var our_v: i64 = 0; if ax_unescaped_quotes(oout, on) == 4 { our_v = 1 } 97 var inc_v: i64 = 0; if ax_unescaped_quotes(nout, inn) == 4 { inc_v = 1 } 98 // ground truth: a correct serializer is ALWAYS well-formed (count 4) for ANY value 99 if our_v == 1 { our_correct = our_correct + 1 } 100 if inc_v == 1 { inc_correct = inc_correct + 1 } 101 // an "injection" value is one that contains a raw quote 102 var has_q: i64 = 0 103 var q: i64 = 0; while q < ln { if (v[q] as i64) == 0x22 { has_q = 1; q = ln } else { q = q + 1 } } 104 if has_q == 1 { 105 n_inj = n_inj + 1 106 if our_v == 1 { inj_our = inj_our + 1 } 107 if inc_v == 1 { inj_inc = inj_inc + 1 } 108 } 109 i = i + 1 110 } 111 112 let verdict: i64 = xcd_verdict(our_correct, inc_correct) 113 ax_w("HEAD-TO-HEAD rest-graphql-api injection-safety: ours=" as *u8); ax_num(our_correct); ax_w("/" as *u8); ax_num(nc) 114 ax_w(" incumbent=" as *u8); ax_num(inc_correct); ax_w("/" as *u8); ax_num(nc) 115 ax_w(" injections-safe ours=" as *u8); ax_num(inj_our); ax_w("/" as *u8); ax_num(n_inj) 116 ax_w(" incumbent=" as *u8); ax_num(inj_inc); ax_w("/" as *u8); ax_num(n_inj) 117 ax_w(" verdict=" as *u8); ax_w(xcd_vname(verdict)); ax_w("\n" as *u8) 118 119 var ok: i64 = 0; if our_correct == nc { ok = 1 } 120 rows=rows+1; pass=pass+ax_row(0, ok, "ours fully correct (well-formed JSON for every value incl injections)" as *u8) 121 ok = 0; if inc_correct < nc { ok = 1 } 122 rows=rows+1; pass=pass+ax_row(1, ok, "incumbent naive concat fails (injection breaks structure = CWE-116)" as *u8) 123 ok = 0; if verdict == XCD_AHEAD { ok = 1 } 124 rows=rows+1; pass=pass+ax_row(2, ok, "measured verdict = AHEAD" as *u8) 125 ok = 0; if inj_our == n_inj { if inj_inc == 0 { if n_inj > 0 { ok = 1 } } } 126 rows=rows+1; pass=pass+ax_row(3, ok, "injection-safety axis: ours safe on all, incumbent safe on 0" as *u8) 127 ok = 0; if xcd_referee_ok(XCD_AHEAD, our_correct, nc) == 1 { ok = 1 } 128 rows=rows+1; pass=pass+ax_row(4, ok, "referee accepts honest AHEAD claim" as *u8) 129 ok = 0; if xcd_referee_ok(XCD_AHEAD, nc - 1, nc) == 0 { ok = 1 } 130 rows=rows+1; pass=pass+ax_row(5, ok, "referee rejects overclaim (anti-false-green)" as *u8) 131 132 ax_w("CMS-API-EXCEED-GATE rows=" as *u8); ax_num(rows); ax_w(" pass=" as *u8); ax_num(pass); ax_w("\n" as *u8) 133 134 if pass == rows { 135 if verdict == XCD_AHEAD { 136 if xcd_referee_ok(XCD_AHEAD, our_correct, nc) == 1 { 137 let line: *u8 = sys_mmap(256) 138 var lo: i64 = ax_cat(line, 0, "CMSEXCEED feature=rest-graphql-api axis=injection-safety cwe=CWE-116 ours=" as *u8) 139 lo = ax_catnum(line, lo, our_correct); lo = ax_cat(line, lo, "/" as *u8); lo = ax_catnum(line, lo, nc) 140 lo = ax_cat(line, lo, " incumbent=" as *u8); lo = ax_catnum(line, lo, inc_correct); lo = ax_cat(line, lo, "/" as *u8); lo = ax_catnum(line, lo, nc) 141 lo = ax_cat(line, lo, " injections-safe=" as *u8); lo = ax_catnum(line, lo, inj_our); lo = ax_cat(line, lo, "/" as *u8); lo = ax_catnum(line, lo, n_inj) 142 lo = ax_cat(line, lo, " verdict=AHEAD\n" as *u8) 143 let gf: i64 = sys_openat_append("knowledge/status/cms_exceed.log" as *u8, 0x1a4) 144 if gf >= 0 { sys_write(gf, line, lo); sys_close(gf) } 145 ax_w("CMS-API-EXCEED-GATE verdict=AHEAD -- measured EXCEED recorded\n" as *u8) 146 sys_exit(0); return 0 147 } 148 } 149 } 150 ax_w("CMS-API-EXCEED-GATE verdict=NOT-RECORDED (no fake-green)\n" as *u8) 151 sys_exit(1); return 1 152}