POST /api/files/folder refuses to create anything directly under VOLUME_ROOT:
Cannot create folders in the root path. Please select a specific volume or folder first.
Rename and delete have no matching guard, so an admin can rename or recursively delete an entire volume through the API. The UI doesn't offer either on a volume (no context menu in the Volumes view or the sidebar), so this is only reachable through a direct API call. The asymmetry looks unintentional.
Reproduced on nxzai/explorer:v3.1.0, signed in as an admin, with one volume /mnt/Files:
| Request |
Body |
Result |
POST /api/files/folder |
{"path":"","name":"Music"} |
400, refused |
POST /api/files/rename |
{"path":"","name":"Files","newName":"Renamed"} |
200, /mnt/Files renamed to /mnt/Renamed |
DELETE /api/files |
{"items":[{"path":"","name":"Files"}]} |
200, /mnt/Files removed recursively; GET /api/volumes returns [] |
Where it happens: routes/files/folder.js checks for an empty parent path, but routes/files/rename.js and resolveDeleteTargets in services/fileTransferService.js don't. Admins get canWrite/canDelete unconditionally in getVolumeAccess, so nothing else stops the request.
Why it matters: a volume is usually a bind mount of real host data, often shared with other applications (media servers, sync tools). One mistaken or scripted request deletes all of it. Renaming it silently breaks every path the other applications and the user-volume assignments hold.
Suggested fix: apply the same empty-parent check to rename and delete. Moves out of the root (transfer.js) probably want it too; I didn't test those.
POST /api/files/folderrefuses to create anything directly underVOLUME_ROOT:Rename and delete have no matching guard, so an admin can rename or recursively delete an entire volume through the API. The UI doesn't offer either on a volume (no context menu in the Volumes view or the sidebar), so this is only reachable through a direct API call. The asymmetry looks unintentional.
Reproduced on
nxzai/explorer:v3.1.0, signed in as an admin, with one volume/mnt/Files:POST /api/files/folder{"path":"","name":"Music"}400, refusedPOST /api/files/rename{"path":"","name":"Files","newName":"Renamed"}200,/mnt/Filesrenamed to/mnt/RenamedDELETE /api/files{"items":[{"path":"","name":"Files"}]}200,/mnt/Filesremoved recursively;GET /api/volumesreturns[]Where it happens:
routes/files/folder.jschecks for an empty parent path, butroutes/files/rename.jsandresolveDeleteTargetsinservices/fileTransferService.jsdon't. Admins getcanWrite/canDeleteunconditionally ingetVolumeAccess, so nothing else stops the request.Why it matters: a volume is usually a bind mount of real host data, often shared with other applications (media servers, sync tools). One mistaken or scripted request deletes all of it. Renaming it silently breaks every path the other applications and the user-volume assignments hold.
Suggested fix: apply the same empty-parent check to rename and delete. Moves out of the root (
transfer.js) probably want it too; I didn't test those.