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}