code wiki / _hdl_build / nx_page_verify.nx

nx_page_verify.nx source

↩ module page · 446 lines · 21766 B

1// nx_page_verify.nx -- BROWSER-GRADE LIVE PAGE VERIFIER (the "my verify was shit" cure, operator 2026-07-16). 2// Status-200-on-the-page is NOT verification: a page can 200 while every image on it is broken (the /render3d 3// incident: the edge 301-strips trailing slashes, so RELATIVE img srcs resolve to the site root -> 404/fallback 4// -> broken images that a direct asset fetch never catches). This organ verifies a page THE WAY A BROWSER 5// EXPERIENCES IT: 6// 1. fetch the page (sovereign TLS, redirects followed) -- must be 200 7// 2. extract every asset ref (img/script src=, link href=) 8// 3. LAW CHECK: a RELATIVE asset ref = RED by construction on our slash-stripping edge (use absolute /path) 9// 4. fetch every same-origin asset -- 200 required 10// 5. DEEP-DECODE: PNG assets run through the full sovereign decoder (nx_png_decode: signature, IHDR, 11// zlib inflate, unfilter incl Paeth) -- "200 + magic bytes" is not proof; DECODED WxH is 12// 6. an asset that answers with HTML (the 404-fallback class) = RED even though its status is 200 13// Composes nx_https_fetch_follow (the games_url_check fetch spine) + nx_png_decode. Exit = broken count. 14// usage: nx_page_verify <https-url> [connect-host:port] (run from repo root: needs data/mozilla_certdata.txt) 15// [connect-host:port] = OPTIONAL connect-override (curl --connect-to): page + every asset fetch opens 16// TCP+TLS to THIS endpoint while SNI/Host/cert-name stay the URL host -- use 127.0.0.1:8443 to verify 17// our OWN vhosts deterministically from the sovereign edge (kills the DSM-nginx :443 coin-flip false-RED). 18// license_tier: ORIGINAL 19import "nx_syscalls.nx" 20import "nx_x509_trust_store.nx" 21import "nx_trust_store_load_from_certdata.nx" 22import "nx_https_url_connect.nx" 23import "nx_https_fetch_follow.nx" 24import "nx_png_decoder.nx" 25 26const PV_CAP: i64 = 8388608 // the EYE must out-read the pages it audits (the 2MiB-truncation lesson) 27const PV_MAXREF: i64 = 64 28// hoisted to satisfy the magic-number ratchet (rule 11 / LAW L001+L006, enforced at the 29// build gate by nx_magicratchet -- it REFUSED the seq911 build until the count went down). 30// Named for what they bound, not for their value, so a future reader can judge the size. 31const PV_CERTDATA_CAP: i64 = 4194304 // trust-store parse arena for mozilla_certdata.txt 32const PV_URL_CAP: i64 = 4096 // one absolute URL (origin, and each resolved ref) 33const PV_REF_MAX: i64 = 2048 // longest relative ref we will resolve 34const PV_FULLURL_CAP: i64 = 8192 // origin + ref joined 35 36func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 37func pn(v: i64) -> i64 { let t: *u8=sys_mmap(24); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} 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} let b: *u8=sys_mmap(24); var j: i64=0; while j<k{b[j]=t[k-1-j];j=j+1} sys_write(1,b,k); return 0 } 38func pw(s: *u8, n: i64) -> i64 { sys_write(1, s, n); return 0 } 39 40func sw_starts(b: *u8, off: i64, lit: *u8) -> i64 { 41 var i: i64 = 0 42 while lit[i] != (0 as u8) { if b[off+i] != lit[i] { return 0 } i = i + 1 } 43 return 1 44} 45 46// origin = "https://host" prefix of url (through the 3rd slash, exclusive) 47func pv_origin(url: *u8, out: *u8) -> i64 { 48 var i: i64 = 0 49 var slashes: i64 = 0 50 while url[i] != (0 as u8) { 51 if url[i] == (47 as u8) { 52 slashes = slashes + 1 53 if slashes == 3 { out[i] = 0 as u8; return i } 54 } 55 out[i] = url[i] 56 i = i + 1 57 } 58 out[i] = 0 as u8 59 return i 60} 61 62// check one asset URL: fetch + validate. kind: 1=img-ish (png expected ok), 0=other. returns 0 ok / 1 broken 63func pv_check_asset(store: *TrustStore, aurl: *u8, body: *u8) -> i64 { 64 let st: *i64 = sys_mmap(8) as *i64 65 let n: i64 = nx_https_fetch_follow(aurl, store, body, PV_CAP, 6, st) 66 hw(" asset " as *u8); hw(aurl) 67 hw(" -> status=" as *u8); pn(st[0]); hw(" bytes=" as *u8); pn(n) 68 if st[0] != 200 { hw(" BROKEN (non-200)\n" as *u8); return 1 } 69 if n <= 0 { hw(" BROKEN (empty body)\n" as *u8); return 1 } 70 // PNG? deep-decode (signature + IHDR + inflate + unfilter) -- the real proof 71 if n > 8 { 72 if body[0] == (137 as u8) { 73 if body[1] == (80 as u8) { 74 let res: *NxPngResult = nx_png_decode(body, n) 75 if (res as i64) == 0 { hw(" BROKEN (png decode null)\n" as *u8); return 1 } 76 if res.error_code != 0 { hw(" BROKEN (png decode err=" as *u8); pn(res.error_code); hw(")\n" as *u8); return 1 } 77 let hdr: *NxPngHeader = res.header 78 if hdr.width <= 0 { hw(" BROKEN (png w=0)\n" as *u8); return 1 } 79 hw(" PNG DECODE-OK " as *u8); pn(hdr.width); hw("x" as *u8); pn(hdr.height); hw("\n" as *u8) 80 return 0 81 } 82 } 83 // an "image" that answers HTML = the 404-fallback class (status was 200!) 84 if body[0] == (60 as u8) { // '<' 85 var isp: i64 = 0 86 if sw_starts(body, 0, "<!doctype" as *u8) == 1 { isp = 1 } 87 if sw_starts(body, 0, "<!DOCTYPE" as *u8) == 1 { isp = 1 } 88 if sw_starts(body, 0, "<html" as *u8) == 1 { isp = 1 } 89 if isp == 1 { 90 // only broken if the URL says it should be an image 91 var j: i64 = 0 92 var dot: i64 = 0 - 1 93 while aurl[j] != (0 as u8) { if aurl[j] == (46 as u8) { dot = j } j = j + 1 } 94 if dot >= 0 { 95 if sw_starts(aurl, dot, ".png" as *u8) == 1 { hw(" BROKEN (.png answers HTML -- 404-fallback class)\n" as *u8); return 1 } 96 if sw_starts(aurl, dot, ".jpg" as *u8) == 1 { hw(" BROKEN (.jpg answers HTML)\n" as *u8); return 1 } 97 if sw_starts(aurl, dot, ".css" as *u8) == 1 { hw(" BROKEN (.css answers HTML)\n" as *u8); return 1 } 98 if sw_starts(aurl, dot, ".js" as *u8) == 1 { hw(" BROKEN (.js answers HTML)\n" as *u8); return 1 } 99 } 100 } 101 } 102 } 103 hw(" OK\n" as *u8) 104 return 0 105} 106 107// a11y-lite: integer substring presence + count over the page (axe-core / Lighthouse-class checks, NO float) 108func pv_has(b: *u8, n: i64, lit: *u8) -> i64 { 109 var i: i64 = 0 110 while i < n { if sw_starts(b, i, lit) == 1 { return 1 } i = i + 1 } 111 return 0 112} 113func pv_count(b: *u8, n: i64, lit: *u8) -> i64 { 114 var c: i64 = 0 115 var i: i64 = 0 116 while i < n { if sw_starts(b, i, lit) == 1 { c = c + 1 } i = i + 1 } 117 return c 118} 119 120// heading-level skip detector (axe/Lighthouse structural rule: no downward jump >1, e.g. h1->h3) -- integer scan 121func pv_hskip(b: *u8, n: i64) -> i64 { 122 var prev: i64 = 0 123 var skips: i64 = 0 124 var i: i64 = 0 125 while i < n - 3 { 126 if b[i] == (60 as u8) { if b[i+1] == (104 as u8) { 127 let d: i64 = b[i+2] as i64 128 if d >= 49 { if d <= 54 { 129 let lvl: i64 = d - 48 130 if prev > 0 { if lvl > prev + 1 { skips = skips + 1 } } 131 prev = lvl 132 } } 133 } } 134 i = i + 1 135 } 136 return skips 137} 138 139// does s start with pfx? returns the offset just past pfx, or -1. Lets the optional 140// args be recognised by NAME in any position, so adding them cannot disturb the existing 141// positional [connect-host:port] callers (rule 19: adding is safe, reordering is not). 142func pv_pfx(s: *u8, pfx: *u8) -> i64 { 143 var i: i64 = 0 144 while pfx[i] != (0 as u8) { 145 if s[i] != pfx[i] { return 0 - 1 } 146 i = i + 1 147 } 148 return i 149} 150// ============================================================================================ 151// AUTO CONNECT-OVERRIDE -- RECOVERED 2026-07-25 (debt seq1008 / frontier F1144). 152// The DEPLOYED nx_page_verify.elf implemented this; NO SOURCE IN THE TREE DID. It was built 153// ~2026-07-20, deployed and documented, and its source was lost, so the ordinary /api/build + 154// /api/promote loop would have SILENTLY DELETED it -- turning every our-domain verification back 155// into a :443 coin flip against the co-squatting DSM nginx. That failure mode already manufactured 156// one false sev-7 debt (seq984, retracted). Reimplemented here from the contract documented in the 157// registry header so the binary is reproducible from source again. 158// CONTRACT: rows are <host><TAB><a.b.c.d:port>, '#' comments skipped, an explicit argv[2] override 159// ALWAYS wins, and an absent registry is FAIL-SAFE (verification still runs) -- but never silently: 160// see the vantage line in main(), which is now unconditional. 161const PV_DOMCONF: *u8 = "knowledge/registry/our_domains.conf" 162const PV_DOMCAP: i64 = 65536 163const PV_EP_CAP: i64 = 256 164 165// host of "https://HOST[:port]/path" -> out. 1 ok, 0 if not an https url. 166func pv_host(url: *u8, out: *u8, cap: i64) -> i64 { 167 if sw_starts(url, 0, "https://" as *u8) != 1 { return 0 } 168 var i: i64 = 8 169 var o: i64 = 0 170 var go: i64 = 1 171 while go == 1 { 172 let c: i64 = url[i] as i64 173 if c == 0 { go = 0 } 174 else { 175 if c == 47 { go = 0 } 176 else { 177 if c == 58 { go = 0 } 178 else { 179 if o < (cap - 1) { out[o] = url[i]; o = o + 1 } 180 i = i + 1 181 } 182 } 183 } 184 } 185 out[o] = 0 as u8 186 if o == 0 { return 0 } 187 return 1 188} 189 190// 1 = hit (ep filled) | 0 = not one of ours | -1 = registry unreadable (caller must SAY SO) 191func pv_dom_lookup(host: *u8, ep: *u8, epcap: i64) -> i64 { 192 let fd: i64 = sys_openat_rd(PV_DOMCONF) 193 if fd < 0 { return 0 - 1 } 194 let buf: *u8 = sys_mmap(PV_DOMCAP) 195 var total: i64 = 0 196 var go: i64 = 1 197 while go == 1 { 198 if total >= PV_DOMCAP { go = 0 } 199 else { 200 let n: i64 = sys_read(fd, (buf as i64 + total) as *u8, PV_DOMCAP - total) 201 if n > 0 { total = total + n } else { go = 0 } 202 } 203 } 204 sys_close(fd) 205 var hl: i64 = 0 206 while host[hl] != (0 as u8) { hl = hl + 1 } 207 var found: i64 = 0 208 var ls: i64 = 0 209 var i: i64 = 0 210 while i <= total { 211 var nl: i64 = 0 212 if i >= total { nl = 1 } else { if buf[i] == (10 as u8) { nl = 1 } } 213 if nl == 1 { 214 if found == 0 { 215 let len: i64 = i - ls 216 if len > 0 { 217 if buf[ls] != (35 as u8) { 218 var t: i64 = ls 219 var tab: i64 = 0 - 1 220 while t < i { if buf[t] == (9 as u8) { if tab < 0 { tab = t } } t = t + 1 } 221 if tab > 0 { 222 if (tab - ls) == hl { 223 var k: i64 = 0 224 var same: i64 = 1 225 while k < hl { if buf[ls + k] != host[k] { same = 0 } k = k + 1 } 226 if same == 1 { 227 var o: i64 = 0 228 var p: i64 = tab + 1 229 while p < i { 230 if buf[p] != (13 as u8) { if o < (epcap - 1) { ep[o] = buf[p]; o = o + 1 } } 231 p = p + 1 232 } 233 ep[o] = 0 as u8 234 found = 1 235 } 236 } 237 } 238 } 239 } 240 } 241 ls = i + 1 242 } 243 i = i + 1 244 } 245 return found 246} 247 248func pv_atoi(a: *u8) -> i64 { 249 var v: i64 = 0 250 var i: i64 = 0 251 while a[i] != (0 as u8) { 252 let c: i64 = a[i] as i64 253 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } 254 i = i + 1 255 } 256 return v 257} 258 259func main(argc: i64, argv: *i64) -> i64 { 260 if argc < 2 { hw("usage: nx_page_verify <https-url> [connect-host:port] [expect=<substring>] [minbytes=<n>]\n" as *u8); sys_exit(2); return 2 } 261 let url: *u8 = argv[1] as *u8 262 hw("=== nx_page_verify -- browser-grade live verification of " as *u8); hw(url); hw(" ===\n" as *u8) 263 var expoff: i64 = 0 264 var minbytes: i64 = 0 265 var ov_given: i64 = 0 266 var ai: i64 = 2 267 while ai < argc { 268 let a: *u8 = argv[ai] as *u8 269 let eo: i64 = pv_pfx(a, "expect=" as *u8) 270 let mo: i64 = pv_pfx(a, "minbytes=" as *u8) 271 if eo >= 0 { expoff = (a as i64) + eo } 272 else { 273 if mo >= 0 { minbytes = pv_atoi(((a as i64) + mo) as *u8) } 274 else { 275 if nx_https_connect_override_set(a) != 1 { hw("RED bad connect-override (want a.b.c.d:port)\n" as *u8); sys_exit(2); return 2 } 276 hw("vantage=EXPLICIT connect-override " as *u8); hw(a); hw(" (SNI/Host stay the URL host)\n" as *u8) 277 ov_given = 1 278 } 279 } 280 ai = ai + 1 281 } 282 283 // ---- VANTAGE RESOLUTION (recovered + hardened 2026-07-25; seq1008 recovery, seq990 hardening) ---- 284 // The whole point: this organ must NEVER be able to grade one server while the reader believes it 285 // graded another. So the vantage is now stated on EVERY run -- there is no silent path. 286 let pv_h: *u8 = sys_mmap(PV_URL_CAP) 287 var pv_isours: i64 = 0 288 var pv_pinned: i64 = ov_given 289 if pv_host(url, pv_h, PV_URL_CAP) == 1 { 290 let pv_ep: *u8 = sys_mmap(PV_EP_CAP) 291 let d: i64 = pv_dom_lookup(pv_h, pv_ep, PV_EP_CAP) 292 if d == 1 { pv_isours = 1 } 293 if ov_given == 0 { 294 if d == 1 { 295 if nx_https_connect_override_set(pv_ep) != 1 { 296 hw("VERDICT=UNMEASURED (our-domain " as *u8); hw(pv_h) 297 hw(" but its registry endpoint " as *u8); hw(pv_ep) 298 hw(" is malformed -- refusing to grade an unknown server)\n" as *u8) 299 sys_exit(4); return 4 300 } 301 hw("auto connect-override (our-domain " as *u8); hw(pv_h) 302 hw(" -> " as *u8); hw(pv_ep); hw("; SNI/Host stay the URL host)\n" as *u8) 303 pv_pinned = 1 304 } 305 if d == (0 - 1) { 306 hw("WARNING registry " as *u8); hw(PV_DOMCONF) 307 hw(" unreadable -- cannot tell if this host is ours; proceeding via DNS (fail-safe)\n" as *u8) 308 } 309 } 310 } 311 if pv_pinned == 1 { hw("vantage=PINNED (deterministic sovereign edge)\n" as *u8) } 312 else { 313 hw("vantage=DNS (no override)" as *u8) 314 if pv_isours == 1 { 315 // Cannot happen once the registry is readable, but if it ever does, the reader is told 316 // LOUDLY rather than handed a confident RED from whatever answered port 443. 317 hw(" <== WARNING: this IS one of our domains and it is NOT pinned. A 404/RED here may be\n" as *u8) 318 hw(" the co-squatting server on :443, not our edge. TREAT AS UNMEASURED." as *u8) 319 } 320 hw("\n" as *u8) 321 } 322 323 let r: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 512, PV_CERTDATA_CAP) 324 if r <= 0 { hw("RED cannot load trust store (run from repo root)\n" as *u8); sys_exit(3); return 3 } 325 let store: *TrustStore = r as *TrustStore 326 327 let page: *u8 = sys_mmap(PV_CAP) 328 let st: *i64 = sys_mmap(8) as *i64 329 let n: i64 = nx_https_fetch_follow(url, store, page, PV_CAP, 6, st) 330 hw("page status=" as *u8); pn(st[0]); hw(" bytes=" as *u8); pn(n); hw("\n" as *u8) 331 if st[0] != 200 { hw("VERDICT=RED (page not 200)\n" as *u8); sys_exit(1); return 1 } 332 if n <= 0 { hw("VERDICT=RED (empty page)\n" as *u8); sys_exit(1); return 1 } 333 334 // ★CONTENT ASSERTION (debt seq911). A 200 DOES NOT MEAN YOUR PAGE. The edge serves a 335 // root fallback for unknown paths, so a failed publish yields 200 with a perfectly 336 // valid, fully-decoding page that simply is not yours -- and every asset on it fetches, 337 // so the old asset-only verdict said GREEN. Measured 2026-07-25: verifying 338 // /research/rt004.html reported status=200 VERDICT=GREEN while the edge was serving the 339 // 835-byte site root and the 10201-byte page had landed nowhere. Asset liveness is not 340 // page identity; the caller must be able to assert WHICH page it expects. 341 if minbytes > 0 { 342 if n < minbytes { 343 hw("VERDICT=RED (page is " as *u8); pn(n) 344 hw(" bytes, below the asserted floor of " as *u8); pn(minbytes) 345 hw(" -- a 200 is not your page)\n" as *u8) 346 sys_exit(1); return 1 347 } 348 hw("byte-floor OK (" as *u8); pn(n); hw(" >= " as *u8); pn(minbytes); hw(")\n" as *u8) 349 } 350 if expoff != 0 { 351 if pv_has(page, n, expoff as *u8) == 0 { 352 hw("VERDICT=RED (expected content ABSENT: " as *u8); hw(expoff as *u8) 353 hw(") -- the server returned 200 but this is not the page you published\n" as *u8) 354 sys_exit(1); return 1 355 } 356 hw("content-assert OK (expect=" as *u8); hw(expoff as *u8); hw(")\n" as *u8) 357 } 358 359 let origin: *u8 = sys_mmap(PV_URL_CAP) 360 pv_origin(url, origin) 361 362 // --- a11y-lite audit (integer, no float): axe-core / Lighthouse-class page checks --- 363 hw("-- a11y-lite --\n" as *u8) 364 var a11y: i64 = 0 365 if pv_has(page, n, "<title" as *u8) == 1 { hw(" title OK\n" as *u8) } else { hw(" A11Y missing <title>\n" as *u8); a11y = a11y + 1 } 366 if pv_has(page, n, "lang=" as *u8) == 1 { hw(" lang OK\n" as *u8) } else { hw(" A11Y missing lang= on <html>\n" as *u8); a11y = a11y + 1 } 367 if pv_has(page, n, "viewport" as *u8) == 1 { hw(" viewport OK\n" as *u8) } else { hw(" A11Y missing <meta viewport>\n" as *u8); a11y = a11y + 1 } 368 let nimg: i64 = pv_count(page, n, "<img" as *u8) 369 let nalt: i64 = pv_count(page, n, " alt=" as *u8) 370 hw(" img=" as *u8); pn(nimg); hw(" with-alt=" as *u8); pn(nalt); hw("\n" as *u8) 371 if nalt < nimg { hw(" A11Y " as *u8); pn(nimg - nalt); hw(" image(s) missing alt\n" as *u8); a11y = a11y + 1 } 372 let nh1: i64 = pv_count(page, n, "<h1" as *u8) 373 if nh1 == 1 { hw(" single-h1 OK\n" as *u8) } else { hw(" A11Y h1-count=" as *u8); pn(nh1); hw(" (want exactly 1)\n" as *u8); a11y = a11y + 1 } 374 let hsk: i64 = pv_hskip(page, n) 375 if hsk == 0 { hw(" heading-order OK\n" as *u8) } else { hw(" A11Y " as *u8); pn(hsk); hw(" heading-level skip(s)\n" as *u8); a11y = a11y + 1 } 376 if pv_has(page, n, "<main" as *u8) == 1 { hw(" landmark <main> OK\n" as *u8) } else { hw(" A11Y no <main> landmark\n" as *u8); a11y = a11y + 1 } 377 hw(" a11y-issues=" as *u8); pn(a11y); hw("\n" as *u8) 378 379 // extract src="..." / href="..." refs (img/script/link surface) 380 let abuf: *u8 = sys_mmap(PV_CAP) 381 var broken: i64 = 0 382 var checked: i64 = 0 383 var skipped: i64 = 0 384 var lawred: i64 = 0 385 var i: i64 = 0 386 while i < n - 8 { 387 var hit: i64 = 0 388 if sw_starts(page, i, "src=\"" as *u8) == 1 { hit = 5 } 389 if hit == 0 { if sw_starts(page, i, "href=\"" as *u8) == 1 { 390 // href only counts for <link ...> stylesheet-ish tags: look back for '<link' within 80 bytes 391 var b: i64 = i 392 var found: i64 = 0 393 var back: i64 = 0 394 while back < 80 { 395 if b <= 0 { back = 80 } else { 396 if page[b] == (60 as u8) { 397 if sw_starts(page, b, "<link" as *u8) == 1 { found = 1 } 398 back = 80 399 } else { b = b - 1; back = back + 1 } 400 } 401 } 402 if found == 1 { hit = 6 } 403 } } 404 if hit > 0 { 405 let rs: i64 = i + hit 406 var re: i64 = rs 407 while page[re] != (34 as u8) { re = re + 1 } 408 let rlen: i64 = re - rs 409 if rlen > 0 { if rlen < PV_REF_MAX { if checked + skipped + lawred < PV_MAXREF { 410 let ref: *u8 = sys_mmap(PV_URL_CAP) 411 var k: i64 = 0 412 while k < rlen { ref[k] = page[rs+k]; k = k + 1 } 413 ref[rlen] = 0 as u8 414 if sw_starts(ref, 0, "data:" as *u8) == 1 { skipped = skipped + 1 } else { 415 if sw_starts(ref, 0, "http" as *u8) == 1 { 416 if sw_starts(ref, 0, origin) == 1 { checked = checked + 1; broken = broken + pv_check_asset(store, ref, abuf) } else { hw(" skip foreign " as *u8); hw(ref); hw("\n" as *u8); skipped = skipped + 1 } 417 } else { 418 if ref[0] == (47 as u8) { 419 let full: *u8 = sys_mmap(PV_FULLURL_CAP) 420 var o: i64 = 0 421 while origin[o] != (0 as u8) { full[o] = origin[o]; o = o + 1 } 422 var q: i64 = 0 423 while ref[q] != (0 as u8) { full[o+q] = ref[q]; q = q + 1 } 424 full[o+q] = 0 as u8 425 checked = checked + 1 426 broken = broken + pv_check_asset(store, full, abuf) 427 } else { 428 // RELATIVE ref = RED BY LAW on our slash-stripping edge (the /render3d broken-image class) 429 hw(" asset " as *u8); hw(ref); hw(" BROKEN-BY-LAW (relative ref; the edge 301-strips trailing slashes so browsers resolve this against the PARENT -- use absolute /path)\n" as *u8) 430 lawred = lawred + 1 431 broken = broken + 1 432 } } } 433 } } } 434 i = re 435 } else { i = i + 1 } 436 } 437 438 hw("checked=" as *u8); pn(checked) 439 hw(" skipped=" as *u8); pn(skipped) 440 hw(" law-violations=" as *u8); pn(lawred) 441 hw(" broken=" as *u8); pn(broken); hw("\n" as *u8) 442 if broken == 0 { hw("VERDICT=GREEN (every referenced asset fetches AND decodes)\n" as *u8); sys_exit(0); return 0 } 443 hw("VERDICT=RED broken=" as *u8); pn(broken); hw("\n" as *u8) 444 sys_exit(broken) 445 return broken 446}