nx_grab_ext_emit_gate.nx source
↩ module page · 266 lines · 13607 B
1// nx_grab_ext_emit_gate.nx -- REFEREE for nx_grab_ext_emit (/compare/mediaingest R10 ge_emit).
2//
3// IN-PROCESS AND NETWORK-FREE. It imports the emitter and calls its pure functions against a PLANTED
4// conf image held in memory, so nothing is written to disk, no browser is involved, and the production
5// knowledge/grab_ext.conf is never read or touched by this gate.
6//
7// THE LOAD-BEARING TOOTH IS THE MV2/MV3 STRUCTURAL SPLIT. The whole reason both twins are emitted from
8// one conf is that they must differ in STRUCTURE and never in POLICY:
9// MV2: background.scripts, browser_action, and <all_urls> INSIDE permissions
10// MV3: background.service_worker, action, and <all_urls> moved OUT to host_permissions
11// A wrong split is the defect that ships a Chrome extension the store rejects, or a Firefox one whose
12// host access silently does nothing. Testing that both manifests merely "contain the name" would pass
13// on a broken split, so the teeth below check the permissions array CONTENTS on each side, in both
14// directions -- present where it belongs and ABSENT where it does not.
15// license_tier: ORIGINAL
16import "nx_syscalls.nx"
17import "nx_grab_ext_emit.nx"
18import "nx_gate_verdict.nx"
19
20const T_CAP: i64 = 262144
21const T_SMALL: i64 = 4096
22
23// A planted conf: two comment lines (one of which contains a pipe, to prove comments are skipped and not
24// parsed as rows), one repeated key with three values, and every key the emitter needs.
25const T_CONF: *u8 = "# comment|not_a_row\nname|Test Grab\nversion|9.9\ndescription|planted\ngecko_id|t@nishi.local\nedge|https://edge.invalid\ncapture_path|/api/vault/capture\nmagnet_path|/t/m\nvideo_path|/t/v\nsession_header|X-Nishi-Session\nsession_key|nsess\nmedia_ext|m3u8\nmedia_ext|mp4\nmedia_ext|webm\nperm|webRequest\nperm|storage\nhost_perm|<all_urls>\n# trailing comment\n"
26
27// The same conf with the edge downgraded -- the neg-control for the https refusal.
28const T_CONF_HTTP: *u8 = "name|X\nversion|1\ndescription|d\ngecko_id|g\nedge|http://edge.invalid\ncapture_path|/c\nsession_header|H\nsession_key|k\nmedia_ext|mp4\nperm|storage\nhost_perm|<all_urls>\n"
29
30// A conf with a host_perm and ZERO perm rows. Two consecutive loops feed the ONE MV2 permissions array,
31// so an emitter that keys its separator off the loop index rather than a wrote-counter emits a LEADING
32// comma here -- invalid JSON that a browser rejects at load with no hint which row caused it. This
33// fixture exists to make that failure reachable; without it the bug is invisible on any normal conf.
34const T_CONF_NOPERM: *u8 = "name|Y\nversion|1\ndescription|d\ngecko_id|g\nedge|https://e.invalid\ncapture_path|/c\nsession_header|H\nsession_key|k\nmedia_ext|mp4\nhost_perm|<all_urls>\n"
35
36func t_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
37
38func t_find(buf: *u8, n: i64, lit: *u8, from: i64) -> i64 {
39 let ll: i64 = t_slen(lit)
40 if ll <= 0 { return 0 - 1 }
41 var i: i64 = from
42 while i + ll <= n {
43 var same: i64 = 1
44 var j: i64 = 0
45 while j < ll { if buf[i + j] != lit[j] { same = 0 } j = j + 1 }
46 if same == 1 { return i }
47 i = i + 1
48 }
49 return 0 - 1
50}
51
52func t_has(buf: *u8, n: i64, lit: *u8) -> i64 { if t_find(buf, n, lit, 0) >= 0 { return 1 } return 0 }
53
54// Extent of the JSON array that follows `key` -- from the '[' after the key to its matching ']'.
55// Returns the length and sets off_out[0]; -1 when the key or a closing bracket is absent.
56func t_array_of(buf: *u8, n: i64, key: *u8, off_out: *i64) -> i64 {
57 off_out[0] = 0 - 1
58 let k: i64 = t_find(buf, n, key, 0)
59 if k < 0 { return 0 - 1 }
60 // Separate cursor and exit FLAG: writing a sentinel into the cursor to break the loop destroys the
61 // very position the caller needs, and the estate measured that defect four times in one day.
62 var i: i64 = k
63 var open: i64 = 0 - 1
64 var scanning: i64 = 1
65 while scanning == 1 {
66 if i >= n { scanning = 0 }
67 else {
68 if buf[i] == (91 as u8) { open = i; scanning = 0 } else { i = i + 1 }
69 }
70 }
71 if open < 0 { return 0 - 1 }
72 var e: i64 = open
73 var close: i64 = 0 - 1
74 scanning = 1
75 while scanning == 1 {
76 if e >= n { scanning = 0 }
77 else {
78 if buf[e] == (93 as u8) { close = e; scanning = 0 } else { e = e + 1 }
79 }
80 }
81 if close < 0 { return 0 - 1 }
82 off_out[0] = open
83 return close - open + 1
84}
85
86func main() -> i64 {
87 gv_head("=== nx_grab_ext_emit_gate -- the extension is EMITTED FROM DATA, both twins (mediaingest R10) ===" as *u8)
88 let c: *i64 = gv_ctr()
89 let cn: i64 = t_slen(T_CONF)
90 let off: *i64 = sys_mmap(16) as *i64
91
92 // ---- ANTI-VACUITY FIRST: the planted conf must actually carry what every tooth below inspects,
93 // so a fixture that lost its rows cannot make this gate pass by examining nothing.
94 var fx: i64 = 0
95 if gx_count(T_CONF, cn, "media_ext" as *u8) == 3 {
96 if gx_count(T_CONF, cn, "perm" as *u8) == 2 {
97 if gx_count(T_CONF, cn, "host_perm" as *u8) == 1 { fx = 1 }
98 }
99 }
100 gv_check("fixture-reached-the-condition-conf-carries-3-media-2-perm-1-hostperm" as *u8, fx, c)
101
102 // ---- conf reader ----
103 let vl: i64 = gx_nth(T_CONF, cn, "version" as *u8, 0, off)
104 var t: i64 = 0
105 if vl == 3 { if T_CONF[off[0]] == (57 as u8) { t = 1 } }
106 gv_check("conf-reads-a-single-value" as *u8, t, c)
107
108 // A comment line containing a pipe must NOT be parsed as a row. If it were, this key would resolve.
109 t = 0
110 if gx_nth(T_CONF, cn, "# comment" as *u8, 0, off) < 0 { t = 1 }
111 gv_check("neg-control-a-commented-line-with-a-pipe-is-not-a-row" as *u8, t, c)
112
113 t = 0
114 if gx_nth(T_CONF, cn, "no_such_key" as *u8, 0, off) < 0 { t = 1 }
115 gv_check("neg-control-absent-key-returns-minus-one" as *u8, t, c)
116
117 // repeated key, read by index
118 let m0: i64 = gx_nth(T_CONF, cn, "media_ext" as *u8, 0, off)
119 var o0: i64 = off[0]
120 let m2: i64 = gx_nth(T_CONF, cn, "media_ext" as *u8, 2, off)
121 var o2: i64 = off[0]
122 t = 0
123 if m0 == 4 { if m2 == 4 { if T_CONF[o0] == (109 as u8) { if T_CONF[o2] == (119 as u8) { t = 1 } } } }
124 gv_check("repeated-key-is-read-by-index-first-and-third-differ" as *u8, t, c)
125
126 // ---- the media alternation: ONE list, and it is what the emitted regex is built from ----
127 let alt: *u8 = sys_mmap(T_SMALL)
128 let al: i64 = gx_media_alt(T_CONF, cn, alt, T_SMALL)
129 t = 0
130 if al > 0 { if t_has(alt, al, "m3u8|mp4|webm" as *u8) == 1 { t = 1 } }
131 gv_check("media-alternation-joins-every-conf-row-in-order" as *u8, t, c)
132
133 // ---- MV2 ----
134 let mv2: *u8 = sys_mmap(T_CAP)
135 let n2: i64 = gx_emit_mv2(T_CONF, cn, mv2, T_CAP)
136 t = 0
137 if n2 > 0 { t = 1 }
138 gv_check("mv2-manifest-emits" as *u8, t, c)
139
140 t = 0
141 if t_has(mv2, n2, "\"manifest_version\": 2" as *u8) == 1 { t = 1 }
142 gv_check("mv2-declares-manifest-version-2" as *u8, t, c)
143 t = 0
144 if t_has(mv2, n2, "\"background\": { \"scripts\"" as *u8) == 1 { t = 1 }
145 gv_check("mv2-uses-background-scripts" as *u8, t, c)
146 t = 0
147 if t_has(mv2, n2, "\"browser_action\"" as *u8) == 1 { t = 1 }
148 gv_check("mv2-uses-browser-action" as *u8, t, c)
149 t = 0
150 if t_has(mv2, n2, "\"service_worker\"" as *u8) == 0 { t = 1 }
151 gv_check("neg-control-mv2-does-NOT-use-a-service-worker" as *u8, t, c)
152 t = 0
153 if t_has(mv2, n2, "\"host_permissions\"" as *u8) == 0 { t = 1 }
154 gv_check("neg-control-mv2-has-NO-host-permissions-key" as *u8, t, c)
155
156 // THE SPLIT, MV2 SIDE: <all_urls> must be INSIDE the permissions array.
157 let pl2: i64 = t_array_of(mv2, n2, "\"permissions\"" as *u8, off)
158 t = 0
159 if pl2 > 0 { if t_has(((mv2 as i64) + off[0]) as *u8, pl2, "<all_urls>" as *u8) == 1 { t = 1 } }
160 gv_check("mv2-permissions-array-CARRIES-all-urls" as *u8, t, c)
161
162 // ---- MV3 ----
163 let mv3: *u8 = sys_mmap(T_CAP)
164 let n3: i64 = gx_emit_mv3(T_CONF, cn, mv3, T_CAP)
165 t = 0
166 if n3 > 0 { t = 1 }
167 gv_check("mv3-manifest-emits" as *u8, t, c)
168 t = 0
169 if t_has(mv3, n3, "\"manifest_version\": 3" as *u8) == 1 { t = 1 }
170 gv_check("mv3-declares-manifest-version-3" as *u8, t, c)
171 t = 0
172 if t_has(mv3, n3, "\"service_worker\": \"background.js\"" as *u8) == 1 { t = 1 }
173 gv_check("mv3-uses-a-background-service-worker" as *u8, t, c)
174 t = 0
175 if t_has(mv3, n3, "\"host_permissions\"" as *u8) == 1 { t = 1 }
176 gv_check("mv3-declares-host-permissions" as *u8, t, c)
177 t = 0
178 if t_has(mv3, n3, "\"browser_action\"" as *u8) == 0 { t = 1 }
179 gv_check("neg-control-mv3-does-NOT-use-browser-action" as *u8, t, c)
180
181 // THE SPLIT, MV3 SIDE -- the tooth that a broken emitter fails. <all_urls> must have MOVED OUT of
182 // permissions and into host_permissions. Checking only that host_permissions exists would pass on an
183 // emitter that wrote <all_urls> into BOTH, which is exactly the MV3 packaging error.
184 let pl3: i64 = t_array_of(mv3, n3, "\"permissions\"" as *u8, off)
185 t = 0
186 if pl3 > 0 { if t_has(((mv3 as i64) + off[0]) as *u8, pl3, "<all_urls>" as *u8) == 0 { t = 1 } }
187 gv_check("mv3-permissions-array-does-NOT-carry-all-urls-it-moved-to-host-permissions" as *u8, t, c)
188
189 let hl3: i64 = t_array_of(mv3, n3, "\"host_permissions\"" as *u8, off)
190 t = 0
191 if hl3 > 0 { if t_has(((mv3 as i64) + off[0]) as *u8, hl3, "<all_urls>" as *u8) == 1 { t = 1 } }
192 gv_check("mv3-host-permissions-array-CARRIES-all-urls" as *u8, t, c)
193
194 // ---- the empty-perm array: no leading comma on either twin ----
195 let en0: i64 = t_slen(T_CONF_NOPERM)
196 let mv2b: *u8 = sys_mmap(T_CAP)
197 let n2b: i64 = gx_emit_mv2(T_CONF_NOPERM, en0, mv2b, T_CAP)
198 t = 0
199 if n2b > 0 {
200 if t_has(mv2b, n2b, "[, " as *u8) == 0 { if t_has(mv2b, n2b, "<all_urls>" as *u8) == 1 { t = 1 } }
201 }
202 gv_check("neg-control-mv2-with-zero-perm-rows-emits-no-leading-comma-and-still-carries-the-host-perm" as *u8, t, c)
203 let mv3b: *u8 = sys_mmap(T_CAP)
204 let n3b: i64 = gx_emit_mv3(T_CONF_NOPERM, en0, mv3b, T_CAP)
205 t = 0
206 if n3b > 0 { if t_has(mv3b, n3b, "[, " as *u8) == 0 { t = 1 } }
207 gv_check("neg-control-mv3-with-zero-perm-rows-emits-no-leading-comma" as *u8, t, c)
208
209 // ---- POLICY IS IDENTICAL ACROSS TWINS even though STRUCTURE differs ----
210 t = 0
211 if t_has(mv2, n2, "\"name\": \"Test Grab\"" as *u8) == 1 {
212 if t_has(mv3, n3, "\"name\": \"Test Grab\"" as *u8) == 1 { t = 1 }
213 }
214 gv_check("both-twins-carry-the-same-name-from-the-one-conf" as *u8, t, c)
215 t = 0
216 if t_has(mv2, n2, "\"version\": \"9.9\"" as *u8) == 1 {
217 if t_has(mv3, n3, "\"version\": \"9.9\"" as *u8) == 1 { t = 1 }
218 }
219 gv_check("both-twins-carry-the-same-version-from-the-one-conf" as *u8, t, c)
220
221 // ---- the emitted scripts ----
222 let bg: *u8 = sys_mmap(T_CAP)
223 let nb: i64 = gx_emit_bg(T_CONF, cn, bg, T_CAP)
224 t = 0
225 if nb > 0 { if t_has(bg, nb, "m3u8|mp4|webm" as *u8) == 1 { t = 1 } }
226 gv_check("background-regex-is-built-from-the-conf-media-rows" as *u8, t, c)
227 t = 0
228 if t_has(bg, nb, "https://edge.invalid" as *u8) == 1 { t = 1 }
229 gv_check("background-carries-the-conf-edge-host" as *u8, t, c)
230 t = 0
231 if t_has(bg, nb, "/api/vault/capture" as *u8) == 1 { t = 1 }
232 gv_check("background-posts-to-the-capture-chokepoint-from-conf" as *u8, t, c)
233 // R11: the whole point is that no native companion exists. If a future edit reaches for native
234 // messaging, this tooth fails and the reviewer is told which promise was broken.
235 t = 0
236 if t_has(bg, nb, "connectNative" as *u8) == 0 {
237 if t_has(bg, nb, "nativeMessaging" as *u8) == 0 { t = 1 }
238 }
239 gv_check("R11-neg-control-emitted-background-uses-NO-native-messaging-host" as *u8, t, c)
240 // nx_cc forbids '!' inside a string literal, so the emitted JS must never contain one. A future edit
241 // that reaches for it would fail to COMPILE, but this tooth says so in words rather than as a lexer error.
242 t = 0
243 if t_has(bg, nb, "===" as *u8) == 1 { t = 1 }
244 gv_check("emitted-js-uses-explicit-equality-the-lexer-forbids-bang-in-a-literal" as *u8, t, c)
245
246 let ct: *u8 = sys_mmap(T_CAP)
247 let nc: i64 = gx_emit_content(T_CONF, cn, ct, T_CAP)
248 t = 0
249 if nc > 0 { if t_has(ct, nc, "location.origin === EDGE" as *u8) == 1 { t = 1 } }
250 gv_check("content-script-harvests-the-session-ONLY-on-the-estate-origin" as *u8, t, c)
251
252 // ---- the https refusal, tested as a PREDICATE (the emitter's own guard calls sys_exit, which a
253 // gate cannot survive in-process; gx_starts is the decision that guard branches on) ----
254 let en: i64 = t_slen(T_CONF_HTTP)
255 let el: i64 = gx_nth(T_CONF_HTTP, en, "edge" as *u8, 0, off)
256 t = 0
257 if el > 0 { if gx_starts(T_CONF_HTTP, off[0], el, "https://" as *u8) == 0 { t = 1 } }
258 gv_check("neg-control-an-http-edge-fails-the-https-predicate-the-emitter-refuses-on" as *u8, t, c)
259 let el2: i64 = gx_nth(T_CONF, cn, "edge" as *u8, 0, off)
260 t = 0
261 if el2 > 0 { if gx_starts(T_CONF, off[0], el2, "https://" as *u8) == 1 { t = 1 } }
262 gv_check("an-https-edge-passes-the-same-predicate" as *u8, t, c)
263
264 return gv_verdict("nx_grab_ext_emit_gate" as *u8, c,
265 "pure emitters against a planted in-memory conf -- nothing is written to disk, the production knowledge/grab_ext.conf is never read, and no browser is involved; a green here says the two manifests are structurally correct for their engines and that both twins carry one policy, and says NOTHING about whether either extension loads in a real browser, which is a separate declared step" as *u8)
266}