code wiki / _hdl_build / nx_security_sentinel.nx

nx_security_sentinel.nx source

↩ module page · 206 lines · 9787 B

1// nx_security_sentinel.nx -- the team's SECURITY SENTINEL role unit. ONE VERB: WATCH. 2// (RACI clean partition: Engineer VERIFIES, Doctor HEALS, Auditor GRADES maturity, 3// security_status OBSERVES posture -- nobody WATCHED the signal stream. This does.) 4// 5// What it does, every run: 6// 1. Reads the durable issues log (knowledge/status/issues_durable.log). 7// 2. Walks a DATA-DRIVEN WATCH TABLE (pattern, threshold, key) -- counts occurrences. 8// 3. For each row at/over threshold, checks its own log for a SENTINEL-RESOLVE line: 9// "SENTINEL-RESOLVE key=<KEY> at=<N>" -- resolved iff N >= current count 10// (signal grew past the resolve mark => RE-ESCALATES; stale resolves don't mask). 11// 4. Unresolved escalations are appended ONCE (idempotent: skip if the exact 12// PM-ESCALATION key line already exists) to knowledge/status/pm_plan_durable.log. 13// 5. Emits one SENTINEL line per row + one SENTINEL-VERDICT line to 14// knowledge/status/security_sentinel.log. Exit 0 = no active escalation, 1 = ATTENTION. 15// 16// Adding coverage = adding a table row (build intelligence, never strip). 17// Resolving = appending "SENTINEL-RESOLVE key=<KEY> at=<count>" to the sentinel log 18// after the named defect's regression gate is green (Engineer's verify, not ours). 19// license_tier: ORIGINAL 20import "nx_syscalls.nx" 21 22const SENT_BUF_MAX: i64 = 4194304 // 4 MiB cap per log read (logs are line-oriented, small) 23const SENT_ROWS: i64 = 5 24 25func _p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 26func _pn(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 } 27func _fp(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 } 28func _fn(fd: i64, 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(fd,bb,k); return 0 } 29func sw_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 30 31// read whole file into buf (cap SENT_BUF_MAX); return bytes read, -1 if absent 32func sw_read_file(path: *u8, buf: *u8) -> i64 { 33 let fd: i64 = sys_openat_rd(path) 34 if fd < 0 { return 0 - 1 } 35 var n: i64 = 0 36 var go: i64 = 1 37 while go == 1 { 38 let base: i64 = buf as i64 39 let r: i64 = sys_read(fd, (base + n) as *u8, SENT_BUF_MAX - n) 40 if r <= 0 { go = 0 } else { n = n + r } 41 if n >= SENT_BUF_MAX { go = 0 } 42 } 43 sys_close(fd) 44 return n 45} 46 47// count non-overlapping occurrences of pat in buf[0..n) 48func sw_count(buf: *u8, n: i64, pat: *u8) -> i64 { 49 let pl: i64 = sw_slen(pat) 50 if pl == 0 { return 0 } 51 var cnt: i64 = 0 52 var i: i64 = 0 53 while i + pl <= n { 54 var k: i64 = 0 55 var hit: i64 = 1 56 while k < pl { 57 if buf[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } 58 } 59 if hit == 1 { cnt = cnt + 1; i = i + pl } else { i = i + 1 } 60 } 61 return cnt 62} 63 64// does buf contain pat at least once? 65func sw_contains(buf: *u8, n: i64, pat: *u8) -> i64 { 66 if sw_count(buf, n, pat) > 0 { return 1 } 67 return 0 68} 69 70// Find the HIGHEST "<prefix><digits>" value in buf, where prefix is e.g. 71// "SENTINEL-RESOLVE key=AR2-MISCOMPILE at=". Returns -1 if never seen. 72func sw_max_marked(buf: *u8, n: i64, prefix: *u8) -> i64 { 73 let pl: i64 = sw_slen(prefix) 74 var best: i64 = 0 - 1 75 var i: i64 = 0 76 while i + pl <= n { 77 var k: i64 = 0 78 var hit: i64 = 1 79 while k < pl { 80 if buf[i+k] != prefix[k] { hit = 0; k = pl } else { k = k + 1 } 81 } 82 if hit == 1 { 83 var v: i64 = 0 84 var j: i64 = i + pl 85 var digits: i64 = 0 86 var scan: i64 = 1 87 while scan == 1 { 88 if j >= n { scan = 0 } 89 else { 90 let c: i64 = buf[j] as i64 91 if c < 48 { scan = 0 } 92 else { 93 if c > 57 { scan = 0 } 94 else { v = v * 10 + (c - 48); digits = digits + 1; j = j + 1 } 95 } 96 } 97 } 98 if digits > 0 { if v > best { best = v } } 99 i = i + pl 100 } else { i = i + 1 } 101 } 102 return best 103} 104 105func sw_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } 106 107func main() -> i64 { 108 _p("=== SECURITY SENTINEL: watch the signal stream, escalate repeats (verb: WATCH) ===\n" as *u8) 109 110 // ---- WATCH TABLE (data-driven; add a row = add coverage) ---- 111 let keys: *i64 = sys_mmap(8 * SENT_ROWS) as *i64 112 let pats: *i64 = sys_mmap(8 * SENT_ROWS) as *i64 113 let thr: *i64 = sys_mmap(8 * SENT_ROWS) as *i64 114 keys[0] = "AR2-MISCOMPILE" as *u8 as i64 115 pats[0] = "kind=CRYPTO-RUNTIME-DEFECT module=nx_argon2id" as *u8 as i64 116 thr[0] = 3 117 keys[1] = "AR2-CRASH" as *u8 as i64 118 pats[1] = "module=_ar2_minrepro signal=11" as *u8 as i64 119 thr[1] = 3 120 keys[2] = "NXASM-XLATE-GAP" as *u8 as i64 121 pats[2] = "NXASM-SYSCALL-XLATE-GAP" as *u8 as i64 122 thr[2] = 3 123 keys[3] = "CRASH-STORM" as *u8 as i64 124 pats[3] = "kind=RUNTIME-CRASH" as *u8 as i64 125 thr[3] = 25 126 keys[4] = "PLAINTEXT-SECRET" as *u8 as i64 127 pats[4] = "ISSUE PLAINTEXT-SECRET" as *u8 as i64 128 thr[4] = 1 129 // row 4 is fed by nx_secret_scan (the DETECT half); ANY committed credential literal 130 // escalates -- resolve only after migration to the vault path + a GREEN re-scan 131 132 let issues: *u8 = sys_mmap(SENT_BUF_MAX) 133 let in_n: i64 = sw_read_file("knowledge/status/issues_durable.log" as *u8, issues) 134 if in_n < 0 { 135 _p(" issues log ABSENT -- nothing to watch (run nx_archivist_durability first)\n" as *u8) 136 sys_exit(1); return 1 137 } 138 let own: *u8 = sys_mmap(SENT_BUF_MAX) 139 var own_n: i64 = sw_read_file("knowledge/status/security_sentinel.log" as *u8, own) 140 if own_n < 0 { own_n = 0 } 141 let pm: *u8 = sys_mmap(SENT_BUF_MAX) 142 var pm_n: i64 = sw_read_file("knowledge/status/pm_plan_durable.log" as *u8, pm) 143 if pm_n < 0 { pm_n = 0 } 144 145 let lfd: i64 = sys_openat_append("knowledge/status/security_sentinel.log" as *u8, 0x1a4) 146 if lfd < 0 { _p(" sentinel log open failed\n" as *u8); sys_exit(1); return 1 } 147 _fp(lfd, "SENTINEL-RUN epoch=" as *u8); _fn(lfd, sys_now_realtime_sec()); _fp(lfd, "\n" as *u8) 148 149 let scratch: *u8 = sys_mmap(512) 150 var active: i64 = 0 151 var ri: i64 = 0 152 while ri < SENT_ROWS { 153 let key: *u8 = keys[ri] as *u8 154 let pat: *u8 = pats[ri] as *u8 155 let cnt: i64 = sw_count(issues, in_n, pat) 156 // resolve mark: highest "SENTINEL-RESOLVE key=<KEY> at=N" in our own log 157 var o: i64 = 0 158 o = sw_cat(scratch, 0, "SENTINEL-RESOLVE key=" as *u8) 159 o = sw_cat(scratch, o, key) 160 o = sw_cat(scratch, o, " at=" as *u8) 161 scratch[o] = 0 as u8 162 let mark: i64 = sw_max_marked(own, own_n, scratch) 163 var state: i64 = 0 // 0=WATCH 1=ESCALATE 2=RESOLVED 164 if cnt >= thr[ri] { 165 if mark >= cnt { state = 2 } else { state = 1; active = active + 1 } 166 } 167 _p(" " as *u8); _p(key); _p(": count=" as *u8); _pn(cnt); _p(" thr=" as *u8); _pn(thr[ri]) 168 if state == 0 { _p(" WATCH\n" as *u8) } 169 if state == 1 { _p(" ESCALATE\n" as *u8) } 170 if state == 2 { _p(" RESOLVED (mark=" as *u8); _pn(mark); _p(")\n" as *u8) } 171 _fp(lfd, "SENTINEL key=" as *u8); _fp(lfd, key) 172 _fp(lfd, " count=" as *u8); _fn(lfd, cnt) 173 _fp(lfd, " thr=" as *u8); _fn(lfd, thr[ri]) 174 if state == 0 { _fp(lfd, " state=WATCH\n" as *u8) } 175 if state == 1 { _fp(lfd, " state=ESCALATE\n" as *u8) } 176 if state == 2 { _fp(lfd, " state=RESOLVED mark=" as *u8); _fn(lfd, mark); _fp(lfd, "\n" as *u8) } 177 // idempotent PM escalation: append only if this key was never filed 178 if state == 1 { 179 var q: i64 = 0 180 q = sw_cat(scratch, 0, "PM-ESCALATION source=sentinel key=" as *u8) 181 q = sw_cat(scratch, q, key) 182 scratch[q] = 0 as u8 183 if sw_contains(pm, pm_n, scratch) == 0 { 184 let pfd: i64 = sys_openat_append("knowledge/status/pm_plan_durable.log" as *u8, 0x1a4) 185 if pfd >= 0 { 186 _fp(pfd, scratch); _fp(pfd, " count=" as *u8); _fn(pfd, cnt) 187 _fp(pfd, " :: repeated security-relevant signal at/over threshold; needs an owner + a re-runnable regression gate; resolve by appending SENTINEL-RESOLVE key=" as *u8) 188 _fp(pfd, key); _fp(pfd, " at=<count> to knowledge/status/security_sentinel.log once the gate is green\n" as *u8) 189 sys_close(pfd) 190 _p(" -> filed PM-ESCALATION (pm_plan_durable.log)\n" as *u8) 191 } 192 } 193 } 194 ri = ri + 1 195 } 196 197 _fp(lfd, "SENTINEL-VERDICT rows=" as *u8); _fn(lfd, SENT_ROWS) 198 _fp(lfd, " active_escalations=" as *u8); _fn(lfd, active) 199 if active == 0 { _fp(lfd, " verdict=QUIET\n" as *u8) } else { _fp(lfd, " verdict=ATTENTION\n" as *u8) } 200 sys_close(lfd) 201 _p(" durable: knowledge/status/security_sentinel.log\n" as *u8) 202 if active == 0 { _p(" SENTINEL: QUIET (no unresolved repeated signals)\n" as *u8); sys_exit(0); return 0 } 203 _p(" SENTINEL: ATTENTION (" as *u8); _pn(active); _p(" active escalation(s) -- see pm_plan_durable.log)\n" as *u8) 204 sys_exit(1) 205 return 1 206}