Feature Unit/System Tests PR-6.2-dev Pending

User tests: Successful: Unsuccessful:

avatar voronkovich
voronkovich
10 May 2026
  • I read the Generative AI policy and my contribution is either not created with the help of AI or is compatible with the policy and GNU/GPL 2 or later.

This PR addresses a potential XSS vulnerability by standardizing and securing the generation of HTML attributes within Joomla. Previously, the ArrayHelper::toString() method was extensively used to convert arrays of attributes into HTML strings. While core code often used this carefully, the lack of automatic HTML escaping within ArrayHelper::toString() presented a "footgun" API, making it easy for developers to accidentally introduce XSS vulnerabilities if user-supplied data was passed un-sanitized.

This PR introduces a dedicated, secure method for building HTML attributes, significantly enhancing the platform's security posture.

Summary of Changes

  1. Introduced HTMLHelper::buildAttributes()

    A new public static method buildAttributes() has been added to libraries/src/HTML/HTMLHelper.php. This method responsibly converts an array of attributes into a properly escaped HTML attribute string using htmlspecialchars(value, ENT_QUOTES, 'UTF-8').

  2. Migrated HTMLHelper::link() and HTMLHelper::iframe()

    The core HTMLHelper::link() and HTMLHelper::iframe() methods were updated to utilize HTMLHelper::buildAttributes() for their attribute generation, replacing their direct use of ArrayHelper::toString().

  3. Comprehensive Migration Across the Codebase

    All other identified instances of ArrayHelper::toString() that were responsible for generating HTML attributes have been replaced with HTMLHelper::buildAttributes().

Testing Instructions

Unit tests are provided.

Actual result BEFORE applying this Pull Request

All should work fine.

Expected result AFTER applying this Pull Request

All should work fine.

Link to documentations

Please select:

  • Documentation link for guide.joomla.org:

  • No documentation changes for guide.joomla.org needed

  • Pull Request link for manual.joomla.org:

  • No documentation changes for manual.joomla.org needed

avatar voronkovich voronkovich - open - 10 May 2026
avatar voronkovich voronkovich - change - 10 May 2026
Status New Pending
avatar joomla-cms-bot joomla-cms-bot - change - 10 May 2026
Category Front End com_users Layout Libraries Templates (site) Unit Tests
avatar voronkovich voronkovich - change - 10 May 2026
Labels Added: Unit/System Tests PR-6.2-dev
avatar richard67 richard67 - change - 10 May 2026
Labels Added: Feature
avatar github-actions[bot]
github-actions[bot] - comment - 7 Jul 2026

This pull request has conflicts, please resolve those before we can evaluate the pull request.

avatar voronkovich voronkovich - change - 20 Jul 2026
Labels Added: Conflicting Files
avatar Fedik
Fedik - comment - 20 Jul 2026

I would suggest to place the new code in to separated Helper under HTML/Helpers or inside one of existing.
Also it shoud be invoced like HTMLHelper::_('foobar') instead of direct call.

avatar Fedik
Fedik - comment - 20 Jul 2026

I would suggest to place the new code in to separated Helper under HTML/Helpers or inside one of existing.
Also it shoud be invoked like HTMLHelper::_('foobar') instead of direct call.

avatar voronkovich voronkovich - change - 20 Jul 2026
Labels Removed: Conflicting Files
avatar voronkovich
voronkovich - comment - 20 Jul 2026

@Fedik, I've implemented your suggestion. Now HTMLHelper::_('attribute.buildAttributes') is used instead of direct call.

avatar voronkovich
voronkovich - comment - 20 Jul 2026

After careful consideration, I've renamed the Attribute helper to HTML. The new HTML helper will handle methods related to HTML, such as tag generation and attribute rendering. I've also renamed buildAttributes to attributes, so the new usage is:

HTMLHelper::_('html.attributes', [...])
avatar QuyTon
QuyTon - comment - 25 Jul 2026

Edit an article.
Click Versions button.
Select a version and click Preview button.

Before PR:

<button class="button-preview btn btn-primary" data-url="/Joomla_6.2.0-alpha4-dev-Development-Full_Package/administrator/index.php?option=com_contenthistory&amp;view=preview&amp;layout=preview&amp;tmpl=component&amp;32b99b8a9aa11a1a3f3f34603c376f67=1" type="button">
    <span class="icon-search" aria-hidden="true"></span>
    Preview</button>

After PR:

