CVE-2022-4582 (PHP, published 2022-12-17T13:15Z)
Description: value: A vulnerability was found in starter-public-edition-4 up to 4.6.10. It has been classified as problematic. Affected is an unknown function. The manipulation leads to cross site scripting. It is possible to launch the attack remotely. Upgrading to version 4.6.11 is able to address this issue. The name of the patch is 2606983c20f6ea3430ac4b36b3d2e88aafef45da. It is recommended to upgrade the affected component. The identifier of this vulnerability is VDB-216168.
Severity: nan (CVSS v3 base score: 6.1)
Weakness: CWE-707 \u2014 Improper Neutralization
The product does not ensure or incorrectly ensures that structured messages or data are well-formed and that certain security properties are met before being read from an upstream component or sent to a downstream component.
Repository: https://github.com/ivantcholakov/starter-public-edition-4
Files changed:
platform/applications/admin/core/Base_Controller.php
platform/applications/admin/modules/login/controllers/Login_controller.php
platform/applications/admin/modules/theme_switcher_widget/controllers/Theme_switcher_widget_controller.php
platform/applications/front/core/Base_Controller.php
platform/applications/front/modules/theme_switcher_widget/controllers/Theme_switcher_widget_controller.php
platform/common/core/Common.php
platform/common/helpers/MY_form_helper.php
Commit message: Security improvements have been implemented - about html atributes escaping and XSS-cleaning.

Vulnerable code:
        if ($this->input->get('theme') != '' && $this->input->method() == 'get' && !$this->input->is_ajax_request()) {
            $theme = (string) $this->input->get('theme');
            redirect(http_build_url(site_url('login'), array('query' => http_build_query(array('continue' => CURRENT_URL)))));
                if ($this->input->get('continue')) {
                    redirect($this->input->get('continue'));
                $this->session->set_flashdata('confirmation_message', '<nobr>Hello, <strong>'.$username.'</strong>.</nobr>');
            $theme_switcher[$key]['link'] = http_build_url(CURRENT_URL, array('query' => http_build_query(array('theme' => $value['key']))), HTTP_URL_JOIN_QUERY);
        if ($this->input->get('theme') != '' && $this->input->method() == 'get' && !$this->input->is_ajax_request()) {
            $theme = (string) $this->input->get('theme');
            $theme_switcher[$key]['link'] = http_build_url(CURRENT_URL, array('query' => http_build_query(array('theme' => $value['key']))), HTTP_URL_JOIN_QUERY);
            $atts .= ($js) ? $key.'='.$val.',' : ' '.$key.'="'.$val.'"';
        return $attr->getAttributes( ! $return_as_array);
        $form = '<form action="'.$action.'"'.$attributes.">\n";
                $form .= '<input type="hidden" name="'.$name.'" value="'.form_prep($value).'" />'."\n";
            $form .= '<input type="hidden" name="'.$name.'" value="'.form_prep($value)."\" />\n";
                $form .= '<optgroup label="'.$key."\">\n";
                $val = form_prep($val);

            $att .= $key.'="'.$val.'" ';

Fixed code:
        $theme = (string) $this->input->get('theme');

        if ($theme != '') {
            $theme = (string) base64_decode($theme);
        }

        if ($theme != '' && $this->input->method() == 'get' && !$this->input->is_ajax_request()) {
            redirect(http_build_url(site_url('login'), array('query' => http_build_query(array('continue' => base64_encode(CURRENT_URL))))));
                $continue = (string) $this->input->get('continue');

                if ($continue != '') {
                    $continue = (string) base64_decode($continue);
                }

                if ($continue != '' && strpos($continue, site_url()) === 0) {
                    redirect($continue);
                $this->session->set_flashdata('confirmation_message', '<nobr>Hello, <strong>'.html_escape($username).'</strong>.</nobr>');
            $theme_switcher[$key]['link'] = http_build_url(CURRENT_URL, array('query' => http_build_query(array('theme' => base64_encode($value['key'])))), HTTP_URL_JOIN_QUERY);
        $theme = (string) $this->input->get('theme');

        if ($theme != '') {
            $theme = (string) base64_decode($theme);
        }

        if ($theme != '' && $this->input->method() == 'get' && !$this->input->is_ajax_request()) {
            $theme_switcher[$key]['link'] = http_build_url(CURRENT_URL, array('query' => http_build_query(array('theme' => base64_encode($value['key'])))), HTTP_URL_JOIN_QUERY);
            $atts .= ($js) ? $key.'='.$val.',' : ' '.get_instance()->security->xss_clean($key).'="'.html_attr_escape($val).'"';
        if (!$return_as_array) {

            $str = '';
            $attributes = $attr->getAttributes(false);

            foreach ($attributes as $key => $value) {
                $str .= ' ' . get_instance()->security->xss_clean($key) . '="' . html_attr_escape($value) . '"';
            }

            return $str;
        }

        return $attr->getAttributes(false);
        $form = '<form action="'.html_attr_escape(get_instance()->security->xss_clean($action)).'"'.$attributes.">\n";
                $form .= '<input type="hidden" name="'.html_attr_escape($name).'" value="'.form_prep($value).'" />'."\n";
            $form .= '<input type="hidden" name="'.html_attr_escape($name).'" value="'.form_prep($value)."\" />\n";
                $form .= '<optgroup label="'.html_attr_escape($key)."\">\n";
                $att .= get_instance()->security->xss_clean($key).'="'.form_prep($val).'" ';
            else
            {
                $att .= get_instance()->security->xss_clean($key).'="'.html_attr_escape($val).'" ';
            }


// Added by Ivan Tcholakov, 05-SEP-2022.
if ( ! function_exists('_attributes_to_string'))
{
    function _attributes_to_string($attributes)
    {
        if (empty($attributes))
        {
            return '';
        }

        if (is_array($attributes) || is_string($attributes) || is_object($attributes))
        {
            return html_attr($attributes, false);
        }

        return FALSE;
    }
}