code wiki / _hdl_build / nx_cms_api_exceed_gate.nx
nx_cms_api_exceed_gate.nx source
↩ module page · 185 lines · 10274 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"
12import "nx_gate_verdict.nx" // D001 base class so nx_gate_rollup can judge this gate
13
14func 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 }
15// Same writer, arbitrary fd. ax_w is hard-wired to fd 1, which is exactly how the failure path came to
16// write only to stdout and leave the LOG tail sitting on the previous success.
17func ax_w2(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
18
19// ---- ONE EXIT, REACHABLE FROM ANY DEPTH 2026-08-07 -------------------------------------------
20// Twin of the defect fixed in nx_access_wall_exceed_gate the same day, and left unfixed there would
21// have been the partial job: this log was APPEND-ON-SUCCESS-ONLY, so a failing run left the previous
22// SUCCESS as the last line forever, and nx_gate_rollup anchors on the LAST line.
23// ★★★★★★A LOG WRITTEN ONLY ON SUCCESS CANNOT EVER REPORT A FAILURE: ITS LAST LINE IS A STANDING PASS.
24// The success path here exits from three levels of nesting, so the contract is a HELPER rather than a
25// rewritten tail -- reachable from any depth, and it also satisfies D001 by inheriting nx_gate_verdict
26// instead of rolling its own word. The escape allow_own_verdict=yes was available and is the wrong
27// answer: it ships an UNREADABLE gate, which is the defect being fixed.
28func ax_finish(ok: i64) -> i64 {
29 let lf: i64 = sys_openat_append("knowledge/status/cms_api_exceed.log" as *u8, 0x1a4)
30 if lf >= 0 {
31 if ok == 0 { ax_w2(lf, "CMSEXCEED feature=rest-graphql-api axis=injection-safety verdict=NOT-MEASURED (gate FAILED this run; no EXCEED claim recorded)\
32" as *u8) }
33 if ok == 1 { ax_w2(lf, "CMS-API-EXCEED verdict=GREEN\
34" as *u8) }
35 if ok == 0 { ax_w2(lf, "CMS-API-EXCEED verdict=RED\
36" as *u8) }
37 sys_close(lf)
38 }
39 let ctr: *i64 = gv_ctr()
40 gv_check("REST/GraphQL injection-safety EXCEED measured on the same corpus as the incumbent" as *u8, ok, ctr)
41 let rc: i64 = gv_verdict("CMS-API-EXCEED" as *u8, ctr, "REST/GraphQL API injection-safety measured ours vs incumbent on one corpus; recorded only when measured" as *u8)
42 sys_exit(rc)
43 return rc
44}
45func ax_num(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)}; 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 }
46func 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 }
47func ax_catnum(dst: *u8, off: i64, v: i64) -> i64 {
48 var o: i64=off; let t: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;dst[o]=45 as u8;o=o+1}; var k: i64=0
49 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}
50 var i: i64=0; while i<k {dst[o]=t[k-1-i]; o=o+1; i=i+1} return o
51}
52func ax_row(id: i64, ok: i64, what: *u8) -> i64 {
53 ax_w("AXROW " as *u8); ax_num(id); ax_w(" " as *u8)
54 if ok==1 { ax_w("PASS " as *u8) } else { ax_w("FAIL " as *u8) }
55 ax_w(what); ax_w("\n" as *u8)
56 return ok
57}
58
59// count double-quotes (0x22) that are NOT escaped (not immediately preceded by a backslash)
60func ax_unescaped_quotes(buf: *u8, n: i64) -> i64 {
61 var c: i64 = 0
62 var i: i64 = 0
63 while i < n {
64 if (buf[i] as i64) == 0x22 {
65 if i == 0 { c = c + 1 } else { if (buf[i-1] as i64) != 0x5C { c = c + 1 } }
66 }
67 i = i + 1
68 }
69 return c
70}
71
72// incumbent: naive concat {"key":"<raw value>"} with NO escaping (CWE-116)
73func ax_naive_render(key: *u8, val: *u8, vl: i64, out: *u8) -> i64 {
74 var o: i64 = 0
75 out[o]=123 as u8; o=o+1 // {
76 out[o]=34 as u8; o=o+1 // "
77 var k: i64=0; while key[k]!=(0 as u8){out[o]=key[k];o=o+1;k=k+1}
78 out[o]=34 as u8; o=o+1; out[o]=58 as u8; o=o+1; out[o]=34 as u8; o=o+1 // ":"
79 var j: i64=0; while j<vl{out[o]=val[j];o=o+1;j=j+1}
80 out[o]=34 as u8; o=o+1; out[o]=125 as u8; o=o+1 // "}
81 return o
82}
83
84// build a one-field store @body\n<value>\n@z\nx\n (value may contain quotes)
85func ax_build_store(val: *u8, vl: i64, out: *u8) -> i64 {
86 var o: i64 = ax_cat(out, 0, "@body\n" as *u8)
87 var j: i64=0; while j<vl { out[o]=val[j]; o=o+1; j=j+1 }
88 o = ax_cat(out, o, "\n@z\nx\n" as *u8)
89 return o
90}
91
92func main() -> i64 {
93 var pass: i64 = 0
94 var rows: i64 = 0
95
96 // corpus: parallel arrays of (value ptr, value len). benign first, then quote-injection.
97 let vp: *i64 = sys_mmap(8 * 16) as *i64
98 let vl: *i64 = sys_mmap(8 * 16) as *i64
99 var nc: i64 = 0
100 let b0: *u8 = "Hello world" as *u8; vp[nc]=b0 as i64; vl[nc]=11; nc=nc+1
101 let b1: *u8 = "estate planning" as *u8; vp[nc]=b1 as i64; vl[nc]=15; nc=nc+1
102 // injections (contain unescaped quotes that break naive concat)
103 let j0: *u8 = "x\",\"role\":\"admin" as *u8; vp[nc]=j0 as i64
104 var l0: i64=0; while j0[l0]!=(0 as u8){l0=l0+1}; vl[nc]=l0; nc=nc+1
105 let j1: *u8 = "a\"b" as *u8; vp[nc]=j1 as i64
106 var l1: i64=0; while j1[l1]!=(0 as u8){l1=l1+1}; vl[nc]=l1; nc=nc+1
107 let j2: *u8 = "\"" as *u8; vp[nc]=j2 as i64; vl[nc]=1; nc=nc+1
108
109 let names: *i64 = sys_mmap(32) as *i64
110 names[0] = "body" as *u8 as i64
111 let store: *u8 = sys_mmap(4096)
112 let oout: *u8 = sys_mmap(4096)
113 let nout: *u8 = sys_mmap(4096)
114
115 var our_correct: i64 = 0
116 var inc_correct: i64 = 0
117 var n_inj: i64 = 0
118 var inj_our: i64 = 0
119 var inj_inc: i64 = 0
120 var i: i64 = 0
121 while i < nc {
122 let v: *u8 = (vp[i]) as *u8
123 let ln: i64 = vl[i]
124 let sn: i64 = ax_build_store(v, ln, store)
125 let on: i64 = cms_api_render_obj(store, sn, names, 1, oout, 4096)
126 let inn: i64 = ax_naive_render("body" as *u8, v, ln, nout)
127 var our_v: i64 = 0; if ax_unescaped_quotes(oout, on) == 4 { our_v = 1 }
128 var inc_v: i64 = 0; if ax_unescaped_quotes(nout, inn) == 4 { inc_v = 1 }
129 // ground truth: a correct serializer is ALWAYS well-formed (count 4) for ANY value
130 if our_v == 1 { our_correct = our_correct + 1 }
131 if inc_v == 1 { inc_correct = inc_correct + 1 }
132 // an "injection" value is one that contains a raw quote
133 var has_q: i64 = 0
134 var q: i64 = 0; while q < ln { if (v[q] as i64) == 0x22 { has_q = 1; q = ln } else { q = q + 1 } }
135 if has_q == 1 {
136 n_inj = n_inj + 1
137 if our_v == 1 { inj_our = inj_our + 1 }
138 if inc_v == 1 { inj_inc = inj_inc + 1 }
139 }
140 i = i + 1
141 }
142
143 let verdict: i64 = xcd_verdict(our_correct, inc_correct)
144 ax_w("HEAD-TO-HEAD rest-graphql-api injection-safety: ours=" as *u8); ax_num(our_correct); ax_w("/" as *u8); ax_num(nc)
145 ax_w(" incumbent=" as *u8); ax_num(inc_correct); ax_w("/" as *u8); ax_num(nc)
146 ax_w(" injections-safe ours=" as *u8); ax_num(inj_our); ax_w("/" as *u8); ax_num(n_inj)
147 ax_w(" incumbent=" as *u8); ax_num(inj_inc); ax_w("/" as *u8); ax_num(n_inj)
148 ax_w(" verdict=" as *u8); ax_w(xcd_vname(verdict)); ax_w("\n" as *u8)
149
150 var ok: i64 = 0; if our_correct == nc { ok = 1 }
151 rows=rows+1; pass=pass+ax_row(0, ok, "ours fully correct (well-formed JSON for every value incl injections)" as *u8)
152 ok = 0; if inc_correct < nc { ok = 1 }
153 rows=rows+1; pass=pass+ax_row(1, ok, "incumbent naive concat fails (injection breaks structure = CWE-116)" as *u8)
154 ok = 0; if verdict == XCD_AHEAD { ok = 1 }
155 rows=rows+1; pass=pass+ax_row(2, ok, "measured verdict = AHEAD" as *u8)
156 ok = 0; if inj_our == n_inj { if inj_inc == 0 { if n_inj > 0 { ok = 1 } } }
157 rows=rows+1; pass=pass+ax_row(3, ok, "injection-safety axis: ours safe on all, incumbent safe on 0" as *u8)
158 ok = 0; if xcd_referee_ok(XCD_AHEAD, our_correct, nc) == 1 { ok = 1 }
159 rows=rows+1; pass=pass+ax_row(4, ok, "referee accepts honest AHEAD claim" as *u8)
160 ok = 0; if xcd_referee_ok(XCD_AHEAD, nc - 1, nc) == 0 { ok = 1 }
161 rows=rows+1; pass=pass+ax_row(5, ok, "referee rejects overclaim (anti-false-green)" as *u8)
162
163 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)
164
165 if pass == rows {
166 if verdict == XCD_AHEAD {
167 if xcd_referee_ok(XCD_AHEAD, our_correct, nc) == 1 {
168 let line: *u8 = sys_mmap(256)
169 var lo: i64 = ax_cat(line, 0, "CMSEXCEED feature=rest-graphql-api axis=injection-safety cwe=CWE-116 ours=" as *u8)
170 lo = ax_catnum(line, lo, our_correct); lo = ax_cat(line, lo, "/" as *u8); lo = ax_catnum(line, lo, nc)
171 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)
172 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)
173 lo = ax_cat(line, lo, " verdict=AHEAD\n" as *u8)
174 let gf: i64 = sys_openat_append("knowledge/status/cms_api_exceed.log" as *u8, 0x1a4)
175 if gf >= 0 { sys_write(gf, line, lo); sys_close(gf) }
176 ax_w("CMS-API-EXCEED-GATE verdict=AHEAD -- measured EXCEED recorded\n" as *u8)
177 ax_finish(1)
178 return 0
179 }
180 }
181 }
182 ax_w("CMS-API-EXCEED-GATE verdict=NOT-RECORDED (no fake-green)\n" as *u8)
183 ax_finish(0)
184 return 1
185}