nx_redirect_resolve_test.nx source
↩ module page · 91 lines · 3626 B
1// nx_redirect_resolve_test.nx -- X-HTTP-RELLOC-001 KAT: redirect-Location
2// resolution + https-only safety gate for the sovereign nish browser.
3//
4// Base mirrors the RFC3986 ยง5.2 normative example (https + a.com authority)
5// so the path arithmetic is the already-proven nx_url_resolve behaviour;
6// this gate proves the WIRING (relative refs followed, downgrades refused).
7//
8// base = "https://a.com/b/c/d;p?q"
9// C1 absolute https "https://x.com/y" -> follow "https://x.com/y"
10// C2 path-absolute "/g" -> follow "https://a.com/g"
11// C3 path-relative "g" -> follow "https://a.com/b/c/g"
12// C4 scheme-relative "//h.com/p" -> follow "https://h.com/p"
13// C5 dot-segments "../g" -> follow "https://a.com/b/g"
14// C6 http downgrade "http://evil/x" -> REFUSE (0) [no downgrade]
15// C7 cross-scheme "ftp://x/y" -> REFUSE (0) [https-only]
16// C8 google live case "/webhp" -> follow "https://www.google.com/webhp"
17// C9 empty Location "" -> REFUSE (0)
18//
19// C6/C7/C9 are the negative controls: the gate must DISCRIMINATE, not just
20// pass everything. expect_exit: 0
21// license_tier: ORIGINAL
22
23import "nx_syscalls.nx"
24import "nx_redirect_resolve.nx"
25
26func _strlen(s: *u8) -> i64 {
27 var i: i64 = 0
28 while (s[i] & 0xff) != 0 { i = i + 1 }
29 return i
30}
31
32func _eq(a: *u8, an: i64, b: *u8, bn: i64) -> i64 {
33 if an != bn { return 0 }
34 var i: i64 = 0
35 while i < an {
36 if (a[i] & 0xff) != (b[i] & 0xff) { return 0 }
37 i = i + 1
38 }
39 return 1
40}
41
42// Returns 0 on pass, base_id on follow-flag mismatch, base_id+1 on
43// resolved-target mismatch.
44func _ck(base_id: i64, base: *u8, loc: *u8, expect_follow: i64, exp_target: *u8) -> i64 {
45 let out: *u8 = sys_mmap(512)
46 let olp: *i64 = sys_mmap(8) as *i64
47 let f: i64 = nx_redirect_resolve(base, _strlen(base), loc, _strlen(loc), out, 512, olp)
48 if f != expect_follow { return base_id }
49 if expect_follow == 1 {
50 if _eq(out, olp[0], exp_target, _strlen(exp_target)) != 1 {
51 sys_write(2, out, olp[0])
52 let nl: *u8 = sys_mmap(4); nl[0]=0x0a as u8; sys_write(2, nl, 1)
53 return base_id + 1
54 }
55 }
56 return 0
57}
58
59func main() -> i64 {
60 let base: *u8 = "https://a.com/b/c/d;p?q" as *u8
61 var r: i64 = 0
62
63 r = _ck(10, base, "https://x.com/y" as *u8, 1, "https://x.com/y" as *u8)
64 if r != 0 { return r }
65 r = _ck(20, base, "/g" as *u8, 1, "https://a.com/g" as *u8)
66 if r != 0 { return r }
67 r = _ck(30, base, "g" as *u8, 1, "https://a.com/b/c/g" as *u8)
68 if r != 0 { return r }
69 r = _ck(40, base, "//h.com/p" as *u8, 1, "https://h.com/p" as *u8)
70 if r != 0 { return r }
71 r = _ck(50, base, "../g" as *u8, 1, "https://a.com/b/g" as *u8)
72 if r != 0 { return r }
73
74 // Negative controls: must REFUSE (https-only).
75 r = _ck(60, base, "http://evil/x" as *u8, 0, "" as *u8)
76 if r != 0 { return r }
77 r = _ck(70, base, "ftp://x/y" as *u8, 0, "" as *u8)
78 if r != 0 { return r }
79 // Empty Location via an explicit NUL buffer (empty string literals are
80 // not a clean NUL under this compiler; this exercises loc_len<=0).
81 let eloc: *u8 = sys_mmap(8); eloc[0] = 0 as u8
82 r = _ck(90, base, eloc, 0, eloc)
83 if r != 0 { return r }
84
85 // Google's real cookie-less bounce, as a relative Location.
86 let gbase: *u8 = "https://www.google.com/search?q=cats" as *u8
87 r = _ck(80, gbase, "/webhp" as *u8, 1, "https://www.google.com/webhp" as *u8)
88 if r != 0 { return r }
89
90 return 0
91}