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}