CVE-2023-4928 (PHP, published 2023-09-13T01:15Z)
Description: value:  SQL Injection in GitHub repository instantsoft/icms2 prior to 2.16.1.
Severity: nan (CVSS v3 base score: 7.2)
Weakness: CWE-89 \u2014 Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
Repository: https://github.com/instantsoft/icms2
Files changed:
system/config/version.ini
system/controllers/admin/frontend.php
system/controllers/moderation/backend/actions/logs.php
system/core/grid.php
system/core/model.php
system/traits/controllers/actions/listgrid.php
update/current/manifest.en.ini
update/current/manifest.ru.ini
Commit message: Fix SQL Injection in datagrid advanced filter

Vulnerable code:
date = 20230613
            $ctrl_file = $this->cms_config->root_path . 'system/controllers/'.$controller_name.'/frontend.php';
            if(!is_readable($ctrl_file)){ continue; }
            if (!$hooks) { continue; }
                if(empty($hook_object->disallow_event_db_register)){
    public function run($target_controller = null, $target_subject = null, $target_id = null, $moderator_id = null){
        $url           = href_to($this->root_url, 'logs');
        $sub_url       = array();
        $url_query     = array();
        $additional_h1 = array();
        $action = $this->request->get('action', -1);
        if($action > -1){
            if($only_to_delete){

            $additional_h1[] = string_lang('LANG_MODERATION_ACTION_'.$action);

        if(!empty($target_controller)){
            if(!empty($target_subject)){
                if($ctype){
                if($ctype){
                if(!empty($target_id)){
                        $item = $this->model->getItem('moderators_logs', function ($item, $model){
                            $item['data'] = cmsModel::yamlToArray($item['data']);
                            return $item;
                        });
                        if($item){
                            $additional_h1[] = $item['data']['title'];
                        }



        if(!empty($moderator_id)){
            if(count($sub_url) == 3){
            } elseif(count($sub_url) == 2){
            } elseif(count($sub_url) == 1){
            if($user){

            $filter     = array();
            if ($filter_str){
                $this->model->applyGridFilter($grid, $filter);
            $perpage = isset($filter['perpage']) ? $filter['perpage'] : admin::perpage;
            $pages = ceil($total / $perpage);
            $data = $this->model->get('moderators_logs', function ($item, $model) use($subj_controller){
                $item['controller_title'] = string_lang($item['target_controller'].'_CONTROLLER');
                if($subj_controller !== null){

            });

            $this->cms_template->renderGridRowsJSON($grid, $data, $total, $pages);

            $this->halt();

        if($additional_h1){
		return $this->cms_template->render('backend/logs', array(
            'url'       => $url.($sub_url ? '/'.implode('/', $sub_url) : '').(($action > -1) ? '?'.http_build_query($url_query) : '')
        ));


            $this->grid[$key] = array_merge(($this->grid[$key] ?? []), $data);
    public function applyGridFilter(cmsModel $model, $filter) {
            $model->applyDatasetFilters($dataset_filters);
    public function applyDatasetFilters($dataset, $ignore_sorting = false, $allowed_fields = []) {
        if (!empty($dataset['filters'])) {
                if (isset($filter['callback']) && is_callable($filter['callback'])) {
                if (!isset($filter['value'])) {
                    continue;
                }
                if (empty($filter['condition'])) {
                    continue;
                }
        if (!empty($dataset['sorting']) && !$ignore_sorting) {
        if (!empty($dataset['index'])) {
        return true;
    public function applyGridFilter(cmsGrid $grid, $filter) {
        $grid->applyGridFilter($this, $filter);
                $this->grid->applyGridFilter($this->model, $filter);
date  = "20230613"
date  = "20230613"

Fixed code:
date = 20230912
            if (!cmsController::enabled($controller_name)) {
                continue;
            }

            $ctrl_file = $this->cms_config->root_path . 'system/controllers/' . $controller_name . '/frontend.php';
            if (!is_readable($ctrl_file)) {
                continue;
            }
            if (!$hooks) {
                continue;
            }
                if (empty($hook_object->disallow_event_db_register)) {
    public function run($target_controller = null, $target_subject = null, $target_id = null, $moderator_id = null) {
        $url             = href_to($this->root_url, 'logs');
        $sub_url         = [];
        $url_query       = [];
        $additional_h1   = [];
        $action         = $this->request->get('action', -1);

        if ($action > -1) {
            if ($only_to_delete) {
            $additional_h1[] = string_lang('LANG_MODERATION_ACTION_' . $action);
        if (!empty($target_controller)) {
            if (!empty($target_subject)) {
                if ($ctype) {
                if ($ctype) {
                if (!empty($target_id)) {
                    $item = $this->model->getItem('moderators_logs', function ($item, $model) {
                        $item['data'] = cmsModel::yamlToArray($item['data']);
                        return $item;
                    });
                    if ($item) {
                        $additional_h1[] = $item['data']['title'];
                    }
        if (!empty($moderator_id)) {
            if (count($sub_url) == 3) {
            } elseif (count($sub_url) == 2) {
            } elseif (count($sub_url) == 1) {
            if ($user) {
            $filter     = [];
            if ($filter_str) {
                $grid->applyGridFilter($this->model, $filter, 'moderators_logs');
            $data = $this->model->get('moderators_logs', function ($item, $model) use ($subj_controller) {
                $item['controller_title'] = string_lang($item['target_controller'] . '_CONTROLLER');
                if ($subj_controller !== null) {
            }) ?: [];
            return $this->cms_template->renderJSON($grid->makeGridRows($data, $total));
        if ($additional_h1) {
        return $this->cms_template->render('backend/logs', [
            'url'       => $url . ($sub_url ? '/' . implode('/', $sub_url) : '') . (($action > -1) ? '?' . http_build_query($url_query) : '')
        ]);
            $this->grid[$key] = is_array($data) ? array_merge(($this->grid[$key] ?? []), $data) : $data;
     * @param array $table_name Таблица, где ищем поля фильтра
    public function applyGridFilter(cmsModel $model, $filter, $table_name) {
            $dataset_filters = [];

            if (!$model->applyDatasetFilters($dataset_filters, true, [], $table_name)) {

                $this->grid['filter']['advanced_filter'] = '';
            }
    /**
     * Применяет набор фильтров из массива
     *
     * @param array $dataset Массив фильтров/сортировки
     * @param boolean $only_filters Применять только фильтры
     * @param array $allowed_fields Разрешённые поля для фильтрации
     * @param string $table_name Имя таблицы: если передано, все поля проверяются на доступность в ней
     * @return boolean true, если что-либо применилось, false, если ничего
     */
    public function applyDatasetFilters($dataset, $only_filters = false, $allowed_fields = [], $table_name = '') {
        $success = false;

        if (!empty($dataset['filters']) && is_array($dataset['filters'])) {
                // Небольшая валидация
                if (
                    empty($filter['field']) || !is_string($filter['field']) ||
                    empty($filter['condition'] || !is_string($filter['condition'])) ||
                    !array_key_exists('value', $filter) || (!is_string($filter['value']) && $filter['condition'] !== 'in')
                    ) {
                    continue;
                }

                // Есть ли такое поле в таблице
                if ($table_name && !$this->db->isFieldExists($table_name, $filter['field'])) {
                    continue;
                }

                // Таблица передаётся, когда field в $dataset могут быть переданы любые
                if (!$table_name && isset($filter['callback']) && is_callable($filter['callback'])) {
                $success = true;

        if (!empty($dataset['sorting']) && !$only_filters) {

            $success = true;

        if (!empty($dataset['index']) && !$only_filters) {

            $success = true;

        return $success;
     * Метод совместимости, не используйте его
     * @param string $table_name
    public function applyGridFilter(cmsGrid $grid, $filter, $table_name = '') {
        $grid->applyGridFilter($this, $filter, $table_name);
                $this->grid->applyGridFilter($this->model, $filter, $this->table_name);
date  = "20230912"
date  = "20230912"