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}