nx_path_canonicalize_test.nx source
↩ module page · 135 lines · 4956 B
1// nx_path_canonicalize_test.nx -- structural smoke + CWE-22 attack
2// corpus.
3//
4// expect_exit: 0
5//
6// license_tier: ORIGINAL
7
8import "nx_syscalls_x86_64.nx"
9import "nx_path_canonicalize.nx"
10
11func bytes_eq(a: *u8, b: *u8, n: i64) -> i64 {
12 var i: i64 = 0
13 while i < n {
14 if a[i] != b[i] { return 0 }
15 i = i + 1
16 }
17 return 1
18}
19
20// Helper: assert verdict matches expected.
21func check_v(src: *u8, src_n: i64, max_out: i64,
22 expect_v: i64, err: i64) -> i64 {
23 let buf: *u8 = sys_mmap(1024)
24 let on: *i64 = sys_mmap(8) as *i64
25 let v: i64 = nx_path_canonicalize(src, src_n, buf, max_out, on)
26 if v != expect_v { return err }
27 return 0
28}
29
30func main() -> i64 {
31 // ---- Verdict enum gate ----
32 if nxp_verdict_is_valid(NXP_OK) != 1 { return 1 }
33 if nxp_verdict_is_valid(NXP_TRAVERSAL) != 1 { return 2 }
34 if nxp_verdict_is_valid(NXP_BAD_ARG) != 1 { return 3 }
35 if nxp_verdict_is_valid(NXP_VERDICT_N) != 0 { return 4 }
36 if nxp_verdict_is_valid(-1) != 0 { return 5 }
37
38 if bytes_eq(nxp_verdict_name(NXP_TRAVERSAL), "TRAVERSAL" as *u8, 9) != 1 { return 6 }
39 if bytes_eq(nxp_verdict_name(NXP_NUL), "NUL" as *u8, 3) != 1 { return 7 }
40 if bytes_eq(nxp_verdict_name(NXP_BACKSLASH), "BACKSLASH" as *u8, 9) != 1 { return 8 }
41 if bytes_eq(nxp_verdict_name(NXP_TOO_LONG), "TOO_LONG" as *u8, 8) != 1 { return 9 }
42
43 // ---- Empty path ----
44 var rc: i64 = check_v("" as *u8, 0, 1024, NXP_EMPTY, 10)
45 if rc != 0 { return rc }
46
47 // ---- TOO_LONG ----
48 rc = check_v("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" as *u8, 40, 10, NXP_TOO_LONG, 20)
49 if rc != 0 { return rc }
50
51 // ---- Valid simple path ----
52 let buf: *u8 = sys_mmap(1024)
53 let on: *i64 = sys_mmap(8) as *i64
54 let v1: i64 = nx_path_canonicalize("audit/index.html" as *u8, 16, buf, 1024, on)
55 if v1 != NXP_OK { return 30 }
56 if on[0] != 16 { return 31 }
57 if bytes_eq(buf, "audit/index.html" as *u8, 16) != 1 { return 32 }
58
59 // ---- Valid: ..foo (filename starts with two dots, NOT traversal) ----
60 let v2: i64 = nx_path_canonicalize("..bashrc" as *u8, 8, buf, 1024, on)
61 if v2 != NXP_OK { return 40 }
62
63 // ---- Valid: docs/..foo (segment-boundary-aware) ----
64 let v2b: i64 = nx_path_canonicalize("docs/..foo" as *u8, 10, buf, 1024, on)
65 if v2b != NXP_OK { return 41 }
66
67 // ---- CWE-22 attack vectors ----
68
69 // 1. classic ../../../etc/passwd
70 rc = check_v("../../../etc/passwd" as *u8, 19, 1024, NXP_TRAVERSAL, 100)
71 if rc != 0 { return rc }
72
73 // 2. ../etc/passwd (single ..)
74 rc = check_v("../etc/passwd" as *u8, 13, 1024, NXP_TRAVERSAL, 110)
75 if rc != 0 { return rc }
76
77 // 3. just ..
78 rc = check_v(".." as *u8, 2, 1024, NXP_TRAVERSAL, 120)
79 if rc != 0 { return rc }
80
81 // 4. dir/.. (trailing traversal)
82 rc = check_v("dir/.." as *u8, 6, 1024, NXP_TRAVERSAL, 130)
83 if rc != 0 { return rc }
84
85 // 5. dir/../file (middle traversal)
86 rc = check_v("dir/../file" as *u8, 11, 1024, NXP_TRAVERSAL, 140)
87 if rc != 0 { return rc }
88
89 // 6. absolute path (caller's base would be ignored)
90 rc = check_v("/etc/passwd" as *u8, 11, 1024, NXP_TRAVERSAL, 150)
91 if rc != 0 { return rc }
92
93 // 7. NUL byte truncation attack: "ok\0.png"
94 let nul_buf: *u8 = sys_mmap(16)
95 nul_buf[0] = 0x6f as u8 // o
96 nul_buf[1] = 0x6b as u8 // k
97 nul_buf[2] = 0 as u8 // NUL
98 nul_buf[3] = 0x2e as u8 // .
99 nul_buf[4] = 0x70 as u8 // p
100 nul_buf[5] = 0x6e as u8 // n
101 nul_buf[6] = 0x67 as u8 // g
102 rc = check_v(nul_buf, 7, 1024, NXP_NUL, 160)
103 if rc != 0 { return rc }
104
105 // 8. backslash (no Windows-path confusion on POSIX target)
106 rc = check_v("dir\\file" as *u8, 8, 1024, NXP_BACKSLASH, 170)
107 if rc != 0 { return rc }
108
109 // ---- BAD_ARG paths ----
110 let bb: *u8 = sys_mmap(64)
111 let bn: *i64 = sys_mmap(8) as *i64
112 if nx_path_canonicalize(0 as *u8, 10, bb, 64, bn) != NXP_BAD_ARG { return 180 }
113 if nx_path_canonicalize("x" as *u8, 1, 0 as *u8, 64, bn) != NXP_BAD_ARG { return 181 }
114 if nx_path_canonicalize("x" as *u8, 1, bb, 64, 0 as *i64) != NXP_BAD_ARG { return 182 }
115 if nx_path_canonicalize("x" as *u8, -1, bb, 64, bn) != NXP_BAD_ARG { return 183 }
116 if nx_path_canonicalize("x" as *u8, 1, bb, 0, bn) != NXP_BAD_ARG { return 184 }
117
118 // ---- nx_path_join ----
119 let jbuf: *u8 = sys_mmap(64)
120 let jn: *i64 = sys_mmap(8) as *i64
121 let jv: i64 = nx_path_join("/var/www" as *u8, 8,
122 "audit/index.html" as *u8, 16,
123 jbuf, 64, jn)
124 if jv != NXP_OK { return 200 }
125 if jn[0] != 25 { return 201 }
126 if bytes_eq(jbuf, "/var/www/audit/index.html" as *u8, 25) != 1 { return 202 }
127
128 // join TOO_LONG
129 let jv2: i64 = nx_path_join("/var/www" as *u8, 8,
130 "abcdef" as *u8, 6,
131 jbuf, 10, jn)
132 if jv2 != NXP_TOO_LONG { return 210 }
133
134 return 0
135}