Check-in [5f05a5d785]
Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Remove remaining emoji Unicode occurences (info, modify, vhosts) |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
5f05a5d785ab5532c0eda6f0f06b5085 |
| User & Date: | mario 2021-02-24 20:16:44 |
Context
|
2021-02-25
| ||
| 13:19 | Removed Wrap and Masquerade menu entries. check-in: c563db9866 user: mario tags: trunk | |
|
2021-02-24
| ||
| 20:16 | Remove remaining emoji Unicode occurences (info, modify, vhosts) check-in: 5f05a5d785 user: mario tags: trunk | |
|
2021-02-02
| ||
| 16:07 | Move to safer Unicode glyphs. Tk doesn't like current system setup: <blockquote> X Error of failed request: BadLength (poly request too large or internal Xlib length error) Major opcode of failed request: 139 (RENDER) Minor opcode of failed request: 20 (RenderAddGlyphs) Serial number of failed request: 28016 Current serial number in output stream: 28018 </blockquote> check-in: acaec56692 user: mario tags: trunk | |
Changes
Changes to Makefile.
1 2 3 4 5 6 | #!/usr/bin/make run: python3 -m modseccfg setup: pandoc README.md -o README.rst | > < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/usr/bin/make run: python3 -m modseccfg setup: version --read modseccfg/__init__.py --write modseccfg/mainwindow.py --write:_raw_ manpage/*md pandoc README.md -o README.rst python3 setup.py bdist_wheel rm -r modseccfg.egg-info upload: setup python3 setup.py bdist_wheel upload rm -r modseccfg.egg-info t: pytest -v -v -v -v |
| ︙ | ︙ | |||
31 32 33 34 35 36 37 |
cd logfmt1 ; PYTHONPATH=. mkdocs build -v -v -v
sed -i 's/table\.docutils/table/g' logfmt1/html/css/theme.css
sed -i 's/[{};]/&\n/g' logfmt1/html/css/theme.css
%.1: %.md
pandoc --standalone -f markdown+pandoc_title_block -t man $< -o $@
%.5: %.md
pandoc --standalone -f markdown+pandoc_title_block -t man $< -o $@
| | | 31 32 33 34 35 36 37 38 39 |
cd logfmt1 ; PYTHONPATH=. mkdocs build -v -v -v
sed -i 's/table\.docutils/table/g' logfmt1/html/css/theme.css
sed -i 's/[{};]/&\n/g' logfmt1/html/css/theme.css
%.1: %.md
pandoc --standalone -f markdown+pandoc_title_block -t man $< -o $@
%.5: %.md
pandoc --standalone -f markdown+pandoc_title_block -t man $< -o $@
man: manpage/modseccfg.1 logfmt1/manpage/logex.1 logfmt1/manpage/update-logfmt.1 logfmt1/manpage/logfmt.5
|
Changes to manpage/modseccfg.1.
1 2 | .\" Automatically generated by Pandoc 2.5 .\" | | | 1 2 3 4 5 6 7 8 9 10 | .\" Automatically generated by Pandoc 2.5 .\" .TH "modseccfg" "1" "" "modseccfg and logfmt1 utilities" "Version 0.7.3" .hy .SH NAME .PP \f[B]modseccfg\f[R] \[em] GUI editor for mod_security rules on Apache setups .SH SYNOPSIS .PP |
| ︙ | ︙ |
Changes to manpage/modseccfg.md.
|
| | | 1 2 3 4 5 6 7 8 | % modseccfg(1) modseccfg and logfmt1 utilities | Version 0.7.3 NAME ==== **modseccfg** โ GUI editor for mod_security rules on Apache setups |
| ︙ | ︙ |
Changes to modseccfg/__init__.py.
1 2 3 4 5 | # encoding: utf-8 # api: python # type: init # title: modseccfg # description: Editor to tame mod_security rulesets | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 | # encoding: utf-8 # api: python # type: init # title: modseccfg # description: Editor to tame mod_security rulesets # version: 0.7.3 # state: prototype # support: none # license: Apache-2.0 # depends: python:pysimplegui (>= 3.0), python:pluginconf (>= 0.7.3), # python:appdirs (>= 1.3), python:logfmt1 (>= 0.4), # python (>= 3.6), deb:python3-tk, bin:sshfs # priority: core |
| ︙ | ︙ |
Changes to modseccfg/install/setup_preconf_scheme.py.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 |
confn = fn
if not confn:
print("Couldn't find 900-EXCLUSION.conf")
else:
# run recipe window
if vhosts.tmp.decl_preconf:
| | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
confn = fn
if not confn:
print("Couldn't find 900-EXCLUSION.conf")
else:
# run recipe window
if vhosts.tmp.decl_preconf:
print("๐ธ *.preconf includes already configured")
else:
print("\n# IncludeOptional .../*.preconf")
src = recipe.template_funcs.crs_preconfig(vars={}, data={"confn":confn})
writer.append(fn=confn, directive=src, value="", comment="")
vhosts.tmp.decl_preconf = True
print(f"โ updated: {confn}")
# conf.preconf
if conf.preconf:
print("๐ธ preconf use already activated")
else:
print("# enable ๐น preconf usage globally")
utils.conf.preconf = True
utils.cfg_write()
print(f"โ updated: {conf.conf_dir}/{conf.conf_file}")
|
Changes to modseccfg/mainwindow.py.
1 2 3 4 5 6 | # encoding: utf-8 # api: python # type: main # title: main window # description: GUI with menus, actions, rules and logs # category: config | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# encoding: utf-8
# api: python
# type: main
# title: main window
# description: GUI with menus, actions, rules and logs
# category: config
# version: 0.7.3
# state: alpha
# license: Apache-2.0
# config:
# { name: theme, type: select, value: DefaultNoMoreNagging, select: "Default|DarkGrey|Black|BlueMono|BluePurple|BrightColors|BrownBlue|Dark|Dark2|DarkAmber|DarkBlack|DarkBlack1|DarkBlue|DarkBlue1|DarkBlue10|DarkBlue11|DarkBlue12|DarkBlue13|DarkBlue14|DarkBlue15|DarkBlue16|DarkBlue17|DarkBlue2|DarkBlue3|DarkBlue4|DarkBlue5|DarkBlue6|DarkBlue7|DarkBlue8|DarkBlue9|DarkBrown|DarkBrown1|DarkBrown2|DarkBrown3|DarkBrown4|DarkBrown5|DarkBrown6|DarkBrown7|DarkGreen|DarkGreen1|DarkGreen2|DarkGreen3|DarkGreen4|DarkGreen5|DarkGreen6|DarkGreen7|DarkGrey|DarkGrey1|DarkGrey10|DarkGrey11|DarkGrey12|DarkGrey13|DarkGrey14|DarkGrey2|DarkGrey3|DarkGrey4|DarkGrey5|DarkGrey6|DarkGrey7|DarkGrey8|DarkGrey9|DarkPurple|DarkPurple1|DarkPurple2|DarkPurple3|DarkPurple4|DarkPurple5|DarkPurple6|DarkPurple7|DarkRed|DarkRed1|DarkRed2|DarkTanBlue|DarkTeal|DarkTeal1|DarkTeal10|DarkTeal11|DarkTeal12|DarkTeal2|DarkTeal3|DarkTeal4|DarkTeal5|DarkTeal6|DarkTeal7|DarkTeal8|DarkTeal9|Default|Default1|DefaultNoMoreNagging|Green|GreenMono|GreenTan|HotDogStand|Kayak|LightBlue|LightBlue1|LightBlue2|LightBlue3|LightBlue4|LightBlue5|LightBlue6|LightBlue7|LightBrown|LightBrown1|LightBrown10|LightBrown11|LightBrown12|LightBrown13|LightBrown2|LightBrown3|LightBrown4|LightBrown5|LightBrown6|LightBrown7|LightBrown8|LightBrown9|LightGray1|LightGreen|LightGreen1|LightGreen10|LightGreen2|LightGreen3|LightGreen4|LightGreen5|LightGreen6|LightGreen7|LightGreen8|LightGreen9|LightGrey|LightGrey1|LightGrey2|LightGrey3|LightGrey4|LightGrey5|LightGrey6|LightPurple|LightTeal|LightYellow|Material1|Material2|NeutralBlue|Purple|Python|Reddit|Reds|SandyBeach|SystemDefault|SystemDefault1|SystemDefaultForReal|Tan|TanBlue|TealMono|Topanga", description: "PySimpleGUI window theme", help: "Requires a restart to take effect." }
# { name: switch_auto, type: bool, value: 0, description: "Automatically switch to matching error.log when selecting vhost" }
# { name: keyboard_binds, type: bool, value: 1, description: "Enable keyboard shortcuts in main window", help: "F1=info, F3/F4=editor, F5=log-viewer, F12=settings" }
# priority: core
|
| ︙ | ︙ | |||
65 66 67 68 69 70 71 |
continue
parent = ""
if r.chained_to:
parent = r.chained_to
if parent in hidden:
continue
# prepare treedata attributes
| | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
continue
parent = ""
if r.chained_to:
parent = r.chained_to
if parent in hidden:
continue
# prepare treedata attributes
state = rulestate.get(id, "๐ธ") # ๐ถ=disabled, โ=modified, โ=wrapped, ๐ธ/None=enabled, formerly: -1=โ, 0=โ, 1=๏๏, undef=โ
rule_tree.insert(
parent=parent,
key=id,
text=id,
values=[
state, str(id), r.msg, r.tag_primary, log_count.get(id, 0)
],
|
| ︙ | ︙ | |||
113 114 115 116 117 118 119 |
[
sg.Column([
# menu
[sg.Menu(menu, key="menu")],
# button row
[
sg.Button("๐ Info", tooltip="SecRule details"),#โญ
| | | | | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
[
sg.Column([
# menu
[sg.Menu(menu, key="menu")],
# 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"),
sg.T(" " * 18),
sg.Button("Filter", key="filter_log", button_color=("white","gray"), font="Sans 10", tooltip="Apply filter phrase to current log"),
sg.Combo(values=["", "injection", "500|429", "bot"], size=(20,1), key="log_filter", enable_events=True, tooltip="Regex to filter with")
],
[sg.T(" "*71+"โ")],
# comboboxes
[sg.Text("vhost/conf", font="bold"),
|
| ︙ | ︙ | |||
324 325 326 327 328 329 330 |
# Clean up comment a little (comments aren't strictly speaking allowed,
# but mod_security effectively proccesses them and simply ignores any
# trailing garbage. So we just need to ensure there aren't any extra
# integers to be interpreted as RemoveById rule numbers.
comment = re.sub("[^\w\s,:./]", "", vhosts.rules[self.id].msg) # retain just a bit of text
comment = re.sub("(\d)", lambda m: chr(0xFEE0 + ord(m.group(1))), comment) # integers to unicode glyphs
writer.append(data["confn"], directive="SecRuleRemoveById", value=self.id, comment=" # "+comment)
| | | | 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# Clean up comment a little (comments aren't strictly speaking allowed,
# but mod_security effectively proccesses them and simply ignores any
# trailing garbage. So we just need to ensure there aren't any extra
# integers to be interpreted as RemoveById rule numbers.
comment = re.sub("[^\w\s,:./]", "", vhosts.rules[self.id].msg) # retain just a bit of text
comment = re.sub("(\d)", lambda m: chr(0xFEE0 + ord(m.group(1))), comment) # integers to unicode glyphs
writer.append(data["confn"], directive="SecRuleRemoveById", value=self.id, comment=" # "+comment)
self._update_rulestate(self.id, "๐ถ")
# remove any "SecRuleRemove* {id}" in vhost.conf
@ui.needs_id
@ui.needs_confn
def enable(self, data):
if self.vh and self.vh.rulestate.get(self.id) != "๐ถ" and self._cancel("SecRule might be wrapped/masked. Reenable anyway?"):
return
writer.remove_remove(data["confn"], "SecRuleRemoveById", self.id)
self._update_rulestate(self.id, None)
# File: Settings - remapped to pluginconf window
def settings(self, data):
utils.cfg_window(self)
|
| ︙ | ︙ | |||
440 441 442 443 444 445 446 |
self.win_register(w, lambda *x: None)
# renew display of ruletree with current log and vhost rulestate
def _update_rules(self, *data):
if self.vh:
self.w["rule"].update(ui.rules(log_count=logs.log_count, rulestate=self.vh.rulestate))
| | | 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
self.win_register(w, lambda *x: None)
# renew display of ruletree with current log and vhost rulestate
def _update_rules(self, *data):
if self.vh:
self.w["rule"].update(ui.rules(log_count=logs.log_count, rulestate=self.vh.rulestate))
# called from disable/enable to set ๐ถ=disabled, โ=modified, โ=wrapped, ๐ธ/None=enabled, etc
def _update_rulestate(self, id, val):
if self.vh:
if val==None and id in self.vh.rulestate:
del self.vh.rulestate[id]
else:
self.vh.rulestate[id] = val
self._update_rules()
|
| ︙ | ︙ |
Changes to modseccfg/modify.py.
| ︙ | ︙ | |||
196 197 198 199 200 201 202 |
# window
layout = [
[sg.Menu([[f"Rule {id}",["Info", "Save", "Close"]]], key="menu")],
[sg.Column(layout, expand_x=1, expand_y=0, size=(635,720), scrollable="no", element_justification='left')],
[sg.Button("Save"), sg.Button("Cancel")]
]
| | | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# window
layout = [
[sg.Menu([[f"Rule {id}",["Info", "Save", "Close"]]], key="menu")],
[sg.Column(layout, expand_x=1, expand_y=0, size=(635,720), scrollable="no", element_justification='left')],
[sg.Button("Save"), sg.Button("Cancel")]
]
self.w = sg.Window(layout=layout, title=f"โ SecRuleUpdate #{id}", resizable=1, font="Sans 12", icon=icons.icon)
mainwindow.win_register(self.w, self.event)
# widget: flags[] checkbox
def w_flag(self, name, radio=0):
kw = {
"text": f"{name} ", "key": f"flags[{name}]", "disabled": name == "chain",
|
| ︙ | ︙ | |||
241 242 243 244 245 246 247 |
sg.Combo([""] + ["!"+name for name in show.targets], k=f"vars[{i}]", default_value=val, size=(30,1), enable_events=True, disabled=self.r.vars=="@SecAction"),
sg.T("!exclude or add", text_color="gray")
]
# widget: help link
def w_help(self, anchor, **kw):
help = dict(text_color="#bbd", pad=[(0,0),(12,0)], enable_events=True); help.update(kw)
| | | 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
sg.Combo([""] + ["!"+name for name in show.targets], k=f"vars[{i}]", default_value=val, size=(30,1), enable_events=True, disabled=self.r.vars=="@SecAction"),
sg.T("!exclude or add", text_color="gray")
]
# widget: help link
def w_help(self, anchor, **kw):
help = dict(text_color="#bbd", pad=[(0,0),(12,0)], enable_events=True); help.update(kw)
return sg.T("(?)", key="https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-%28v2.x%29#"+anchor, **help)
# description text
def t_target_desc(self, r):
if isinstance(r.id, float):
return "(CHAINED rule!)"
elif r.vars == "@SecAction":
return "(Faux rule)"
|
| ︙ | ︙ |
Changes to modseccfg/ruleinfo.py.
| ︙ | ︙ | |||
65 66 67 68 69 70 71 |
# rule lookup
r = vhosts.rules[id] # SecRule
is_virt = ""
if type(id) is float:
is_virt = "(virtual id/chained rule)"
decl_vh = r.vhost()
file_url = "file://"+decl_vh.fn
| | | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# rule lookup
r = vhosts.rules[id] # SecRule
is_virt = ""
if type(id) is float:
is_virt = "(virtual id/chained rule)"
decl_vh = r.vhost()
file_url = "file://"+decl_vh.fn
is_state = "๐ธ"
if decl_vh and decl_vh.rulestate.get(id):
is_state = decl_vh.rulestate[id] + " in rules.conf" # conditional SecRule declaration
elif vh and vh.rulestate.get(id):
is_state = vh.rulestate[id]
# params 2 widget
layout = [
[
# SecRule #123456
sg.T(f"SecRule {id}", **style.head),
# (virtual id)
sg.T(is_virt, **style.virt),
# ๐ถ โ โ ๐ธ
sg.T(f"state={is_state}", **style.state)
],
[
# rule comment
sg.Frame("doc", size=(90,4), layout=[
[sg.T(file_url, key=file_url, **style.link)],
[sg.Multiline(r.help(), auto_size_text=1, size=(60,4), background_color="lightgray")],
|
| ︙ | ︙ |
Changes to modseccfg/vhosts.py.
| ︙ | ︙ | |||
206 207 208 209 210 211 212 |
# vhost properties
self.fn = fn
self.t = "cfg"
self.name = ""
self.logs = []
self.cfg = {}
| | | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# vhost properties
self.fn = fn
self.t = "cfg"
self.name = ""
self.logs = []
self.cfg = {}
self.rulestate = {} # ๐ถ=disabled, โ=modified, โ=wrapped, ๐ธ=enabled
self.ruledecl = {}
self.update = {} # SecRuleUpdateโฆ map
self.warn = ""
# internal state
self.linemap = {} # lineno โ first id: occurence
self.mk_linemap(src) # fill .linemap{}
|
| ︙ | ︙ | |||
324 325 326 327 328 329 330 |
# modsec: just a secrule without conditions
def secaction(self, args):
self.secrule(["@SecAction", "setvar:", args[0]])
# modsec: SecRuleRemoveById 900001 900002 900003
def secruleremovebyid(self, args):
| | | | 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# modsec: just a secrule without conditions
def secaction(self, args):
self.secrule(["@SecAction", "setvar:", args[0]])
# modsec: SecRuleRemoveById 900001 900002 900003
def secruleremovebyid(self, args):
state = "๐ถ"
if self.wrap: # record if within <Dir|File|If|Wrap> section
state = "โ"
#log.info("wrapped SecRuleRm", self.fn, self.wrap, args)
for a in args:
if re.match("^\d+-\d+$", a): # are ranges still allowed?
a = [int(x) for x in a.split("-")]
for i in range(*a):
if i in rules: # only apply state for known/existing rules, not the whole range()
self.rulestate[i] = state
|
| ︙ | ︙ | |||
360 361 362 363 364 365 366 |
def _secruleupdate(self, cls, id, arg, *repl):
if re.match("^\d+:\d$", id):
id = float(id.replace(":", "."))
elif re.match("^\d+$", id):
id = int(id)
else:
return
| | | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
def _secruleupdate(self, cls, id, arg, *repl):
if re.match("^\d+:\d$", id):
id = float(id.replace(":", "."))
elif re.match("^\d+$", id):
id = int(id)
else:
return
self.rulestate[id] = "โ"
# We don't really use the detail. This is just to record that any one rule has been "modified".
if repl:
arg = f"!{repl[0]},{arg}" # merge third parameter from `SecRuleUpdateTarget 123456 NEW_TARGET REMOVE_VAR`
if not self.update.get(id):
self.update[id] = {"vars":[], "actions":[]}
self.update[id][cls].append(arg)
|
| ︙ | ︙ |