aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/api_files.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/admin/api_files.go')
-rw-r--r--internal/admin/api_files.go22
1 files changed, 16 insertions, 6 deletions
diff --git a/internal/admin/api_files.go b/internal/admin/api_files.go
index b9f54c5..a19cb3b 100644
--- a/internal/admin/api_files.go
+++ b/internal/admin/api_files.go
@@ -23,6 +23,20 @@ func resolveDataPath(dataDir, relPath string) (string, error) {
return filepath.Join(dataDir, clean), nil
}
+// canonicalDataRelPath normalizes a client-supplied data-relative path so that
+// repeated leading "/" or "data/" prefixes cannot make the security
+// classifiers (isPlayerDataFilePath / isAccountFilePath) disagree with the
+// path resolveDataPath actually reads. Without this, "//players/..." resolved
+// to the real player file while the classifiers saw a non-player path.
+func canonicalDataRelPath(relPath string) string {
+ clean := filepath.ToSlash(filepath.Clean("/" + strings.TrimPrefix(relPath, "/")))
+ clean = strings.TrimPrefix(clean, "/")
+ for strings.HasPrefix(clean, "data/") {
+ clean = strings.TrimPrefix(clean, "data/")
+ }
+ return clean
+}
+
// evictDeletedRoomFile drops cached room state and any cached in-room games
// when the raw file manager deletes a room YAML (rooms/<dir>/<id>.yaml).
// Best-effort: only numeric .yaml stems under rooms/ count as room files.
@@ -75,8 +89,7 @@ func (s *AdminServer) handleFileRename(w http.ResponseWriter, r *http.Request) {
}
}
- relPath := strings.TrimPrefix(body.Path, "/")
- relPath = strings.TrimPrefix(relPath, "data/")
+ relPath := canonicalDataRelPath(body.Path)
oldAbs, err := resolveDataPath(s.dataDir, relPath)
if err != nil {
failJSON(w, err.Error())
@@ -145,9 +158,7 @@ func (s *AdminServer) handleFileRename(w http.ResponseWriter, r *http.Request) {
// is currently online is refused: overwriting it would clobber the in-memory
// player the game will later save.
func (s *AdminServer) handleFiles(w http.ResponseWriter, r *http.Request) {
- relPath := r.URL.Query().Get("path")
- relPath = strings.TrimPrefix(relPath, "/")
- relPath = strings.TrimPrefix(relPath, "data/")
+ relPath := canonicalDataRelPath(r.URL.Query().Get("path"))
fullPath, pathErr := resolveDataPath(s.dataDir, relPath)
if pathErr != nil {
failJSON(w, pathErr.Error())
@@ -173,7 +184,6 @@ func (s *AdminServer) handleFiles(w http.ResponseWriter, r *http.Request) {
return
}
- fullPath := filepath.Join(s.dataDir, relPath)
info, err := os.Stat(fullPath)
if err != nil {
failJSON(w, "not found: "+relPath)