code wiki / _hdl_build / nx_pkg_gate.nx

nx_pkg_gate.nx source

↩ module page · 494 lines · 27328 B

1// nx_pkg_gate.nx -- THE GATE FOR THE CONTENT-ADDRESSED PACKAGE LANE (LR3 of /compare/lang). 2// 3// Subject: the DEPLOYED nx_pkg binary (fork-style end-to-end; argv[1] overrides the subject path so a 4// mutation bite can aim at a staged mutant without touching live). Every fixture is assembled AT 5// RUNTIME in /tmp/nx_pkg_gate/ -- nothing is checked in, so a source scanner can never find this gate's 6// own bad inputs, and setup is idempotent so a rerun cannot inherit a prior run's artifacts. 7// 8// WHAT IT PROVES, AND WHY EACH TOOTH EXISTS: 9// A GUARD THAT REFUSES EVERYTHING PASSES EVERY NEGATIVE TEST. So every refusal tooth here is paired 10// with a POSITIVE CONTROL that MUST be allowed, and every refusal asserts WHICH RULE FIRED -- both 11// the rule name on stdout and the distinct exit code. A suite that only asks `was it refused?` is 12// blind by construction, and this estate has already shipped that exact defect once (four SSRF deny 13// tests green while the guard refused every url). 14// 15// ZERO INSTALL-TIME EXECUTION is proven MECHANICALLY, never asserted. The module fixture carries a 16// real would-be install hook, and the gate first RUNS that hook itself to prove it genuinely produces 17// an observable marker (the anti-vacuity control -- otherwise `the marker is absent` proves only that 18// the marker was never producible). The marker is then cleared, the same module resolved through the 19// subject, and the marker asserted ABSENT while the resolve itself SUCCEEDED. 20// 21// NOT ROSTER-ADMITTED BY DEFAULT: it forks the subject ~25 times. Run via nx_job_run or /api/gate_run. 22// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 23import "nx_syscalls.nx" 24import "nx_gate_verdict.nx" 25import "nx_sha256.nx" 26 27const PG_SUBJECT_DEFAULT: *u8 = "./nx_pkg.elf" as *u8 28const PG_DIR: *u8 = "/tmp/nx_pkg_gate" as *u8 29const PG_STORE: *u8 = "/tmp/nx_pkg_gate/store" as *u8 30const PG_CONF: *u8 = "/tmp/nx_pkg_gate/pkg_cas.conf" as *u8 31const PG_CONF_BAD: *u8 = "/tmp/nx_pkg_gate/bad_missing_key.conf" as *u8 32const PG_LOCK: *u8 = "/tmp/nx_pkg_gate/pkg.lock" as *u8 33const PG_JRNL: *u8 = "/tmp/nx_pkg_gate/pkgcas.jrnl" as *u8 34const PG_OUT: *u8 = "/tmp/nx_pkg_gate/capture.txt" as *u8 35const PG_MOD_HOOK: *u8 = "/tmp/nx_pkg_gate/mod_with_install_hook.nx" as *u8 36const PG_MOD_SQUAT: *u8 = "/tmp/nx_pkg_gate/mod_typosquat.nx" as *u8 37const PG_PWN_CONF: *u8 = "/tmp/nx_pkg_gate/pwn_conf.conf" as *u8 38const PG_PWN_PAYLOAD: *u8 = "/tmp/nx_pkg_gate/pwn_payload.txt" as *u8 39const PG_PWN_MARKER: *u8 = "/tmp/nx_pkg_gate/PWNED" as *u8 40const PG_PWN_LOCK: *u8 = "/tmp/nx_pkg_gate/pwn.lock" as *u8 41const PG_PWN_STORE: *u8 = "/tmp/nx_pkg_gate/pwnstore" as *u8 42const PG_DEST_OK: *u8 = "/tmp/nx_pkg_gate/resolved_hook.nx" as *u8 43const PG_DEST_GOOD: *u8 = "/tmp/nx_pkg_gate/resolved_pinned.nx" as *u8 44const PG_DEST_SQUAT: *u8 = "/tmp/nx_pkg_gate/resolved_squat.nx" as *u8 45const PG_DEST_MISMATCH: *u8 = "/tmp/nx_pkg_gate/resolved_mismatch.nx" as *u8 46const PG_DEST_OUTSIDE: *u8 = "/etc/nx_pkg_gate_should_never_appear" as *u8 47// the legitimate logical name, and the lookalike an attacker would publish beside it 48const PG_NAME_REAL: *u8 = "nx_syscalls" as *u8 49const PG_NAME_SQUAT: *u8 = "nx_sysca11s" as *u8 50 51const PG_SHAHEX: i64 = 64 52const PG_DIGEST_BYTES: i64 = 32 53const PG_NL: i64 = 10 54const PG_BAR: i64 = 124 55const PG_ASCII_0: i64 = 48 56const PG_ASCII_9: i64 = 57 57const PG_SCRATCH: i64 = 4096 58const PG_MODE_X: i64 = 493 59// distinct exit codes the subject promises; asserted individually so a refusal for the WRONG reason 60// cannot score as a pass (a red for the wrong reason is not evidence) 61const PG_EXIT_USAGE: i64 = 3 62const PG_EXIT_CONF: i64 = 4 63const PG_EXIT_MALFORMED: i64 = 5 64const PG_EXIT_ABSENT: i64 = 6 65const PG_EXIT_MISMATCH: i64 = 7 66const PG_EXIT_DEST: i64 = 8 67const PG_EXIT_NOTPINNED: i64 = 9 68const PG_EXIT_PINCONFLICT: i64 = 10 69const PG_EXIT_CHAIN: i64 = 11 70const PG_EXIT_PINAMBIG: i64 = 13 71 72func pg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 73func pg_mkdir(path: *u8) -> i64 { 74 let nbox: *i64 = sys_mmap(16) as *i64 75 nbox[0] = 258 76 let rc: i64 = __syscall(nbox[0], 0 - 100, path as i64, 0x1ed, 0, 0, 0) 77 sys_munmap(nbox as *u8, 16) 78 return rc 79} 80func pg_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 } 81func pg_cat(d: *u8, o: i64, s: *u8) -> i64 { var p: i64 = o; var i: i64 = 0; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p } 82func pg_write(path: *u8, buf: *u8, n: i64) -> i64 { 83 let fd: i64 = sys_openat_wr(path, MODE_0644) 84 if fd < 0 { return 0 - 1 } 85 var w: i64 = 0 86 while w < n { 87 let r: i64 = sys_write(fd, ((buf as i64) + w) as *u8, n - w) 88 if r <= 0 { sys_close(fd); return 0 - 1 } 89 w = w + r 90 } 91 sys_close(fd) 92 return n 93} 94func pg_write_str(path: *u8, s: *u8) -> i64 { return pg_write(path, s, pg_slen(s)) } 95func pg_find(hay: *u8, n: i64, needle: *u8) -> i64 { 96 let m: i64 = pg_slen(needle) 97 if m == 0 { return 0 - 1 } 98 var i: i64 = 0 99 while i + m <= n { 100 var j: i64 = 0 101 var ok: i64 = 1 102 while j < m { if hay[i + j] != needle[j] { ok = 0; j = m } else { j = j + 1 } } 103 if ok == 1 { return i } 104 i = i + 1 105 } 106 return 0 - 1 107} 108func pg_num_after(hay: *u8, n: i64, key: *u8) -> i64 { 109 let at: i64 = pg_find(hay, n, key) 110 if at < 0 { return 0 - 1 } 111 var i: i64 = at + pg_slen(key) 112 var v: i64 = 0 113 var seen: i64 = 0 114 while i < n { 115 let c: i64 = hay[i] as i64 116 if c < PG_ASCII_0 { break } 117 if c > PG_ASCII_9 { break } 118 v = v * 10 + (c - PG_ASCII_0) 119 seen = 1 120 i = i + 1 121 } 122 if seen == 0 { return 0 - 1 } 123 return v 124} 125func pg_hex_into(dig: *u8, out: *u8) -> i64 { 126 let h: *u8 = "0123456789abcdef" as *u8 127 var i: i64 = 0 128 while i < PG_DIGEST_BYTES { let v: i64 = dig[i] as i64; out[i*2] = h[(v >> 4) & 15]; out[i*2+1] = h[v & 15]; i = i + 1 } 129 out[PG_SHAHEX] = 0 as u8 130 return PG_SHAHEX 131} 132// independent digest of a file ON DISK -- the gate hashes the RESULT itself rather than believing the 133// subject's own receipt. Returns byte count, or -1 when absent. 134func pg_file_sha(path: *u8, outhex: *u8) -> i64 { 135 let lp: *i64 = sys_mmap(16) as *i64 136 let b: *u8 = sys_read_file(path, lp) 137 if (b as i64) == 0 { outhex[0] = 0 as u8; return 0 - 1 } 138 let d: *u8 = sys_mmap(PG_DIGEST_BYTES + 8) 139 sha256_digest(b, lp[0], d) 140 pg_hex_into(d, outhex) 141 let n: i64 = lp[0] 142 sys_free_file(b, n) 143 return n 144} 145// fork the subject with up to 4 args; stdout+stderr captured to PG_OUT; returns the exit code 146func pg_run(subject: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, nargs: i64) -> i64 { 147 let arr: *i64 = sys_mmap(64) as *i64 148 arr[0] = subject as i64 149 if nargs >= 1 { arr[1] = a1 as i64 } 150 if nargs >= 2 { arr[2] = a2 as i64 } 151 if nargs >= 3 { arr[3] = a3 as i64 } 152 if nargs >= 4 { arr[4] = a4 as i64 } 153 arr[nargs + 1] = 0 154 let env: *i64 = sys_mmap(16) as *i64 155 env[0] = 0 156 let pid: i64 = sys_fork() 157 if pid == 0 { 158 let fd: i64 = sys_openat_wr(PG_OUT, MODE_0644) 159 if fd >= 0 { sys_dup3(fd, 1, 0); sys_dup3(fd, 2, 0) } 160 sys_execve(subject, arr, env) 161 sys_exit(127) 162 } 163 let st: *i64 = sys_mmap(16) as *i64 164 sys_wait4(pid, st, 0) 165 return wait_status_rc(st[0]) 166} 167func pg_read_out(lp: *i64) -> *u8 { 168 let b: *u8 = sys_read_file(PG_OUT, lp) 169 if (b as i64) == 0 { lp[0] = 0; return sys_mmap(16) } 170 return b 171} 172// did the last capture name THIS rule? Both the rule name and the exit code are asserted by callers. 173func pg_saw_rule(rule: *u8) -> i64 { 174 let lp: *i64 = sys_mmap(16) as *i64 175 let b: *u8 = pg_read_out(lp) 176 let need: i64 = pg_slen(rule) + 8 177 let pat: *u8 = sys_mmap(need) 178 var o: i64 = pg_cat(pat, 0, "rule=" as *u8) 179 o = pg_cat(pat, o, rule) 180 var hit: i64 = 0 181 if pg_find(b, lp[0], pat) >= 0 { hit = 1 } 182 sys_munmap(pat, need) 183 return hit 184} 185 186func main(argc: i64, argv: *i64) -> i64 { 187 let ctr: *i64 = gv_ctr() 188 gv_head("nx_pkg_gate -- content-addressed resolution, a typosquat that cannot resolve, and zero install-time execution proven by an absent side effect" as *u8) 189 var subject: *u8 = PG_SUBJECT_DEFAULT 190 if argc >= 2 { subject = argv[1] as *u8 } 191 gv_puts(" subject: " as *u8); gv_puts(subject); gv_puts("\n" as *u8) 192 193 // ---- SETUP: idempotent, runtime-assembled --------------------------------------------------- 194 pg_mkdir(PG_DIR) 195 pg_mkdir(PG_STORE) 196 sys_unlinkat(PG_LOCK) 197 sys_unlinkat(PG_JRNL) 198 sys_unlinkat(PG_PWN_MARKER) 199 sys_unlinkat(PG_PWN_LOCK) 200 sys_unlinkat(PG_DEST_OK) 201 sys_unlinkat(PG_DEST_GOOD) 202 sys_unlinkat(PG_DEST_SQUAT) 203 sys_unlinkat(PG_DEST_MISMATCH) 204 sys_unlinkat(PG_DEST_OUTSIDE) 205 206 let cbuf: *u8 = sys_mmap(PG_SCRATCH) 207 var co: i64 = pg_cat(cbuf, 0, "store_root=" as *u8) 208 co = pg_cat(cbuf, co, PG_STORE) 209 co = pg_cat(cbuf, co, "\nlock_path=" as *u8) 210 co = pg_cat(cbuf, co, PG_LOCK) 211 co = pg_cat(cbuf, co, "\nlog_path=" as *u8) 212 co = pg_cat(cbuf, co, PG_JRNL) 213 co = pg_cat(cbuf, co, "\n" as *u8) 214 pg_write(PG_CONF, cbuf, co) 215 216 // a conf missing log_path -- an absent key must REFUSE, never default 217 var bo: i64 = pg_cat(cbuf, 0, "store_root=" as *u8) 218 bo = pg_cat(cbuf, bo, PG_STORE) 219 bo = pg_cat(cbuf, bo, "\nlock_path=" as *u8) 220 bo = pg_cat(cbuf, bo, PG_LOCK) 221 bo = pg_cat(cbuf, bo, "\n" as *u8) 222 pg_write(PG_CONF_BAD, cbuf, bo) 223 224 // the would-be install hook: a SEPARATE conf whose journal path IS the marker, so running the hook 225 // command creates exactly one observable file that a single probe can settle. 226 var po: i64 = pg_cat(cbuf, 0, "store_root=" as *u8) 227 po = pg_cat(cbuf, po, PG_PWN_STORE) 228 po = pg_cat(cbuf, po, "\nlock_path=" as *u8) 229 po = pg_cat(cbuf, po, PG_PWN_LOCK) 230 po = pg_cat(cbuf, po, "\nlog_path=" as *u8) 231 po = pg_cat(cbuf, po, PG_PWN_MARKER) 232 po = pg_cat(cbuf, po, "\n" as *u8) 233 pg_write(PG_PWN_CONF, cbuf, po) 234 pg_write_str(PG_PWN_PAYLOAD, "payload bytes the hook would install\n" as *u8) 235 pg_mkdir(PG_PWN_STORE) 236 237 // THE MODULE UNDER TEST: ordinary-looking source that carries a live install hook in its body. 238 var mo: i64 = pg_cat(cbuf, 0, "// module fixture: this file carries a WORKING install hook\npostinstall=" as *u8) 239 mo = pg_cat(cbuf, mo, subject) 240 mo = pg_cat(cbuf, mo, " cas_put " as *u8) 241 mo = pg_cat(cbuf, mo, PG_PWN_PAYLOAD) 242 mo = pg_cat(cbuf, mo, " " as *u8) 243 mo = pg_cat(cbuf, mo, PG_PWN_CONF) 244 mo = pg_cat(cbuf, mo, "\nfunc hookmod_id() -> i64 { return 1 }\n" as *u8) 245 pg_write(PG_MOD_HOOK, cbuf, mo) 246 let hooklen: i64 = mo 247 248 // the typosquat's payload: different bytes, same intent to be mistaken for the real module 249 pg_write_str(PG_MOD_SQUAT, "// TYPOSQUAT: lookalike name, attacker-chosen bytes\nfunc syscalls_init() -> i64 { return 0 - 1 }\n" as *u8) 250 251 let lp: *i64 = sys_mmap(16) as *i64 252 var rc: i64 = 0 253 var ob: *u8 = sys_mmap(16) 254 var onn: i64 = 0 255 256 // the population this run actually examined: two distinct modules through the whole lane 257 gv_subjects("modules driven end-to-end through the CAS lane" as *u8, 2, ctr) 258 259 // ---- T1 the fixture genuinely contains a live install hook ------------------------------------ 260 let hb: *u8 = sys_read_file(PG_MOD_HOOK, lp) 261 var t1: i64 = 0 262 if (hb as i64) != 0 { if pg_find(hb, lp[0], "postinstall=" as *u8) >= 0 { if lp[0] == hooklen { t1 = 1 } } } 263 gv_puts(" fixture module bytes=" as *u8); gv_num(hooklen); gv_puts("\n" as *u8) 264 gv_check("fixture-module-really-carries-an-install-hook-line (the condition, asserted before any outcome)" as *u8, t1 == 1, ctr) 265 266 // ---- T2 ANTI-VACUITY: the hook is genuinely executable and produces the marker ---------------- 267 // Without this, `the marker is absent` would prove only that the marker was never producible. 268 sys_unlinkat(PG_PWN_MARKER) 269 let before_hook: i64 = pg_exists(PG_PWN_MARKER) 270 pg_run(subject, "cas_put" as *u8, PG_PWN_PAYLOAD, PG_PWN_CONF, 0 as *u8, 3) 271 let after_hook: i64 = pg_exists(PG_PWN_MARKER) 272 gv_puts(" hook control: marker before=" as *u8); gv_num(before_hook) 273 gv_puts(" after_running_the_hook=" as *u8); gv_num(after_hook); gv_puts("\n" as *u8) 274 gv_bite("neg-control-the-planted-install-hook-DOES-fire-when-executed (bad=marker appears, good=absent after reset)" as *u8, after_hook, before_hook, ctr) 275 276 // reset, and PROVE the reset landed before measuring the thing under test 277 sys_unlinkat(PG_PWN_MARKER) 278 gv_check("marker-cleared-before-the-resolve-under-test (a stale marker would make the next tooth lie)" as *u8, pg_exists(PG_PWN_MARKER) == 0, ctr) 279 280 // ---- T3 put the hook-carrying module into the store ------------------------------------------ 281 rc = pg_run(subject, "cas_put" as *u8, PG_MOD_HOOK, PG_CONF, 0 as *u8, 3) 282 ob = pg_read_out(lp); onn = lp[0] 283 let dig_hook: *u8 = sys_mmap(PG_SHAHEX + 8) 284 var dh: i64 = pg_find(ob, onn, "digest=" as *u8) 285 var t3: i64 = 0 286 if rc == 0 { if dh >= 0 { 287 var i: i64 = 0 288 while i < PG_SHAHEX { dig_hook[i] = ob[dh + 7 + i]; i = i + 1 } 289 dig_hook[PG_SHAHEX] = 0 as u8 290 t3 = 1 291 } } 292 // the gate re-derives the digest itself: the subject's receipt is never the oracle 293 let own_hook: *u8 = sys_mmap(PG_SHAHEX + 8) 294 let ownn: i64 = pg_file_sha(PG_MOD_HOOK, own_hook) 295 var t3b: i64 = 0 296 if t3 == 1 { if ownn == hooklen { var same: i64 = 1; var i2: i64 = 0; while i2 < PG_SHAHEX { if dig_hook[i2] != own_hook[i2] { same = 0; i2 = PG_SHAHEX } else { i2 = i2 + 1 } } t3b = same } } 297 gv_puts(" cas_put rc=" as *u8); gv_num(rc); gv_puts(" digest=" as *u8); gv_puts(dig_hook); gv_puts("\n" as *u8) 298 gv_check("cas-put-digest-equals-the-gate-s-own-independent-sha256-of-the-file (not the subject's receipt)" as *u8, t3b == 1, ctr) 299 300 // ---- T4 THE CENTRAL CLAIM: resolve SUCCEEDS and executes nothing ------------------------------ 301 rc = pg_run(subject, "cas_resolve" as *u8, dig_hook, PG_DEST_OK, PG_CONF, 4) 302 let marker_after_resolve: i64 = pg_exists(PG_PWN_MARKER) 303 let resolved_present: i64 = pg_exists(PG_DEST_OK) 304 gv_puts(" cas_resolve rc=" as *u8); gv_num(rc) 305 gv_puts(" dest_present=" as *u8); gv_num(resolved_present) 306 gv_puts(" install_marker_present=" as *u8); gv_num(marker_after_resolve); gv_puts("\n" as *u8) 307 var t4: i64 = 0 308 if rc == 0 { if resolved_present == 1 { if marker_after_resolve == 0 { t4 = 1 } } } 309 gv_check("ZERO-INSTALL-EXECUTION: a hook-carrying module RESOLVES SUCCESSFULLY and the hook's side effect is absent (rc, dest and marker all bound in the condition)" as *u8, t4 == 1, ctr) 310 311 // ---- T5 the resolved bytes hash to the digest that was ASKED FOR ------------------------------ 312 let got: *u8 = sys_mmap(PG_SHAHEX + 8) 313 let gotn: i64 = pg_file_sha(PG_DEST_OK, got) 314 var t5: i64 = 0 315 if gotn == hooklen { var same2: i64 = 1; var i3: i64 = 0; while i3 < PG_SHAHEX { if got[i3] != dig_hook[i3] { same2 = 0; i3 = PG_SHAHEX } else { i3 = i3 + 1 } } t5 = same2 } 316 gv_puts(" resolved bytes=" as *u8); gv_num(gotn); gv_puts(" sha=" as *u8); gv_puts(got); gv_puts("\n" as *u8) 317 gv_check("resolved-file-re-hashes-to-the-requested-digest (the STATE is the oracle, not the message)" as *u8, t5 == 1, ctr) 318 319 // ---- T6 POSITIVE CONTROL: a legitimately pinned name RESOLVES --------------------------------- 320 // Without this every refusal below is satisfied by a resolver that refuses everything. 321 rc = pg_run(subject, "pin" as *u8, PG_NAME_REAL, dig_hook, PG_CONF, 4) 322 let pin_rc: i64 = rc 323 rc = pg_run(subject, "resolve" as *u8, PG_NAME_REAL, PG_DEST_GOOD, PG_CONF, 4) 324 var t6: i64 = 0 325 if pin_rc == 0 { if rc == 0 { if pg_exists(PG_DEST_GOOD) == 1 { t6 = 1 } } } 326 gv_puts(" pin rc=" as *u8); gv_num(pin_rc); gv_puts(" resolve-by-name rc=" as *u8); gv_num(rc); gv_puts("\n" as *u8) 327 gv_check("neg-control-POSITIVE: a legitimately pinned name RESOLVES (a resolver that refused everything would fail HERE)" as *u8, t6 == 1, ctr) 328 329 // ---- T7 THE TYPOSQUAT: a lookalike name cannot resolve ---------------------------------------- 330 // The attacker even gets their bytes INTO the store first -- the squat still cannot be reached, 331 // because resolution is exact-pin then digest, with no near-name search and no `latest`. 332 pg_run(subject, "cas_put" as *u8, PG_MOD_SQUAT, PG_CONF, 0 as *u8, 3) 333 rc = pg_run(subject, "resolve" as *u8, PG_NAME_SQUAT, PG_DEST_SQUAT, PG_CONF, 4) 334 let squat_rule: i64 = pg_saw_rule("name-not-pinned" as *u8) 335 let squat_file: i64 = pg_exists(PG_DEST_SQUAT) 336 gv_puts(" typosquat rc=" as *u8); gv_num(rc) 337 gv_puts(" named_rule=" as *u8); gv_num(squat_rule) 338 gv_puts(" wrote_a_file=" as *u8); gv_num(squat_file); gv_puts("\n" as *u8) 339 var t7: i64 = 0 340 if rc == PG_EXIT_NOTPINNED { if squat_rule == 1 { if squat_file == 0 { t7 = 1 } } } 341 gv_check("TYPOSQUAT-CANNOT-RESOLVE: a lookalike name refuses with rule=name-not-pinned, the named exit code, and writes NO file" as *u8, t7 == 1, ctr) 342 343 // ---- T8 A POISONED STORE serves a refusal, never different code ------------------------------- 344 // Overwrite the blob AT the legitimate digest with the typosquat's bytes. The path is a hint; the 345 // hash is the identity. Bite-proven in both directions: poisoned refuses, restored resolves. 346 let bp: *u8 = sys_mmap(PG_SCRATCH) 347 var bpo: i64 = pg_cat(bp, 0, PG_STORE) 348 bpo = pg_cat(bp, bpo, "/" as *u8) 349 bpo = pg_cat(bp, bpo, dig_hook) 350 let orig: *u8 = sys_read_file(bp, lp) 351 let orign: i64 = lp[0] 352 pg_write_str(bp, "ATTACKER BYTES SUBSTITUTED AT THE LEGITIMATE DIGEST PATH\n" as *u8) 353 rc = pg_run(subject, "cas_resolve" as *u8, dig_hook, PG_DEST_MISMATCH, PG_CONF, 4) 354 let mm_rule: i64 = pg_saw_rule("digest-mismatch" as *u8) 355 let mm_file: i64 = pg_exists(PG_DEST_MISMATCH) 356 var poisoned_refused: i64 = 0 357 if rc == PG_EXIT_MISMATCH { if mm_rule == 1 { if mm_file == 0 { poisoned_refused = 1 } } } 358 pg_write(bp, orig, orign) 359 sys_unlinkat(PG_DEST_MISMATCH) 360 let rc_restored: i64 = pg_run(subject, "cas_resolve" as *u8, dig_hook, PG_DEST_MISMATCH, PG_CONF, 4) 361 var restored_ok: i64 = 1 362 if rc_restored == 0 { restored_ok = 0 } 363 gv_puts(" poisoned rc=" as *u8); gv_num(rc) 364 gv_puts(" restored rc=" as *u8); gv_num(rc_restored); gv_puts("\n" as *u8) 365 gv_bite("neg-control-THE-PATH-IS-A-HINT-THE-HASH-IS-THE-IDENTITY: a substituted blob refuses digest-mismatch, the restored blob resolves" as *u8, poisoned_refused, restored_ok, ctr) 366 367 // ---- T9 an unparseable digest fails CLOSED ---------------------------------------------------- 368 let short_dig: *u8 = sys_mmap(PG_SHAHEX + 8) 369 var si: i64 = 0 370 while si < PG_SHAHEX - 1 { short_dig[si] = dig_hook[si]; si = si + 1 } 371 short_dig[PG_SHAHEX - 1] = 0 as u8 372 let rc_short: i64 = pg_run(subject, "cas_resolve" as *u8, short_dig, PG_DEST_SQUAT, PG_CONF, 4) 373 let r_short: i64 = pg_saw_rule("malformed-digest" as *u8) 374 let rc_nonhex: i64 = pg_run(subject, "cas_resolve" as *u8, "zzzz611184eed3163d3ff8b059045c777b2c3fad3a6db3b8b5a5128c45504ca9" as *u8, PG_DEST_SQUAT, PG_CONF, 4) 375 let r_nonhex: i64 = pg_saw_rule("malformed-digest" as *u8) 376 let rc_upper: i64 = pg_run(subject, "cas_resolve" as *u8, "2990611184EED3163D3FF8B059045C777B2C3FAD3A6DB3B8B5A5128C45504CA9" as *u8, PG_DEST_SQUAT, PG_CONF, 4) 377 let r_upper: i64 = pg_saw_rule("malformed-digest" as *u8) 378 var t9: i64 = 0 379 if rc_short == PG_EXIT_MALFORMED { if rc_nonhex == PG_EXIT_MALFORMED { if rc_upper == PG_EXIT_MALFORMED { 380 if r_short == 1 { if r_nonhex == 1 { if r_upper == 1 { if pg_exists(PG_DEST_SQUAT) == 0 { t9 = 1 } } } } } } } 381 gv_puts(" malformed rc short=" as *u8); gv_num(rc_short) 382 gv_puts(" nonhex=" as *u8); gv_num(rc_nonhex) 383 gv_puts(" uppercase=" as *u8); gv_num(rc_upper); gv_puts("\n" as *u8) 384 gv_check("UNPARSEABLE-INPUT-FAILS-CLOSED: short, non-hex and uppercase digests each refuse malformed-digest and write nothing" as *u8, t9 == 1, ctr) 385 386 // ---- T10 an absent digest refuses ------------------------------------------------------------- 387 let rc_abs: i64 = pg_run(subject, "cas_resolve" as *u8, "0000000000000000000000000000000000000000000000000000000000000000" as *u8, PG_DEST_SQUAT, PG_CONF, 4) 388 var t10: i64 = 0 389 if rc_abs == PG_EXIT_ABSENT { if pg_saw_rule("digest-absent" as *u8) == 1 { if pg_exists(PG_DEST_SQUAT) == 0 { t10 = 1 } } } 390 gv_check("absent-digest-refuses-rule=digest-absent-and-writes-nothing" as *u8, t10 == 1, ctr) 391 392 // ---- T11 a destination outside the allowlist refuses ------------------------------------------ 393 let rc_dest: i64 = pg_run(subject, "cas_resolve" as *u8, dig_hook, PG_DEST_OUTSIDE, PG_CONF, 4) 394 var t11: i64 = 0 395 if rc_dest == PG_EXIT_DEST { if pg_saw_rule("dest-refused" as *u8) == 1 { if pg_exists(PG_DEST_OUTSIDE) == 0 { t11 = 1 } } } 396 gv_check("dest-outside-the-safe-root-allowlist-refuses-rule=dest-refused-and-creates-no-file" as *u8, t11 == 1, ctr) 397 398 // ---- T12 an unrecognised verb refuses, it does not fall through ------------------------------- 399 let rc_verb: i64 = pg_run(subject, "cas_reslove" as *u8, dig_hook, PG_DEST_SQUAT, PG_CONF, 4) 400 let verb_rule: i64 = pg_saw_rule("unknown-verb" as *u8) 401 var t12: i64 = 0 402 if rc_verb == PG_EXIT_USAGE { if verb_rule == 1 { if pg_exists(PG_DEST_SQUAT) == 0 { t12 = 1 } } } 403 gv_puts(" unknown verb rc=" as *u8); gv_num(rc_verb); gv_puts(" named_rule=" as *u8); gv_num(verb_rule); gv_puts("\n" as *u8) 404 gv_check("UNKNOWN-VERB-FAILS-CLOSED naming rule=unknown-verb (a typo must not report as a CONFIGURATION fault -- the refusal names the conjunct that fired)" as *u8, t12 == 1, ctr) 405 406 // ---- T13 an absent conf key refuses, it does not default ------------------------------------- 407 let rc_conf: i64 = pg_run(subject, "cas_resolve" as *u8, dig_hook, PG_DEST_SQUAT, PG_CONF_BAD, 4) 408 var t13: i64 = 0 409 if rc_conf == PG_EXIT_CONF { if pg_saw_rule("conf-missing-key" as *u8) == 1 { if pg_exists(PG_DEST_SQUAT) == 0 { t13 = 1 } } } 410 gv_check("absent-conf-key-refuses-rule=conf-missing-key (config is data; a missing row never silently defaults)" as *u8, t13 == 1, ctr) 411 412 // ---- T14 pin conflict and pin ambiguity both refuse ------------------------------------------- 413 let squat_dig: *u8 = sys_mmap(PG_SHAHEX + 8) 414 pg_file_sha(PG_MOD_SQUAT, squat_dig) 415 let rc_conflict: i64 = pg_run(subject, "pin" as *u8, PG_NAME_REAL, squat_dig, PG_CONF, 4) 416 var t14: i64 = 0 417 if rc_conflict == PG_EXIT_PINCONFLICT { if pg_saw_rule("pin-conflict" as *u8) == 1 { t14 = 1 } } 418 gv_puts(" re-pin to a different digest rc=" as *u8); gv_num(rc_conflict); gv_puts("\n" as *u8) 419 gv_check("re-pinning-a-name-to-a-DIFFERENT-digest-refuses-rule=pin-conflict (a pin is not silently movable)" as *u8, t14 == 1, ctr) 420 421 // hand-append a duplicate row: a declaration file that permits two answers for one key is a coin 422 // flip wearing a schema, so the reader must REFUSE rather than pick first-match or last-match. 423 let dupfd: i64 = sys_openat_append(PG_LOCK, MODE_0644) 424 if dupfd >= 0 { 425 var duo: i64 = pg_cat(cbuf, 0, PG_NAME_REAL) 426 cbuf[duo] = PG_BAR as u8; duo = duo + 1 427 duo = pg_cat(cbuf, duo, squat_dig) 428 cbuf[duo] = PG_NL as u8; duo = duo + 1 429 sys_write(dupfd, cbuf, duo) 430 sys_close(dupfd) 431 } 432 sys_unlinkat(PG_DEST_GOOD) 433 let rc_ambig: i64 = pg_run(subject, "resolve" as *u8, PG_NAME_REAL, PG_DEST_GOOD, PG_CONF, 4) 434 var t15: i64 = 0 435 if rc_ambig == PG_EXIT_PINAMBIG { if pg_saw_rule("pin-ambiguous" as *u8) == 1 { if pg_exists(PG_DEST_GOOD) == 0 { t15 = 1 } } } 436 gv_puts(" duplicate pin row rc=" as *u8); gv_num(rc_ambig); gv_puts("\n" as *u8) 437 gv_check("DUPLICATE-PIN-ROW-REFUSES-rule=pin-ambiguous rather than choosing first-match or last-match" as *u8, t15 == 1, ctr) 438 439 // ---- T16 the transparency journal verifies, and a tampered row breaks it ---------------------- 440 let rc_log: i64 = pg_run(subject, "logverify" as *u8, PG_CONF, 0 as *u8, 0 as *u8, 2) 441 ob = pg_read_out(lp); onn = lp[0] 442 let entries: i64 = pg_num_after(ob, onn, "entries=" as *u8) 443 var t16: i64 = 0 444 if rc_log == 0 { if entries > 0 { if pg_find(ob, onn, "chain=OK" as *u8) >= 0 { t16 = 1 } } } 445 gv_puts(" journal entries=" as *u8); gv_num(entries); gv_puts(" logverify rc=" as *u8); gv_num(rc_log); gv_puts("\n" as *u8) 446 gv_check("journal-chain-verifies-with-the-entry-count-BOUND-IN-THE-CONDITION (a zero-row journal cannot pass)" as *u8, t16 == 1, ctr) 447 448 // tamper one byte inside row 0's digest field, then restore 449 let jb: *u8 = sys_read_file(PG_JRNL, lp) 450 let jn: i64 = lp[0] 451 var tampered_break: i64 = 0 452 var tampered_at: i64 = 0 - 1 453 var append_refused: i64 = 0 454 if (jb as i64) != 0 { if jn > 0 { 455 let copy: *u8 = sys_mmap(jn + 16) 456 var ci: i64 = 0 457 while ci < jn { copy[ci] = jb[ci]; ci = ci + 1 } 458 // second '|' of the first line starts the digest field 459 var bars: i64 = 0 460 var k: i64 = 0 461 var digoff: i64 = 0 - 1 462 while k < jn { 463 if (copy[k] as i64) == PG_BAR { bars = bars + 1; if bars == 2 { digoff = k + 1; k = jn } } 464 if (copy[k] as i64) == PG_NL { k = jn } 465 k = k + 1 466 } 467 if digoff > 0 { 468 let old: i64 = copy[digoff] as i64 469 var nw: i64 = PG_ASCII_0 470 if old == PG_ASCII_0 { nw = PG_ASCII_0 + 1 } 471 copy[digoff] = nw as u8 472 pg_write(PG_JRNL, copy, jn) 473 let rc_t: i64 = pg_run(subject, "logverify" as *u8, PG_CONF, 0 as *u8, 0 as *u8, 2) 474 ob = pg_read_out(lp); onn = lp[0] 475 tampered_at = pg_num_after(ob, onn, "chain=BREAK at=" as *u8) 476 if rc_t == PG_EXIT_CHAIN { if tampered_at == 0 { if pg_saw_rule("chain-break" as *u8) == 1 { tampered_break = 1 } } } 477 // and an append onto a broken chain must REFUSE rather than extend it 478 sys_unlinkat(PG_DEST_MISMATCH) 479 let rc_ap: i64 = pg_run(subject, "cas_resolve" as *u8, dig_hook, PG_DEST_MISMATCH, PG_CONF, 4) 480 if rc_ap == PG_EXIT_CHAIN { append_refused = 1 } 481 // restore the banked bytes 482 pg_write(PG_JRNL, jb, jn) 483 } 484 } } 485 let rc_log2: i64 = pg_run(subject, "logverify" as *u8, PG_CONF, 0 as *u8, 0 as *u8, 2) 486 var restored_log_bad: i64 = 1 487 if rc_log2 == 0 { restored_log_bad = 0 } 488 gv_puts(" tampered chain break_at=" as *u8); gv_num(tampered_at) 489 gv_puts(" restored logverify rc=" as *u8); gv_num(rc_log2); gv_puts("\n" as *u8) 490 gv_bite("neg-control-TAMPER-EVIDENCE: editing one byte of a past row breaks the chain AT THE NAMED INDEX, and the restored journal verifies" as *u8, tampered_break, restored_log_bad, ctr) 491 gv_check("an-append-onto-a-BROKEN-chain-is-REFUSED-not-extended (you cannot grow a tampered journal)" as *u8, append_refused == 1, ctr) 492 493 return gv_verdict("PKG-CAS" as *u8, ctr, "content-addressed resolution with no name lookup at the digest layer, an exact-pin lane whose unpinned names fail closed, and a hash-chained journal" as *u8) 494}