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}