Merge branch 'uuid' into assign-ids
This commit is contained in:
+3
-1
@@ -8,7 +8,9 @@ from version 0.14.0.
|
||||
|
||||
## Unreleased
|
||||
|
||||
Changes from this section will be contained in the next release.
|
||||
### Added
|
||||
|
||||
* sigma-uuid tool
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
+9
-1
@@ -34,7 +34,14 @@ setup(
|
||||
'Environment :: Console',
|
||||
],
|
||||
keywords='security monitoring siem logging signatures elasticsearch splunk ids sysmon',
|
||||
packages=['sigma', 'sigma.backends', 'sigma.config', 'sigma.parser', 'sigma.parser.modifiers'],
|
||||
packages=[
|
||||
'sigma',
|
||||
'sigma.backends',
|
||||
'sigma.config',
|
||||
'sigma.parser',
|
||||
'sigma.parser.modifiers',
|
||||
'sigma.output',
|
||||
],
|
||||
python_requires='~=3.6',
|
||||
install_requires=['PyYAML', 'pymisp', 'progressbar2'],
|
||||
extras_require={
|
||||
@@ -71,5 +78,6 @@ setup(
|
||||
'merge_sigma',
|
||||
'sigma2misp',
|
||||
'sigma-similarity',
|
||||
'sigma-uuid',
|
||||
]
|
||||
)
|
||||
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
# Assign UUIDs to Sigma rules and verify UUID assignment for a Sigma rule repository
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
from uuid import uuid4, UUID
|
||||
import yaml
|
||||
from sigma.output import SigmaYAMLDumper
|
||||
|
||||
argparser = ArgumentParser(description="Assign and verfify UUIDs of Sigma rules")
|
||||
argparser.add_argument("--verify", "-V", action="store_true", help="Verify existence and uniqueness of UUID assignments. Exits with error code if verification fails.")
|
||||
argparser.add_argument("--verbose", "-v", action="store_true", help="Be verbose.")
|
||||
argparser.add_argument("--recursive", "-r", action="store_true", help="Recurse into directories.")
|
||||
argparser.add_argument("--error", "-e", action="store_true", help="Exit with error code 10 on verification failures.")
|
||||
argparser.add_argument("inputs", nargs="+", help="Sigma rule files or repository directories")
|
||||
args = argparser.parse_args()
|
||||
|
||||
if args.recursive:
|
||||
paths = [ p for pathname in args.inputs for p in Path(pathname).glob("**/*") if p.is_file() ]
|
||||
else:
|
||||
paths = [ Path(pathname) for pathname in args.inputs ]
|
||||
|
||||
def print_verbose(*arg, **kwarg):
|
||||
if args.verbose:
|
||||
print(*arg, **kwarg)
|
||||
|
||||
# Define order-preserving representer from dicts/maps
|
||||
def yaml_preserve_order(self, dict_data):
|
||||
return self.represent_mapping("tag:yaml.org,2002:map", dict_data.items())
|
||||
|
||||
yaml.add_representer(dict, yaml_preserve_order)
|
||||
|
||||
uuids = set()
|
||||
passed = True
|
||||
for path in paths:
|
||||
print_verbose("Rule {}".format(str(path)))
|
||||
with path.open("r") as f:
|
||||
rules = list(yaml.safe_load_all(f))
|
||||
|
||||
if args.verify:
|
||||
i = 1
|
||||
for rule in rules:
|
||||
if "title" in rule: # Rule with a title should also have a UUID
|
||||
try:
|
||||
UUID(rule["id"])
|
||||
except ValueError: # id is not a valid UUID
|
||||
print("Rule {} in file {} has a malformed UUID '{}'.".format(i, str(path), rule["id"]))
|
||||
passed = False
|
||||
except KeyError: # rule has no id
|
||||
print("Rule {} in file {} has no UUID.".format(i, str(path)))
|
||||
passed = False
|
||||
i += 1
|
||||
else:
|
||||
newrules = list()
|
||||
changed = False
|
||||
i = 1
|
||||
for rule in rules:
|
||||
if "title" in rule and "id" not in rule: # only assign id to rules that have a title and no id
|
||||
newrule = dict()
|
||||
changed = True
|
||||
for k, v in rule.items():
|
||||
newrule[k] = v
|
||||
if k == "title": # insert id after title
|
||||
uuid = uuid4()
|
||||
newrule["id"] = str(uuid)
|
||||
print("Assigned UUID '{}' to rule {} in file {}.".format(uuid, i, str(path)))
|
||||
newrules.append(newrule)
|
||||
else:
|
||||
newrules.append(rule)
|
||||
i += 1
|
||||
|
||||
if changed:
|
||||
with path.open("w") as f:
|
||||
yaml.dump_all(newrules, f, Dumper=SigmaYAMLDumper, indent=4, width=160, default_flow_style=False)
|
||||
|
||||
if args.error and not passed:
|
||||
exit(10)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Sigma output library
|
||||
# Copyright 2016-2019 Thomas Patzke, Florian Roth
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import yaml
|
||||
|
||||
class SigmaYAMLDumper(yaml.Dumper):
|
||||
"""YAML dumper that increases amount of indentation, e.g. for lists"""
|
||||
def increase_indent(self, flow=False, indentless=False):
|
||||
return super().increase_indent(flow, False)
|
||||
@@ -5,6 +5,7 @@ from argparse import ArgumentParser
|
||||
import yaml
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from sigma.output import SigmaYAMLDumper
|
||||
|
||||
class Output(object):
|
||||
"""Output base class"""
|
||||
@@ -75,11 +76,6 @@ def get_output(output):
|
||||
else:
|
||||
return SingleFileOutput(output)
|
||||
|
||||
class SigmaYAMLDumper(yaml.Dumper):
|
||||
"""YAML dumper that increases amount of indentation, e.g. for lists"""
|
||||
def increase_indent(self, flow=False, indentless=False):
|
||||
return super().increase_indent(flow, False)
|
||||
|
||||
class AmbiguousRuleException(TypeError):
|
||||
def __init__(self, ids):
|
||||
super().__init__()
|
||||
|
||||
Reference in New Issue
Block a user