Files
haproxy-wi/app/static/js/codemirror/mode/modsec.js
Aidaho 33b18a4a35 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.
2025-04-25 09:44:36 +03:00

172 lines
5.6 KiB
JavaScript

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Modified for Modsec by Roxy-WI
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineMode("modsec", function(config) {
function words(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}
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 tokenBase(stream, state) {
stream.eatWhile(/[\w\$_]/);
var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) {
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("*")) {
state.tokenize = tokenCComment;
return tokenCComment(stream, state);
} else if (ch == "<" && stream.eat("!")) {
state.tokenize = tokenSGMLComment;
return tokenSGMLComment(stream, state);
} 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 == "#") {
stream.skipToEnd();
return ret("comment", "comment");
} else if (ch == "!") {
stream.match(/^\s*\w*/);
return ret("keyword", "important");
} else if (/\d/.test(ch)) {
stream.eatWhile(/[\w.%]/);
return ret("number", "unit");
} else if (/[,.+>*\/]/.test(ch)) {
return ret(null, "select-op");
} else if (/[;{}:\[\]]/.test(ch)) {
return ret(null, ch);
} else {
stream.eatWhile(/[\w\\\-]/);
return ret("variable", "variable");
}
}
function tokenCComment(stream, state) {
var maybeEnd = false, ch;
while ((ch = stream.next()) != null) {
if (maybeEnd && ch == "/") {
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("comment", "comment");
}
function tokenSGMLComment(stream, state) {
var dashes = 0, ch;
while ((ch = stream.next()) != null) {
if (dashes >= 2 && ch == ">") {
state.tokenize = tokenBase;
break;
}
dashes = (ch == "-") ? dashes + 1 : 0;
}
return ret("comment", "comment");
}
function tokenString(quote) {
return function (stream, state) {
var escaped = false, ch;
while ((ch = stream.next()) != null) {
if (ch == quote && !escaped)
break;
escaped = !escaped && ch == "\\";
}
if (!escaped) state.tokenize = tokenBase;
return ret("string", "string");
};
}
return {
startState: function (base) {
return {
tokenize: tokenBase,
baseIndent: base || 0,
stack: []
};
},
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];
if (type == "hash" && context == "rule") style = "atom";
else if (style == "variable") {
if (context == "rule") style = "number";
else if (!context || context == "@media{") style = "tag";
}
if (context == "rule" && /^[\{\};]$/.test(type))
state.stack.pop();
if (type == "{") {
if (context == "@media") state.stack[state.stack.length - 1] = "@media{";
else state.stack.push("{");
} 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) {
var n = state.stack.length;
if (/^\}/.test(textAfter))
n -= state.stack[state.stack.length - 1] == "rule" ? 2 : 1;
return state.baseIndent + n * indentUnit;
},
electricChars: "}",
lineComment: "#"
};
});
CodeMirror.defineMIME("text/x-modsec-conf", "modsec");
});