GUI editor to tame mod_security rules

⌈⌋ branch:  modseccfg


Check-in [0993a0e78c]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:accessible MenuInstance for newer PSG, migrate to pluginconf.bind interface (partly)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 0993a0e78cbdb04e3c4f8ef15bfb64e162db614b81eb5387e7aa10628abc6388
User & Date: mario 2022-11-01 23:27:56
Context
2023-10-26
17:36
patch for PluginMeta() wrapper required in last pluginconf.gui.window() Leaf check-in: 4f8b060eda user: mario tags: trunk
2022-11-01
23:27
accessible MenuInstance for newer PSG, migrate to pluginconf.bind interface (partly) check-in: 0993a0e78c user: mario tags: trunk
2022-10-21
22:50
comment fixes check-in: a7de0f8780 user: mario tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to modseccfg/mainwindow.py.

106
107
108
109
110
111
112


113
114
115
116
117
118
119
120
121
122
123
124
#-- widget structure
menu = [
    ["File", ["Edit conf/vhost file (F4)", "---", "Settings (F12)", "SecEngine options", "CoreRuleSet options", "Rescan configs", "---", "Test", "Debug", "Help", "About", "---", "Exit"]],
    ["Rule", ["Info (F1)", "Disable", "Enable", "Modify"]],#, "<Wrap>", "Masquerade"]],
    ["Recipe"], #['<Wrap> exclusions', ['<Location>', '<Directory>', '<FilesMatch>'], 'Exclude Parameter', 'Rule to DetectionOnly', 'URL to DetectionOnly', 'Example Exclusions', ['Ignore param for tag', 'Ignore param for all', 'Remove rule range'], 'Whitelist IP', ['Exempt REMOTE_ADDR', 'Whitelist from file'], 'Macros', ['Macro definitions'], 'Setup', ['CRS *.preconf includes', 'Cloudflare / IP2LOCATION', 'Cloudflare RemoteIP', 'Apache Error/LogFormat', 'preconf_stub']]],
    ["Log", ["Advise", "Log View (F3)", "Reread log"]],
]


