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.
pull/418/head
Aidaho 2025-04-25 09:44:36 +03:00
parent 0909fe8022
commit 33b18a4a35
4 changed files with 74 additions and 62 deletions

View File

@ -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]: def create_user(new_user: str, email: str, password: str, role: int, enabled: int, group: int) -> Union[int, tuple]:
try: try:
user_id = user_sql.add_user(new_user, email, password, role, enabled, group) 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: except IntegrityError as e:
not_unique_error(e) not_unique_error(e)
except Exception as 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}" f"Password: {password}"
alerting.send_email(email, 'A user has been created for you', message) alerting.send_email(email, 'A user has been created for you', message)
except Exception as e: 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 return user_id
@ -48,7 +48,7 @@ def delete_user(user_id: int):
user = user_sql.get_user_id(user_id) user = user_sql.get_user_id(user_id)
user_sql.delete_user(user_id) user_sql.delete_user(user_id)
user_sql.delete_user_groups(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: except Exception as e:
return roxywi_common.handler_exceptions_for_json_data(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): def update_user_password(password, user_id):
user = user_sql.get_user_id(user_id) user = user_sql.get_user_id(user_id)
user_sql.update_user_password(password, 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: 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) user_sql.update_user_services(services=services, user_id=user_id)
except Exception as e: except Exception as e:
raise Exception(f'error: Cannot save: {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: 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) user_sql.update_user_current_groups(group_id, user_id)
return 'Ok' return 'Ok'
except Exception as e: 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: 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: except Exception as e:
raise Exception(f'error: Cannot update groups: {e}') raise Exception(f'error: Cannot update groups: {e}')
else: 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 return resp

View File

@ -83,7 +83,7 @@ def change_waf_mode(waf_mode: str, server_id: int, service: str):
except Exception as e: except Exception as e:
return str(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): 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' en_for_log = 'enabled'
try: try:
roxywi_common.logging('WAF', f' Has been {en_for_log} WAF rule: {rule_file} for the server {serv}', roxywi_common.logging('WAF', f' Has been {en_for_log} WAF rule: {rule_file} for the server {serv}')
roxywi=1, login=1)
except Exception: except Exception:
pass 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) last_id = waf_sql.insert_new_waf_rule(new_waf_rule, rule_file, new_rule_desc, service, serv)
try: try:
roxywi_common.logging('WAF', f' A new rule has been created {rule_file} on the server {serv}', roxywi_common.logging('WAF', f'A new rule has been created {rule_file} on the server {serv}')
roxywi=1, login=1)
except Exception: except Exception:
pass pass

View File

@ -16,69 +16,80 @@ CodeMirror.defineMode("modsec", function(config) {
for (var i = 0; i < words.length; ++i) obj[words[i]] = true; for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj; return obj;
} }
var keywords = words(
/* hapDirectiveControl */ "SecRule" + const keywords = words(
/* hapDirective */ " SecMarker SecAction" "SecRule SecAction SecMarker SecRequestBodyAccess SecResponseBodyAccess " +
); "SecDefaultAction SecDebugLog SecRuleEngine SecAuditLog"
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"
); const operators = words(
var keywords_important = words( "eq ge gt le lt ne rx streq contains inspectFile validateByteRange " +
/*hapDirectiveImportant */ "User-Agent TX tx msg setvar anomaly_score none warning_anomaly_score severity GET HEAD POST PROPFIND OPTIONS phase" "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; 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) { function tokenBase(stream, state) {
stream.eatWhile(/[\w\$_]/); stream.eatWhile(/[\w\$_]/);
var cur = stream.current(); var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) { if (keywords.propertyIsEnumerable(cur)) {
return "keyword"; return ret("keyword", "keyword"); // Основные директивы
} } else if (operators.propertyIsEnumerable(cur)) {
else if (keywords_block.propertyIsEnumerable(cur)) { return ret("operator", "operator"); // Операторы
return "variable-2"; } else if (flags.propertyIsEnumerable(cur)) {
} return ret("attribute", "flag"); // Флаги
else if (keywords_important.propertyIsEnumerable(cur)) { } else if (actions.propertyIsEnumerable(cur)) {
return "string-2"; return ret("variable-2", "action"); // Действия
} }
var ch = stream.next(); var ch = stream.next();
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());} if (ch == "@") {
else if (ch == "/" && stream.eat("*")) { stream.eatWhile(/[\w\\\-]/);
return ret("meta", stream.current());
} else if (ch == "/" && stream.eat("*")) {
state.tokenize = tokenCComment; state.tokenize = tokenCComment;
return tokenCComment(stream, state); return tokenCComment(stream, state);
} } else if (ch == "<" && stream.eat("!")) {
else if (ch == "<" && stream.eat("!")) {
state.tokenize = tokenSGMLComment; state.tokenize = tokenSGMLComment;
return tokenSGMLComment(stream, state); 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 == "|") && stream.eat("=")) return ret(null, "compare");
else if (ch == "\"" || ch == "'") { else if (ch == "\"" || ch == "'") {
state.tokenize = tokenString(ch); state.tokenize = tokenString(ch);
return state.tokenize(stream, state); return state.tokenize(stream, state);
} } else if (ch == "#") {
else if (ch == "#") {
stream.skipToEnd(); stream.skipToEnd();
return ret("comment", "comment"); return ret("comment", "comment");
} } else if (ch == "!") {
else if (ch == "!") {
stream.match(/^\s*\w*/); stream.match(/^\s*\w*/);
return ret("keyword", "important"); return ret("keyword", "important");
} } else if (/\d/.test(ch)) {
else if (/\d/.test(ch)) {
stream.eatWhile(/[\w.%]/); stream.eatWhile(/[\w.%]/);
return ret("number", "unit"); return ret("number", "unit");
} } else if (/[,.+>*\/]/.test(ch)) {
else if (/[,.+>*\/]/.test(ch)) {
return ret(null, "select-op"); return ret(null, "select-op");
} } else if (/[;{}:\[\]]/.test(ch)) {
else if (/[;{}:\[\]]/.test(ch)) {
return ret(null, ch); return ret(null, ch);
} } else {
else {
stream.eatWhile(/[\w\\\-]/); stream.eatWhile(/[\w\\\-]/);
return ret("variable", "variable"); return ret("variable", "variable");
} }
} }
function tokenCComment(stream, state) { function tokenCComment(stream, state) {
var maybeEnd = false, ch; var maybeEnd = false, ch;
while ((ch = stream.next()) != null) { while ((ch = stream.next()) != null) {
@ -90,6 +101,7 @@ CodeMirror.defineMode("modsec", function(config) {
} }
return ret("comment", "comment"); return ret("comment", "comment");
} }
function tokenSGMLComment(stream, state) { function tokenSGMLComment(stream, state) {
var dashes = 0, ch; var dashes = 0, ch;
while ((ch = stream.next()) != null) { while ((ch = stream.next()) != null) {
@ -101,8 +113,9 @@ CodeMirror.defineMode("modsec", function(config) {
} }
return ret("comment", "comment"); return ret("comment", "comment");
} }
function tokenString(quote) { function tokenString(quote) {
return function(stream, state) { return function (stream, state) {
var escaped = false, ch; var escaped = false, ch;
while ((ch = stream.next()) != null) { while ((ch = stream.next()) != null) {
if (ch == quote && !escaped) if (ch == quote && !escaped)
@ -113,18 +126,21 @@ CodeMirror.defineMode("modsec", function(config) {
return ret("string", "string"); return ret("string", "string");
}; };
} }
return { return {
startState: function(base) { startState: function (base) {
return {tokenize: tokenBase, return {
baseIndent: base || 0, tokenize: tokenBase,
stack: []}; baseIndent: base || 0,
stack: []
};
}, },
token: function(stream, state) { token: function (stream, state) {
if (stream.eatSpace()) return null; if (stream.eatSpace()) return null;
type = null; type = null;
var style = state.tokenize(stream, state); 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"; if (type == "hash" && context == "rule") style = "atom";
else if (style == "variable") { else if (style == "variable") {
if (context == "rule") style = "number"; if (context == "rule") style = "number";
@ -133,18 +149,17 @@ CodeMirror.defineMode("modsec", function(config) {
if (context == "rule" && /^[\{\};]$/.test(type)) if (context == "rule" && /^[\{\};]$/.test(type))
state.stack.pop(); state.stack.pop();
if (type == "{") { 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 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 (type == "@media") state.stack.push("@media");
else if (context == "{" && type != "comment") state.stack.push("rule"); else if (context == "{" && type != "comment") state.stack.push("rule");
return style; return style;
}, },
indent: function(state, textAfter) { indent: function (state, textAfter) {
var n = state.stack.length; var n = state.stack.length;
if (/^\}/.test(textAfter)) 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; return state.baseIndent + n * indentUnit;
}, },

View File

@ -68,8 +68,7 @@
<script src="/static/js/codemirror/addon/fold/brace-fold.js"></script> <script src="/static/js/codemirror/addon/fold/brace-fold.js"></script>
<script src="/static/js/codemirror/addon/fold/comment-fold.js"></script> <script src="/static/js/codemirror/addon/fold/comment-fold.js"></script>
<script src="/static/js/codemirror/addon/scroll/annotatescrollbar.js"></script> <script src="/static/js/codemirror/addon/scroll/annotatescrollbar.js"></script>
<script src="/static/js/codemirror/mode/nginx.js"></script> <script src="/static/js/codemirror/mode/modsec.js"></script>
<script src="/static/js/codemirror/mode/haproxy.js"></script>
<script src="/static/js/codemirror/keymap/sublime.js"></script> <script src="/static/js/codemirror/keymap/sublime.js"></script>
<script src="/static/js/configshow.js"></script> <script src="/static/js/configshow.js"></script>
<h4>{{lang.words.config|title()}} {{waf_rule_file}} {{lang.words.from|title()}} {{ serv }}</h4> <h4>{{lang.words.config|title()}} {{waf_rule_file}} {{lang.words.from|title()}} {{ serv }}</h4>