Skip to content

Commit da440b6

Browse files
committed
t
functionally working iprep parsing fixing tests Revert "Add new iprep sources" This reverts commit bdd94ac There is no need to add a different type of repo for iprep based repos. We can just parse each as if it is either. fixing integrations pulling variables from yaml instead of hard coding. refactoring logic to ignore hidden "." files removing unneeded code iprep merge start creating category class and tests creating separate file_parser separating out iprep from rule removing iprep from rule fixed iprep mapping bug moving wite_merg logic to main get iprep configs in the right dir fix get_iprep_dir adding more tests & clean up making python2 compatible removing python2 incompatible callable typing fixing requirements file_parser refactor py2 compatibility modifying rules to match new fileparser format testing docker change adding pip2 py2-ipaddress installs for all configs adding python3 install to all distros using pip from python installing pip undoing all test changes removing use of ipaddress module for max compatability
1 parent bdd94ac commit da440b6

File tree

13 files changed

+657
-52
lines changed

13 files changed

+657
-52
lines changed

suricata/update/category.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Copyright (C) 2017-2019 Open Information Security Foundation
2+
# Copyright (c) 2020 Michael Schem
3+
#
4+
# You can copy, redistribute or modify this Program under the terms of
5+
# the GNU General Public License version 2 as published by the Free
6+
# Software Foundation.
7+
#
8+
# This program is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
# GNU General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU General Public License
14+
# version 2 along with this program; if not, write to the Free Software
15+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16+
# 02110-1301, USA.
17+
18+
""" Module for parsing categories.txt files.
19+
20+
Parsing is done using regular expressions and the job of the module is
21+
to do its best at parsing out fields of interest from the categories file
22+
rather than perform a sanity check.
23+
24+
"""
25+
26+
from __future__ import print_function
27+
28+
from suricata.update import fileparser
29+
30+
import io
31+
import re
32+
import logging
33+
34+
logger = logging.getLogger(__name__)
35+
36+
# Compile a re pattern for basic iprep directive matching
37+
category_pattern = re.compile(r"^(?P<enabled>#)*[\s#]*"
38+
r"(?P<id>\d+),"
39+
r"(?P<short_name>\w+),"
40+
r"(?P<description>.*$)")
41+
42+
class Category(dict):
43+
""" Class representing an iprep category
44+
45+
The category class also acts like a dictionary.
46+
47+
Dictionary fields:
48+
49+
- **enabled**: True if the category is enabled (uncommented), false is
50+
disabled (commented)
51+
- **id**: The maximum value for the category id is hard coded at 60
52+
currently (Suricata 5.0.3).
53+
- **short_name**: The shortname that refers to the category.
54+
- **description**: A description of the category.
55+
56+
:param enabled: Optional parameter to set the enabled state of the category
57+
58+
"""
59+
60+
def __init__(self, enabled=None):
61+
dict.__init__(self)
62+
self["enabled"] = enabled
63+
self["id"] = None
64+
self["short_name"] = None
65+
self["description"] = None
66+
67+
def __getattr__(self, name):
68+
return self[name]
69+
70+
@property
71+
def id(self):
72+
""" The ID of the category.
73+
74+
:returns: An int ID of the category
75+
:rtype: int
76+
"""
77+
return int(self["id"])
78+
79+
@property
80+
def idstr(self):
81+
"""Return the gid and sid of the rule as a string formatted like:
82+
'[id]'"""
83+
return "[%s]" % str(self.id)
84+
85+
def __str__(self):
86+
""" The string representation of the category.
87+
88+
If the category is disabled it will be returned as commented out.
89+
"""
90+
return self.format()
91+
92+
def format(self):
93+
return "{0}{1},{2},{3}".format(u"" if self["enabled"] else u"# ",
94+
self['id'],
95+
self['short_name'],
96+
self['description'])
97+
98+
99+
def parse(buf, group=None):
100+
""" Parse a single Iprep category from a string buffer.
101+
102+
:param buf: A string buffer containing a single Iprep category.
103+
104+
:returns: An instance of a :py:class:`.Category` representing the parsed Iprep category
105+
"""
106+
107+
if type(buf) == type(b""):
108+
buf = buf.decode("utf-8")
109+
buf = buf.strip()
110+
111+
m = category_pattern.match(buf)
112+
if not m:
113+
return None
114+
115+
if m.group("enabled") == "#":
116+
enabled = False
117+
else:
118+
enabled = True
119+
120+
# header = m.group("header").strip()
121+
122+
category = Category(enabled=enabled)
123+
124+
category["id"] = int(m.group("id").strip())
125+
126+
if not 0 < category["id"] < 60:
127+
logging.error("Category id of {0}, not valid. Id is required to be between 0 and 60.".format(category["id"]))
128+
return None
129+
130+
category["short_name"] = m.group("short_name").strip()
131+
132+
category["description"] = m.group("description").strip()
133+
134+
return category
135+
136+
137+
def parse_fileobj(fileobj, group=None):
138+
""" Parse multiple ipreps from a file like object.
139+
140+
Note: At this time ipreps must exist on one line.
141+
142+
:param fileobj: A file like object to parse rules from.
143+
144+
:returns: A list of :py:class:`.Iprep` instances, one for each rule parsed
145+
"""
146+
return fileparser.parse_fileobj(fileobj, parse, group)
147+
148+
def parse_file(filename, group=None):
149+
""" Parse multiple ipreps from the provided filename.
150+
151+
:param filename: Name of file to parse ipreps from
152+
153+
:returns: A list of :py:class:`.Iprep` instances, one for each iprep parsed
154+
"""
155+
with io.open(filename, encoding="utf-8") as fileobj:
156+
return parse_fileobj(fileobj, group)
157+

