diff --git a/README.md b/README.md index 2d099c9..2af7b79 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,15 @@ * Шаблоны (файлы .tpl) отвечающие за вывод в публичную часть: * comments_tree.tpl - выводит на страницу форму "Написать комментарий" * comments_tree_sub.tpl - выводит на страницу кооментарий (ветку комментариев) - * last_comments.tpl - выводит на страницу виджет "Последние комментарии" + * last_comments.tpl - выводит на страницу виджет "Последние комментарии" +* Лог загрузки опасных файлов: + * Вы можете вкл/выкл запись лога загрузки опасных файлов. При включенном режиме в Админ панели в списке комментариев появится ссылка "Просмотреть логи безопасности", при новых записях будет включен оранжевый индикатор и текст ссылки будет изменен на "Есть новые записи в логах!". Основные типы записи в лог: + * Обнаружена подмена (Fake Image) + * Вредоносный тип контента (MIME) + * Опасный формат файла + * Файл слишком большой: Превышение лимита + * Пустой файл: Попытка загрузки файла нулевого размера. + * Запрещенное расширение: Файл не прошел по списку разрешенных расширений. * Система тегов: * Тег [mod_comment] (Без параметров), - Это основной системный тег для вывода полноценного функционала на странице документа, выводит форму создания нового комментария + сами комментарии. * Тег [mod_comment:X] (Один параметр) - Выводит виджет последних комментариев, где X - количество выводимых комментариев, количество символов текста комментария равно 150 по умолчанию. diff --git a/class/comment.php b/class/comment.php index 307bbf9..0deb93b 100644 --- a/class/comment.php +++ b/class/comment.php @@ -241,6 +241,13 @@ function _commentSettingsGet($param = '') */ private function _logUploadSecurity($status, $file_info, $context = []) { + // Читаем настройки из базы + $settings = $this->_commentSettingsGet(); + + // ПРОВЕРКА: Если лог выключен в админке — просто выходим + if (empty($settings['comment_log_dangerous']) || $settings['comment_log_dangerous'] == 0) { + return; + } $log_dir = BASE_DIR . '/modules/comment/logs/'; if (!is_dir($log_dir)) @mkdir($log_dir, 0775, true); @@ -1901,6 +1908,30 @@ $current_params = $_GET; unset($current_params['page']); $query_string = http_build_query($current_params); +// --- ОБНОВЛЕННЫЙ БЛОК ПРОВЕРКИ ЛОГОВ --- +// Получаем настройки, чтобы проверить, включено ли логирование вообще +$settings = $this->_commentSettingsGet(); +$log_enabled = !empty($settings['comment_log_dangerous']); // true, если 1 + +$log_path = BASE_DIR . '/modules/comment/logs/security_upload.log'; +$has_new_logs = false; + +// Проверяем наличие новых записей только если файл существует +if (file_exists($log_path)) { + $last_mod = @filemtime($log_path); + $last_view = isset($_SESSION['last_log_view_time']) ? (int)$_SESSION['last_log_view_time'] : 0; + + if ($last_mod > $last_view) { + $has_new_logs = true; + } +} + +// Передаем в шаблон флаг включения логирования и наличие новых логов +$AVE_Template->assign('log_enabled', $log_enabled); // Передаем состояние настройки +$AVE_Template->assign('has_new_logs', $has_new_logs); +$AVE_Template->assign('sess', $session_id); +// --------------------------------------- + $AVE_Template->assign([ 'docs' => $docs, 'page_nav' => ($num > $limit) ? get_pagination($seiten, 'page', ' {t} ') : '', @@ -2096,6 +2127,9 @@ function commentAdminSettingsEdit($tpl_dir) $post_max_file_size = $_POST['comment_max_file_size'] ?? 2048; $post_max_files = $_POST['comment_max_files'] ?? 5; + // --- НАСТРОЙКА лога загрузки опасных файлов --- + $post_log_dangerous = $_POST['comment_log_dangerous'] ?? 0; + $post_rating_type = $_POST['comment_rating_type'] ?? 0; $post_show_user_rating = $_POST['comment_show_user_rating'] ?? 0; @@ -2152,7 +2186,8 @@ function commentAdminSettingsEdit($tpl_dir) comment_allow_files_anon = '" . (int)$post_allow_files_anon . "', comment_allowed_extensions = '" . addslashes($clean_extensions) . "', comment_max_file_size = '" . (int)$post_max_file_size . "', - comment_max_files = '" . (int)$post_max_files . "', + comment_max_files = '" . (int)$post_max_files . "', + comment_log_dangerous = '" . (int)$post_log_dangerous . "', comment_rating_type = '" . (int)$post_rating_type . "', comment_show_user_rating = '" . (int)$post_show_user_rating . "', comment_show_user_rating_replies = '" . (int)$post_show_user_rating_replies . "', @@ -2284,6 +2319,87 @@ function getLatestComments($limit = 10, $chars = 150) return $items; } +/** + * Отображение логов безопасности (security_upload.log) + */ +public function commentAdminLogsShow($tpl_dir) +{ + global $AVE_Template, $AVE_DB; + $log_file = BASE_DIR . '/modules/comment/logs/security_upload.log'; + $logs = []; + + if (file_exists($log_file)) { + $file_content = file($log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if ($file_content) { + foreach ($file_content as $line) { + preg_match('/^\[(.*?)\]/', $line, $date_match); + + $get_val = function($key) use ($line) { + if (preg_match('/' . $key . ':\s*(.*?)\s*($|\|)/', $line, $m)) { + return trim($m[1]); + } + return ''; + }; + + $uid = (int)$get_val('UID'); + $comm_id = (int)$get_val('COMMENT_ID'); + $email = $get_val('EMAIL'); + $full_status = $get_val('STATUS'); + + // Короткое название для колонки "Действие" + $short_action = (strpos($full_status, 'СОЗДАНИИ') !== false) ? 'Создание' : 'Правка'; + + // Текст ошибки без префикса + $error_reason = trim(preg_replace('/^.*?:/', '', $full_status)); + + if (empty($email) && $comm_id > 0) { + $sql = "SELECT comment_author_email FROM " . PREFIX . "_module_comment_info WHERE Id = '" . $comm_id . "' LIMIT 1"; + $result = $AVE_DB->Query($sql); + if ($result) { + $c_row = $result->FetchAssocArray(); + if (!empty($c_row['comment_author_email'])) { + $email = $c_row['comment_author_email']; + } + } + } + + $logs[] = [ + 'date' => $date_match[1] ?? '', + 'ip' => $get_val('IP'), + 'user_id' => $uid, + 'comm_id' => $comm_id ?: '', + 'author' => $get_val('AUTHOR') ?: $get_val('AUTHOR_DB'), + 'email' => $email, + 'status' => $short_action, // ВЕРНУЛ КЛЮЧ STATUS (теперь это "Создание" или "Правка") + 'reason' => $error_reason, // Детали ошибки + 'file' => $get_val('FILE'), + 'mime' => $get_val('MIME'), + 'size' => $get_val('SIZE') + ]; + } + } + $logs = array_reverse($logs); + } + + $AVE_Template->assign(['logs' => $logs, 'sess' => SESSION]); + $AVE_Template->display($tpl_dir . 'view_logs.tpl'); + exit; +} + +/** + * Очистка файла security_upload.log + */ +public function commentAdminClearLogs() +{ + $log_file = BASE_DIR . '/modules/comment/logs/security_upload.log'; + if (file_exists($log_file)) { + @unlink($log_file); + } + header('Content-Type: application/json'); + echo json_encode(['status' => 'success']); + exit; +} + } ?> \ No newline at end of file diff --git a/info.php b/info.php index 5f25745..71f79f3 100644 --- a/info.php +++ b/info.php @@ -3,7 +3,7 @@ if (!defined('BASE_DIR')) exit; $module = array( 'ModuleSysName' => 'comment', - 'ModuleVersion' => '3.33', + 'ModuleVersion' => '3.34', 'ModuleAutor' => 'Repellent', 'ModuleCopyright' => '© 2025-' . date('Y') . ' ave4cms.ru', 'ModuleStatus' => 1, diff --git a/lang/ru.txt b/lang/ru.txt index e55b8ee..4971f71 100644 --- a/lang/ru.txt +++ b/lang/ru.txt @@ -193,6 +193,31 @@ COMMENT_FILES_ALLOWED_EXT = "Допустимые расширения:" COMMENT_FILES_MAX_SIZE = "Макс. размер файла (Кб):" COMMENT_FILES_MAX_COUNT = "Макс. кол-во файлов:" COMMENT_FILES_MAX_COUNT_TEXT = "шт. на один комментарий" +COMMENT_FILES_LOG_TITEL = "Включить лог загрузки опасных файлов" +COMMENT_FILES_LOG_TITEL_A = "Вести запись попыток" +COMMENT_FILES_LOG_TITEL_B = "Журнал безопасности загрузок" +COMMENT_FILES_LOG_TITEL_C = "Есть новые записи в логах!" +COMMENT_FILES_LOG_TITEL_D = "Просмотреть логи безопасности" + +COMMENT_FILES_LOG_MODAL_A = "Дата / Время" +COMMENT_FILES_LOG_MODAL_B = "IP адрес" +COMMENT_FILES_LOG_MODAL_C = "ID Авт." +COMMENT_FILES_LOG_MODAL_D = "ID Ком." +COMMENT_FILES_LOG_MODAL_E = "Автор" +COMMENT_FILES_LOG_MODAL_F = "Email адрес" +COMMENT_FILES_LOG_MODAL_G = "Действие" +COMMENT_FILES_LOG_MODAL_H = "Причина блокировки загрузки файла" +COMMENT_FILES_LOG_MODAL_I = "не указан" +COMMENT_FILES_LOG_MODAL_J = "Файл:" +COMMENT_FILES_LOG_MODAL_K = "Журнал пуст. Подозрительных действий не зафиксировано." +COMMENT_FILES_LOG_MODAL_L = "Очистить лог" +COMMENT_FILES_LOG_MODAL_M = "Закрыть окно" +COMMENT_FILES_LOG_MODAL_N = "Вы уверены, что хотите полностью очистить файл лога безопасности?" +COMMENT_FILES_LOG_MODAL_O = "Подтверждение очистки" +COMMENT_FILES_LOG_MODAL_P = "Журнал успешно очищен." +COMMENT_FILES_LOG_MODAL_R = "Просмотреть логи безопасности" +COMMENT_FILES_LOG_MODAL_S = "Ошибка" + COMMENT_PAGE_NAV_COUNT = "Кол-во комментариев на странице (родителей)" diff --git a/module.php b/module.php index 673f2a8..4739ed7 100644 --- a/module.php +++ b/module.php @@ -171,6 +171,20 @@ if (defined('ACP') && !empty($_REQUEST['moduleaction'])) $comment_id = $_REQUEST['id'] ?? $_REQUEST['Id']; $comment->commentAdminDelete($comment_id); break; + + case 'view_logs': + $comment->commentAdminLogsShow($tpl_dir); // Называем его так + break; + + case 'clear_logs': + $comment->commentAdminClearLogs(); + break; + case 'mark_log_read': + if (isset($_SESSION['user_group']) && $_SESSION['user_group'] == 1) { + $_SESSION['last_log_view_time'] = time(); + echo json_encode(['status' => 'ok']); + } + exit; } } diff --git a/sql.php b/sql.php index e3ac0c7..cb35ebb 100644 --- a/sql.php +++ b/sql.php @@ -4,6 +4,7 @@ /** * AVE.cms - Модуль Комментарии * Исправлено: Поддержка Emoji (utf8mb4) + * Добавлено: Управление логом опасных файлов */ $module_sql_install = array(); @@ -48,6 +49,7 @@ `comment_ajax_replies_limit` tinyint(3) NOT NULL DEFAULT '5', `comment_allow_self_answer` tinyint(1) NOT NULL DEFAULT '0', `comment_sort_order` varchar(20) NOT NULL DEFAULT 'ASC', + `comment_log_dangerous` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`Id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci PACK_KEYS=0 AUTO_INCREMENT=1; "; @@ -98,7 +100,11 @@ ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci PACK_KEYS=0 AUTO_INCREMENT=1; "; - $module_sql_install[] = "INSERT INTO `%%PRFX%%_module_comments` VALUES (1, 1000, '1,3', '1,2,3,4', '0', '1', '1' , '0', '', 1, 0, '', 1, 0, '', 0, 'jpg,jpeg,png,gif,webp', 2048, 5, 0, 1, 0, 0, 0, 0, 60, 30, 5, 0, 'ASC');"; + // Добавлена 0 в конце (для comment_log_dangerous) + $module_sql_install[] = "INSERT INTO `%%PRFX%%_module_comments` VALUES (1, 1000, '1,3', '1,2,3,4', '0', '1', '1' , '0', '', 1, 0, '', 1, 0, '', 0, 'jpg,jpeg,png,gif,webp', 2048, 5, 0, 1, 0, 0, 0, 0, 60, 30, 5, 0, 'ASC', 0);"; + + // Если нужно добавить поле в уже существующую таблицу при обновлении + $module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_log_dangerous` tinyint(1) NOT NULL DEFAULT '0';"; $module_sql_update[] = " UPDATE `%%PRFX%%_module` diff --git a/templates/admin_comments.tpl b/templates/admin_comments.tpl index 2162254..b306fb3 100644 --- a/templates/admin_comments.tpl +++ b/templates/admin_comments.tpl @@ -229,7 +229,47 @@ .tfoot { border-top: 1px solid #CBD5DD; -} +} +/* Индикатор логов безопасности */ +.log-indicator-container { + display: flex; + align-items: center; + padding: 10px 15px; + background: #fdfdfd; + border: 1px dashed #cbd5dd; + border-radius: 4px; + margin-top: 15px; +} + +.log-dot { + width: 12px; + height: 12px; + border-radius: 50%; + display: inline-block; + margin-right: 8px; +} + +.log-dot-gray { background-color: #bdc3c7; } + +.log-dot-new { + background-color: #f39c12; + box-shadow: 0 0 8px #f39c12; + animation: log-blink 1.2s infinite; +} + +@keyframes log-blink { + 0% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.4; transform: scale(0.9); } + 100% { opacity: 1; transform: scale(1); } +} + +.log-link { + font-size: 13px; + font-weight: bold; + text-decoration: none; + color: #34495e; +} +.log-link:hover { color: #2980b9; } @@ -551,15 +591,30 @@ {if !empty($docs)} -