From 33b18a4a3576b0562cd33223ad035ff9c44247c0 Mon Sep 17 00:00:00 2001 From: Aidaho Date: Fri, 25 Apr 2025 09:44:36 +0300 Subject: [PATCH] v8.1.8: Refactor logging and update ModSec highlight logic Refactored Roxy-WI logging to remove redundant arguments and streamline log formatting. Improved ModSecurity CodeMirror mode with enhanced keyword, operator, flag, and action support, ensuring better syntax highlighting. Replaced Nginx and HAProxy modes in WAF templates with ModSec mode. --- app/modules/roxywi/user.py | 14 +-- app/modules/roxywi/waf.py | 8 +- app/static/js/codemirror/mode/modsec.js | 111 ++++++++++++++---------- app/templates/waf.html | 3 +- 4 files changed, 74 insertions(+), 62 deletions(-) diff --git a/app/modules/roxywi/user.py b/app/modules/roxywi/user.py index a46405a7..0091157b 100644 --- a/app/modules/roxywi/user.py +++ b/app/modules/roxywi/user.py @@ -15,7 +15,7 @@ from app.modules.db.common import not_unique_error def create_user(new_user: str, email: str, password: str, role: int, enabled: int, group: int) -> Union[int, tuple]: try: user_id = user_sql.add_user(new_user, email, password, role, enabled, group) - roxywi_common.logging(f'a new user {new_user}', 'has been created', roxywi=1, login=1) + roxywi_common.logging('Roxy-WI server', f'a new user {new_user} has been created') except IntegrityError as e: not_unique_error(e) except Exception as e: @@ -34,7 +34,7 @@ def create_user(new_user: str, email: str, password: str, role: int, enabled: in f"Password: {password}" alerting.send_email(email, 'A user has been created for you', message) except Exception as e: - roxywi_common.logging('error: Cannot send email for a new user', str(e), roxywi=1, login=1) + roxywi_common.logging('', f'error: Cannot send email for a new user: {e}') return user_id @@ -48,7 +48,7 @@ def delete_user(user_id: int): user = user_sql.get_user_id(user_id) user_sql.delete_user(user_id) user_sql.delete_user_groups(user_id) - roxywi_common.logging(user.username, 'has been deleted user', roxywi=1, login=1) + roxywi_common.logging(user.username, 'has been deleted user') except Exception as e: return roxywi_common.handler_exceptions_for_json_data(e) @@ -56,7 +56,7 @@ def delete_user(user_id: int): def update_user_password(password, user_id): user = user_sql.get_user_id(user_id) user_sql.update_user_password(password, user_id) - roxywi_common.logging(f'user {user.username}', 'has changed password', roxywi=1, login=1) + roxywi_common.logging('Roxy-WI server', f'User {user.username} has changed password') def get_user_services(user_id: int) -> str: @@ -79,7 +79,7 @@ def change_user_services(user: str, user_id: int, user_services: dict): user_sql.update_user_services(services=services, user_id=user_id) except Exception as e: raise Exception(f'error: Cannot save: {e}') - roxywi_common.logging('Roxy-WI server', f'Access to the services has been updated for user: {user}', roxywi=1, login=1) + roxywi_common.logging('Roxy-WI server', f'Access to the services has been updated for user: {user}') def change_user_active_group(group_id: int, user_id: int) -> str: @@ -87,7 +87,7 @@ def change_user_active_group(group_id: int, user_id: int) -> str: user_sql.update_user_current_groups(group_id, user_id) return 'Ok' except Exception as e: - roxywi_common.handle_exceptions(e, 'Roxy-WI server', 'Cannot change the group', roxywi=1, login=1) + roxywi_common.handle_exceptions(e, 'Roxy-WI server', 'Cannot change the group') def get_user_active_group(group_id: int, user_id: int) -> str: @@ -113,7 +113,7 @@ def save_user_group_and_role(user: str, groups_and_roles: dict): except Exception as e: raise Exception(f'error: Cannot update groups: {e}') else: - roxywi_common.logging('Roxy-WI server', f'Groups and roles have been updated for user: {user}', roxywi=1, login=1) + roxywi_common.logging('Roxy-WI server', f'Groups and roles have been updated for user: {user}') return resp diff --git a/app/modules/roxywi/waf.py b/app/modules/roxywi/waf.py index a9c089bd..9102d5f0 100644 --- a/app/modules/roxywi/waf.py +++ b/app/modules/roxywi/waf.py @@ -83,7 +83,7 @@ def change_waf_mode(waf_mode: str, server_id: int, service: str): except Exception as e: return str(e) - roxywi_common.logging(serv.hostname, f'Has been changed WAF mod to {waf_mode}', roxywi=1, login=1) + roxywi_common.logging(serv.hostname, f'Has been changed WAF mod to {waf_mode}') def switch_waf_rule(serv: str, enable: int, rule_id: int): @@ -100,8 +100,7 @@ def switch_waf_rule(serv: str, enable: int, rule_id: int): en_for_log = 'enabled' try: - roxywi_common.logging('WAF', f' Has been {en_for_log} WAF rule: {rule_file} for the server {serv}', - roxywi=1, login=1) + roxywi_common.logging('WAF', f' Has been {en_for_log} WAF rule: {rule_file} for the server {serv}') except Exception: pass @@ -129,8 +128,7 @@ def create_waf_rule(serv: str, service: str, json_data: dict) -> int: last_id = waf_sql.insert_new_waf_rule(new_waf_rule, rule_file, new_rule_desc, service, serv) try: - roxywi_common.logging('WAF', f' A new rule has been created {rule_file} on the server {serv}', - roxywi=1, login=1) + roxywi_common.logging('WAF', f'A new rule has been created {rule_file} on the server {serv}') except Exception: pass diff --git a/app/static/js/codemirror/mode/modsec.js b/app/static/js/codemirror/mode/modsec.js index d506696b..13e808cd 100644 --- a/app/static/js/codemirror/mode/modsec.js +++ b/app/static/js/codemirror/mode/modsec.js @@ -16,69 +16,80 @@ CodeMirror.defineMode("modsec", function(config) { for (var i = 0; i < words.length; ++i) obj[words[i]] = true; return obj; } - var keywords = words( - /* hapDirectiveControl */ "SecRule" + - /* hapDirective */ " SecMarker SecAction" - ); - var keywords_block = words( - /* hapDirectiveBlock */ "REQUEST_FILENAME REQUEST_HEADERS REQUEST_PROTOCOL REQUEST_BASENAME REQUEST_HEADERS_NAMES HEADER_NAME ARGS_NAMES ARGS REQUEST_LINE QUERY_STRING REQUEST_BODY" - ); - var keywords_important = words( - /*hapDirectiveImportant */ "User-Agent TX tx msg setvar anomaly_score none warning_anomaly_score severity GET HEAD POST PROPFIND OPTIONS phase" - ); + + const keywords = words( + "SecRule SecAction SecMarker SecRequestBodyAccess SecResponseBodyAccess " + + "SecDefaultAction SecDebugLog SecRuleEngine SecAuditLog" + ); + + const operators = words( + "eq ge gt le lt ne rx streq contains inspectFile validateByteRange " + + "validateUrlEncoding validateUtf8Encoding inspectSignature ipMatch ipMatchFromFile rsub rbl pm pmFromFile validateNidsPayload ranges md5" + ); + + const flags = words( + "deny allow log skip chain redirect pass tag ctl capture auditlog id " + + "severity msg phase skipAfter status t transform persistBlock exec resetConnection pause intercept replace removeTag setvar expirevar drop" + ); + + const actions = words( + "id msg rev severity accuracy maturity skipAfter logdata t chain capture deny block allow ctl tag ver ipMatch pm isBase64Decode status transform nxssDecode compress" + ); + var indentUnit = config.indentUnit, type; - function ret(style, tp) {type = tp; return style;} + + function ret(style, tp) { + type = tp; + return style; + } + function tokenBase(stream, state) { stream.eatWhile(/[\w\$_]/); var cur = stream.current(); if (keywords.propertyIsEnumerable(cur)) { - return "keyword"; - } - else if (keywords_block.propertyIsEnumerable(cur)) { - return "variable-2"; - } - else if (keywords_important.propertyIsEnumerable(cur)) { - return "string-2"; + return ret("keyword", "keyword"); // Основные директивы + } else if (operators.propertyIsEnumerable(cur)) { + return ret("operator", "operator"); // Операторы + } else if (flags.propertyIsEnumerable(cur)) { + return ret("attribute", "flag"); // Флаги + } else if (actions.propertyIsEnumerable(cur)) { + return ret("variable-2", "action"); // Действия } + var ch = stream.next(); - if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());} - else if (ch == "/" && stream.eat("*")) { + if (ch == "@") { + stream.eatWhile(/[\w\\\-]/); + return ret("meta", stream.current()); + } else if (ch == "/" && stream.eat("*")) { state.tokenize = tokenCComment; return tokenCComment(stream, state); - } - else if (ch == "<" && stream.eat("!")) { + } else if (ch == "<" && stream.eat("!")) { state.tokenize = tokenSGMLComment; return tokenSGMLComment(stream, state); - } - else if (ch == "=") ret(null, "compare"); + } else if (ch == "=") ret(null, "compare"); else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare"); else if (ch == "\"" || ch == "'") { state.tokenize = tokenString(ch); return state.tokenize(stream, state); - } - else if (ch == "#") { + } else if (ch == "#") { stream.skipToEnd(); return ret("comment", "comment"); - } - else if (ch == "!") { + } else if (ch == "!") { stream.match(/^\s*\w*/); return ret("keyword", "important"); - } - else if (/\d/.test(ch)) { + } else if (/\d/.test(ch)) { stream.eatWhile(/[\w.%]/); return ret("number", "unit"); - } - else if (/[,.+>*\/]/.test(ch)) { + } else if (/[,.+>*\/]/.test(ch)) { return ret(null, "select-op"); - } - else if (/[;{}:\[\]]/.test(ch)) { + } else if (/[;{}:\[\]]/.test(ch)) { return ret(null, ch); - } - else { + } else { stream.eatWhile(/[\w\\\-]/); return ret("variable", "variable"); } } + function tokenCComment(stream, state) { var maybeEnd = false, ch; while ((ch = stream.next()) != null) { @@ -90,6 +101,7 @@ CodeMirror.defineMode("modsec", function(config) { } return ret("comment", "comment"); } + function tokenSGMLComment(stream, state) { var dashes = 0, ch; while ((ch = stream.next()) != null) { @@ -101,8 +113,9 @@ CodeMirror.defineMode("modsec", function(config) { } return ret("comment", "comment"); } + function tokenString(quote) { - return function(stream, state) { + return function (stream, state) { var escaped = false, ch; while ((ch = stream.next()) != null) { if (ch == quote && !escaped) @@ -113,18 +126,21 @@ CodeMirror.defineMode("modsec", function(config) { return ret("string", "string"); }; } + return { - startState: function(base) { - return {tokenize: tokenBase, - baseIndent: base || 0, - stack: []}; + startState: function (base) { + return { + tokenize: tokenBase, + baseIndent: base || 0, + stack: [] + }; }, - token: function(stream, state) { + token: function (stream, state) { if (stream.eatSpace()) return null; type = null; var style = state.tokenize(stream, state); - var context = state.stack[state.stack.length-1]; + var context = state.stack[state.stack.length - 1]; if (type == "hash" && context == "rule") style = "atom"; else if (style == "variable") { if (context == "rule") style = "number"; @@ -133,18 +149,17 @@ CodeMirror.defineMode("modsec", function(config) { if (context == "rule" && /^[\{\};]$/.test(type)) state.stack.pop(); if (type == "{") { - if (context == "@media") state.stack[state.stack.length-1] = "@media{"; + if (context == "@media") state.stack[state.stack.length - 1] = "@media{"; else state.stack.push("{"); - } - else if (type == "}") state.stack.pop(); + } else if (type == "}") state.stack.pop(); else if (type == "@media") state.stack.push("@media"); else if (context == "{" && type != "comment") state.stack.push("rule"); return style; }, - indent: function(state, textAfter) { + indent: function (state, textAfter) { var n = state.stack.length; if (/^\}/.test(textAfter)) - n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1; + n -= state.stack[state.stack.length - 1] == "rule" ? 2 : 1; return state.baseIndent + n * indentUnit; }, diff --git a/app/templates/waf.html b/app/templates/waf.html index ad57c567..b8a31104 100644 --- a/app/templates/waf.html +++ b/app/templates/waf.html @@ -68,8 +68,7 @@ - - +

{{lang.words.config|title()}} {{waf_rule_file}} {{lang.words.from|title()}} {{ serv }}