Passed
Branch master (7424d3)
by jvo
01:37
created

FlaskrTestCase.test_explain_rule()   A

Complexity

Conditions 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 12
rs 9.4285
1
from time import strftime, localtime
2
import re
3
4
from spike import create_app
5
from spike.model import db
6
from spike.model.naxsi_rules import NaxsiRules
7
8
try:
9
    from urlparse import urlparse
10
except ImportError:  # python3
11
    from urllib.parse import urlparse
12
13
import unittest
14
15
16
class FlaskrTestCase(unittest.TestCase):
17
    def setUp(self):
18
        app = create_app()
19
        db.init_app(app)
20
        app.config['TESTING'] = True
21
        self.app = app.test_client()
22
        self.created_rules = list()
23
        self.__create_rule()
24
25
    def tearDown(self):
26
        self.__delete_rule()
27
28
    def __create_rule(self):
29
        """
30
31
        :return int: The id of the new rule
32
        """
33
        current_sid = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first()
34
        current_sid = 1337 if current_sid is None else current_sid.sid + 1
35
36
        db.session.add(NaxsiRules(u'POUET', 'str:test', u'BODY', u'$SQL:8', current_sid, u'WEB_APPS',
37
                                  u'f hqewifueiwf hueiwhf uiewh fiewh fhw', '1', True, 1457101045))
38
        self.created_rules.append(current_sid)
39
        return int(current_sid)
40
41
    def __delete_rule(self, sid=None):
42
        if sid:
43
            db.session.delete(NaxsiRules.query.filter(sid == NaxsiRules.sid).first())
44
        for sid in self.created_rules:
45
            _rule = NaxsiRules.query.filter(sid == NaxsiRules.sid).first()
46
            if _rule:
47
                db.session.delete(_rule)
48
49
    def test_index(self):
50
        rv = self.app.get('/', follow_redirects=True)
51
        self.assertEqual(rv.status_code, 200)
52
        self.assertIn('<title>SPIKE! - WAF Rules Builder</title>', str(rv.data))
53
        self.assertTrue(re.search(r'<h2>Naxsi - Rules \( \d+ \)</h2>', str(rv.data)) is not None)
54
55
    def test_view(self):
56
        _rule = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first()
57
        rv = self.app.get('/rules/view/%d' % _rule.sid)
58
        self.assertEqual(rv.status_code, 200)
59
60
        _sid = _rule.sid + 1
61
        rv = self.app.get('/rules/view/%d' % _sid)
62
        self.assertEqual(urlparse(rv.location).path, '/rules/')
63
64
    def test_new_rule(self):
65
        rv = self.app.get('/rules/new')
66
        self.assertEqual(rv.status_code, 200)
67
68
        data = {
69
            'msg': 'this is a test message',
70
            'detection': 'str:DETECTION',
71
            'mz': 'BODY',
72
            'custom_mz_val': '',
73
            'negative': 'checked',
74
            'score_$SQL': 8,
75
            'score': '$SQL',
76
            'rmks': 'this is a test remark',
77
            'ruleset': 'WEB_APPS'
78
        }
79
        rv = self.app.post('/rules/new', data=data, follow_redirects=True)
80
81
        _rule = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first()
82
83
        self.assertIn(('<li> - OK: created %d : %s</li>' % (_rule.sid, _rule.msg)), str(rv.data))
84
        self.assertEqual(_rule.msg, data['msg'])
85
        self.assertEqual(_rule.detection, data['detection'])
86
        self.assertEqual(_rule.mz, data['mz'])
87
        self.assertEqual(_rule.score, data['score'] + ':' + str(data['score_$SQL']))
88
        self.assertEqual(_rule.rmks, data['rmks'])
89
        self.assertEqual(_rule.ruleset, data['ruleset'])
90
        db.session.delete(_rule)
91
        db.session.commit()
92
93
        # Try to insert an invalid rule
94
        _sid = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first().sid
95
        data['detection'] = 'this string does not start with "str:" or "rx:", sorry'
96
        rv = self.app.post('/rules/new', data=data)
97
        self.assertEqual(rv.status_code, 302)
98
        self.assertEqual(_sid, NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first().sid)
99
100
101
    def test_del_rule(self):
102
        _rule = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first()
103
104
        db.session.add(NaxsiRules(u'PIF', 'str:test', u'BODY', u'$SQL:8', _rule.sid + 1, u'WEB_APPS',
105
                                  u'f hqewifueiwf hueiwhf uiewh fiewh fhw', '1', True, 1457101045))
106
        _sid = _rule.sid + 1
107
        rv = self.app.get('/rules/del/%d' % _sid)
