| 267 | } |
| 268 | |
| 269 | func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { |
| 270 | repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) |
| 271 | |
| 272 | if runtime.GOOS == "windows" { |
| 273 | // reject paths with Alternate Data Streams (ADS) |
| 274 | if strings.Contains(r.URL.Path, ":") { |
| 275 | return caddyhttp.Error(http.StatusBadRequest, fmt.Errorf("illegal ADS path")) |
| 276 | } |
| 277 | // reject paths with "8.3" short names |
| 278 | trimmedPath := strings.TrimRight(r.URL.Path, ". ") // Windows ignores trailing dots and spaces, sigh |
| 279 | if len(path.Base(trimmedPath)) <= 12 && strings.Contains(trimmedPath, "~") { |
| 280 | return caddyhttp.Error(http.StatusBadRequest, fmt.Errorf("illegal short name")) |
| 281 | } |
| 282 | // both of those could bypass file hiding or possibly leak information even if the file is not hidden |
| 283 | } |
| 284 | |
| 285 | filesToHide := fsrv.transformHidePaths(repl) |
| 286 | |
| 287 | root := repl.ReplaceAll(fsrv.Root, ".") |
| 288 | fsName := repl.ReplaceAll(fsrv.FileSystem, "") |
| 289 | |
| 290 | fileSystem, ok := fsrv.fsmap.Get(fsName) |
| 291 | if !ok { |
| 292 | return caddyhttp.Error(http.StatusNotFound, fmt.Errorf("filesystem not found")) |
| 293 | } |
| 294 | |
| 295 | // remove any trailing `/` as it breaks fs.ValidPath() in the stdlib |
| 296 | filename := strings.TrimSuffix(caddyhttp.SanitizedPathJoin(root, r.URL.Path), "/") |
| 297 | |
| 298 | if c := fsrv.logger.Check(zapcore.DebugLevel, "sanitized path join"); c != nil { |
| 299 | c.Write( |
| 300 | zap.String("site_root", root), |
| 301 | zap.String("fs", fsName), |
| 302 | zap.String("request_path", r.URL.Path), |
| 303 | zap.String("result", filename), |
| 304 | ) |
| 305 | } |
| 306 | |
| 307 | // get information about the file |
| 308 | info, err := fs.Stat(fileSystem, filename) |
| 309 | if err != nil { |
| 310 | err = fsrv.mapDirOpenError(fileSystem, err, filename) |
| 311 | if errors.Is(err, fs.ErrNotExist) { |
| 312 | return fsrv.notFound(w, r, next) |
| 313 | } else if errors.Is(err, fs.ErrInvalid) { |
| 314 | return caddyhttp.Error(http.StatusBadRequest, err) |
| 315 | } else if errors.Is(err, fs.ErrPermission) { |
| 316 | return caddyhttp.Error(http.StatusForbidden, err) |
| 317 | } |
| 318 | return caddyhttp.Error(http.StatusInternalServerError, err) |
| 319 | } |
| 320 | |
| 321 | // if the request mapped to a directory, see if |
| 322 | // there is an index file we can serve |
| 323 | var implicitIndexFile bool |
| 324 | if info.IsDir() && len(fsrv.IndexNames) > 0 { |
| 325 | for _, indexPage := range fsrv.IndexNames { |
| 326 | indexPage := repl.ReplaceAll(indexPage, "") |