Environment:
Joomla 6.1.3, PHP 8.4.17, Apache 2.4 (mod_fcgid), MySQL 8.4.7. Reproduced on a clean request, no authentication, no third-party code in the path.
Summary
CMSApplication::sanityCheckSystemVariables() correctly rejects a request when a reserved system variable arrives as an array. That part works as designed and has since 3.9. The problem is what happens next: the exception it throws cannot be rendered, because rendering an error page requires an admin-only class whose namespace has not been registered yet. The controlled RuntimeException is therefore replaced by an uncaught ClassNotFoundError and the visitor gets a bare HTTP 500.
Steps to reproduce
On any Joomla 6.1.3 site, as an anonymous visitor:
curl -i "https://example.org/?task[]=x"
Result: HTTP/1.1 500. The same happens for each of the eight reserved names, on the site, on /administrator/ and on /api/, over GET and over POST:
?option[]=x 500 ?Itemid[]=x 500
?view[]=x 500 ?template[]=x 500
?format[]=x 500 ?templateStyle[]=x 500
?lang[]=x 500 ?task[]=x 500
Control: ?limit[]=5 returns 200, and a clean request immediately after any of the above returns 200 (the failure is per-request, it does not poison later requests).
With debug enabled the response body contains the full exception chain and absolute file paths.
Why it happens
libraries/src/Application/CMSApplication.php:267-288 - sanityCheckSystemVariables() collects any of option, view, format, lang, Itemid, template, templateStyle, task that arrive as arrays and throws new \RuntimeException('Invalid input, aborting application.'). This is intentional (@since 3.9, from PR #28751).
It is called from execute() at :300 - the first statement in the try block, before createExtensionNamespaceMap() at :302 and before any plugin is imported at :305/:308.
The catch block at :338-351 hands the exception to ExceptionHandler::handleException() -> render() (libraries/src/Exception/ExceptionHandler.php:88-200), which calls HtmlRenderer::render() at :142.
libraries/src/Error/Renderer/HtmlRenderer.php:50 needs the active template, so it calls $app->getTemplate(true) -> SiteApplication::initialiseTemplate() -> getTemplates() -> bootComponent('templates') (ExtensionManagerTrait.php:42-52,113-187) -> administrator/components/com_templates/services/provider.php:46, new TemplatesComponent(...).
That class cannot be autoloaded. The PSR-4 entry mapping Joomla\Component\Templates\Administrator\ to administrator/components/com_templates/src is created by createExtensionNamespaceMap() - step 2, which never ran. The result is a Symfony\Component\ErrorHandler\Error\ClassNotFoundError.
That second throwable escapes the try/catch inside ExceptionHandler::render() at :167; the code at :176-196 merges the two and rethrows. Nothing inside execute() can catch it now, so it travels through includes/app.php:58 and index.php:51 to the global Symfony handler registered in libraries/bootstrap.php:49, which emits HTTP 500.
com_templates has nothing to do with the original request - it is simply the first administrator-only class any themed error page must load, so it is where the missing namespace map surfaces.
Impact
Any anonymous visitor can make every page of a Joomla site return 500 with a single query parameter. There is no persistent damage and no privilege gain: the request is refused as intended, and the next request succeeds. The concrete harm is that a deliberate, well-defined refusal is presented as a server fault - it fills error logs, it can trip uptime monitoring, and on a site left in debug mode it discloses full filesystem paths.
Suggested direction
The general problem is that HtmlRenderer assumes a bootable extension namespace map, while sanityCheckSystemVariables() (and potentially anything else failing that early) runs before one exists. Two options that seem open to you:
createExtensionNamespaceMap() above sanityCheckSystemVariables() in execute(), so the error renderer always has what it needs.HtmlRenderer (or ExceptionHandler::render()) fall back to the plain-text/minimal renderer when the template cannot be resolved, instead of letting a second throwable replace the first.The second seems more broadly useful, since the same class of failure has been reported for other early errors - see issue #39653, where a database failure produces the same "error renderer cannot render" outcome through a different trigger.
Related
id[] in PreprocessRules.php:106). Worth noting we also observe ?id[]=5 returning 500 on some routes on 6.1.3 while /some-page?id[]=5 returns 200, which looks like that separate issue rather than this one.Thank you!
6.1
No response
No response
Joomla 6.1.3, PHP 8.4.17, Apache 2.4 (mod_fcgid), MySQL 8.4.7. Reproduced on a clean request, no authentication, no third-party code in the path.
No response
| Labels |
Added:
No Code Attached Yet
bug
|
||
Thanks for reaport.
As you wrote:
There is no persistent damage and no privilege gain: the request is refused as intended,
And about:
The concrete harm is that a deliberate, well-defined refusal is presented as a server fault - it fills error logs, it can trip uptime monitoring, and on a site left in debug mode it discloses full filesystem paths.
This is configuration mistake, which we cannot cover any possible misconfiguration.
I suggest to close the issue.
Thanks for reaport.
As you wrote:
About:
This is configuration mistake, which we cannot cover any possible misconfiguration.
I suggest to close the issue.