code wiki / _hdl_build / nx_doc_serve_gate.nx
nx_doc_serve_gate.nx source
↩ module page · 145 lines · 7075 B
1// nx_doc_serve_gate.nx -- GATE for LEGAL D3 (nx_doc_serve).
2//
3// Asserts the open-in-app-of-choice serve invariants, each w/ a negative control:
4// T1 CONTENT-TYPE : every format maps to its exact MIME (browser opens the
5// right app).
6// T2 BYTE-EXACT : a body that itself contains CRLFCRLF + NUL round-trips
7// bit-for-bit (Content-Length framed); flipping one body
8// byte is detected.
9// T3 DISPOSITION : DOCX defaults to download (open in Word), PDF inline;
10// the attachment response carries the filename.
11// T4 LENGTH+HARDENING : Content-Length is exact + X-Content-Type-Options:nosniff
12// present (no MIME-sniff of a legal doc).
13// T5 INJECTION-SAFE : a "name\r\nSet-Cookie: ..." filename cannot inject a
14// header -- framing intact AND the CRLF is stripped.
15// T6 SAFE-BY-DEFAULT : an unknown format is octet-stream + attachment (never
16// inlined).
17// T7 TENANT ISOLATION : the vault prefix discipline rejects '.'/'/' tenant ids.
18//
19// Evidence -> knowledge/status/doc_serve.log
20// license_tier: ORIGINAL
21import "nx_doc_serve.nx"
22import "nx_doc_vault.nx"
23import "nx_syscalls.nx"
24import "nx_gate_verdict.nx"
25
26const DS_LOG: *u8 = "knowledge/status/doc_serve.log"
27
28func ew(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
29func ewn(fd: i64, v: i64) -> i64 {
30 let bb: *u8 = sys_mmap(28); var m: i64 = v
31 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) }
32 let t: *u8 = sys_mmap(28); var k: i64 = 0
33 if m == 0 { t[0] = 48; k = 1 }
34 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
35 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
36 sys_write(fd, bb, k); return 0
37}
38func streq(a: *u8, b: *u8) -> i64 {
39 var i: i64 = 0
40 while a[i] != (0 as u8) {
41 if a[i] != b[i] { return 0 }
42 i = i + 1
43 }
44 if b[i] != (0 as u8) { return 0 }
45 return 1
46}
47
48func main() -> i64 {
49 var ok: i64 = 1
50
51 // ---- T1: content-type mapping ----
52 var t1: i64 = 1
53 if streq(nx_doc_content_type(DF_PDF), "application/pdf" as *u8) != 1 { t1 = 0 }
54 if streq(nx_doc_content_type(DF_HTML), "text/html; charset=utf-8" as *u8) != 1 { t1 = 0 }
55 if streq(nx_doc_content_type(DF_TXT), "text/plain; charset=utf-8" as *u8) != 1 { t1 = 0 }
56 if streq(nx_doc_content_type(DF_NXDOC), "application/x-nishi-doc" as *u8) != 1 { t1 = 0 }
57 if streq(nx_doc_content_type(DF_DOCX), "application/vnd.openxmlformats-officedocument.wordprocessingml.document" as *u8) != 1 { t1 = 0 }
58 if streq(nx_doc_content_type(DF_OCTET), "application/octet-stream" as *u8) != 1 { t1 = 0 }
59 if t1 != 1 { ok = 0 }
60
61 // ---- a document body with adversarial bytes (CRLFCRLF + NUL inside) ----
62 let body: *u8 = sys_mmap(64)
63 body[0]=72; body[1]=105; body[2]=13; body[3]=10; body[4]=13; body[5]=10; body[6]=0; body[7]=66; body[8]=121; body[9]=101
64 let blen: i64 = 10
65
66 // ---- T2: byte-exact round-trip + corruption detection ----
67 var t2: i64 = 1
68 let resp: *u8 = sys_mmap(4096)
69 let n2: i64 = nx_doc_serve_response(resp, body, blen, DF_PDF, DISP_INLINE, "x" as *u8)
70 if nx_doc_serve_roundtrip_ok(resp, n2, body, blen) != 1 { t2 = 0 }
71 resp[n2 - 1] = (resp[n2 - 1] ^ 0x20) as u8
72 if nx_doc_serve_roundtrip_ok(resp, n2, body, blen) != 0 { t2 = 0 }
73 resp[n2 - 1] = (resp[n2 - 1] ^ 0x20) as u8
74 if nx_doc_serve_roundtrip_ok(resp, n2, body, blen) != 1 { t2 = 0 }
75 if t2 != 1 { ok = 0 }
76
77 // ---- T3: disposition (open-in-app vs download) ----
78 var t3: i64 = 1
79 if nx_doc_default_disposition(DF_DOCX) != DISP_ATTACHMENT { t3 = 0 }
80 if nx_doc_default_disposition(DF_PDF) != DISP_INLINE { t3 = 0 }
81 let r3: *u8 = sys_mmap(4096)
82 let n3: i64 = nx_doc_serve_response(r3, body, blen, DF_DOCX, DISP_ATTACHMENT, "estate_plan.docx" as *u8)
83 if nx_doc_serve_contains(r3, n3, "attachment; filename=" as *u8) != 1 { t3 = 0 }
84 if nx_doc_serve_contains(r3, n3, "estate_plan.docx" as *u8) != 1 { t3 = 0 }
85 let r3b: *u8 = sys_mmap(4096)
86 let n3b: i64 = nx_doc_serve_response(r3b, body, blen, DF_PDF, DISP_INLINE, "x" as *u8)
87 if nx_doc_serve_contains(r3b, n3b, "Content-Disposition: inline" as *u8) != 1 { t3 = 0 }
88 if t3 != 1 { ok = 0 }
89
90 // ---- T4: exact Content-Length + nosniff hardening ----
91 var t4: i64 = 1
92 if nx_doc_serve_contains(r3b, n3b, "Content-Length: 10" as *u8) != 1 { t4 = 0 }
93 if nx_doc_serve_contains(r3b, n3b, "X-Content-Type-Options: nosniff" as *u8) != 1 { t4 = 0 }
94 if t4 != 1 { ok = 0 }
95
96 // ---- T5: a malicious filename cannot inject a header ----
97 var t5: i64 = 1
98 let r5: *u8 = sys_mmap(4096)
99 let n5: i64 = nx_doc_serve_response(r5, body, blen, DF_DOCX, DISP_ATTACHMENT, "deed\r\nSet-Cookie: evil.docx" as *u8)
100 if nx_doc_serve_roundtrip_ok(r5, n5, body, blen) != 1 { t5 = 0 }
101 if nx_doc_serve_contains(r5, n5, "\r\nSet-Cookie" as *u8) != 0 { t5 = 0 }
102 if t5 != 1 { ok = 0 }
103
104 // ---- T6: unknown format is octet-stream + attachment (never inlined) ----
105 var t6: i64 = 1
106 if streq(nx_doc_content_type(DF_OCTET), "application/octet-stream" as *u8) != 1 { t6 = 0 }
107 if nx_doc_default_disposition(DF_OCTET) != DISP_ATTACHMENT { t6 = 0 }
108 if t6 != 1 { ok = 0 }
109
110 // ---- T7: per-tenant prefix discipline ----
111 var t7: i64 = 1
112 if nx_vault_valid_tid("client_andelinwest" as *u8) != 1 { t7 = 0 }
113 if nx_vault_valid_tid("../secret" as *u8) != 0 { t7 = 0 }
114 if nx_vault_valid_tid("a/b" as *u8) != 0 { t7 = 0 }
115 if t7 != 1 { ok = 0 }
116
117 // ---- evidence ----
118 var fd: i64 = 1
119 while fd >= 1 {
120 ew(fd, "DOCSERVEGATE authored=organ composes=D1 content_type=" as *u8); ewn(fd, t1)
121 ew(fd, " byte_exact_roundtrip=" as *u8); ewn(fd, t2)
122 ew(fd, " disposition_open_in_app=" as *u8); ewn(fd, t3)
123 ew(fd, " length_exact_nosniff=" as *u8); ewn(fd, t4)
124 ew(fd, " filename_injection_safe=" as *u8); ewn(fd, t5)
125 ew(fd, " unknown_safe_default=" as *u8); ewn(fd, t6)
126 ew(fd, " tenant_isolation=" as *u8); ewn(fd, t7)
127 if ok == 1 { ew(fd, " verdict=GREEN\n" as *u8) } else { ew(fd, " verdict=RED\n" as *u8) }
128 if fd == 1 {
129 let lf: i64 = sys_openat_append(DS_LOG, 420)
130 if lf >= 1 { fd = lf } else { fd = 0 }
131 } else {
132 sys_close(fd); fd = 0
133 }
134 }
135
136 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
137 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
138 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
139 let ctr__dry: *i64 = gv_ctr()
140 ctr__dry[0] = ok
141 ctr__dry[1] = 1
142 let rc__dry: i64 = gv_verdict("DOC-SERVE-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
143 sys_exit(rc__dry)
144 return rc__dry
145}