suricata/update/commands/addsource.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ def register(parser):
3737
help="Additional HTTP header to add to requests")
3838
parser.add_argument("--no-checksum", action="store_false",
3939
help="Skips downloading the checksum URL")
40-
parser.add_argument("--iprep", action="store_true",
41-
help="Identifies source as an IPRep Source")
4240
parser.set_defaults(func=add_source)
4341

4442

@@ -69,8 +67,6 @@ def add_source():
6967

7068
header = args.http_header if args.http_header else None
7169

72-
is_iprep = args.iprep
73-
7470
source_config = sources.SourceConfiguration(
75-
name, header=header, url=url, checksum=checksum, is_iprep=is_iprep)
71+
name, header=header, url=url, checksum=checksum)
7672
sources.save_source_config(source_config)

suricata/update/config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
DROP_CONF_KEY = "drop-conf"
5050
LOCAL_CONF_KEY = "local"
5151
OUTPUT_KEY = "output"
52+
IPREP_OUTPUT_KEY = "iprep"
5253
DIST_RULE_DIRECTORY_KEY = "dist-rule-directory"
5354

5455
if has_defaults:
@@ -83,6 +84,7 @@
8384
# The default file patterns to ignore.
8485
"ignore": [
8586
"*deleted.rules",
87+
".*"
8688
],
8789
}
8890

@@ -136,6 +138,12 @@ def get_output_dir():
136138
return _config[OUTPUT_KEY]
137139
return os.path.join(get_state_dir(), "rules")
138140

141+
def get_iprep_dir():
142+
"""Get the rule output directory."""
143+
if IPREP_OUTPUT_KEY in _config:
144+
return _config[IPREP_OUTPUT_KEY]
145+
return os.path.join(get_state_dir(), "iprep")
146+
139147
def args():
140148
"""Return sthe parsed argument object."""
141149
return _args

suricata/update/fileparser.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Copyright (C) 2017-2019 Open Information Security Foundation
2+
# Copyright (c) 2020 Michael Schem
3+
#
4+
# You can copy, redistribute or modify this Program under the terms of
5+
# the GNU General Public License version 2 as published by the Free
6+
# Software Foundation.
7+
#
8+
# This program is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
# GNU General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU General Public License
14+
# version 2 along with this program; if not, write to the Free Software
15+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16+
# 02110-1301, USA.
17+
18+
""" Module for files with either rules, ipreps, or category files.
19+
20+
Parse funcitons
21+
22+
"""
23+
24+
from __future__ import print_function
25+
26+
import sys
27+
import re
28+
import logging
29+
import io
30+
31+
32+
logger = logging.getLogger(__name__)
33+
34+
35+
def parse_fileobj(fileobj, parse, group=None):
36+
""" Parse multiple line based items from a file like object.
37+
38+
Note: At this point items must exist on one line
39+
40+
:param fileobj: A file like object to parse items from.
41+
:param parse: A function used to parse items.
42+
43+
:returns: A list of :py:class:`.Rule`, :py:class:`.Iprep`, or :py:class:`.Category`
44+
instances depending on what parsing function is passed in.
45+
"""
46+
items = []
47+
buf = ""
48+
for line in fileobj:
49+
try:
50+
if type(line) == type(b""):
51+
line = line.decode()
52+
except:
53+
pass
54+
if line.rstrip().endswith("\\"):
55+
buf = "%s%s " % (buf, line.rstrip()[0:-1])
56+
continue
57+
buf = buf + line
58+
try:
59+
item = parse(buf, group)
60+
if item:
61+
items.append(item)
62+
except Exception as err:
63+
logger.error("Failed to parse: %s: %s", buf.rstrip(), err)
64+
buf = ""
65+
return items
66+
67+
68+
def parse_file(filename, group=None):
69+
""" Parse multiple rules from the provided filename.
70+
71+
:param filename: Name of file to parse ipreps from
72+
73+
:returns: A list of .rules files or :py:class:`.Iprep` instances
74+
for .list files, one for each rule parsed
75+
"""
76+
with io.open(filename, encoding="utf-8") as fileobj:
77+
return parse_fileobj(fileobj, group)

0 commit comments

Comments
 (0)