No Code Attached Yet
avatar CyrusXxXxX
CyrusXxXxX
28 Jul 2026

Environment: Joomla 6.1.2, PHP 8.4.17.

Steps to reproduce

  1. As a guest, request any core list view with an array-typed limit, e.g. index.php?option=com_tags&view=tags&limit[]=5 or index.php?option=com_contact&view=featured&limit[]=5. The page renders normally (HTTP 200).
  2. Inspect the session file. The user state now contains global.list.limit as an array: s:6:"global";...s:4:"list";...s:5:"limit";a:1:{i:0;i:5;}.
  3. Every later reader of that key - in any component, for the rest of the session - receives an array where the API contract promised an unsigned integer.

Expected: getUserStateFromRequest($key, $request, $default, 'uint') either returns an unsigned int or falls back to the default. It should never persist an array under a key filtered as a scalar.

Actual: CMSApplication::getUserStateFromRequest() (libraries/src/Application/CMSApplication.php:771-784) stores whatever $this->input->get($request, null, $type) returned, without checking that the filter actually produced a scalar:

$new_state = $this->input->get($request, null, $type);

if ($new_state === null) {
    return $cur_state;
}

// Save the new value only if it was set in this request.
$this->setUserState($key, $new_state);

The filter cannot produce a scalar here, because Joomla\Filter\InputFilter::clean() branches on is_array($source) (libraries/vendor/joomla/filter/src/InputFilter.php:227-235) and recurses, returning an array, long before cleanInt/cleanUint (lines 728-763) are reached. Confirmed directly against the shipped library on PHP 8.4.17:

$input = new Joomla\Input\Input(['limit' => ['5']]);
$input->getUint('limit', 0);        // array(1) { [0]=> int(1) }
$input->get('limit', 0, 'uint');    // array(1) { [0]=> int(1) }
(new Joomla\Filter\InputFilter())->clean(['5'], 'UINT');  // array(1) { [0]=> int(1) }

Why this matters beyond one component

global.list.limit is not namespaced per component. It is written by com_contact (FeaturedModel.php:169), com_tags (TagsModel.php:142, TagModel.php:224), com_newsfeeds (CategoryModel.php:252) and the B/C branch of ListModel::populateState() (ListModel.php:552), and read by every component that uses the same key. So a request to one component silently changes the type of a value another component will read.

Core itself does not crash, because the value is eventually coerced by DatabaseQuery::setLimit() (libraries/vendor/joomla/database/src/DatabaseQuery.php:1648, $this->limit = (int) $limit;), and (int) ['5'] is 1 in PHP 8.4 with no notice at all. So core silently paginates by one row instead of failing loudly. Third-party components that do arithmetic on the value before it reaches the query builder get a hard TypeError instead, and because the poisoned value lives in the session, every subsequent request from that visitor fails too - a persistent per-session denial of service triggered by a single link. We hit exactly this with a commercial component (reported separately to its vendor).

Note that the same recursion also makes Input::getInt() / getUint() return arrays generally, so the usual "just use getUint()" advice does not protect calling code.

Suggested fix (any one of these, in order of preference)

  1. In InputFilter::clean(), do not recurse for scalar filter types - return the default/zero value for a non-scalar source when the requested type is INT, UINT, FLOAT, BOOL or WORD.
  2. Alternatively, in CMSApplication::getUserStateFromRequest(), only persist $new_state when it is a scalar and the requested $type is a scalar type; otherwise fall back to $cur_state / $default.
  3. At minimum, namespace the list-limit key per component so one component cannot alter another component's stored state.

This is not a data-disclosure issue, but it is a remote, unauthenticated, persistent DoS primitive against any site running a component that trusts the documented return type. A session cookie is sent on top-level navigation under the default SameSite=Lax, so a single shared link is enough to brick a victim's session.

Votes

# of Users Experiencing Issue
1/1
Average Importance Score
4.00

avatar CyrusXxXxX CyrusXxXxX - open - 28 Jul 2026
avatar CyrusXxXxX CyrusXxXxX - change - 28 Jul 2026
Labels Removed: ?
avatar joomla-cms-bot joomla-cms-bot - change - 28 Jul 2026
Labels Added: No Code Attached Yet
avatar joomla-cms-bot joomla-cms-bot - labeled - 28 Jul 2026
avatar CyrusXxXxX CyrusXxXxX - change - 28 Jul 2026
The description was changed
avatar CyrusXxXxX CyrusXxXxX - edited - 28 Jul 2026

Add a Comment

Login with GitHub to post a comment