nx_adversarial_pattern_audit.nx source
↩ module page · 466 lines · 20751 B
1// nx_adversarial_pattern_audit.nx -- scans for adversarial-exploit patterns.
2//
3// module: nishi-core.security.adversarial_pattern_audit
4// depends: nishi-core.security.threat_model,
5// nishi-core.audit.wired_status,
6// nishi-core.audit.claim_audit,
7// nishi-core.io.syscalls
8// disk_kb: 6
9// capability: CRYPTO
10// wired_status: FULLY_WIRED
11//
12// (Honest: substrate STRUCTURE + the FALSE_OK_RETURN-class
13// heuristic counter are wired AND smoke-verified via
14// runtime/nx_adversarial_pattern_audit_test.nx PASS on qemu-rv64.
15// The counter is a substring-based observation primitive that
16// counts `_STUB_NOT_WIRED` markers + `wired_status:` headers per
17// file; smoke asserts known counts on three runtime files. The
18// FALSE_OK_RETURN substring detector is the WIRED kernel today;
19// other 13 pattern-detectors in the 14-class taxonomy remain
20// HEURISTIC_QUEUED. Per-file tree-walking via nx_dir is queued
21// pending syscall-module unification -- nx_dir imports
22// `syscalls.nx` while threat_model+wired_status+claim_audit import
23// `nx_syscalls.nx`, and both define `sys_read_file` so they cannot
24// link together. Caller passes pre-read bytes for now.)
25//
26// license_tier: PUBLIC_NISHI_SUBSTRATE
27// genealogy_id: nishi_racing_crew_team_honesty_threat_aware_cardinal +
28// cert_secure_coding_standards +
29// misra_c_2012_safety_critical_rules +
30// nasa_jpl_power_of_10
31//
32// Per cardinal [[feedback-racing-crew-team-honesty-threat-aware]]:
33// substrate scanner extends nx_claim_audit with patterns specifically
34// dangerous when an adversary (especially AI-class) can read the
35// substrate code looking for exploitable false-OK paths.
36//
37// Catches the exact failure pattern that triggered the no-false-OK
38// cardinal:
39// - function declares "fetch resource X" + returns *_OK from
40// uninitialized verdict field → AI adversary writes caller code
41// that assumes the fetch happened, builds attack on missing data
42// - sealed enum that has NO *_STUB_NOT_WIRED variant → caller
43// can't tell if the verdict is real or stub
44// - error path silently swallows failures → adversary triggers
45// errors, gets the substrate into known-state
46// - documentation claims "TLS-protected" but the body never calls
47// nx_tls13_* → adversary downgrades to plaintext
48
49// Imports nx_syscalls.nx (not syscalls.nx) to match the upstream
50// audit primitives (nx_threat_model + nx_wired_status +
51// nx_claim_audit) -- mixing the two syscall modules in one binary
52// would double-define sys_read_file at link.
53import "nx_syscalls.nx"
54import "nx_threat_model.nx"
55import "nx_wired_status.nx"
56
57// ===== Verdict ====================================================
58
59const NX_APA_OK: i64 = 1
60const NX_APA_PATTERN_DETECTED: i64 = 2
61const NX_APA_CRITICAL_VULNERABILITY: i64 = 3
62const NX_APA_SCAN_FAIL: i64 = 4
63const NX_APA_STUB_NOT_WIRED: i64 = -7777 // honest stub-variant per cardinal
64
65func nx_apa_verdict_name(v: i64) -> *u8 {
66 if v == NX_APA_OK { return "OK" }
67 if v == NX_APA_PATTERN_DETECTED { return "PATTERN_DETECTED" }
68 if v == NX_APA_CRITICAL_VULNERABILITY { return "CRITICAL_VULNERABILITY" }
69 if v == NX_APA_SCAN_FAIL { return "SCAN_FAIL" }
70 if v == NX_APA_STUB_NOT_WIRED { return "STUB_NOT_WIRED" }
71 return "UNKNOWN"
72}
73
74// ===== Adversarial pattern kind sealed enum =======================
75
76const NX_APA_PATTERN_FALSE_OK_RETURN: i64 = 1 // function returns *_OK from no-op body
77const NX_APA_PATTERN_MISSING_STUB_VARIANT: i64 = 2 // sealed enum lacks *_STUB_NOT_WIRED
78const NX_APA_PATTERN_UNINITIALIZED_VERDICT: i64 = 3 // verdict field reads OK by default-init
79const NX_APA_PATTERN_SILENT_ERROR_SWALLOW: i64 = 4 // catch + ignore error
80const NX_APA_PATTERN_CLAIM_BODY_MISMATCH: i64 = 5 // header claims X; body does Y
81const NX_APA_PATTERN_TLS_DOWNGRADE_PATH: i64 = 6 // fallback to plaintext possible
82const NX_APA_PATTERN_AUTH_BYPASS_PATH: i64 = 7 // codepath skips auth check
83const NX_APA_PATTERN_TIMING_LEAK: i64 = 8 // variable-time crypto compare
84const NX_APA_PATTERN_INTEGER_OVERFLOW: i64 = 9 // unchecked arithmetic
85const NX_APA_PATTERN_UNBOUNDED_LOOP: i64 = 10 // violates JPL Rule 2
86const NX_APA_PATTERN_DESERIALIZE_UNTRUSTED: i64 = 11 // parses untrusted data without bounds
87const NX_APA_PATTERN_PROMPT_INJECTION_TRUST: i64 = 12 // executes instructions from ingested data
88const NX_APA_PATTERN_HARDCODED_CREDENTIAL: i64 = 13 // API key / secret in source
89const NX_APA_PATTERN_THIRD_PARTY_SAAS_LEAK: i64 = 14 // new external SaaS dep detected
90
91func nx_apa_pattern_name(p: i64) -> *u8 {
92 if p == NX_APA_PATTERN_FALSE_OK_RETURN { return "FALSE_OK_RETURN" }
93 if p == NX_APA_PATTERN_MISSING_STUB_VARIANT { return "MISSING_STUB_VARIANT" }
94 if p == NX_APA_PATTERN_UNINITIALIZED_VERDICT { return "UNINITIALIZED_VERDICT" }
95 if p == NX_APA_PATTERN_SILENT_ERROR_SWALLOW { return "SILENT_ERROR_SWALLOW" }
96 if p == NX_APA_PATTERN_CLAIM_BODY_MISMATCH { return "CLAIM_BODY_MISMATCH" }
97 if p == NX_APA_PATTERN_TLS_DOWNGRADE_PATH { return "TLS_DOWNGRADE_PATH" }
98 if p == NX_APA_PATTERN_AUTH_BYPASS_PATH { return "AUTH_BYPASS_PATH" }
99 if p == NX_APA_PATTERN_TIMING_LEAK { return "TIMING_LEAK" }
100 if p == NX_APA_PATTERN_INTEGER_OVERFLOW { return "INTEGER_OVERFLOW" }
101 if p == NX_APA_PATTERN_UNBOUNDED_LOOP { return "UNBOUNDED_LOOP" }
102 if p == NX_APA_PATTERN_DESERIALIZE_UNTRUSTED { return "DESERIALIZE_UNTRUSTED" }
103 if p == NX_APA_PATTERN_PROMPT_INJECTION_TRUST { return "PROMPT_INJECTION_TRUST" }
104 if p == NX_APA_PATTERN_HARDCODED_CREDENTIAL { return "HARDCODED_CREDENTIAL" }
105 if p == NX_APA_PATTERN_THIRD_PARTY_SAAS_LEAK { return "THIRD_PARTY_SAAS_LEAK" }
106 return "UNKNOWN"
107}
108
109// Which threat-actor classes can exploit each pattern? Drives
110// severity ranking.
111func nx_apa_pattern_severity_min_threat_tier(p: i64) -> i64 {
112 if p == NX_APA_PATTERN_FALSE_OK_RETURN { return 5 } // AI adversary current-gen
113 if p == NX_APA_PATTERN_MISSING_STUB_VARIANT { return 5 }
114 if p == NX_APA_PATTERN_UNINITIALIZED_VERDICT { return 5 }
115 if p == NX_APA_PATTERN_SILENT_ERROR_SWALLOW { return 3 } // targeted criminal
116 if p == NX_APA_PATTERN_CLAIM_BODY_MISMATCH { return 6 } // Mythos-class
117 if p == NX_APA_PATTERN_TLS_DOWNGRADE_PATH { return 8 } // nation-state
118 if p == NX_APA_PATTERN_AUTH_BYPASS_PATH { return 3 }
119 if p == NX_APA_PATTERN_TIMING_LEAK { return 8 }
120 if p == NX_APA_PATTERN_INTEGER_OVERFLOW { return 4 }
121 if p == NX_APA_PATTERN_UNBOUNDED_LOOP { return 4 }
122 if p == NX_APA_PATTERN_DESERIALIZE_UNTRUSTED { return 5 }
123 if p == NX_APA_PATTERN_PROMPT_INJECTION_TRUST { return 5 } // primary AI-adversary vector
124 if p == NX_APA_PATTERN_HARDCODED_CREDENTIAL { return 1 } // script-kiddie
125 if p == NX_APA_PATTERN_THIRD_PARTY_SAAS_LEAK { return 9 } // supply chain
126 return 0
127}
128
129// ===== AdversarialFinding (per-file detection) ====================
130
131struct AdversarialFinding {
132 finding_hk: i64,
133 file_path_ptr: *u8,
134 file_path_len: i64,
135 line_number: i64,
136 pattern_kind: i64, // NX_APA_PATTERN_*
137 severity_min_threat_tier: i64,
138 suggested_fix_ptr: *u8,
139 discovered_at_unix: i64,
140 is_resolved: i64,
141 is_current: i64,
142}
143
144const NX_ADV_FINDING_BYTES: i64 = 80 // 10 fields * 8 bytes
145
146// ===== Scan a file for adversarial patterns =======================
147//
148// Bounded loop per JPL Rule 2 cardinal.
149
150const NX_APA_MAX_FINDINGS_PER_FILE: i64 = 1024
151
152func nx_adversarial_scan_file(
153 file_path_ptr: *u8,
154 file_path_len: i64,
155 file_bytes: *u8,
156 file_len: i64,
157 out_findings: **AdversarialFinding,
158 out_cap: i64,
159 now_unix: i64
160) -> i64 {
161 if file_len <= 0 { return 0 }
162 if out_cap <= 0 { return 0 }
163 // Real implementation walks file looking for each pattern:
164 // FALSE_OK_RETURN: function body has < 3 statements and ends
165 // with "return NX_*_OK"
166 // UNINITIALIZED_VERDICT: struct field "verdict" not assigned
167 // before return
168 // MISSING_STUB_VARIANT: sealed enum named "*_VERDICT" without
169 // a *_STUB_NOT_WIRED constant
170 // ...etc per pattern_kind taxonomy
171 //
172 // v1 returns 0 findings (honest stub at scan-implementation).
173 // Once nx_dir + line-by-line walker land, this primitive
174 // delivers real findings.
175 return 0
176}
177
178// ===== Aggregate scan report ======================================
179
180struct AdversarialScanReport {
181 report_hk: i64,
182 scope_name_ptr: *u8,
183 files_scanned: i64,
184 n_total_findings: i64,
185 n_critical: i64, // severity_tier >= 8
186 n_high: i64, // severity_tier 5-7
187 n_medium: i64, // severity_tier 3-4
188 n_low: i64, // severity_tier 1-2
189 scanned_at_unix: i64,
190 verdict: i64,
191}
192
193const NX_ADV_SCAN_REPORT_BYTES: i64 = 80 // 10 fields * 8 bytes
194
195// ===== Recovery action recommendation =============================
196//
197// Given a finding, what should the racing crew do?
198
199const NX_APA_ACTION_FIX_IMMEDIATELY: i64 = 1 // critical; block commits
200const NX_APA_ACTION_FIX_THIS_SESSION: i64 = 2 // high; on session backlog
201const NX_APA_ACTION_QUEUE_FIX: i64 = 3 // medium; queued
202const NX_APA_ACTION_DOCUMENT_AS_RESIDUAL: i64 = 4 // low; document the residual risk
203const NX_APA_ACTION_ACCEPT_RISK: i64 = 5 // not applicable to substrate's threat model
204
205func nx_apa_action_for_severity(severity_tier: i64) -> i64 {
206 if severity_tier >= 8 { return NX_APA_ACTION_FIX_IMMEDIATELY }
207 if severity_tier >= 5 { return NX_APA_ACTION_FIX_THIS_SESSION }
208 if severity_tier >= 3 { return NX_APA_ACTION_QUEUE_FIX }
209 return NX_APA_ACTION_DOCUMENT_AS_RESIDUAL
210}
211
212func nx_apa_action_name(a: i64) -> *u8 {
213 if a == NX_APA_ACTION_FIX_IMMEDIATELY { return "FIX_IMMEDIATELY" }
214 if a == NX_APA_ACTION_FIX_THIS_SESSION { return "FIX_THIS_SESSION" }
215 if a == NX_APA_ACTION_QUEUE_FIX { return "QUEUE_FIX" }
216 if a == NX_APA_ACTION_DOCUMENT_AS_RESIDUAL { return "DOCUMENT_AS_RESIDUAL" }
217 if a == NX_APA_ACTION_ACCEPT_RISK { return "ACCEPT_RISK" }
218 return "UNKNOWN"
219}
220
221// ===== Substring counter (bounded per JPL Rule 2) =================
222//
223// Counts the number of (possibly overlapping) occurrences of `needle`
224// in `haystack`. Bounded: outer loop walks at most haystack_len
225// positions; inner loop walks at most needle_len bytes. Used by the
226// per-file pattern-marker counter below. Linear time O(n*m) is
227// adequate for runtime-file scale (<= ~64 KiB / file).
228
229const NX_APA_MAX_SCAN_BYTES: i64 = 8388608 // 8 MiB / file ceiling
230
231func nx_apa_count_substring(
232 haystack: *u8,
233 haystack_len: i64,
234 needle: *u8,
235 needle_len: i64
236) -> i64 {
237 if haystack_len < needle_len { return 0 }
238 if needle_len <= 0 { return 0 }
239 var count: i64 = 0
240 var i: i64 = 0
241 var iter: i64 = 0
242 var verdict: i64 = 0
243 let limit: i64 = haystack_len - needle_len
244 while verdict == 0 && iter < NX_APA_MAX_SCAN_BYTES {
245 if i > limit { verdict = 1 }
246 if verdict == 0 {
247 var j: i64 = 0
248 var inner_iter: i64 = 0
249 var inner_verdict: i64 = 0
250 var matched: i64 = 1
251 while inner_verdict == 0 && inner_iter < needle_len {
252 if haystack[i + j] != needle[j] {
253 matched = 0
254 inner_verdict = 1
255 }
256 if inner_verdict == 0 {
257 j = j + 1
258 inner_iter = inner_iter + 1
259 }
260 }
261 if matched == 1 { count = count + 1 }
262 i = i + 1
263 iter = iter + 1
264 }
265 }
266 return count
267}
268
269// ===== ApaFileScanCounts (per-file pattern marker observations) ===
270//
271// Records substring-occurrence counts for the FALSE_OK_RETURN-class
272// patterns the substrate audits today. Caller-supplied buffers
273// remain valid as long as the substrate doesn't recycle them.
274
275struct ApaFileScanCounts {
276 counts_hk: i64,
277 file_path_ptr: *u8,
278 file_path_len: i64,
279 file_bytes_len: i64,
280 n_stub_not_wired_markers: i64, // "_STUB_NOT_WIRED" total
281 n_header_partial_wired: i64, // "wired_status: PARTIAL_WIRED"
282 n_header_fully_wired: i64, // "wired_status: FULLY_WIRED"
283 n_header_honest_stub: i64, // "wired_status: HONEST_STUB"
284 n_func_decls: i64, // "\nfunc " count
285 n_return_nx: i64, // "return NX_" count
286 scanned_at_unix: i64,
287 verdict: i64,
288}
289
290const NX_APA_FILE_SCAN_COUNTS_BYTES: i64 = 104 // 13 fields * 8 bytes
291
292// Pre-allocated needle constants (constructed once per scan call to
293// keep the parser-quirks cardinal happy -- no compile-time string
294// literals as struct fields).
295
296func nx_apa_make_needle_stub(out: *u8) {
297 // "_STUB_NOT_WIRED"
298 out[0] = 0x5f; out[1] = 0x53; out[2] = 0x54; out[3] = 0x55 // _STU
299 out[4] = 0x42; out[5] = 0x5f; out[6] = 0x4e; out[7] = 0x4f // B_NO
300 out[8] = 0x54; out[9] = 0x5f; out[10] = 0x57; out[11] = 0x49 // T_WI
301 out[12] = 0x52; out[13] = 0x45; out[14] = 0x44; out[15] = 0 // RED\0
302}
303
304func nx_apa_make_needle_partial(out: *u8) {
305 // "wired_status: PARTIAL_WIRED"
306 out[0] = 0x77; out[1] = 0x69; out[2] = 0x72; out[3] = 0x65 // wire
307 out[4] = 0x64; out[5] = 0x5f; out[6] = 0x73; out[7] = 0x74 // d_st
308 out[8] = 0x61; out[9] = 0x74; out[10] = 0x75; out[11] = 0x73 // atus
309 out[12] = 0x3a; out[13] = 0x20; out[14] = 0x50; out[15] = 0x41 // : PA
310 out[16] = 0x52; out[17] = 0x54; out[18] = 0x49; out[19] = 0x41 // RTIA
311 out[20] = 0x4c; out[21] = 0x5f; out[22] = 0x57; out[23] = 0x49 // L_WI
312 out[24] = 0x52; out[25] = 0x45; out[26] = 0x44; out[27] = 0 // RED\0
313}
314
315func nx_apa_make_needle_fully(out: *u8) {
316 // "wired_status: FULLY_WIRED"
317 out[0] = 0x77; out[1] = 0x69; out[2] = 0x72; out[3] = 0x65 // wire
318 out[4] = 0x64; out[5] = 0x5f; out[6] = 0x73; out[7] = 0x74 // d_st
319 out[8] = 0x61; out[9] = 0x74; out[10] = 0x75; out[11] = 0x73 // atus
320 out[12] = 0x3a; out[13] = 0x20; out[14] = 0x46; out[15] = 0x55 // : FU
321 out[16] = 0x4c; out[17] = 0x4c; out[18] = 0x59; out[19] = 0x5f // LLY_
322 out[20] = 0x57; out[21] = 0x49; out[22] = 0x52; out[23] = 0x45 // WIRE
323 out[24] = 0x44; out[25] = 0 // D\0
324}
325
326func nx_apa_make_needle_honest(out: *u8) {
327 // "wired_status: HONEST_STUB"
328 out[0] = 0x77; out[1] = 0x69; out[2] = 0x72; out[3] = 0x65 // wire
329 out[4] = 0x64; out[5] = 0x5f; out[6] = 0x73; out[7] = 0x74 // d_st
330 out[8] = 0x61; out[9] = 0x74; out[10] = 0x75; out[11] = 0x73 // atus
331 out[12] = 0x3a; out[13] = 0x20; out[14] = 0x48; out[15] = 0x4f // : HO
332 out[16] = 0x4e; out[17] = 0x45; out[18] = 0x53; out[19] = 0x54 // NEST
333 out[20] = 0x5f; out[21] = 0x53; out[22] = 0x54; out[23] = 0x55 // _STU
334 out[24] = 0x42; out[25] = 0 // B\0
335}
336
337func nx_apa_make_needle_funcdecl(out: *u8) {
338 // "\nfunc " (5 bytes)
339 out[0] = 0x0a; out[1] = 0x66; out[2] = 0x75; out[3] = 0x6e
340 out[4] = 0x63; out[5] = 0x20; out[6] = 0
341}
342
343func nx_apa_make_needle_return_nx(out: *u8) {
344 // "return NX_" (10 bytes)
345 out[0] = 0x72; out[1] = 0x65; out[2] = 0x74; out[3] = 0x75
346 out[4] = 0x72; out[5] = 0x6e; out[6] = 0x20; out[7] = 0x4e
347 out[8] = 0x58; out[9] = 0x5f; out[10] = 0
348}
349
350// ===== Scan a file's bytes for marker patterns ====================
351
352func nx_apa_scan_file_returns(
353 file_path_ptr: *u8,
354 file_path_len: i64,
355 file_bytes: *u8,
356 file_len: i64,
357 now_unix: i64
358) -> *ApaFileScanCounts {
359 let raw: *u8 = sys_mmap(NX_APA_FILE_SCAN_COUNTS_BYTES)
360 let s: *ApaFileScanCounts = raw as *ApaFileScanCounts
361 s.counts_hk = 0
362 s.file_path_ptr = file_path_ptr
363 s.file_path_len = file_path_len
364 s.file_bytes_len = file_len
365 s.n_stub_not_wired_markers = 0
366 s.n_header_partial_wired = 0
367 s.n_header_fully_wired = 0
368 s.n_header_honest_stub = 0
369 s.n_func_decls = 0
370 s.n_return_nx = 0
371 s.scanned_at_unix = now_unix
372 s.verdict = NX_APA_SCAN_FAIL
373
374 if file_bytes == 0 as *u8 { return s }
375 if file_len <= 0 { return s }
376
377 let n_stub: *u8 = sys_mmap(32)
378 nx_apa_make_needle_stub(n_stub)
379 s.n_stub_not_wired_markers = nx_apa_count_substring(file_bytes, file_len, n_stub, 15)
380
381 let n_partial: *u8 = sys_mmap(32)
382 nx_apa_make_needle_partial(n_partial)
383 s.n_header_partial_wired = nx_apa_count_substring(file_bytes, file_len, n_partial, 27)
384
385 let n_fully: *u8 = sys_mmap(32)
386 nx_apa_make_needle_fully(n_fully)
387 s.n_header_fully_wired = nx_apa_count_substring(file_bytes, file_len, n_fully, 25)
388
389 let n_honest: *u8 = sys_mmap(32)
390 nx_apa_make_needle_honest(n_honest)
391 s.n_header_honest_stub = nx_apa_count_substring(file_bytes, file_len, n_honest, 25)
392
393 let n_func: *u8 = sys_mmap(16)
394 nx_apa_make_needle_funcdecl(n_func)
395 s.n_func_decls = nx_apa_count_substring(file_bytes, file_len, n_func, 6)
396
397 let n_ret: *u8 = sys_mmap(16)
398 nx_apa_make_needle_return_nx(n_ret)
399 s.n_return_nx = nx_apa_count_substring(file_bytes, file_len, n_ret, 10)
400
401 s.verdict = NX_APA_OK
402 return s
403}
404
405// ===== File consistency judgment ==================================
406//
407// Heuristic 3-tier verdict comparing declared wired_status header
408// against observed pattern markers.
409//
410// 1 = CONSISTENT: declared header matches observed stub-marker count
411// 2 = OVERSTATED: declared FULLY_WIRED but body has stub markers
412// 3 = UNDERSTATED: declared PARTIAL_WIRED/HONEST_STUB but no stub markers
413//
414// Returns 0 (NOT_VERIFIED) if no header was found at all.
415
416const NX_APA_CONSISTENCY_NOT_VERIFIED: i64 = 0
417const NX_APA_CONSISTENCY_CONSISTENT: i64 = 1
418const NX_APA_CONSISTENCY_OVERSTATED: i64 = 2
419const NX_APA_CONSISTENCY_UNDERSTATED: i64 = 3
420
421func nx_apa_consistency_name(c: i64) -> *u8 {
422 if c == NX_APA_CONSISTENCY_NOT_VERIFIED { return "NOT_VERIFIED" }
423 if c == NX_APA_CONSISTENCY_CONSISTENT { return "CONSISTENT" }
424 if c == NX_APA_CONSISTENCY_OVERSTATED { return "OVERSTATED" }
425 if c == NX_APA_CONSISTENCY_UNDERSTATED { return "UNDERSTATED" }
426 return "UNKNOWN"
427}
428
429// Threshold: a file with at most 2 STUB-marker occurrences is
430// considered "no usage" (the sealed-enum DECLARATION + verdict-name
431// resolver alone produce ~2 occurrences in audit primitives;
432// stubbed-return USAGE pushes the count higher).
433const NX_APA_STUB_USAGE_THRESHOLD: i64 = 3
434
435func nx_apa_judge_file_consistency(s: *ApaFileScanCounts) -> i64 {
436 if s == 0 as *ApaFileScanCounts { return NX_APA_CONSISTENCY_NOT_VERIFIED }
437 let declared_fully: i64 = s.n_header_fully_wired
438 let declared_partial: i64 = s.n_header_partial_wired
439 let declared_honest: i64 = s.n_header_honest_stub
440 let stub_count: i64 = s.n_stub_not_wired_markers
441
442 let total_headers: i64 = declared_fully + declared_partial + declared_honest
443 if total_headers == 0 { return NX_APA_CONSISTENCY_NOT_VERIFIED }
444
445 // FULLY_WIRED + visible stub-USAGE = overstated
446 if declared_fully >= 1 {
447 if declared_partial == 0 {
448 if declared_honest == 0 {
449 if stub_count >= NX_APA_STUB_USAGE_THRESHOLD { return NX_APA_CONSISTENCY_OVERSTATED }
450 return NX_APA_CONSISTENCY_CONSISTENT
451 }
452 }
453 }
454
455 // PARTIAL_WIRED or HONEST_STUB without stub markers = understated
456 // (no honest-stub markers present to corroborate the partial claim)
457 if declared_partial >= 1 {
458 if stub_count == 0 { return NX_APA_CONSISTENCY_UNDERSTATED }
459 return NX_APA_CONSISTENCY_CONSISTENT
460 }
461 if declared_honest >= 1 {
462 if stub_count == 0 { return NX_APA_CONSISTENCY_UNDERSTATED }
463 return NX_APA_CONSISTENCY_CONSISTENT
464 }
465 return NX_APA_CONSISTENCY_CONSISTENT
466}