code wiki / _hdl_build / nx_video_embed_export_gate.nx

nx_video_embed_export_gate.nx source

↩ module page · 148 lines · 7147 B

1import "nx_gate_base.nx" 2// nx_video_embed_export_gate.nx -- proves the shipped embed bundle (web_assets/nishi-video.js) is 3// INTERNALLY EXPORT-CONSISTENT: every wasm export the JS component calls (NX.<name>) is REALLY exported 4// by the wasm carried in the SAME bundle. It decodes the embedded base64 core, parses its WASM export 5// section (section id 7), and checks every NX.<ident> the component references against the export set. 6// Catches the #1 silent browser break the marker-check + byte-identical gate CANNOT: a typo'd / renamed / 7// missing export ("NX.vc_x is not a function") that serves 200, passes every funcheck, then throws at 8// runtime in every browser. This is the "serves != works" honesty rung for the native <nishi-video> embed. 9// expect_exit: 0 license_tier: ORIGINAL 10import "nx_syscalls.nx" 11import "nx_base64.nx" 12 13func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 14" as *u8); return ok } 15func gn(v: i64) -> i64 { 16 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} 17 let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} 18 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 19func wr_bytes(p: *u8, off: i64, n: i64) -> i64 { sys_write(1, ((p as i64)+off) as *u8, n); return 0 } 20 21// find NUL-terminated needle in hay[0..n); returns offset AFTER the needle, or -1 22func find_after(hay: *u8, n: i64, needle: *u8) -> i64 { 23 var nl: i64=0; while needle[nl]!=(0 as u8){nl=nl+1} 24 var i: i64=0 25 while i+nl<=n { var j: i64=0; var ok: i64=1; while j<nl { if hay[i+j]!=needle[j]{ok=0;j=nl} else {j=j+1} } if ok==1{return i+nl} i=i+1 } 26 return 0 - 1 } 27 28func isident(c: i64) -> i64 { 29 if c>=48 { if c<=57 { return 1 } } 30 if c>=65 { if c<=90 { return 1 } } 31 if c>=97 { if c<=122 { return 1 } } 32 if c==95 { return 1 } 33 return 0 } 34 35func buf_eq(a: *u8, ao: i64, al: i64, b: *u8, bo: i64, bl: i64) -> i64 { 36 if al != bl { return 0 } 37 var i: i64 = 0 38 while i < al { if (a[ao+i] as i64) != (b[bo+i] as i64) { return 0 } i = i + 1 } 39 return 1 } 40 41// LEB128 unsigned decode from buf at cur[0]; advances cur[0] past the value (no <</| in nx -> use *128) 42func uleb(buf: *u8, cur: *i64) -> i64 { 43 var result: i64 = 0 44 var powv: i64 = 1 45 var go: i64 = 1 46 while go==1 { 47 let bb: i64 = buf[cur[0]] as i64 48 cur[0] = cur[0] + 1 49 result = result + ((bb & 0x7f) * powv) 50 if (bb & 0x80) == 0 { go = 0 } else { powv = powv * 128 } 51 } 52 return result } 53 54func main() -> i64 { 55 gw("=== nx_video_embed_export_gate: shipped bundle JS<->wasm export consistency ===\n" as *u8) 56 let jbox: *i64 = sys_mmap(16) as *i64; jbox[0]=0 57 let js: *u8 = sys_read_file("web_assets/nishi-video.js" as *u8, jbox) 58 if (js as i64)==0 { gw("no nishi-video.js (run nx_video_embed_bundle)\n" as *u8); return 1 } 59 let jn: i64 = jbox[0] 60 // decode the embedded wasm core from the SAME bundle we ship 61 let start: i64 = find_after(js, jn, "WASM_B64=\"" as *u8) 62 if start < 0 { gw("no WASM_B64 in bundle -> RED\n" as *u8); return 1 } 63 var end: i64 = start 64 var goe: i64 = 1 65 while goe==1 { if end<jn { if (js[end] as i64)==34 { goe=0 } else { end=end+1 } } else { goe=0 } } 66 let blen: i64 = end - start 67 let wasm: *u8 = sys_mmap(blen + 64) 68 let wlen: i64 = b64_decode(((js as i64)+start) as *u8, blen, wasm) 69 gw(" embedded wasm decoded = " as *u8); gn(wlen); gw("B\n" as *u8) 70 // wasm magic: 00 61 73 6d ("\0asm") 71 if (wasm[0] as i64)!=0 { gw("bad wasm magic -> RED\n" as *u8); return 1 } 72 if (wasm[1] as i64)!=97 { gw("bad wasm magic -> RED\n" as *u8); return 1 } 73 if (wasm[2] as i64)!=115 { gw("bad wasm magic -> RED\n" as *u8); return 1 } 74 if (wasm[3] as i64)!=109 { gw("bad wasm magic -> RED\n" as *u8); return 1 } 75 // walk sections to the export section (id 7) 76 let cbox: *i64 = sys_mmap(16) as *i64 77 var cur: i64 = 8 78 var exp_start: i64 = 0 - 1 79 var gs: i64 = 1 80 while gs==1 { 81 if cur < wlen { 82 let sid: i64 = wasm[cur] as i64 83 cur = cur + 1 84 cbox[0] = cur 85 let ssize: i64 = uleb(wasm, cbox) 86 cur = cbox[0] 87 if sid == 7 { exp_start = cur } 88 cur = cur + ssize 89 } else { gs = 0 } 90 } 91 if exp_start < 0 { gw("no export section -> RED\n" as *u8); return 1 } 92 // parse export names: count, then count x [name_len][name][kind:1][index] 93 cbox[0] = exp_start 94 let ecount: i64 = uleb(wasm, cbox) 95 let eoff: *i64 = sys_mmap((ecount + 8) * 8) as *i64 96 let elen: *i64 = sys_mmap((ecount + 8) * 8) as *i64 97 var e: i64 = 0 98 while e < ecount { 99 let nlen: i64 = uleb(wasm, cbox) 100 eoff[e] = cbox[0] 101 elen[e] = nlen 102 cbox[0] = cbox[0] + nlen 103 cbox[0] = cbox[0] + 1 // export kind byte 104 uleb(wasm, cbox) // export index (advance past, unused) 105 e = e + 1 106 } 107 gw(" wasm exports = " as *u8); gn(ecount); gw("\n" as *u8) 108 // scan the bundle for NX.<ident> (base64 blob has no '.', so scanning the whole file is safe) 109 let soff: *i64 = sys_mmap(256 * 8) as *i64 110 let slen: *i64 = sys_mmap(256 * 8) as *i64 111 var scount: i64 = 0 112 var green: i64 = 1 113 var i: i64 = 0 114 while i + 3 <= jn { 115 var k: i64 = i + 1 116 var matched: i64 = 0 117 if (js[i] as i64)==78 { if (js[i+1] as i64)==88 { if (js[i+2] as i64)==46 { matched=1 } } } 118 if matched==1 { 119 k = i + 3 120 var g: i64 = 1 121 while g==1 { if k<jn { if isident(js[k] as i64)==1 { k=k+1 } else { g=0 } } else { g=0 } } 122 let idoff: i64 = i + 3 123 let idlen: i64 = k - idoff 124 if idlen > 0 { 125 var seen: i64 = 0 126 var s: i64 = 0 127 while s < scount { if buf_eq(js, soff[s], slen[s], js, idoff, idlen)==1 { seen=1; s=scount } else { s=s+1 } } 128 if seen==0 { 129 if scount < 256 { soff[scount]=idoff; slen[scount]=idlen; scount=scount+1 } 130 var found: i64 = 0 131 var m: i64 = 0 132 while m < ecount { if buf_eq(js, idoff, idlen, wasm, eoff[m], elen[m])==1 { found=1; m=ecount } else { m=m+1 } } 133 gw(" NX." as *u8); wr_bytes(js, idoff, idlen) 134 if found==1 { gw(" -> exported OK\n" as *u8) } else { gw(" -> !!! NOT EXPORTED (browser will throw) !!!\n" as *u8); green=0 } 135 } 136 } 137 } 138 i = k 139 } 140 gw(" distinct NX.<export> calls checked = " as *u8); gn(scount); gw("\n" as *u8) 141 if scount==0 { gw("EMBED-EXPORT-GATE verdict=RED -- no NX.<export> calls found (scan/regression bug)\n" as *u8); return 1 } 142 if green==1 { 143 gw("EMBED-EXPORT-GATE verdict=GREEN -- every export the shipped component calls is really exported by the bundled wasm\n" as *u8) 144 return 0 145 } 146 gw("EMBED-EXPORT-GATE verdict=RED -- a called export is MISSING from the wasm (native embed would throw)\n" as *u8) 147 return 1 148}