layout = [
    [
        sg.Column([
            # menu
            [sg.Menu(menu, key="menu", font="Sans 11")],
            # button row
            [
                sg.Button("🛈 Info", tooltip="SecRule details"),#⭐
                sg.Button("🗶 Disable", tooltip="SecRuleRemoveById"),
                sg.Button("🗸 Enable", tooltip="remove SecRuleRemove"),
                sg.Button("⋇ Modify", tooltip="SecRuleUpdateAction/Target", disabled=0),
                sg.ButtonMenu("⋚ Wrap", ["Wrap",["<FilesMatch>","<Location>","<Directory>"]], disabled=0, k="menu_wrap"),







>
>




|







106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#-- widget structure
menu = [
    ["File", ["Edit conf/vhost file (F4)", "---", "Settings (F12)", "SecEngine options", "CoreRuleSet options", "Rescan configs", "---", "Test", "Debug", "Help", "About", "---", "Exit"]],
    ["Rule", ["Info (F1)", "Disable", "Enable", "Modify"]],#, "<Wrap>", "Masquerade"]],
    ["Recipe"], #['<Wrap> exclusions', ['<Location>', '<Directory>', '<FilesMatch>'], 'Exclude Parameter', 'Rule to DetectionOnly', 'URL to DetectionOnly', 'Example Exclusions', ['Ignore param for tag', 'Ignore param for all', 'Remove rule range'], 'Whitelist IP', ['Exempt REMOTE_ADDR', 'Whitelist from file'], 'Macros', ['Macro definitions'], 'Setup', ['CRS *.preconf includes', 'Cloudflare / IP2LOCATION', 'Cloudflare RemoteIP', 'Apache Error/LogFormat', 'preconf_stub']]],
    ["Log", ["Advise", "Log View (F3)", "Reread log"]],
]
MenuInstance = sg.Menu(menu, key="menu", font="Sans 11")
menu = MenuInstance.MenuDefinition
layout = [
    [
        sg.Column([
            # menu
            [MenuInstance],
            # button row
            [
                sg.Button("🛈 Info", tooltip="SecRule details"),#⭐
                sg.Button("🗶 Disable", tooltip="SecRuleRemoveById"),
                sg.Button("🗸 Enable", tooltip="remove SecRuleRemove"),
                sg.Button("⋇ Modify", tooltip="SecRuleUpdateAction/Target", disabled=0),
                sg.ButtonMenu("⋚ Wrap", ["Wrap",["<FilesMatch>","<Location>","<Directory>"]], disabled=0, k="menu_wrap"),
170
171
172
173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
class gui_event_handler:

    # prepare window
    def __init__(self):
        #-- hooks
        log.init.info("scan plugins")
        self.plugins = [recipe, install, scripts] + utils.load_plugins()
        #print(self.plugins)
        for mod in self.plugins:

            if hasattr(mod, "init"):
                menu = layout[0][0].Rows[0][0].MenuDefinition
                mod.init(menu=menu, layout=layout, mainwindow=self)
        
        #-- build
        gui_event_handler.mainwindow = self
        log.init.info("build window")
        self.w = sg.Window(
            title=f"mod_security config {utils.srvroot.srv}", layout=layout, font="Sans 12",







|
|
>

<







172
173
174
175
176
177
178
179
180
181
182

183
184
185
186
187
188
189
class gui_event_handler:

    # prepare window
    def __init__(self):
        #-- hooks
        log.init.info("scan plugins")
        self.plugins = [recipe, install, scripts] + utils.load_plugins()
        print(self.plugins)
        #print(self.plugins)
        for mod in list(set(self.plugins)):
            if hasattr(mod, "init"):

                mod.init(menu=menu, layout=layout, mainwindow=self)
        
        #-- build
        gui_event_handler.mainwindow = self
        log.init.info("build window")
        self.w = sg.Window(
            title=f"mod_security config {utils.srvroot.srv}", layout=layout, font="Sans 12",

Changes to modseccfg/utils.py.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# encoding: utf-8
# api: python
# type: function
# category: utils
# title:  Utils & Config
# description: various shortcut functions, config data, UI and remoting wrappers
# version: 0.4
# depends: pluginconf (>= 0.7.2), python:appdirs, python:pathlib
# config:
#   { name: sshfs_mount, type: str, value: "~/.config/modseccfg/mnt/", description: "Remote connection (sshfs) mount point", help: "This will be used for `modseccfg vps123:` invocations to bind the servers / root locally." }
#   { name: debug, type: bool, value: 0, description: modseccfg debug messages, help: terribly startup slowdown }
# state: alpha
# license: Apache-2.0
#
# Contains some utility code, and Python module monkeypatching.







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# encoding: utf-8
# api: python
# type: function
# category: utils
# title:  Utils & Config
# description: various shortcut functions, config data, UI and remoting wrappers
# version: 0.4
# depends: pluginconf (>= 0.8), python:appdirs, python:pathlib
# config:
#   { name: sshfs_mount, type: str, value: "~/.config/modseccfg/mnt/", description: "Remote connection (sshfs) mount point", help: "This will be used for `modseccfg vps123:` invocations to bind the servers / root locally." }
#   { name: debug, type: bool, value: 0, description: modseccfg debug messages, help: terribly startup slowdown }
# state: alpha
# license: Apache-2.0
#
# Contains some utility code, and Python module monkeypatching.
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import pathlib
import re
import functools
import subprocess
import importlib
import atexit
import json
import pluginconf, pluginconf.gui
import appdirs
try: import frosch; frosch.hook()
except: pass
import logging, inspect, traceback


#-- dict mirrored into object properties







|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import pathlib
import re
import functools
import subprocess
import importlib
import atexit
import json
import pluginconf, pluginconf.bind, pluginconf.gui
import appdirs
try: import frosch; frosch.hook()
except: pass
import logging, inspect, traceback


#-- dict mirrored into object properties
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
        "ruleinfo": 1,
        "advise": 1,
        "recipe": 1,
    }
})

#-- plugin lookup
pluginconf.module_base = __name__
pluginconf.plugin_base = [__package__]
for module,meta in pluginconf.all_plugin_meta().items():
    pluginconf.add_plugin_defaults(conf, conf.plugins, meta, module)

# invoked from main
def load_plugins():
    add = []
    for name, state in conf.plugins.items():
        module_name = f"modseccfg.{name}"
        if state and not name.startswith("_") and not module_name in sys.modules:
            try:
                add.append(importlib.import_module(module_name))
            except:
                log.error(traceback.format_exc())
    return add

#-- path
def expandpath(dir):
    return str(pathlib.Path(dir).expanduser())

#-- @decorator to override module function
@functools.singledispatch







|
|
|
<



<
<
<
<
<
<
<
<
|







81
82
83
84
85
86
87
88
89
90

91
92
93








94
95
96
97
98
99
100
101
        "ruleinfo": 1,
        "advise": 1,
        "recipe": 1,
    }
})

#-- plugin lookup
pluginconf.data_root = __name__
pluginconf.bind.base(__package__)
pluginconf.bind.defaults(conf)


# invoked from main
def load_plugins():








    return list(pluginconf.bind.load_enabled(conf))

#-- path
def expandpath(dir):
    return str(pathlib.Path(dir).expanduser())

#-- @decorator to override module function
@functools.singledispatch