GUI editor to tame mod_security rules

⌈⌋ branch:  modseccfg


Check-in [cb3a619fe0]

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

Overview
Comment:publish data test (mostly vhost/secrule extraction)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: cb3a619fe085b6aa7bb0cd4904502a288d1404375b40b34fbea5765c126a53f7
User & Date: mario 2020-12-01 20:49:54
Context
2020-12-01
20:52
Build task for modsec-flameeyes.deb check-in: b362727f15 user: mario tags: trunk
20:49
publish data test (mostly vhost/secrule extraction) check-in: cb3a619fe0 user: mario tags: trunk
20:46
Release as 0.5.0 check-in: 6a7949da87 user: mario tags: trunk, 0.5.0
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added pytest.ini.















>
>
>
>
>
>
>
1
2
3
4
5
6
7
[pytest]
minversion = 1.0
addopts = -ra -q --ignore=test/util.py --ignore=test/_*.py --ignore=test/__*.py
testpaths = test/
python_files = *.py
# Yes sure pytest, why not do the obvious thing anyway
python_functions = !_* [a-z]* [A-Z]* !_*

Added test/Makefile.



















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
all:	test

test:
	echo "Test data not transferable, due to embedded fn: values."
	pytest -v -v -v -v

clean:
	rm test/*.out
	echo "Needs a few pytest reruns to generate .out files"

Added test/plugin_meta.py.























>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
#t:t
#d:simple pluginconf read

import pluginconf, modseccfg


def pluginconf_meta_read():
    pluginconf.plugin_meta(module="modseccfg").get("version")

def _dear_pytest_this_shall_not_run():
    assert False

Added test/rule.py.

































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#t:t
#d:basic rule parsing

import pytest, util
from modseccfg import vhosts

    
def _rule(i):
    def run():
        inp, out = f"test/rule_{i}.inp", f"test/rule_{i}.out"
        util.run(inp, vhosts.vhost, out)
    return run

rule_1 = _rule(1)
rule_2 = _rule(2)
rule_3 = _rule(3)
rule_4 = _rule(4)
rule_5 = _rule(5)
rule_6 = _rule(6)
rule_7 = _rule(7)
rule_8 = _rule(8)
rule_9 = _rule(9)

rule_10 = _rule(10)
rule_11 = _rule(11)
rule_12 = _rule(12)

rule_20 = _rule(20)
rule_21 = _rule(21)
rule_22 = _rule(22)
rule_25 = _rule(25)
    

Added test/rule_1.inp.



>
1
SecRule ANY "@always" "id:1,phase:2,block:3,t:four"

Added test/rule_1.out.









































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "1": {
            "id": 1,
            "chained_to": 0,
            "msg": "ANY   @always",
            "flags": [
                "t:four"
            ],
            "params": {
                "phase": "2",
                "block": "3"
            },
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "ANY",
            "pattern": "@always",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 1
    },
    "wrap": []
}

Added test/rule_10.inp.







>
>
>
1
2
3
SecRule DUPLICATE "@x" "id:10,pass"

SecRule DUPLICATE "@x" "id:10,deny"

Added test/rule_10.out.





































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "10": {
            "id": 10,
            "chained_to": 0,
            "msg": "DUPLICATE   @x",
            "flags": [
                "deny"
            ],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "DUPLICATE",
            "pattern": "@x",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 10,
        "2": 10
    },
    "wrap": []
}

Added test/rule_11.inp.



>
1
SecRule LOOK_NO_QUOTES @eq0 id:11,pass

Added test/rule_11.out.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "11": {
            "id": 11,
            "chained_to": 0,
            "msg": "LOOK_NO_QUOTES   @eq0",
            "flags": [
                "pass"
            ],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "LOOK_NO_QUOTES",
            "pattern": "@eq0",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 11
    },
    "wrap": []
}

Added test/rule_12.inp.



>
1
SecRule "LOOK_SOME_QUOTES" @eq0 'id:12,pass'

Added test/rule_12.out.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "12": {
            "id": 12,
            "chained_to": 0,
            "msg": "LOOK_SOME_QUOTES   @eq0",
            "flags": [
                "pass"
            ],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "LOOK_SOME_QUOTES",
            "pattern": "@eq0",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 12
    },
    "wrap": []
}

Added test/rule_2.inp.



>
1
SecRule AND "@some" "id:2,tag:TWO"

Added test/rule_2.out.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "2": {
            "id": 2,
            "chained_to": 0,
            "msg": "AND   @some",
            "flags": [],
            "params": {},
            "tags": [
                "TWO"
            ],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "AND",
            "pattern": "@some",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 2
    },
    "wrap": []
}

Added test/rule_20.inp.



>
1
SecRule CTL ctl "id:20,ctl:restart=1"

Added test/rule_20.out.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "20": {
            "id": 20,
            "chained_to": 0,
            "msg": "CTL   ctl",
            "flags": [],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {
                "restart": "1"
            },
            "setvar": {},
            "vars": "CTL",
            "pattern": "ctl",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 20
    },
    "wrap": []
}

Added test/rule_21.inp.



>
1
SecRule VAR ctl "id:21,setvar:restart=1"

Added test/rule_21.out.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "21": {
            "id": 21,
            "chained_to": 0,
            "msg": "VAR   ctl",
            "flags": [],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {
                "restart": "1"
            },
            "vars": "VAR",
            "pattern": "ctl",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 21
    },
    "wrap": []
}

Added test/rule_22.inp.



>
1
SecRule PARAMS @pm "id:22,setenv:CRON=x"

Added test/rule_22.out.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "22": {
            "id": 22,
            "chained_to": 0,
            "msg": "PARAMS   @pm",
            "flags": [],
            "params": {
                "setenv": "CRON=x"
            },
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "PARAMS",
            "pattern": "@pm",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 22
    },
    "wrap": []
}

Added test/rule_25.inp.









>
>
>
>
1
2
3
4
SecRule PAIRED p1 "id:25,chain"
SecRule PAIRED p2 "pass,chain"
SecRule PAIRED p3 "pass,chain"
SecRule PAIRED p4 "pass,msg:end"

Added test/rule_25.out.













































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
{
    "fn": "test",
    "t": "rules",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "25": {
            "id": 25,
            "chained_to": 0,
            "msg": "PAIRED   p1",
            "flags": [
                "chain"
            ],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "PAIRED",
            "pattern": "p1",
            "hidden": false,
            "wrap": false
        },
        "25.1": {
            "id": 25.1,
            "chained_to": 25,
            "msg": "PAIRED   p2",
            "flags": [
                "pass",
                "chain"
            ],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "PAIRED",
            "pattern": "p2",
            "hidden": false,
            "wrap": false
        },
        "25.2": {
            "id": 25.2,
            "chained_to": 25,
            "msg": "PAIRED   p3",
            "flags": [
                "pass",
                "chain"
            ],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "PAIRED",
            "pattern": "p3",
            "hidden": false,
            "wrap": false
        },
        "25.3": {
            "id": 25.3,
            "chained_to": 25,
            "msg": "end",
            "flags": [
                "pass"
            ],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "PAIRED",
            "pattern": "p4",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 25
    },
    "wrap": []
}

Added test/rule_3.inp.



>
1
SecRule OR "@none" "id:3,-"

Added test/rule_3.out.































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "3": {
            "id": 3,
            "chained_to": 0,
            "msg": "OR   @none",
            "flags": [],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "OR",
            "pattern": "@none",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 3
    },
    "wrap": []
}

Added test/rule_30.inp.



>
1
SecAction "id:30,pass,setvar:x=2"

Added test/rule_30.out.







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "30": {
            "id": 30,
            "chained_to": 0,
            "msg": "@SecAction {'x': '2'}",
            "flags": [
                "pass"
            ],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {
                "x": "2"
            },
            "vars": "@SecAction",
            "pattern": "setvar:",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 30
    },
    "wrap": []
}

Added test/rule_4.inp.



>
1
SecRule WITH "@out" "id:4,msg:"

Added test/rule_4.out.































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "4": {
            "id": 4,
            "chained_to": 0,
            "msg": "WITH   @out",
            "flags": [],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "WITH",
            "pattern": "@out",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 4
    },
    "wrap": []
}

Added test/rule_5.inp.



>
1
SecRule HAS|NO|KEYS "?" "id:5,:"

Added test/rule_5.out.































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "5": {
            "id": 5,
            "chained_to": 0,
            "msg": "HAS|NO|KEYS   ?",
            "flags": [],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "HAS|NO|KEYS",
            "pattern": "?",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 5
    },
    "wrap": []
}

Added test/rule_6.inp.



>
1
SecRule &DO,!NOT,CARE: "./." "id:6,deny"

Added test/rule_6.out.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "6": {
            "id": 6,
            "chained_to": 0,
            "msg": "&DO,!NOT,CARE:   ./.",
            "flags": [
                "deny"
            ],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "&DO,!NOT,CARE:",
            "pattern": "./.",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 6
    },
    "wrap": []
}

Added test/rule_7.inp.



>
1
SecRule ARGS:normal "@rx ^.*$" "id:7,logdata:'things'"

Added test/rule_7.out.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "7": {
            "id": 7,
            "chained_to": 0,
            "msg": "things",
            "flags": [],
            "params": {
                "logdata": "things"
            },
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "ARGS:normal",
            "pattern": "@rx ^.*$",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 7
    },
    "wrap": []
}

Added test/rule_8.inp.



>
1
SecRule ARGS:normal "@pmFromFile file.txt" "id:8,msg:it_is_what_it_is"

Added test/rule_8.out.































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "8": {
            "id": 8,
            "chained_to": 0,
            "msg": "it_is_what_it_is",
            "flags": [],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "ARGS:normal",
            "pattern": "@pmFromFile file.txt",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 8
    },
    "wrap": []
}

Added test/rule_9.inp.



>
1
SecRule ARGS:normal "@pmFromFile file.txt" "id:9,msg:it_is_what_it_is"

Added test/rule_9.out.































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
    "fn": "test",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {},
    "rulestate": {},
    "ruledecl": {
        "9": {
            "id": 9,
            "chained_to": 0,
            "msg": "it_is_what_it_is",
            "flags": [],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {},
            "setvar": {},
            "vars": "ARGS:normal",
            "pattern": "@pmFromFile file.txt",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "0": 9
    },
    "wrap": []
}

Added test/secdirectives.inp.















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SecRuleRemoveById 123  123   # @SecAction {'tx.crs_setup_version': '320'}
SecRuleRemoveById 9001110    # REQUEST_FILENAME   @endsWith /core/install.php
SecAuditEngine RelevantOnly
SecAuditLogType Concurrent
SecRuleInheritance On
SecRuleEngine DetectionOnly
SecDebugLogLevel 8
SecUploadFileMode octal_mode
SecPdfProtect Off
SecHashEngine On
SecHashKey RemoteIP
SecAction "id:5999,pass,nolog,noauditlog,t:none, \
    setvar:tx.crs_exclusions_xenforo=1, \
       ctl:ruleRemoveById=900130, \
    setvar:tx.block_spammer_ip=1, \
       ctl:ruleRemoveById=900500, \
    setvar:tx.do_reput_block=1, \
       ctl:ruleRemoveById=900960, \
    setvar:tx.crs_exclusions_dokuwiki=1, \
    setvar:tx.block_suspicious_ip=1, \
    setvar:tx.crs_exclusions_drupal=1, \
    setvar:tx.crs_exclusions_nextcloud=1, \
    setvar:tx.crs_exclusions_wordpress=1"

Added test/secdirectives.out.

























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{
    "fn": "/home/mario/projects/modseccfg/test/secdirectives.inp",
    "t": "cfg",
    "name": "",
    "logs": [],
    "cfg": {
        "secauditengine": "RelevantOnly",
        "secauditlogtype": "Concurrent",
        "secruleinheritance": "On",
        "secruleengine": "DetectionOnly",
        "secdebugloglevel": "8",
        "secuploadfilemode": "octal_mode",
        "secpdfprotect": "Off",
        "sechashengine": "On",
        "sechashkey": "RemoteIP"
    },
    "rulestate": {
        "123": "\u274c",
        "9001110": "\u274c"
    },
    "ruledecl": {
        "5999": {
            "id": 5999,
            "chained_to": 0,
            "msg": "@SecAction {'tx.crs_exclusions_xenforo': '1', 'tx.block_spammer_ip': '1', 'tx.do_reput_block': '1', 'tx.crs_exclusions_dokuwiki': '1', 'tx.block_suspicious_ip': '1', 'tx.crs_exclusions_drupal': '1', 'tx.crs_exclusions_nextcloud': '1', 'tx.crs_exclusions_wordpress': '1'}",
            "flags": [
                "pass",
                "nolog",
                "noauditlog",
                "t:none"
            ],
            "params": {},
            "tags": [],
            "tag_primary": "",
            "ctl": {
                "ruleRemoveById": "900960"
            },
            "setvar": {
                "tx.crs_exclusions_xenforo": "1",
                "tx.block_spammer_ip": "1",
                "tx.do_reput_block": "1",
                "tx.crs_exclusions_dokuwiki": "1",
                "tx.block_suspicious_ip": "1",
                "tx.crs_exclusions_drupal": "1",
                "tx.crs_exclusions_nextcloud": "1",
                "tx.crs_exclusions_wordpress": "1"
            },
            "vars": "@SecAction",
            "pattern": "setvar:",
            "hidden": false,
            "wrap": false
        }
    },
    "update": {},
    "warn": "",
    "linemap": {
        "11": 5999
    },
    "wrap": []
}

Added test/secdirectives.py.

























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
#t:t
#d:lots of SecEngine directives

import pytest, util
from modseccfg import vhosts

@util.io
def secdirectives(inp, out):
    src = util.inp_read(inp)
    vh = vhosts.vhost(inp, src)
    util.out_test(vh, out)

Added test/util.py.











































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# encoding: utf8
# type: decorator
# title: test utils
# description: data-driven helper code
# depends: python (>= 3.4)
#
# Adds .inp and .out file stubs.
# Where .inp usually contains source, and is checked
# against an .out JSON representation of whatever
# transformation happened.
# Mostly for vhost() and secrule() properties.
#

import pytest, functools, re, os, json
__test__ = False  # pytest: "Nah, let's not be consistent"
    

# Adds two params (inp, out) with filenames
# named after the wrapped function.
@functools.singledispatch
def io(func):
    print(func.__name__)
    def wrapper():
        __dir__ = re.sub("[^/]+$", "", __file__) # ought to use funcs` path
        inp_fn = __dir__ + func.__name__ + ".inp"
        out_fn = __dir__ + func.__name__ + ".out"
        return func(inp_fn, out_fn)
    return wrapper

# should have been a decorator too
def run(inp, subject, out):
    src = inp_read(inp)
    vh = subject(inp, src)
    if hasattr(vh, "fn"): vh.fn="test"
    out_test(vh, out)

# read
def inp_read(inp_fn):
    return open(inp_fn, "r", encoding="utf8").read()

# write output, or compare to existing result
def out_test(result, out_fn):

    # turn e.g. vhost/secrule into dict
    result = _recurse_obj_to_dict(result)

    # write current data, and/or read previous dump
    if not os.path.exists(out_fn):
        open(out_fn, "w", encoding="utf8").write(json.dumps(result, indent=4))
    else:
        out = json.loads(open(out_fn, "r", encoding="utf8").read())

    print (out)
    print (result)
    # test
    assert out == result, f"Output {out_fn} doesn't match current data"
    passed = 1

    # else prepare update (but assert can't double as expr)
    if not passed:
        open(out_fn+".new", "w", encoding="utf8").write(json.dumps(result, indent=4))

# flatten objects to dictionaries, keys to strings
def _recurse_obj_to_dict(o):
    if hasattr(o, "__dict__"):
        o = o.__dict__
    if isinstance(o, dict):
        o = { str(k): _recurse_obj_to_dict(v)  for k,v  in  o.items() }
    return o

Added test/vhost.py.





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#t:t
#d:basic rule parsing

import pytest, util
from modseccfg import vhosts

@util.io
def vhost_simple(inp, out):
    util.run(inp, vhosts.vhost, out)

@util.io
def vhost_list(inp, out):
    util.run(inp, vhosts.vhost, out)
    

Added test/vhost_list.inp.











































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# This will just collect one `vhost`, with the first directives sticking,
# for server name and documentroot at least.
# We don't actually collect vhosts per section, but on per-filename basis.

<VirtualHost *>
 ServerName first
 DocumentRoot /tmp
</VirtualHost>

<VirtualHost *>
 ServerName second
 DocumentRoot /dev/null
</VirtualHost>

<VirtualHost *>
 ServerName third
 ServerAlias *.localnet
 DocumentRoot /
</VirtualHost>

# end

Added test/vhost_list.out.































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
    "fn": "test",
    "t": "vhost",
    "name": "first",
    "logs": [],
    "cfg": {
        "documentroot": "/tmp"
    },
    "rulestate": {},
    "ruledecl": {},
    "update": {},
    "warn": "Unreasonable number of <VirtualHost> entries in conf. It probably shouldn't be edited through modseccfg.",
    "linemap": {},
    "wrap": []
}

Added test/vhost_simple.inp.







>
>
>
1
2
3
# comment
ServerName example.com
SecRuleDisablyById 12345

Added test/vhost_simple.out.































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
    "fn": "test",
    "t": "vhost",
    "name": "example.com",
    "logs": [],
    "cfg": {
        "secruledisablybyid": "12345"
    },
    "rulestate": {},
    "ruledecl": {},
    "update": {},
    "warn": "",
    "linemap": {},
    "wrap": []
}

Added tox.ini.



















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
[tox]
envlist = py37

[testenv]
deps =
   pytest
   pluginconf
commands =
   pytest test