nx_http_template_test.nx source
↩ module page · 169 lines · 6844 B
1// nx_http_template_test.nx -- smoke for typed-context templates.
2//
3// expect_exit: 0
4//
5// license_tier: ORIGINAL
6
7import "nx_syscalls_x86_64.nx"
8import "nx_http_template.nx"
9
10func bytes_eq(a: *u8, b: *u8, n: i64) -> i64 {
11 var i: i64 = 0
12 while i < n {
13 if a[i] != b[i] { return 0 }
14 i = i + 1
15 }
16 return 1
17}
18
19func bytes_contains(haystack: *u8, h_n: i64, needle: *u8, n_n: i64) -> i64 {
20 if n_n > h_n { return 0 }
21 var i: i64 = 0
22 while i <= h_n - n_n {
23 if bytes_eq(((haystack as i64) + i) as *u8, needle, n_n) == 1 { return 1 }
24 i = i + 1
25 }
26 return 0
27}
28
29// Helper: populate vars table with three entries.
30func setup_vars(vars: *NxTemplateVars) -> i64 {
31 let names: *u8 = sys_mmap(256)
32 let name_offs: *i64 = sys_mmap(64) as *i64
33 let name_lens: *i64 = sys_mmap(64) as *i64
34 let values: *u8 = sys_mmap(1024)
35 let value_offs: *i64 = sys_mmap(64) as *i64
36 let value_lens: *i64 = sys_mmap(64) as *i64
37
38 // "name" -> "Alice"
39 let n0: *u8 = "name" as *u8
40 var a: i64 = 0
41 while a < 4 { names[a] = n0[a]; a = a + 1 }
42 name_offs[0] = 0; name_lens[0] = 4
43 let v0: *u8 = "Alice" as *u8
44 var b: i64 = 0
45 while b < 5 { values[b] = v0[b]; b = b + 1 }
46 value_offs[0] = 0; value_lens[0] = 5
47
48 // "url" -> "/page?q=hello world"
49 let n1: *u8 = "url" as *u8
50 var c: i64 = 0
51 while c < 3 { names[4 + c] = n1[c]; c = c + 1 }
52 name_offs[1] = 4; name_lens[1] = 3
53 let v1: *u8 = "/page?q=hello world" as *u8
54 var d: i64 = 0
55 while d < 19 { values[5 + d] = v1[d]; d = d + 1 }
56 value_offs[1] = 5; value_lens[1] = 19
57
58 // "evil" -> "<script>alert(1)</script>"
59 let n2: *u8 = "evil" as *u8
60 var e: i64 = 0
61 while e < 4 { names[7 + e] = n2[e]; e = e + 1 }
62 name_offs[2] = 7; name_lens[2] = 4
63 let v2: *u8 = "<script>alert(1)</script>" as *u8
64 var f: i64 = 0
65 while f < 25 { values[24 + f] = v2[f]; f = f + 1 }
66 value_offs[2] = 24; value_lens[2] = 25
67
68 return nx_template_vars_init(vars,
69 names, name_offs, name_lens,
70 values, value_offs, value_lens,
71 3)
72}
73
74func main() -> i64 {
75 // ---- Verdict enum ----
76 if nxt_verdict_is_valid(NXT_OK) != 1 { return 1 }
77 if nxt_verdict_is_valid(NXT_BAD_MODIFIER) != 1 { return 2 }
78 if nxt_verdict_is_valid(NXT_VERDICT_N) != 0 { return 3 }
79 if bytes_eq(nxt_verdict_name(NXT_OK), "OK" as *u8, 2) != 1 { return 4 }
80 if bytes_eq(nxt_verdict_name(NXT_BAD_TEMPLATE), "BAD_TEMPLATE" as *u8, 12) != 1 { return 5 }
81
82 // ---- Modifier -> ctx ----
83 if nxt_modifier_to_ctx("text" as *u8, 4) != NXH_CTX_TEXT { return 10 }
84 if nxt_modifier_to_ctx("attr" as *u8, 4) != NXH_CTX_ATTR_DQ { return 11 }
85 if nxt_modifier_to_ctx("url" as *u8, 3) != NXH_CTX_URL { return 12 }
86 if nxt_modifier_to_ctx("raw" as *u8, 3) != NXH_CTX_RAW { return 13 }
87 if nxt_modifier_to_ctx("nope" as *u8, 4) != -1 { return 14 }
88
89 // ---- Init vars ----
90 let vars: *NxTemplateVars = (sys_mmap(NX_TEMPLATE_VARS_BYTES)) as *NxTemplateVars
91 if setup_vars(vars) != NXT_OK { return 20 }
92
93 let out: *u8 = sys_mmap(2048)
94 let out_n: *i64 = sys_mmap(8) as *i64
95
96 // ---- Default TEXT context ----
97 let tmpl1: *u8 = "Hello, {{ name }}!" as *u8
98 out_n[0] = 0
99 if nx_http_template_render(tmpl1, 18, vars, out, 2048, out_n) != NXT_OK { return 30 }
100 if out_n[0] != 13 { return 31 }
101 if bytes_eq(out, "Hello, Alice!" as *u8, 13) != 1 { return 32 }
102
103 // ---- XSS prevention via TEXT context ----
104 let tmpl2: *u8 = "<p>{{ evil }}</p>" as *u8
105 out_n[0] = 0
106 if nx_http_template_render(tmpl2, 17, vars, out, 2048, out_n) != NXT_OK { return 40 }
107 // Substrate must have escaped < and > to < / >
108 if bytes_contains(out, out_n[0], "<script>" as *u8, 8) != 0 { return 41 }
109 if bytes_contains(out, out_n[0], "<script>" as *u8, 14) != 1 { return 42 }
110
111 // ---- URL context: spaces -> %20 ----
112 let tmpl3: *u8 = "<a href=\"{{ url | url }}\">click</a>" as *u8
113 out_n[0] = 0
114 if nx_http_template_render(tmpl3, 35, vars, out, 2048, out_n) != NXT_OK { return 50 }
115 if bytes_contains(out, out_n[0], "hello%20world" as *u8, 13) != 1 { return 51 }
116
117 // ---- ATTR_DQ context: quote in value -> " ----
118 let n_evil: *u8 = "evil" as *u8 // value contains "..." with quotes around the JS
119 let tmpl4: *u8 = "<div class=\"{{ evil | attr }}\">x</div>" as *u8
120 out_n[0] = 0
121 if nx_http_template_render(tmpl4, 38, vars, out, 2048, out_n) != NXT_OK { return 60 }
122 // < and > and " should be encoded
123 if bytes_contains(out, out_n[0], "<script>" as *u8, 14) != 1 { return 61 }
124
125 // ---- RAW bypass (audited) ----
126 let tmpl5: *u8 = "<p>{{ evil | raw }}</p>" as *u8
127 out_n[0] = 0
128 if nx_http_template_render(tmpl5, 23, vars, out, 2048, out_n) != NXT_OK { return 70 }
129 // RAW lets the value through unescaped (caller asserts safety).
130 if bytes_contains(out, out_n[0], "<script>" as *u8, 8) != 1 { return 71 }
131
132 // ---- BAD_MODIFIER ----
133 let tmpl6: *u8 = "<p>{{ evil | onclick }}</p>" as *u8
134 out_n[0] = 0
135 if nx_http_template_render(tmpl6, 27, vars, out, 2048, out_n) != NXT_BAD_MODIFIER { return 80 }
136
137 // ---- VAR_NOT_FOUND ----
138 let tmpl7: *u8 = "{{ undefined_var }}" as *u8
139 out_n[0] = 0
140 if nx_http_template_render(tmpl7, 19, vars, out, 2048, out_n) != NXT_VAR_NOT_FOUND { return 90 }
141
142 // ---- BAD_TEMPLATE (missing close `}}`) ----
143 let tmpl8: *u8 = "{{ name" as *u8
144 out_n[0] = 0
145 if nx_http_template_render(tmpl8, 7, vars, out, 2048, out_n) != NXT_BAD_TEMPLATE { return 100 }
146
147 // ---- Multiple substitutions ----
148 let tmpl9: *u8 = "Hi {{ name }}, see <a href=\"{{ url | url }}\">link</a>." as *u8
149 out_n[0] = 0
150 if nx_http_template_render(tmpl9, 54, vars, out, 2048, out_n) != NXT_OK { return 110 }
151 if bytes_contains(out, out_n[0], "Hi Alice" as *u8, 8) != 1 { return 111 }
152 if bytes_contains(out, out_n[0], "hello%20world" as *u8, 13) != 1 { return 112 }
153
154 // ---- Template with no vars (plain literal) ----
155 let tmpl10: *u8 = "<h1>plain</h1>" as *u8
156 out_n[0] = 0
157 if nx_http_template_render(tmpl10, 14, vars, out, 2048, out_n) != NXT_OK { return 120 }
158 if out_n[0] != 14 { return 121 }
159 if bytes_eq(out, tmpl10, 14) != 1 { return 122 }
160
161 // ---- BAD_ARG paths ----
162 out_n[0] = 0
163 if nx_http_template_render(0 as *u8, 10, vars, out, 2048, out_n) != NXT_BAD_ARG { return 130 }
164 if nx_http_template_render(tmpl1, 18, 0 as *NxTemplateVars, out, 2048, out_n) != NXT_BAD_ARG { return 131 }
165 if nx_http_template_render(tmpl1, 18, vars, 0 as *u8, 2048, out_n) != NXT_BAD_ARG { return 132 }
166 if nx_http_template_render(tmpl1, 18, vars, out, 0, out_n) != NXT_BAD_ARG { return 133 }
167
168 return 0
169}