code wiki / _hdl_build / nx_route_ctl_gate.nx

nx_route_ctl_gate.nx source

↩ module page · 108 lines · 7853 B

1// nx_route_ctl_gate.nx -- proves the API-pure route-control primitives (md_route_valid + md_route_append) BEFORE 2// they can touch the live edge proxy table. NEVER-BRICK RELEVANT: a malformed proxy_routes.conf row is edge-wide, 3// so validation is fail-closed by construction and the upsert must preserve every UNRELATED row. Exercised only 4// against a /tmp fixture -- never the real proxy_routes.conf. license_tier: ORIGINAL 5import "nx_mgmt_data.nx" 6 7func rg_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 8func rg_wn(v: i64) -> i64 { let b: *u8 = sys_mmap(32); let o: i64 = md_catn(b, 0, v); b[o] = 10 as u8; sys_write(1, b, o + 1); return 0 } 9 10// assert got==want; returns 1 on pass, 0 on fail (main accumulates). 11func rg_eq(name: *u8, got: i64, want: i64) -> i64 { 12 if got == want { rg_w(" ok " as *u8); rg_w(name); rg_w("\n" as *u8); return 1 } 13 rg_w(" FAIL " as *u8); rg_w(name); rg_w(" got=" as *u8); rg_wn(got); return 0 14} 15 16func rg_seed(path: *u8, s: *u8) -> i64 { 17 var n: i64 = 0 18 while s[n] != (0 as u8) { n = n + 1 } 19 let fd: i64 = sys_openat_wr(path, 0x1a4) 20 if fd < 0 { return 0 } 21 sys_write(fd, s, n) 22 sys_close(fd) 23 return 1 24} 25func rg_countlines(buf: *u8, n: i64) -> i64 { 26 var c: i64 = 0 27 var i: i64 = 0 28 while i < n { if (buf[i] as i64) == 10 { c = c + 1 } i = i + 1 } 29 return c 30} 31func rg_has(path: *u8, needle: *u8) -> i64 { 32 let szp: *i64 = sys_mmap(16) as *i64 33 szp[0] = 0 34 let buf: *u8 = sys_read_file(path, szp) 35 if (buf as i64) == 0 { return 0 } 36 var nl: i64 = 0 37 while needle[nl] != (0 as u8) { nl = nl + 1 } 38 return md_pk_contains(buf, szp[0], needle, nl) 39} 40func rg_lines(path: *u8) -> i64 { 41 let szp: *i64 = sys_mmap(16) as *i64 42 szp[0] = 0 43 let buf: *u8 = sys_read_file(path, szp) 44 if (buf as i64) == 0 { return 0 } 45 return rg_countlines(buf, szp[0]) 46} 47 48func main(argc: i64, argv: *i64) -> i64 { 49 rg_w("== nx_route_ctl_gate ==\n" as *u8) 50 var pass: i64 = 0 51 var total: i64 = 0 52 53 // ---- md_route_valid: accept the good, reject every bad class (fail-closed) ---- 54 rg_w("- md_route_valid\n" as *u8) 55 total = total + 1; pass = pass + rg_eq("T1 accept andelinwest.com /site 18466 buffered" as *u8, md_route_valid("andelinwest.com" as *u8, "/site" as *u8, 18466, "buffered" as *u8), 1) 56 total = total + 1; pass = pass + rg_eq("T2 accept nested prefix + stream" as *u8, md_route_valid("nishifamily.com" as *u8, "/site/ed" as *u8, 18466, "stream" as *u8), 1) 57 total = total + 1; pass = pass + rg_eq("T3 accept gated mode" as *u8, md_route_valid("a.b.com" as *u8, "/x" as *u8, 8027, "gated" as *u8), 1) 58 total = total + 1; pass = pass + rg_eq("T4 reject host without a dot" as *u8, md_route_valid("localhost" as *u8, "/site" as *u8, 18466, "buffered" as *u8), 0) 59 total = total + 1; pass = pass + rg_eq("T5 reject uppercase host" as *u8, md_route_valid("Andelin.com" as *u8, "/site" as *u8, 18466, "buffered" as *u8), 0) 60 total = total + 1; pass = pass + rg_eq("T6 reject leading-dot host" as *u8, md_route_valid(".com" as *u8, "/site" as *u8, 18466, "buffered" as *u8), 0) 61 total = total + 1; pass = pass + rg_eq("T7 reject double-dot host" as *u8, md_route_valid("a..b.com" as *u8, "/site" as *u8, 18466, "buffered" as *u8), 0) 62 total = total + 1; pass = pass + rg_eq("T8 reject prefix missing leading slash" as *u8, md_route_valid("a.com" as *u8, "site" as *u8, 18466, "buffered" as *u8), 0) 63 total = total + 1; pass = pass + rg_eq("T9 reject prefix path-traversal .." as *u8, md_route_valid("a.com" as *u8, "/a/../b" as *u8, 18466, "buffered" as *u8), 0) 64 total = total + 1; pass = pass + rg_eq("T10 reject prefix double-slash" as *u8, md_route_valid("a.com" as *u8, "//x" as *u8, 18466, "buffered" as *u8), 0) 65 total = total + 1; pass = pass + rg_eq("T11 reject prefix with space (injection)" as *u8, md_route_valid("a.com" as *u8, "/a b" as *u8, 18466, "buffered" as *u8), 0) 66 total = total + 1; pass = pass + rg_eq("T12 reject privileged port <1024" as *u8, md_route_valid("a.com" as *u8, "/x" as *u8, 80, "buffered" as *u8), 0) 67 total = total + 1; pass = pass + rg_eq("T13 reject port >65535" as *u8, md_route_valid("a.com" as *u8, "/x" as *u8, 70000, "buffered" as *u8), 0) 68 total = total + 1; pass = pass + rg_eq("T14 reject unknown mode" as *u8, md_route_valid("a.com" as *u8, "/x" as *u8, 8443, "raw" as *u8), 0) 69 70 // ---- md_route_append: upsert preserves unrelated rows, replaces the match, appends the new ---- 71 rg_w("- md_route_append (fixture /tmp/nx_route_gate.conf)\n" as *u8) 72 let fx: *u8 = "/tmp/nx_route_gate.conf" as *u8 73 rg_seed(fx, "example.com / 9001 buffered\nother.com /api 9002 stream\n" as *u8) 74 75 // (a) append a genuinely new route 76 total = total + 1; pass = pass + rg_eq("T15 append returns ok" as *u8, md_route_append(fx, "andelinwest.com" as *u8, "/site" as *u8, 18466, "buffered" as *u8), 1) 77 total = total + 1; pass = pass + rg_eq("T16 new row present" as *u8, rg_has(fx, "andelinwest.com /site 18466 buffered" as *u8), 1) 78 total = total + 1; pass = pass + rg_eq("T17 unrelated row example preserved" as *u8, rg_has(fx, "example.com / 9001 buffered" as *u8), 1) 79 total = total + 1; pass = pass + rg_eq("T18 unrelated row other preserved" as *u8, rg_has(fx, "other.com /api 9002 stream" as *u8), 1) 80 total = total + 1; pass = pass + rg_eq("T19 line count == 3" as *u8, rg_lines(fx), 3) 81 82 // (b) UPSERT: same host+prefix -> replace in place (old value gone), count unchanged 83 total = total + 1; pass = pass + rg_eq("T20 upsert returns ok" as *u8, md_route_append(fx, "example.com" as *u8, "/" as *u8, 9999, "gated" as *u8), 1) 84 total = total + 1; pass = pass + rg_eq("T21 new value present" as *u8, rg_has(fx, "example.com / 9999 gated" as *u8), 1) 85 total = total + 1; pass = pass + rg_eq("T22 old value gone (no 9001)" as *u8, rg_has(fx, "9001" as *u8), 0) 86 total = total + 1; pass = pass + rg_eq("T23 sibling other.com still present" as *u8, rg_has(fx, "other.com /api 9002 stream" as *u8), 1) 87 total = total + 1; pass = pass + rg_eq("T24 line count still 3 (no dup)" as *u8, rg_lines(fx), 3) 88 89 // (c) NEVER-BRICK: an empty/unreadable table is REFUSED (a transient read-fail must never let one row 90 // replace the populated edge table -> /api self-lockout). 0-byte file via open(O_CREAT|O_TRUNC)+close; 91 // NOT rg_seed(path,"") -- an empty "" literal aliases the constant pool (documented nx_cc gotcha). 92 let fx2: *u8 = "/tmp/nx_route_gate2.conf" as *u8 93 let tfd: i64 = sys_openat_wr(fx2, 0x1a4) 94 sys_close(tfd) 95 total = total + 1; pass = pass + rg_eq("T25 empty table REFUSED (fail-closed)" as *u8, md_route_append(fx2, "fresh.com" as *u8, "/z" as *u8, 12000, "buffered" as *u8), 0) 96 total = total + 1; pass = pass + rg_eq("T26 empty file untouched (0 lines)" as *u8, rg_lines(fx2), 0) 97 total = total + 1; pass = pass + rg_eq("T27 absent file REFUSED" as *u8, md_route_append("/tmp/nx_route_gate_absent.conf" as *u8, "fresh.com" as *u8, "/z" as *u8, 12000, "buffered" as *u8), 0) 98 // (d) NEVER-BRICK: .prev banks the PRE-edit table (recovery = cp .prev back). After the T20 upsert 99 // (example.com / 9001->9999), .prev must hold the OLD 9001 row and NOT the new 9999 value. 100 total = total + 1; pass = pass + rg_eq("T28 .prev holds pre-edit row (9001)" as *u8, rg_has("/tmp/nx_route_gate.conf.prev" as *u8, "example.com / 9001 buffered" as *u8), 1) 101 total = total + 1; pass = pass + rg_eq("T29 .prev lacks the new value (9999)" as *u8, rg_has("/tmp/nx_route_gate.conf.prev" as *u8, "9999" as *u8), 0) 102 103 rg_w("== result " as *u8); rg_wn(pass) 104 rg_w(" total=" as *u8); rg_wn(total) 105 if pass == total { rg_w("ROUTE_CTL_GATE PASS\n" as *u8); return 0 } 106 rg_w("ROUTE_CTL_GATE FAIL\n" as *u8) 107 return 1 108}