nx_security_census_lib.nx source
↩ module page · 72 lines · 4221 B
1// nx_security_census_lib.nx -- PURE core for the SECURITY maturity census. NO main (a lib), so the census
2// organ AND its gate import the SAME measured core and can never diverge -- the nx_ecomat_lib pattern.
3//
4// It carries three things the rest of the census composes on:
5// 1. sc_have(path) -- HAVE iff a real sovereign organ opens on disk (auditor discipline, no false-green;
6// existence != full correctness -> the per-organ gate is the ratchet). Same oracle
7// nx_adversarial_census uses.
8// 2. sc_weight(crit) -- WEAKEST-LINK weighting: a critical control counts SC_CRIT_W(3)x, standard 1x, so a
9// missing CRITICAL control drags the grade hard. Encodes the "security = weakest link"
10// maturity principle mechanically (a research-grade primitive that is BUILT-BUT-UNWIRED
11// does not defend the live path, and must not read as coverage).
12// 3. sc_permil(hw,tw)-- have_weight*1000/total_weight (0 if none). THE number the ecosystem-maturity rollup
13// live-derives the `security` domain grade from.
14// license_tier: ORIGINAL
15import "nx_syscalls.nx"
16import "nx_artifact_root.nx"
17
18// Scratch for the resolved artifact path. Named so the ALLOCATION and the BOUND are the same symbol
19// and cannot drift apart -- that drift is exactly how a bounded copy turns into a silent overrun.
20const SC_PATH_CAP: i64 = 512
21
22// attack-surface tags (which surface a control defends)
23const SC_SURF_WIRE: i64 = 0 // over the wire: transport, auth, crypto
24const SC_SURF_NET: i64 = 1 // in the network: host, lateral movement, tenancy
25const SC_SURF_DEV: i64 = 2 // on the device: firmware, boot, supply chain
26const SC_SURF_AI: i64 = 3 // ai-operational: the agents
27
28// weakest-link multiplier: a missing CRITICAL control weighs SC_CRIT_W as much as a standard one.
29const SC_CRIT_W: i64 = 3
30
31func sc_surf_name(s: i64) -> *u8 {
32 if s == SC_SURF_WIRE { return "wire" as *u8 }
33 if s == SC_SURF_NET { return "net " as *u8 }
34 if s == SC_SURF_DEV { return "dev " as *u8 }
35 if s == SC_SURF_AI { return "ai " as *u8 }
36 return "??? " as *u8
37}
38
39// HAVE iff a real sovereign organ opens on disk. one-way disk oracle (no fabrication).
40// ★CWD-INDEPENDENT (fixed 2026-08-08, measured -- this was a bare RELATIVE open).
41// The control table holds paths like "runtime/nx_tls13.nx". Run from nishihost there is no runtime/
42// (it lives at buildroot/runtime/), so EVERY control read GAP and this organ appended
43// coverage_permil=87 over a true 456 -- demoting the `security` domain FUNCTIONAL->TOY and the
44// published headline 450->442 permil. coverage_permil is consumed by nx_ecosystem_maturity_rollup,
45// so a CWD-relative oracle here SILENTLY REWRITES A PUBLISHED GRADE and nothing in the log says
46// which directory it ran from.
47// ★COMPOSED, NOT RE-IMPLEMENTED: ar_resolve is the estate's ONE artifact-root resolver, and
48// knowledge/evidence_roots.conf ALREADY carries the `root=buildroot/` row whose own comment documents
49// exactly this case ("sources are named runtime/nx_X.nx and live at buildroot/runtime/nx_X.nx here").
50// ★SAFE BY CONSTRUCTION IN BOTH DIRECTIONS: ar_resolve tries the path VERBATIM FIRST, so anything that
51// resolves today is unchanged; and it is FAIL-CLOSED (unresolvable -> leaves out==input and returns 0),
52// so it can only rescue a control that genuinely exists under a CONFIGURED root -- it cannot invent one.
53// A security census must never get more permissive by accident; this gets more permissive only about
54// WHERE it looks, never about WHETHER the file is really there.
55func sc_have(path: *u8) -> i64 {
56 let rp: *u8 = sys_mmap(SC_PATH_CAP)
57 let ok: i64 = ar_resolve(path, rp)
58 sys_munmap(rp, SC_PATH_CAP)
59 return ok
60}
61
62// weakest-link weight for a control: critical -> SC_CRIT_W, standard -> 1.
63func sc_weight(crit: i64) -> i64 {
64 if crit == 1 { return SC_CRIT_W }
65 return 1
66}
67
68// coverage permille (0..1000) = have_weight * 1000 / total_weight ; guarded div-by-zero -> 0.
69func sc_permil(have_w: i64, total_w: i64) -> i64 {
70 if total_w <= 0 { return 0 }
71 return (have_w * 1000) / total_w
72}