fix(files): enforce file groups and permissions in the file manager, file browser and document save - #2480
Merged
Seiger merged 4 commits intoSep 27, 2026
Conversation
… and browser - Root checks used a bare prefix match, so a root at assets/alice also admitted assets/alice-private. FileManagerAccess::isWithin() replaces them. - Classic file manager actions checked the raw request (own/../private) against the listing's restriction map, then acted on the resolved target. They now resolve first and look the target's groups up fresh. - Deleting a folder is refused when it holds entries the manager may not reach; unzip checks the archive itself; the directory zip filters against the whole subtree, not only the current folder's entries. - The embedded browser checked groups only when listing: thumbnail, download, rename, delete, clipboard copy/move/delete, folder delete and all three zip downloads now check the named entry. - file_groups rows are keyed by the site-wide filemanager_path. A manager's own filemanager_path gave every path another key, which nobody had restricted, and switched groups off for them in both tools. - Rows follow renames, moves and deletes whoever performs them; before, only group managers moved them in the file manager and the browser never did, so renaming a parent folder made everything below it public. The LIKE match no longer catches siblings through _ or %. - ls() was never given the manager's groups, hiding entries they may see. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…unusable root - browse.php checked assets_images/assets_files only for type=images and type=files. media, image, file, a missing type and an unknown one (both open the files folder) needed nothing. Every type now maps to a permission; file_manager still opens all of them. - A manager whose image_base_upload_dir could not be resolved (outside the site, or climbing out with ..) was silently given the whole rb_base_dir, with every other manager's folders in it. The browser now refuses, before the uploader creates folders or prunes archives under an empty root. - The root test was a bare prefix match; assets2 was taken for a folder below assets and got the URL assets/2. - The refusal printed an empty page: there is no global $_lang in the browser. It now uses the translator and answers 403. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editor page refuses a document the manager's groups do not reach, but a posted save only checked save_document, and the parent only when it changed. A manager who knew a restricted document's id and parent could overwrite it. The save service now checks the document itself when use_udperms is on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The committed autoloader is classmap-authoritative, and EvolutionCMS\Tracy\ConnectionTiming was missing from it, so TracyServiceProvider failed with "class not found" on a site while tests, with a regenerated vendor/, passed. A test now reads the committed classmap from git and requires every class under core/src in it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes authorization gaps in the classic file manager (
a=31), the embedded file browser (manager/media/browser/mcpuk) and the document save path. The two tools stay independent: the file manager is rooted atfilemanager_path; the browser atrb_base_dir, optionally narrowed per manager byimage_base_upload_dir.File manager and browser: file groups (
use_udperms)strpos($path, $root) === 0, so a root atassets/alicealso admittedassets/alice-private. They now useFileManagerAccess::isWithin().own/../private) against the listing's restriction map, then acted on the resolved target. Actions now resolve first, then look the target's groups up fresh: delete folder, duplicate, rename file and folder, view/edit, the groups page and saving it, and unzip.file_groupsrows are now keyed by the site-widefilemanager_path. Before, a manager's ownfilemanager_pathgave every path a different key, so groups set by an admin were silently ignored for that manager in both tools.LIKEmatch no longer rewrites siblings through_or%.ls()was never given the manager's groups, so it hid entries the manager may see.Embedded browser: types and root
browse.phpchecked onlytype=imagesandtype=files, somedia,image,file, a missing type and an unknown one needed nothing (the last two open the files folder). Image types now needassets_imagesand all othersassets_files;file_managerstill opens everything.image_base_upload_diris refused. A value outside the site, or one using.., used to fall back to the wholerb_base_dir, handing a confined editor everyone's folders. The refusal happens before the uploader touches the disk.assets2was treated as a folder belowassets(URLassets/2).$_langin the browser. It now uses the translator and answers 403.Document save
save_document, and the parent only when it changed. The save service now checks the document itself whenuse_udpermsis on.Classmap
core/vendoris committed and classmap-authoritative, andEvolutionCMS\Tracy\ConnectionTimingwas missing from it. A new test reads the committed classmap from git and requires every class undercore/srcto be in it. The newFileBrowserAccessis added.Settings reference (unchanged behaviour, for review)
filemanager_patha=31, needsfile_manager)[(base_path)]rb_base_dir/rb_base_url[(base_path)]assets//assets/image_base_upload_dirrb_base_diror absolute inside the siteTo give each editor their own part of the images, set
image_base_upload_dirper manager (e.g.alicegivesassets/alice/images).Test plan
FileManagerAccessTest(containment,..resolution, subtree restrictions, ACL keys, row move/forget including_siblings),FileBrowserAccessTest(type permissions, root resolution incl. fail-closed cases),DocumentSaveServiceTest(edit denied on an unreachable document),FileManagerAclSourceTest(every action's check),CommittedClassmapTest. The new tests fail on3.5.x.use_udperms, an HR and an Editors group, and non-admin managers:team/../hr/salaries.txtand../alice-privateare deniedfilemanager_pathno longer sees HR imagesimage_base_upload_dir=/etcanswers 403 and creates nothingfiles,file,media, missing and unknown types🤖 Generated with Claude Code