Repository navigation
Fix manager privilege escalation - #2493
Merged
Seiger merged 7 commits intoOct 8, 2026
Merged
Conversation
Validate every dot-separated part of an uploaded, renamed, copied or moved file name against the denied extensions list, not just the final extension - a name like shell.php.jpg passed before. Also require that a name claiming a raster image format (jpg, png, gif, webp, bmp, ico, avif, tif, psd) actually decodes as that format. Extend the file manager's ZIP extractor to skip entries whose name would be treated as server-executable (a PHP-like extension anywhere in the name, or .htaccess/.user.ini/etc.), closing the same bypass there. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@eval (PHP eval()) and @select (raw SQL) were reachable from any template variable value or definition field, and from resource content via @DOCUMENT/@inherit, with no gate beyond save_template or save_document. A manager with only those permissions could store a binding that runs when the TV renders. Add TvBindingGuard, which recognizes @EVAL/@select by the same prefix match the parser uses (no word boundary, since the parser has none either - "@EVALreturn 1;" still reaches eval()), looks through any @inherit chain, and also checks the page text fields a binding's [*field*] placeholder can pull in. A manager without save_snippet may not add or change a binding; an existing one some other editor stored is left alone. Apply it in DocumentSaveService (resource save) and save_tmplvars.processor.php (TV definition save). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Page 118 maps to a plain view (ManagerTheme::handle() renders it without a controller permission check), and that view includes mutate_settings.ajax.php, which let any authenticated manager change a system setting or update/delete a plugin - no settings, save_plugin or delete_plugin check. Require settings for setsetting, except keys starting with _hide_ (client-side notice-dismiss flags, harmless and meant to be open to every manager), and require save_plugin or delete_plugin for updateplugin, limited to the disabled column or the _delete_ action. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TemplateVariableValues::forTemplate() joined document_groups on the saved resource and used that join to decide whether a non-admin manager could see a TV restricted by site_tmplvar_access. That checks the document's groups, not the TV's: a manager whose groups overlap the resource's groups could get a TV restricted to some other, unrelated group into the editable set. Join site_tmplvar_access.documentgroup against the manager's own groups instead, dropping the document_groups join entirely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
resolveAtBindFilePath() blocked traversal out of the install and reads from manager/, but not from core/ - a @file or @include binding, or the file_get_contents/readfile modifier, could still reach core/.env, core/config, core/custom and other core files that happen to sit below the web root. Refuse core/ the same way manager/ already was, falling back to doing nothing when it is not actually below the base path (a relocated core). Add Core::atBindFileIsReadable(): even inside the allowed area, @file and the file_get_contents/readfile modifier must not hand out PHP-like source (snippets, plugins, modules - anywhere, not just a fixed extension list), hidden files/folders, assets/cache, assets/backup, or files like .env/.ini/.sql/.log/.bak/.conf/.pem/.key. Apply it to the TV custom widget's @file branch too, which had no extension check of its own. @include is unaffected beyond the core/ block - it exists to run PHP, so it still may. The modifier's error messages now say why a read was refused (denied extension, hidden/generated file, or outside the allowed area) instead of a single generic message, without naming the path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The preview iframe loaded same-origin, with no sandbox, so script in previewed content (which a lower-privileged editor can supply, since HTML in resources is intentionally allowed) could reach window.top, read the manager's CSRF meta tag, and ride a previewing manager's session into a same-origin request. The session cookie is already HttpOnly, so this is request forgery via a stolen token, not cookie theft. Add sandbox="allow-scripts allow-forms allow-popups", deliberately without allow-same-origin: previewed script and form submission still work, but the frame gets an opaque origin and can no longer reach outside itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
main.css stretches .warning to ~100% width whenever it sits as a direct child of td:first-child in these forms (#documentPane, #webUserPane) - meant for a label whose whole text is the warning. The web user form's required-email row only wrapped the "*" in .warning, so that rule stretched the lone asterisk across almost the entire first column and pushed "E-mail address:" out to the right, behind the input field. Wrap the whole label in an outer span so .warning is no longer a direct child of the td and the rule stops matching, same as every other label row in this form. Co-Authored-By: Claude Sonnet 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.
No description provided.