From 3a7160d52b720408ec899558d147e6e8a3d50994 Mon Sep 17 00:00:00 2001 From: christophetd Date: Sat, 23 Feb 2019 13:20:20 +0100 Subject: [PATCH] Accept backend options from a configuration file (closes #213) --- tools/sigma/backends/base.py | 57 ++++++++++++++++++++++++++++++++---- tools/sigmac | 4 ++- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/tools/sigma/backends/base.py b/tools/sigma/backends/base.py index 2b6cab3cb..0c4c32c84 100644 --- a/tools/sigma/backends/base.py +++ b/tools/sigma/backends/base.py @@ -14,28 +14,73 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . +import sys + import sigma +import yaml + from .mixins import RulenameCommentMixin, QuoteCharMixin + class BackendOptions(dict): - """Object contains all options that should be passed to the backend from command line (or other user interfaces)""" + """ + Object containing all the options that should be passed to the backend. + + The options can come from command line and a YAML configuration file, and will be merged together. + Options from the command line take precedence. + """ - def __init__(self, options): + def __init__(self, options, config_file): + """ + :param options: unparsed options coming from the CLI + :param config_file: path to a YAML configuration file """ - Receives the argparser result from the backend option paramater value list (nargs=*) and builds the dict from it. There are two option types: - * key=value: self{key} = value - * key: self{key} = True + self._load_config_file(config_file) + self._parse_options(options) + + def _parse_options(self, options): """ - if options == None: + Populates options from the unparsed options of the CLI + + :param options: list unparsed options from the CLI. + Each option can have one of the following formats: + - "key=value": the option key:value will be passed to the backend + - "key": the option key:True will be passed to the backend + """ + + if options is None: return + for option in options: parsed = option.split("=", 1) try: self[parsed[0]] = parsed[1] except IndexError: + # If the option is present but doesn't map to a value, treat it as a boolean flag self[parsed[0]] = True + def _load_config_file(self, path): + """ + Populates options from a configuration file + + :param path: Path to the configuration file + """ + if path is None: + return + + try: + with open(path, 'r') as config_file: + backend_config = yaml.safe_load(config_file.read()) + for key in backend_config: + self[key] = backend_config[key] + except (IOError, OSError) as e: + print("Failed to open backend configuration file '%s': %s" % (path, str(e)), file=sys.stderr) + exit(1) + except yaml.YAMLError as e: + print("Failed to parse backend configuration file '%s' as valid YAML: %s" % (path, str(e)), file=sys.stderr) + exit(1) + ### Generic backend base classes class BaseBackend: """Base class for all backends""" diff --git a/tools/sigmac b/tools/sigmac index 0d8e0a02d..0bb58f824 100755 --- a/tools/sigmac +++ b/tools/sigmac @@ -90,6 +90,7 @@ argparser.add_argument("--target-list", "-l", action="store_true", help="List av argparser.add_argument("--config", "-c", help="Configuration with field name and index mapping for target environment") argparser.add_argument("--output", "-o", default=None, help="Output file or filename prefix if multiple files are generated") argparser.add_argument("--backend-option", "-O", action="append", help="Options and switches that are passed to the backend") +argparser.add_argument("--backend-config", help="Configuration file containing options to pass to the backend") argparser.add_argument("--defer-abort", "-d", action="store_true", help="Don't abort on parse or conversion errors, proceed with next rule. The exit code from the last error is returned") argparser.add_argument("--ignore-backend-errors", "-I", action="store_true", help="Only return error codes for parse errors and ignore errors for rules that cause backend errors. Useful, when you want to get as much queries as possible.") argparser.add_argument("--verbose", "-v", action="store_true", help="Be verbose") @@ -133,7 +134,8 @@ if cmdargs.config: print("Sigma configuration parse error in %s: %s" % (conffile, str(e)), file=sys.stderr) exit(7) -backend_options = BackendOptions(cmdargs.backend_option) + +backend_options = BackendOptions(cmdargs.backend_option, cmdargs.backend_config) backend = backends.getBackend(cmdargs.target)(sigmaconfig, backend_options) filename = cmdargs.output if filename: