new changes
This commit is contained in:
@@ -13,28 +13,46 @@ router = APIRouter(prefix="/files", tags=["files"])
|
||||
|
||||
|
||||
def _resolve_path(path: str) -> str:
|
||||
"""Resolve and validate path within allowed roots (cross-platform)"""
|
||||
cfg = get_runtime_config()
|
||||
www_root = os.path.abspath(cfg["www_root"])
|
||||
setup_path = os.path.abspath(cfg["setup_path"])
|
||||
allowed = [www_root, setup_path]
|
||||
if os.name != "nt":
|
||||
allowed.append(os.path.abspath("/www"))
|
||||
"""
|
||||
Resolve API path to an OS path.
|
||||
|
||||
On Linux/macOS: path is an absolute POSIX path from filesystem root (/) so admins
|
||||
can browse the whole server (same expectation as BT/aaPanel-style panels).
|
||||
|
||||
On Windows (dev): paths stay sandboxed under www_root / setup_path.
|
||||
"""
|
||||
if ".." in path:
|
||||
raise HTTPException(status_code=400, detail="Path traversal not allowed")
|
||||
norm_path = path.strip().replace("\\", "/").strip("/")
|
||||
# Root or www_root-style path
|
||||
if not norm_path or norm_path in ("www", "www/wwwroot", "wwwroot"):
|
||||
full = www_root
|
||||
elif norm_path.startswith("www/wwwroot/"):
|
||||
full = os.path.abspath(os.path.join(www_root, norm_path[12:]))
|
||||
else:
|
||||
full = os.path.abspath(os.path.join(www_root, norm_path))
|
||||
if not any(
|
||||
full == r or (full + os.sep).startswith(r + os.sep)
|
||||
for r in allowed
|
||||
):
|
||||
raise HTTPException(status_code=403, detail="Path not allowed")
|
||||
|
||||
raw = path.strip().replace("\\", "/")
|
||||
|
||||
if os.name == "nt":
|
||||
cfg = get_runtime_config()
|
||||
www_root = os.path.abspath(cfg["www_root"])
|
||||
setup_path = os.path.abspath(cfg["setup_path"])
|
||||
allowed = [www_root, setup_path]
|
||||
norm_path = raw.strip("/")
|
||||
if not norm_path or norm_path in ("www", "www/wwwroot", "wwwroot"):
|
||||
full = www_root
|
||||
elif norm_path.startswith("www/wwwroot/"):
|
||||
full = os.path.abspath(os.path.join(www_root, norm_path[12:]))
|
||||
else:
|
||||
full = os.path.abspath(os.path.join(www_root, norm_path))
|
||||
if not any(
|
||||
full == r or (full + os.sep).startswith(r + os.sep)
|
||||
for r in allowed
|
||||
):
|
||||
raise HTTPException(status_code=403, detail="Path not allowed")
|
||||
return full
|
||||
|
||||
# POSIX: absolute paths from /
|
||||
if not raw or raw == "/":
|
||||
return "/"
|
||||
if not raw.startswith("/"):
|
||||
raw = "/" + raw
|
||||
full = os.path.normpath(raw)
|
||||
if not full.startswith("/"):
|
||||
raise HTTPException(status_code=400, detail="Invalid path")
|
||||
return full
|
||||
|
||||
|
||||
@@ -51,7 +69,15 @@ async def files_list(
|
||||
if not os.path.isdir(full):
|
||||
raise HTTPException(status_code=404, detail="Not a directory")
|
||||
items = []
|
||||
for name in os.listdir(full):
|
||||
try:
|
||||
names = os.listdir(full)
|
||||
except PermissionError:
|
||||
raise HTTPException(status_code=403, detail="Permission denied")
|
||||
except FileNotFoundError:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
except OSError as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
for name in names:
|
||||
item_path = os.path.join(full, name)
|
||||
try:
|
||||
stat = os.stat(item_path)
|
||||
@@ -62,7 +88,8 @@ async def files_list(
|
||||
})
|
||||
except OSError:
|
||||
pass
|
||||
return {"path": path, "items": items}
|
||||
display_path = full if full == "/" else full.rstrip("/")
|
||||
return {"path": display_path, "items": items}
|
||||
|
||||
|
||||
@router.get("/read")
|
||||
|
||||
Reference in New Issue
Block a user