<button class="button-preview btn btn-primary" data-url="/Joomla_6.2.0-alpha4-dev+pr.47754-Development-Full_Package/administrator/index.php?option=com_contenthistory&amp;amp;view=preview&amp;amp;layout=preview&amp;amp;tmpl=component&amp;amp;6d97440605421c5b4567d4a117f90a48=1" type="button">
    <span class="icon-search" aria-hidden="true"></span>
    Preview</button>
An error has occurred.
404 View not found [name, type, prefix]: joomla\component\contenthistory\administrator\controller\display, html, Administrator
Call Stack
#	Function	Location
1	()	JROOT\libraries\src\MVC\Controller\BaseController.php:884
2	Joomla\CMS\MVC\Controller\BaseController->getView()	JROOT\libraries\src\MVC\Controller\BaseController.php:662
3	Joomla\CMS\MVC\Controller\BaseController->display()	JROOT\libraries\src\MVC\Controller\BaseController.php:738
4	Joomla\CMS\MVC\Controller\BaseController->execute()	JROOT\libraries\src\Dispatcher\ComponentDispatcher.php:143
5	Joomla\CMS\Dispatcher\ComponentDispatcher->dispatch()	JROOT\libraries\src\Component\ComponentHelper.php:361
6	Joomla\CMS\Component\ComponentHelper::renderComponent()	JROOT\libraries\src\Application\AdministratorApplication.php:150
7	Joomla\CMS\Application\AdministratorApplication->dispatch()	JROOT\libraries\src\Application\AdministratorApplication.php:206
8	Joomla\CMS\Application\AdministratorApplication->doExecute()	JROOT\libraries\src\Application\CMSApplication.php:320
9	Joomla\CMS\Application\CMSApplication->execute()	JROOT\administrator\includes\app.php:58
10	require_once()	JROOT\administrator\index.php:32
avatar QuyTon
QuyTon - comment - 25 Jul 2026

Edit an article.
Click Versions button.
Select a version.
Click Preview button.

Before PR:

<button class="button-preview btn btn-primary" data-url="/Joomla_6.2.0-alpha4-dev-Development-Full_Package/administrator/index.php?option=com_contenthistory&amp;view=preview&amp;layout=preview&amp;tmpl=component&amp;32b99b8a9aa11a1a3f3f34603c376f67=1" type="button">
    <span class="icon-search" aria-hidden="true"></span>
    Preview</button>

After PR:

<button class="button-preview btn btn-primary" data-url="/Joomla_6.2.0-alpha4-dev+pr.47754-Development-Full_Package/administrator/index.php?option=com_contenthistory&amp;amp;view=preview&amp;amp;layout=preview&amp;amp;tmpl=component&amp;amp;6d97440605421c5b4567d4a117f90a48=1" type="button">
    <span class="icon-search" aria-hidden="true"></span>
    Preview</button>
An error has occurred.
404 View not found [name, type, prefix]: joomla\component\contenthistory\administrator\controller\display, html, Administrator
Call Stack
#	Function	Location
1	()	JROOT\libraries\src\MVC\Controller\BaseController.php:884
2	Joomla\CMS\MVC\Controller\BaseController->getView()	JROOT\libraries\src\MVC\Controller\BaseController.php:662
3	Joomla\CMS\MVC\Controller\BaseController->display()	JROOT\libraries\src\MVC\Controller\BaseController.php:738
4	Joomla\CMS\MVC\Controller\BaseController->execute()	JROOT\libraries\src\Dispatcher\ComponentDispatcher.php:143
5	Joomla\CMS\Dispatcher\ComponentDispatcher->dispatch()	JROOT\libraries\src\Component\ComponentHelper.php:361
6	Joomla\CMS\Component\ComponentHelper::renderComponent()	JROOT\libraries\src\Application\AdministratorApplication.php:150
7	Joomla\CMS\Application\AdministratorApplication->dispatch()	JROOT\libraries\src\Application\AdministratorApplication.php:206
8	Joomla\CMS\Application\AdministratorApplication->doExecute()	JROOT\libraries\src\Application\CMSApplication.php:320
9	Joomla\CMS\Application\CMSApplication->execute()	JROOT\administrator\includes\app.php:58
10	require_once()	JROOT\administrator\index.php:32
avatar voronkovich
voronkovich - comment - 25 Jul 2026

Thanks for the heads-up, @QuyTon ! Without your help, I wouldn’t have realized the issue was due to double escaping. I’ve updated the htmlspecialchars call to prevent that. Now all tests are passing.

avatar voronkovich
voronkovich - comment - 25 Jul 2026

Thanks for the heads-up, @QuyTon ! Without your help, I wouldn’t have realized the issue was due to double encoding. I’ve updated the htmlspecialchars call to prevent that. Now all tests are passing.

Add a Comment

Login with GitHub to post a comment