108
        self.assertEqual(rv.status_code, 302)
109
110
        _rule = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first()
111
        self.assertEqual(_rule.sid, _rule.sid)
112
113
    def test_explain_rule(self):
114
        rv = self.app.get('/rules/explain/')
115
        self.assertEqual(rv.status_code, 302)
116
        self.assertEqual(urlparse(rv.location).path, '/rules/')
117
118
        _rule = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first()
119
        rv = self.app.get('/rules/explain/?rule={0}'.format(_rule.sid + 1), follow_redirects=True)
120
        self.assertIn('Not rule with id {0}'.format(_rule.sid + 1), str(rv.data))
121
122
        rv = self.app.get('/rules/explain/?rule={0}'.format(_rule.sid))
123
        self.assertEqual(rv.status_code, 200)
124
        self.assertIn(_rule.explain(), str(rv.data))
125
126
    def test_plain_rule(self):
127
        _rule = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first()
128
        rv = self.app.get('/rules/plain/%d' % _rule.sid)
129
        self.assertEqual(rv.status_code, 200)
130
        rdate = strftime("%F - %H:%M", localtime(float(str(_rule.timestamp))))
131
        rmks = "# ".join(_rule.rmks.strip().split("\n"))
132
        expected = """#
133
# sid: {0} | date: {1}
134
#
135
# {2}
136
#
137
{3}""".format(_rule.sid, rdate, rmks, str(_rule))
138
        self.assertEqual(expected.encode(), rv.data)
139
140
    def test_deact_rule(self):
141
        last_insert = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first().sid
142
143
        rv = self.app.get('/rules/deact/%d' % last_insert)  # deactivate
144
        self.assertEqual(rv.status_code, 200)
145
        _rule = NaxsiRules.query.filter(NaxsiRules.sid == last_insert).first()
146
        self.assertEqual(_rule.active, 0)
147
148
        rv = self.app.get('/rules/deact/%d' % last_insert)  # activate
149
        self.assertEqual(rv.status_code, 200)
150
        _rule = NaxsiRules.query.filter(NaxsiRules.sid == last_insert).first()
151
        self.assertEqual(_rule.active, 1)
152
153
        non_existent_sid = last_insert + 1
154
        rv = self.app.get('/rules/deact/%d' % non_existent_sid)
155
        self.assertEqual(rv.status_code, 302)
156
157
        rv = self.app.get('/rules/deact/')
158
        self.assertEqual(rv.status_code, 404)
159
160
    def test_search_rule(self):
161
        rv = self.app.get('/rules/search/')
162
        self.assertEqual(rv.status_code, 302)
163
164
        rv = self.app.get('/rules/search/?s=a')
165
        self.assertEqual(rv.status_code, 302)
166
167
        rv = self.app.get('/rules/search/?s="OR 1=1;--')
168
        self.assertEqual(rv.status_code, 200)
169
        self.assertIn('<input type="text" name="s" size="20" value="&#34;OR 1=1;--"', str(rv.data))
170
        self.assertIn('<p><strong>Search: OR 11--</strong></p>', str(rv.data))  # filtered data
171
172
        rv = self.app.get('/rules/search/?s=1337')  # get rule by id
173
        self.assertEqual(rv.status_code, 200)
174
175
    def test_edit_rule(self):
176
        non_existent_sid = NaxsiRules.query.order_by(NaxsiRules.sid.desc()).first().sid + 1
177
        rv = self.app.get('/rules/edit/%d' % non_existent_sid)
178
        self.assertEqual(rv.status_code, 302)
179
180
    def test_parse_rule(self):
181
        rule_parser = NaxsiRules()
182
        rv = rule_parser.parse_rule("""MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;""")
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (200/120).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
183
        self.assertEqual(rv, True)
184
185
        self.assertEqual(rule_parser.warnings, ['Cookie in $HEADERS_VAR:Cookie is not lowercase. naxsi is case-insensitive', 'rule IDs below 10k are reserved (1000)'])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (167/120).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
186
        rv = rule_parser.parse_rule("""BasicRule "rx:^ratata$" "mz:$URL:/foobar|$BODY_VAR_X:^tutu$" id:4200001 "s:$SQL:8";""")
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (126/120).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
187
        self.assertEqual(rv, False)
188
        self.assertIn('$BODY_VAR_X', str(rule_parser.error))
189
        self.assertIn('$URL', str(rule_parser.error))
190
        self.assertIn("You can't mix static $* with regex $*_X", str(rule_parser.error))
191
        self.assertIn("parsing of element 'mz:$URL:/foobar|$BODY_VAR_X:^tutu$' failed.", rule_parser.error)
192