@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 205 lines 4.61 KB
Raw
1 package caddyhttp
2
3 import (
4 "net/url"
5 "path/filepath"
6 "runtime"
7 "testing"
8 )
9
10 func TestSanitizedPathJoin(t *testing.T) {
11 // For reference:
12 // %2e = .
13 // %2f = /
14 // %5c = \
15 for i, tc := range []struct {
16 inputRoot string
17 inputPath string
18 expect string
19 expectWindows string
20 }{
21 {
22 inputPath: "",
23 expect: ".",
24 },
25 {
26 inputPath: "/",
27 expect: ".",
28 },
29 {
30 // fileserver.MatchFile passes an inputPath of "//" for some try_files values.
31 // See https://github.com/caddyserver/caddy/issues/6352
32 inputPath: "//",
33 expect: filepath.FromSlash("./"),
34 },
35 {
36 inputPath: "/foo",
37 expect: "foo",
38 },
39 {
40 inputPath: "/foo/",
41 expect: filepath.FromSlash("foo/"),
42 },
43 {
44 inputPath: "/foo/bar",
45 expect: filepath.FromSlash("foo/bar"),
46 },
47 {
48 inputRoot: "/a",
49 inputPath: "/foo/bar",
50 expect: filepath.FromSlash("/a/foo/bar"),
51 },
52 {
53 inputPath: "/foo/../bar",
54 expect: "bar",
55 },
56 {
57 inputRoot: "/a/b",
58 inputPath: "/foo/../bar",
59 expect: filepath.FromSlash("/a/b/bar"),
60 },
61 {
62 inputRoot: "/a/b",
63 inputPath: "/..%2fbar",
64 expect: filepath.FromSlash("/a/b/bar"),
65 },
66 {
67 inputRoot: "/a/b",
68 inputPath: "/%2e%2e%2fbar",
69 expect: filepath.FromSlash("/a/b/bar"),
70 },
71 {
72 // inputPath fails the IsLocal test so only the root is returned,
73 // but with a trailing slash since one was included in inputPath
74 inputRoot: "/a/b",
75 inputPath: "/%2e%2e%2f%2e%2e%2f",
76 expect: filepath.FromSlash("/a/b/"),
77 },
78 {
79 inputRoot: "/a/b",
80 inputPath: "/foo%2fbar",
81 expect: filepath.FromSlash("/a/b/foo/bar"),
82 },
83 {
84 inputRoot: "/a/b",
85 inputPath: "/foo%252fbar",
86 expect: filepath.FromSlash("/a/b/foo%2fbar"),
87 },
88 {
89 inputRoot: "C:\\www",
90 inputPath: "/foo/bar",
91 expect: filepath.Join("C:\\www", "foo", "bar"),
92 },
93 {
94 inputRoot: "C:\\www",
95 inputPath: "/D:\\foo\\bar",
96 expect: filepath.Join("C:\\www", "D:\\foo\\bar"),
97 expectWindows: "C:\\www", // inputPath fails IsLocal on Windows
98 },
99 {
100 inputRoot: `C:\www`,
101 inputPath: `/..\windows\win.ini`,
102 expect: `C:\www/..\windows\win.ini`,
103 expectWindows: `C:\www`,
104 },
105 {
106 inputRoot: `C:\www`,
107 inputPath: `/..\..\..\..\..\..\..\..\..\..\windows\win.ini`,
108 expect: `C:\www/..\..\..\..\..\..\..\..\..\..\windows\win.ini`,
109 expectWindows: `C:\www`,
110 },
111 {
112 inputRoot: `C:\www`,
113 inputPath: `/..%5cwindows%5cwin.ini`,
114 expect: `C:\www/..\windows\win.ini`,
115 expectWindows: `C:\www`,
116 },
117 {
118 inputRoot: `C:\www`,
119 inputPath: `/..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5cwindows%5cwin.ini`,
120 expect: `C:\www/..\..\..\..\..\..\..\..\..\..\windows\win.ini`,
121 expectWindows: `C:\www`,
122 },
123 {
124 // https://github.com/golang/go/issues/56336#issuecomment-1416214885
125 inputRoot: "root",
126 inputPath: "/a/b/../../c",
127 expect: filepath.FromSlash("root/c"),
128 },
129 } {
130 // we don't *need* to use an actual parsed URL, but it
131 // adds some authenticity to the tests since real-world
132 // values will be coming in from URLs; thus, the test
133 // corpus can contain paths as encoded by clients, which
134 // more closely emulates the actual attack vector
135 u, err := url.Parse("http://test:9999" + tc.inputPath)
136 if err != nil {
137 t.Fatalf("Test %d: invalid URL: %v", i, err)
138 }
139 actual := SanitizedPathJoin(tc.inputRoot, u.Path)
140 if runtime.GOOS == "windows" && tc.expectWindows != "" {
141 tc.expect = tc.expectWindows
142 }
143 if actual != tc.expect {
144 t.Errorf("Test %d: SanitizedPathJoin('%s', '%s') => '%s' (expected '%s')",
145 i, tc.inputRoot, tc.inputPath, actual, tc.expect)
146 }
147 }
148 }
149
150 func TestCleanPath(t *testing.T) {
151 for i, tc := range []struct {
152 input string
153 mergeSlashes bool
154 expect string
155 }{
156 {
157 input: "/foo",
158 expect: "/foo",
159 },
160 {
161 input: "/foo/",
162 expect: "/foo/",
163 },
164 {
165 input: "//foo",
166 expect: "//foo",
167 },
168 {
169 input: "//foo",
170 mergeSlashes: true,
171 expect: "/foo",
172 },
173 {
174 input: "/foo//bar/",
175 mergeSlashes: true,
176 expect: "/foo/bar/",
177 },
178 {
179 input: "/foo/./.././bar",
180 expect: "/bar",
181 },
182 {
183 input: "/foo//./..//./bar",
184 expect: "/foo//bar",
185 },
186 {
187 input: "/foo///./..//./bar",
188 expect: "/foo///bar",
189 },
190 {
191 input: "/foo///./..//.",
192 expect: "/foo//",
193 },
194 {
195 input: "/foo//./bar",
196 expect: "/foo//bar",
197 },
198 } {
199 actual := CleanPath(tc.input, tc.mergeSlashes)
200 if actual != tc.expect {
201 t.Errorf("Test %d [input='%s' mergeSlashes=%t]: Got '%s', expected '%s'",
202 i, tc.input, tc.mergeSlashes, actual, tc.expect)
203 }
204 }
205 }