CVE-2023-45958 (PHP, published 2023-10-18T22:15Z)
Description: value: Thirty Bees Core v1.4.0 was discovered to contain a reflected cross-site scripting (XSS) vulnerability via the backup_pagination parameter at /controller/AdminController.php. This vulnerability allows attackers to execute arbitrary JavaScript in the web browser of a user via a crafted payload.
Severity: nan (CVSS v3 base score: 6.1)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/thirtybees/thirtybees
Files changed:
classes/controller/AdminController.php
classes/helper/HelperList.php
Commit message: Fix XSS vulnerability related to list pagination

Special thanks to Ulaş Deniz İlhan from Zigrin Security for discovering
and reporting this vulnerability.


Vulnerable code:
        $useLimit = true;
        } elseif (empty($limit)) {
            if (isset($this->context->cookie->{$this->list_id.'_pagination'}) && $this->context->cookie->{$this->list_id.'_pagination'}) {
                $limit = (int)$this->context->cookie->{$this->list_id.'_pagination'};
                $limit = (int)$this->_default_pagination;
        $limit = Tools::getIntValue($this->list_id.'_pagination', $limit);
        if (in_array($limit, $this->_pagination) && $limit != $this->_default_pagination) {
            $this->context->cookie->{$this->list_id.'_pagination'} = $limit;
        } else {
            unset($this->context->cookie->{$this->list_id.'_pagination'});
        }
        $pagination = $this->_default_pagination;
        $cookie = $this->context->cookie;
        if (in_array(Tools::getIntValue($this->list_id.'_pagination'), $this->_pagination)) {
            $pagination = Tools::getIntValue($this->list_id.'_pagination');
        } elseif (isset($cookie->{$this->list_id.'_pagination'}) && $cookie->{$this->list_id.'_pagination'}) {
            $pagination = $cookie->{$this->list_id.'_pagination'};
        }

//        $order = '';
//        if (Tools::getIsset($this->table.'Orderby')) {
//            $order = '&'.$this->table.'Orderby='.urlencode($this->orderBy).'&'.$this->table.'Orderway='.urlencode(strtolower($this->orderWay));
//        }

        if (!$page) {


        $this->page = (int) $page;

        /* Choose number of results per page */
        $selectedPagination = Tools::getValue(
            $this->list_id.'_pagination',
            $cookie->{$this->list_id . '_pagination'} ?? $this->_default_pagination
        );
                'selected_pagination' => $selectedPagination,

Fixed code:
        } else {
            $useLimit = true;
            $limit = HelperList::resolvePagination($this->list_id, $this->context->cookie, $this->_pagination, $this->_default_pagination);
            if ($limit !== $this->_default_pagination) {
                $this->context->cookie->{$this->list_id.'_pagination'} = $limit;
                unset($this->context->cookie->{$this->list_id.'_pagination'});

        $pagination = $this->getSelectedPagination();
        if ($page <= 0) {
        $this->page = (int)$page;
        $cookie = $this->context->cookie;
                'selected_pagination' => $this->getSelectedPagination(),

    /**
     * @return int
     */
    protected function getSelectedPagination()
    {
        return static::resolvePagination($this->list_id, $this->context->cookie, $this->_pagination, $this->_default_pagination);
    }

    /**
     * @param string $listId
     * @param Cookie $cookie
     * @param array $pagination
     * @param int $defaultPagination
     *
     * @return int
     */
    public static function resolvePagination(string $listId, Cookie $cookie, array $pagination, int $defaultPagination)
    {
        if ($pagination) {
            $value = static::resolvePaginationValue($listId, $cookie, $defaultPagination);
            if (in_array($value, $pagination)) {
                return $value;
            }
            if (in_array($defaultPagination, $pagination)) {
                return $defaultPagination;
            }
            return $pagination[0];
        } else {
            trigger_error("Pagination not set for list $listId", E_USER_WARNING);
            return 20;
        }
    }

    /**
     * @param string $listId
     * @param Cookie $cookie
     * @param int $defaultPagination
     *
     * @return int
     */
    protected static function resolvePaginationValue(string $listId, Cookie $cookie, int $defaultPagination)
    {
        $paginationKey = $listId.'_pagination';
        $pagination = Tools::getIntValue($paginationKey);
        if ($pagination > 0) {
            return $pagination;
        }
        if (isset($cookie->{$paginationKey})) {
            $pagination = (int)$cookie->{$paginationKey};
            if ($pagination > 0) {
                return $pagination;
            }
        }
        return $defaultPagination;
    }