mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-10 19:27:41 +08:00
fix webui static root path guard
This commit is contained in:
@@ -100,7 +100,7 @@ func (h *Handler) serveFromDisk(w http.ResponseWriter, r *http.Request, staticDi
|
||||
path = strings.TrimPrefix(path, "/")
|
||||
if path != "" && strings.Contains(path, ".") {
|
||||
full := filepath.Join(root, filepath.Clean(path))
|
||||
if full != root && !strings.HasPrefix(full, root+string(os.PathSeparator)) {
|
||||
if !isPathInsideRoot(full, root) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
@@ -127,6 +127,20 @@ func (h *Handler) serveFromDisk(w http.ResponseWriter, r *http.Request, staticDi
|
||||
http.ServeFile(w, r, index)
|
||||
}
|
||||
|
||||
func isPathInsideRoot(path, root string) bool {
|
||||
cleanPath := filepath.Clean(path)
|
||||
cleanRoot := filepath.Clean(root)
|
||||
if cleanPath == cleanRoot {
|
||||
return true
|
||||
}
|
||||
volume := filepath.VolumeName(cleanRoot)
|
||||
rootWithoutVolume := cleanRoot[len(volume):]
|
||||
if rootWithoutVolume == string(os.PathSeparator) {
|
||||
return strings.HasPrefix(cleanPath, cleanRoot)
|
||||
}
|
||||
return strings.HasPrefix(cleanPath, cleanRoot+string(os.PathSeparator))
|
||||
}
|
||||
|
||||
func resolveStaticAdminDir(preferred string) string {
|
||||
if strings.TrimSpace(os.Getenv("DS2API_STATIC_ADMIN_DIR")) != "" {
|
||||
return filepath.Clean(preferred)
|
||||
|
||||
@@ -105,6 +105,25 @@ func TestServeFromDiskRejectsSiblingDirectoryWithSharedPrefix(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPathInsideRootAllowsFilesystemRootChildren(t *testing.T) {
|
||||
root := filepath.VolumeName(os.TempDir()) + string(os.PathSeparator)
|
||||
child := filepath.Join(root, "assets", "index.css")
|
||||
|
||||
if !isPathInsideRoot(child, root) {
|
||||
t.Fatalf("expected filesystem-root child %q inside %q", child, root)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPathInsideRootRejectsSharedPrefixSibling(t *testing.T) {
|
||||
parent := t.TempDir()
|
||||
root := filepath.Join(parent, "admin")
|
||||
sibling := filepath.Join(parent, "admin-leak", "secret.txt")
|
||||
|
||||
if isPathInsideRoot(sibling, root) {
|
||||
t.Fatalf("expected shared-prefix sibling %q outside %q", sibling, root)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSetStaticContentTypeUnknownExtensionFallsThrough verifies that unknown
|
||||
// extensions leave the Content-Type header unset, so http.ServeFile can apply
|
||||
// its own detection (sniffing or mime.TypeByExtension) for cases the pinned
|
||||
|
||||
Reference in New Issue
Block a user