code wiki / _hdl_build / nx_cwe_scan_gate.nx
nx_cwe_scan_gate.nx source
↩ module page · 183 lines · 15450 B
1// nx_cwe_scan_gate.nx -- gates the ISO-5055 scanner on BOTH directions, because a detector that only ever fires
2// is as useless as one that never does. Each rule gets a POSITIVE fixture (must be caught) AND a NEGATIVE control
3// -- the same construct written safely (must NOT be caught). That negative half is the whole point: it proves the
4// scanner discriminates rather than pattern-matching everything, which is what makes a zero finding meaningful.
5// T1 CWE-476 unguarded read caught, guarded one ignored · T2 CWE-252 unchecked fd caught, checked ignored
6// T3 CWE-798 embedded cap-token caught · T4 CWE-1050 mmap-in-loop caught, mmap outside a loop ignored
7// T5 comment lines never fire (prose is not a defect) · T6 fail-closed with no taxonomy plane.
8// license_tier: ORIGINAL expect_exit: 0
9import "nx_store_seed_lib.nx"
10import "nx_seg_store.nx"
11import "nx_syscalls.nx"
12
13func cg_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
14func cg_pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 }
15func cg_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
16func cg_wfile(path: *u8, content: *u8) -> i64 {
17 let fd: i64=sys_openat_wr(path, 0x1a4)
18 if fd<0 { return 0-1 }
19 sys_write(fd, content, cg_slen(content))
20 sys_close(fd)
21 return 0
22}
23func cg_has(buf: *u8, n: i64, needle: *u8) -> i64 {
24 let nl: i64=cg_slen(needle)
25 if nl==0 { return 0 }
26 var i: i64=0
27 while i+nl<=n {
28 var k: i64=0; var ok: i64=1
29 while k<nl { if buf[i+k]!=needle[k] { ok=0; k=nl } else { k=k+1 } }
30 if ok==1 { return 1 }
31 i=i+1
32 }
33 return 0
34}
35// `elf` MUST be ABSOLUTE: we chdir into the fixture root first, so a relative path would resolve against
36// the wrong directory. v1 hardcoded /tmp/nx_cwe_scan.sov.elf -- a compiler INTERMEDIATE that is not there
37// when the gate runs from a staged artifact, so all nine teeth failed 127 (NOT-FOUND) and the gate looked
38// like a code regression it had not detected. Taking the scanner as an argument also lets a STAGED binary
39// be gated before promotion, which is the only order that can catch anything.
40func cg_run(elf: *u8, dir: *u8, a1: *u8) -> i64 {
41 let pid: i64=sys_fork()
42 if pid<0 { return 201 }
43 if pid==0 {
44 sys_chdir(dir)
45 let av: *i64=sys_mmap(32) as *i64
46 av[0]=elf as i64
47 av[1]=a1 as i64; av[2]=0
48 sys_execve(elf, av, 0 as *i64)
49 sys_exit(127)
50 return 127
51 }
52 let stp: *i64=sys_mmap(16) as *i64
53 sys_wait4(pid, stp, 0)
54 return (stp[0]>>8)&255
55}
56
57func main(argc: i64, argv: *i64) -> i64 {
58 cg_puts("=== nx_cwe_scan_gate -- ISO-5055 detectors: positive fixture AND negative control per rule ===\n" as *u8)
59 // argv[1] = ABSOLUTE path to the scanner under test; defaults to the live organ. Point it at a staged
60 // .new to gate a build BEFORE promoting it.
61 var scanner: *u8 = "/volume1/homes/elderwesto/nishihost/nx_cwe_scan.elf\x00" as *u8
62 if argc>=2 { scanner = argv[1] as *u8 }
63 cg_puts("scanner-under-test: " as *u8); cg_puts(scanner); cg_puts("\n" as *u8)
64 var fails: i64=0
65 sys_mkdir("/tmp/cwx\x00" as *u8, 0x1ed)
66 sys_mkdir("/tmp/cwx/knowledge\x00" as *u8, 0x1ed)
67 sys_mkdir("/tmp/cwx/knowledge/store\x00" as *u8, 0x1ed)
68 sys_mkdir("/tmp/cwx/knowledge/status\x00" as *u8, 0x1ed)
69 sys_mkdir("/tmp/cwx/src\x00" as *u8, 0x1ed)
70 sys_mkdir("/tmp/cwx_bare\x00" as *u8, 0x1ed)
71 let rules: *u8="CWE-476\tReliability\tnull deref\t5\nCWE-252\tReliability\tunchecked return\t4\nCWE-798\tSecurity\thard-coded credential\t9\nCWE-1050\tPerformance\talloc in loop\t3\nCWE-330\tSecurity\tconstant key material\t9\n\x00" as *u8
72 // The SAME five rules in a DELIBERATELY DIFFERENT ORDER, plus one weakness the standard names that we
73 // do NOT detect. T8 runs the scanner against this and proves every count still lands on its own CWE.
74 let rules_reordered: *u8="CWE-330\tSecurity\tconstant key material\t9\nCWE-1050\tPerformance\talloc in loop\t3\nCWE-476\tReliability\tnull deref\t5\nCWE-798\tSecurity\thard-coded credential\t9\nCWE-252\tReliability\tunchecked return\t4\nCWE-121\tReliability\tstack overflow we do not detect\t5\n\x00" as *u8
75 sts_seed("/tmp/cwx/knowledge/store/cwerules-\x00" as *u8, rules, cg_slen(rules))
76 // POSITIVE fixtures: one of each weakness
77 cg_wfile("/tmp/cwx/src/bad.nx\x00" as *u8,
78 "func a() -> i64 {\n let b: *u8=sys_read_file(\"p\", s)\n return b[0] as i64\n}\nfunc a1() -> i64 {\n let b1: *u8=sys_read_file(\"p2\", s)\n return b1[0] as i64\n}\nfunc c() -> i64 {\n let fd: i64=sys_openat_wr(\"q\", 420)\n sys_write(fd, \"x\", 1)\n return 0\n}\nfunc d() -> i64 {\n let cap: *u8=\"nx_thing~1813088624~72180.abc\"\n return 0\n}\nfunc e() -> i64 {\n var i: i64=0\n while i<10 {\n let t: *u8=sys_mmap(64)\n i=i+1\n }\n return 0\n}\n\x00" as *u8)
79 // NEGATIVE controls: the SAME constructs, written safely -- must NOT be flagged.
80 // e2 = mmap OUTSIDE a loop. e3 = mmap INSIDE a loop but reusing a HOISTED buffer (idiomatic, not a defect)
81 // -- this is the seq318 CWE-1050 precision fix. d2 = a bare "~18" literal with no cap shape, which is exactly
82 // what the scanner's own pattern looked like and what made v1 self-match on a SECURITY rule.
83 cg_wfile("/tmp/cwx/src/good.nx\x00" as *u8,
84 "func a2() -> i64 {\n let b: *u8=sys_read_file(\"p\", s)\n if (b as i64)!=0 { return b[0] as i64 }\n return 0\n}\nfunc c2() -> i64 {\n let fd: i64=sys_openat_wr(\"q\", 420)\n if fd<0 { return 0-1 }\n sys_write(fd, \"x\", 1)\n return 0\n}\nfunc d2() -> i64 {\n let pat: *u8=\"~18\"\n return 0\n}\nfunc e2() -> i64 {\n let t: *u8=sys_mmap(64)\n var i: i64=0\n while i<10 { i=i+1 }\n return 0\n}\nfunc e3() -> i64 {\n let buf: *u8=sys_mmap(64)\n var i: i64=0\n while i<10 {\n buf = sys_mmap(64)\n i=i+1\n }\n return 0\n}\n\x00" as *u8)
85 // COMMENT-ONLY prose naming every pattern -- must never fire
86 cg_wfile("/tmp/cwx/src/prose.nx\x00" as *u8,
87 "// this file mentions sys_read_file( and sys_openat_wr( and sys_mmap( in a while loop\n// and even a token shape ~1813088624~72180 in a \"quote\"\nfunc z() -> i64 { return 0 }\n\x00" as *u8)
88 // CWE-330 POSITIVE + its TEST TWIN. Byte-identical bodies; only the FILENAME differs. This is the
89 // whole non-vacuity claim for the new rule: the production copy must be caught and the _test copy
90 // must not, so a pass cannot come from a rule that fires on everything OR one that fires on nothing.
91 cg_wfile("/tmp/cwx/src/key_bad.nx\x00" as *u8,
92 "func kb() -> i64 {\n let priv: *u8 = sys_mmap(32)\n var i: i64 = 0\n while i < 32 { priv[i] = (0xA0 + i) as u8; i = i + 1 }\n return 0\n}\n\x00" as *u8)
93 cg_wfile("/tmp/cwx/src/key_test.nx\x00" as *u8,
94 "func kt() -> i64 {\n let priv: *u8 = sys_mmap(32)\n var i: i64 = 0\n while i < 32 { priv[i] = (0xA0 + i) as u8; i = i + 1 }\n return 0\n}\n\x00" as *u8)
95 let rc: i64=cg_run(scanner, "/tmp/cwx\x00" as *u8, "src\x00" as *u8)
96 let szp: *i64=sys_mmap(16) as *i64
97 let lg: *u8=sys_read_file("/tmp/cwx/knowledge/status/cwe_scan.log\x00" as *u8, szp)
98 var n: i64=0
99 if (lg as i64)!=0 { n=szp[0] }
100 // Each rule must count EXACTLY 1: the positive fires, the negative control and the prose do not.
101 var t1: i64=0
102 if rc==0 { if n>0 { if cg_has(lg, n, "\x22cwe\x22:\x22CWE-476\x22,\x22factor\x22:\x22Reliability\x22,\x22name\x22:\x22null deref\x22,\x22weight\x22:5,\x22candidates\x22:2\x00" as *u8)==1 { t1=1 } } }
103 if t1==1 { cg_puts("T1 PASS CWE-476: unguarded read caught, guarded read NOT caught (discriminates)\n" as *u8) } else { fails=fails+1; cg_puts("T1 FAIL rc="); cg_pn(rc); cg_puts("\n" as *u8) }
104 var t2: i64=0
105 if n>0 { if cg_has(lg, n, "\x22cwe\x22:\x22CWE-252\x22,\x22factor\x22:\x22Reliability\x22,\x22name\x22:\x22unchecked return\x22,\x22weight\x22:4,\x22candidates\x22:1\x00" as *u8)==1 { t2=1 } }
106 if t2==1 { cg_puts("T2 PASS CWE-252: unchecked fd caught, checked fd NOT caught\n" as *u8) } else { fails=fails+1; cg_puts("T2 FAIL\n" as *u8) }
107 var t3: i64=0
108 if n>0 { if cg_has(lg, n, "\x22cwe\x22:\x22CWE-798\x22,\x22factor\x22:\x22Security\x22,\x22name\x22:\x22hard-coded credential\x22,\x22weight\x22:9,\x22candidates\x22:1\x00" as *u8)==1 { t3=1 } }
109 if t3==1 { cg_puts("T3 PASS CWE-798: embedded capability-token literal caught (and prose mention ignored)\n" as *u8) } else { fails=fails+1; cg_puts("T3 FAIL\n" as *u8) }
110 var t4: i64=0
111 if n>0 { if cg_has(lg, n, "\x22cwe\x22:\x22CWE-1050\x22,\x22factor\x22:\x22Performance\x22,\x22name\x22:\x22alloc in loop\x22,\x22weight\x22:3,\x22candidates\x22:1\x00" as *u8)==1 { t4=1 } }
112 if t4==1 { cg_puts("T4 PASS CWE-1050: mmap INSIDE a loop caught, mmap outside the loop NOT caught\n" as *u8) } else { fails=fails+1; cg_puts("T4 FAIL\n" as *u8) }
113 // T5 totals prove the negative controls contributed nothing at all
114 var t5: i64=0
115 if n>0 { if cg_has(lg, n, "\x22candidates_total\x22:6\x00" as *u8)==1 { if cg_has(lg, n, "\x22organs_scanned\x22:5\x00" as *u8)==1 { t5=1 } } }
116 if t5==1 { cg_puts("T5 PASS exactly 6 candidates across 5 organs -- prose + safe code + the _test twin added ZERO\n" as *u8) } else { fails=fails+1; cg_puts("T5 FAIL totals\n" as *u8) }
117 // T7 THE NEW RULE, BOTH DIRECTIONS AT ONCE. candidates:1 means the production offender was caught AND
118 // the byte-identical _test twin was not -- 0 would prove it blind, 2 would prove it indiscriminate.
119 var t7: i64=0
120 if n>0 { if cg_has(lg, n, "\x22cwe\x22:\x22CWE-330\x22,\x22factor\x22:\x22Security\x22,\x22name\x22:\x22constant key material\x22,\x22weight\x22:9,\x22candidates\x22:1\x00" as *u8)==1 { t7=1 } }
121 if t7==1 { cg_puts("T7 PASS CWE-330: constant key material caught in production, IDENTICAL body in _test NOT counted\n" as *u8) } else { fails=fails+1; cg_puts("T7 FAIL CWE-330 non-vacuity\n" as *u8) }
122 // T8 THE DATA/LOGIC DECOUPLING, PROVEN BY MUTATION. Same source, same detectors, taxonomy rows in a
123 // DIFFERENT ORDER plus a CWE we do not detect. Counts must follow the CWE ID, never the row position.
124 // This is why CWE-476 seeds TWO hits and CWE-330 one: under the old positional join, CWE-330 sitting
125 // at row 0 would inherit CWE-476's 2 and this tooth would go red. Equal counts could not tell them apart.
126 sys_mkdir("/tmp/cwx_ord\x00" as *u8, 0x1ed)
127 sys_mkdir("/tmp/cwx_ord/knowledge\x00" as *u8, 0x1ed)
128 sys_mkdir("/tmp/cwx_ord/knowledge/store\x00" as *u8, 0x1ed)
129 sys_mkdir("/tmp/cwx_ord/knowledge/status\x00" as *u8, 0x1ed)
130 sys_mkdir("/tmp/cwx_ord/src\x00" as *u8, 0x1ed)
131 sts_seed("/tmp/cwx_ord/knowledge/store/cwerules-\x00" as *u8, rules_reordered, cg_slen(rules_reordered))
132 cg_wfile("/tmp/cwx_ord/src/bad.nx\x00" as *u8,
133 "func a() -> i64 {\n let b: *u8=sys_read_file(\"p\", s)\n return b[0] as i64\n}\nfunc a1() -> i64 {\n let b1: *u8=sys_read_file(\"p2\", s)\n return b1[0] as i64\n}\n\x00" as *u8)
134 cg_wfile("/tmp/cwx_ord/src/key_bad.nx\x00" as *u8,
135 "func kb() -> i64 {\n let priv: *u8 = sys_mmap(32)\n var i: i64 = 0\n while i < 32 { priv[i] = (0xA0 + i) as u8; i = i + 1 }\n return 0\n}\n\x00" as *u8)
136 let rc8: i64=cg_run(scanner, "/tmp/cwx_ord\x00" as *u8, "src\x00" as *u8)
137 let sz8: *i64=sys_mmap(16) as *i64
138 let lg8: *u8=sys_read_file("/tmp/cwx_ord/knowledge/status/cwe_scan.log\x00" as *u8, sz8)
139 var n8: i64=0
140 if (lg8 as i64)!=0 { n8=sz8[0] }
141 var t8: i64=0
142 if rc8==0 { if n8>0 {
143 if cg_has(lg8, n8, "\x22cwe\x22:\x22CWE-330\x22,\x22factor\x22:\x22Security\x22,\x22name\x22:\x22constant key material\x22,\x22weight\x22:9,\x22candidates\x22:1\x00" as *u8)==1 {
144 if cg_has(lg8, n8, "\x22cwe\x22:\x22CWE-476\x22,\x22factor\x22:\x22Reliability\x22,\x22name\x22:\x22null deref\x22,\x22weight\x22:5,\x22candidates\x22:2\x00" as *u8)==1 {
145 if cg_has(lg8, n8, "\x22cwe\x22:\x22CWE-121\x22\x00" as *u8)==1 {
146 if cg_has(lg8, n8, "\x22detector\x22:false\x00" as *u8)==1 { t8=1 } } } } } }
147 if t8==1 { cg_puts("T8 PASS taxonomy REORDERED: every count still lands on its own CWE id, and an undetected CWE says detector:false\n" as *u8) } else { fails=fails+1; cg_puts("T8 FAIL keyed join rc8="); cg_pn(rc8); cg_puts("\n" as *u8) }
148 // T9 THE RATCHET, BOTH DIRECTIONS. Ceiling 0 against 1 offender must go RED with exit 2 (distinct from
149 // the exit-1 fail-closed); ceiling 1 against the same 1 offender must stay GREEN. A ratchet that only
150 // ever reds is a cliff, and one that never reds is decoration.
151 sys_mkdir("/tmp/cwx_rat0\x00" as *u8, 0x1ed)
152 sys_mkdir("/tmp/cwx_rat0/knowledge\x00" as *u8, 0x1ed)
153 sys_mkdir("/tmp/cwx_rat0/knowledge/store\x00" as *u8, 0x1ed)
154 sys_mkdir("/tmp/cwx_rat0/knowledge/status\x00" as *u8, 0x1ed)
155 sys_mkdir("/tmp/cwx_rat0/src\x00" as *u8, 0x1ed)
156 sts_seed("/tmp/cwx_rat0/knowledge/store/cwerules-\x00" as *u8, rules, cg_slen(rules))
157 sts_seed("/tmp/cwx_rat0/knowledge/store/cweratchet-\x00" as *u8, "CWE-330\t0\n\x00" as *u8, 10)
158 cg_wfile("/tmp/cwx_rat0/src/key_bad.nx\x00" as *u8,
159 "func kb() -> i64 {\n let priv: *u8 = sys_mmap(32)\n var i: i64 = 0\n while i < 32 { priv[i] = (0xA0 + i) as u8; i = i + 1 }\n return 0\n}\n\x00" as *u8)
160 let rc9: i64=cg_run(scanner, "/tmp/cwx_rat0\x00" as *u8, "src\x00" as *u8)
161 sys_mkdir("/tmp/cwx_rat1\x00" as *u8, 0x1ed)
162 sys_mkdir("/tmp/cwx_rat1/knowledge\x00" as *u8, 0x1ed)
163 sys_mkdir("/tmp/cwx_rat1/knowledge/store\x00" as *u8, 0x1ed)
164 sys_mkdir("/tmp/cwx_rat1/knowledge/status\x00" as *u8, 0x1ed)
165 sys_mkdir("/tmp/cwx_rat1/src\x00" as *u8, 0x1ed)
166 sts_seed("/tmp/cwx_rat1/knowledge/store/cwerules-\x00" as *u8, rules, cg_slen(rules))
167 sts_seed("/tmp/cwx_rat1/knowledge/store/cweratchet-\x00" as *u8, "CWE-330\t1\n\x00" as *u8, 10)
168 cg_wfile("/tmp/cwx_rat1/src/key_bad.nx\x00" as *u8,
169 "func kb() -> i64 {\n let priv: *u8 = sys_mmap(32)\n var i: i64 = 0\n while i < 32 { priv[i] = (0xA0 + i) as u8; i = i + 1 }\n return 0\n}\n\x00" as *u8)
170 let rc9b: i64=cg_run(scanner, "/tmp/cwx_rat1\x00" as *u8, "src\x00" as *u8)
171 var t9: i64=0
172 if rc9==2 { if rc9b==0 { t9=1 } }
173 if t9==1 { cg_puts("T9 PASS ratchet: ceiling 0 vs 1 offender = RED exit 2; ceiling 1 vs the same offender = GREEN exit 0\n" as *u8) } else { fails=fails+1; cg_puts("T9 FAIL ratchet rc0="); cg_pn(rc9); cg_puts(" rc1="); cg_pn(rc9b); cg_puts("\n" as *u8) }
174 // T6 fail-closed without the standard's taxonomy
175 let rc6: i64=cg_run(scanner, "/tmp/cwx_bare\x00" as *u8, "src\x00" as *u8)
176 var t6: i64=0
177 if rc6==1 { t6=1 }
178 if t6==1 { cg_puts("T6 PASS no taxonomy plane = RED exit 1 (no ISO-5055 grade without the standard)\n" as *u8) } else { fails=fails+1; cg_puts("T6 FAIL rc6="); cg_pn(rc6); cg_puts("\n" as *u8) }
179 if fails==0 { cg_puts("CWE-SCAN GREEN -- every detector fires on the defect and stays silent on the safe twin\n" as *u8); sys_exit(0); return 0 }
180 cg_puts("CWE-SCAN RED fails="); cg_pn(fails); cg_puts("\n" as *u8)
181 sys_exit(1)
182 return 1
183}