@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 214 lines 6.98 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package fileserver
16
17 import (
18 "bytes"
19 "context"
20 "errors"
21 "net/http"
22 "net/http/httptest"
23 "os"
24 "path/filepath"
25 "testing"
26
27 "github.com/caddyserver/caddy/v2"
28 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
29 "github.com/caddyserver/caddy/v2/modules/caddyhttp/encode"
30 )
31
32 func TestHiddenPrecompressedSidecar(t *testing.T) {
33 root := t.TempDir()
34 base := []byte("public response body")
35 sidecar := gzipBytes(t, []byte("different hidden body"))
36 if err := os.WriteFile(filepath.Join(root, "public.txt"), base, 0o600); err != nil {
37 t.Fatal(err)
38 }
39 hiddenPath := filepath.Join(root, "public.txt.gz")
40 if err := os.WriteFile(hiddenPath, sidecar, 0o600); err != nil {
41 t.Fatal(err)
42 }
43
44 fsrv := FileServer{
45 Root: root,
46 Hide: []string{hiddenPath},
47 CanonicalURIs: new(bool),
48 PrecompressedOrder: []string{"gzip"},
49 }
50 ctx, _ := caddy.NewContext(caddy.Context{Context: context.Background()})
51 if err := fsrv.Provision(ctx); err != nil {
52 t.Fatal(err)
53 }
54 fsrv.precompressors = map[string]encode.Precompressed{
55 "gzip": testPrecompressed{encoding: "gzip", suffix: ".gz"},
56 }
57
58 w := httptest.NewRecorder()
59 r := newPrecompressedRequest(t, "/public.txt")
60 r.Header.Set("Accept-Encoding", "gzip")
61 if err := fsrv.ServeHTTP(w, r, nil); err != nil {
62 t.Fatal(err)
63 }
64 if got := w.Code; got != http.StatusOK {
65 t.Fatalf("status = %d, want %d", got, http.StatusOK)
66 }
67 if got := w.Header().Get("Content-Encoding"); got != "" {
68 t.Errorf("Content-Encoding = %q, want no encoding for a hidden sidecar", got)
69 }
70 if got := w.Body.Bytes(); !bytes.Equal(got, base) {
71 t.Errorf("body = %x, want base file body %x", got, base)
72 }
73 }
74
75 func TestBrowseHidesPathQualifiedEntries(t *testing.T) {
76 for _, tc := range []struct {
77 name string
78 requestURI string
79 dirName string
80 }{
81 {name: "root", requestURI: "/"},
82 {name: "encoded slash and dot segment", requestURI: "/a%2Fb/../", dirName: "a"},
83 } {
84 t.Run(tc.name, func(t *testing.T) {
85 root := t.TempDir()
86 dir := filepath.Join(root, tc.dirName)
87 if err := os.MkdirAll(dir, 0o700); err != nil {
88 t.Fatal(err)
89 }
90 hiddenPath := filepath.Join(dir, "secret.txt")
91 if err := os.WriteFile(hiddenPath, []byte("hidden body"), 0o600); err != nil {
92 t.Fatal(err)
93 }
94 if err := os.WriteFile(filepath.Join(dir, "public.txt"), []byte("public body"), 0o600); err != nil {
95 t.Fatal(err)
96 }
97
98 fsrv := FileServer{Root: root, Hide: []string{hiddenPath}, Browse: &Browse{}}
99 ctx, _ := caddy.NewContext(caddy.Context{Context: context.Background()})
100 if err := fsrv.Provision(ctx); err != nil {
101 t.Fatal(err)
102 }
103
104 r := httptest.NewRequest(http.MethodGet, tc.requestURI, nil)
105 original := *r
106 reqCtx := context.WithValue(r.Context(), caddy.ReplacerCtxKey, caddy.NewReplacer())
107 reqCtx = context.WithValue(reqCtx, caddyhttp.OriginalRequestCtxKey, original)
108 r = r.WithContext(reqCtx)
109 r.Header.Set("Accept", "application/json")
110 w := httptest.NewRecorder()
111 if err := fsrv.ServeHTTP(w, r, nil); err != nil {
112 t.Fatal(err)
113 }
114 if got := w.Code; got != http.StatusOK {
115 t.Fatalf("status = %d, want %d", got, http.StatusOK)
116 }
117 if bytes.Contains(w.Body.Bytes(), []byte("secret.txt")) {
118 t.Errorf("browse response includes hidden path: %s", w.Body.String())
119 }
120 if !bytes.Contains(w.Body.Bytes(), []byte("public.txt")) {
121 t.Errorf("browse response omits public path: %s", w.Body.String())
122 }
123 })
124 }
125 }
126
127 func TestHideRelativeDataDir(t *testing.T) {
128 for _, env := range []string{"XDG_DATA_HOME", "HOME", "AppData", "HOMEDRIVE", "HOMEPATH", "USERPROFILE", "home"} {
129 t.Setenv(env, "")
130 }
131
132 fsrv := FileServer{Root: "."}
133 ctx, _ := caddy.NewContext(caddy.Context{Context: context.Background()})
134 if err := fsrv.Provision(ctx); err != nil {
135 t.Fatal(err)
136 }
137
138 key := filepath.Join("caddy", "certificates", "local", "localhost", "localhost.key")
139 if !fileHidden(key, fsrv.Hide) {
140 t.Errorf("%s is not hidden; hide list: %v", key, fsrv.Hide)
141 }
142 if fileHidden("caddyfile.go", fsrv.Hide) {
143 t.Errorf("caddyfile.go is hidden; hide list: %v", fsrv.Hide)
144 }
145 }
146
147 func TestHideOtherLetterCases(t *testing.T) {
148 root := t.TempDir()
149 if err := os.WriteFile(filepath.Join(root, "Caddyfile"), []byte("hidden config"), 0o600); err != nil {
150 t.Fatal(err)
151 }
152 if err := os.Mkdir(filepath.Join(root, "secretdir"), 0o700); err != nil {
153 t.Fatal(err)
154 }
155 if err := os.WriteFile(filepath.Join(root, "secretdir", "p.txt"), []byte("hidden body"), 0o600); err != nil {
156 t.Fatal(err)
157 }
158
159 fsrv := FileServer{
160 Root: root,
161 Hide: []string{filepath.Join(root, "Caddyfile"), "secretdir"},
162 CanonicalURIs: new(bool),
163 }
164 ctx, _ := caddy.NewContext(caddy.Context{Context: context.Background()})
165 if err := fsrv.Provision(ctx); err != nil {
166 t.Fatal(err)
167 }
168
169 for _, uri := range []string{"/Caddyfile", "/caddyfile", "/CADDYFILE", "/secretdir/p.txt", "/SecretDir/p.txt"} {
170 t.Run(uri, func(t *testing.T) {
171 r := httptest.NewRequest(http.MethodGet, uri, nil)
172 original := *r
173 reqCtx := context.WithValue(r.Context(), caddy.ReplacerCtxKey, caddy.NewReplacer())
174 reqCtx = context.WithValue(reqCtx, caddyhttp.OriginalRequestCtxKey, original)
175 r = r.WithContext(reqCtx)
176 w := httptest.NewRecorder()
177 err := fsrv.ServeHTTP(w, r, nil)
178 var handlerErr caddyhttp.HandlerError
179 if !errors.As(err, &handlerErr) || handlerErr.StatusCode != http.StatusNotFound {
180 t.Errorf("got error %v and status %d, want 404", err, w.Code)
181 }
182 })
183 }
184 }
185
186 func TestHideRelativeConfigDir(t *testing.T) {
187 for _, env := range []string{"XDG_CONFIG_HOME", "HOME", "AppData", "HOMEDRIVE", "HOMEPATH", "USERPROFILE", "home"} {
188 t.Setenv(env, "")
189 }
190 // an absolute data directory must not mask a relative config directory:
191 // autosave.json holds the running configuration
192 t.Setenv("XDG_DATA_HOME", t.TempDir())
193
194 if dir := caddy.AppConfigDir(); filepath.IsAbs(dir) {
195 t.Fatalf("test setup: config directory is absolute: %s", dir)
196 }
197 if dir := caddy.AppDataDir(); !filepath.IsAbs(dir) {
198 t.Fatalf("test setup: data directory is relative: %s", dir)
199 }
200
201 fsrv := FileServer{Root: "."}
202 ctx, _ := caddy.NewContext(caddy.Context{Context: context.Background()})
203 if err := fsrv.Provision(ctx); err != nil {
204 t.Fatal(err)
205 }
206
207 autosave := filepath.Join("caddy", "autosave.json")
208 if !fileHidden(autosave, fsrv.Hide) {
209 t.Errorf("%s is not hidden; hide list: %v", autosave, fsrv.Hide)
210 }
211 if fileHidden("caddyfile.go", fsrv.Hide) {
212 t.Errorf("caddyfile.go is hidden; hide list: %v", fsrv.Hide)
213 }
214 }