Пример #1
0
def handler(q=False):
    if q is False:
        return False
    request = json.loads(q)
    if not request.get('sigma'):
        misperrors['error'] = 'Sigma rule missing'
        return misperrors
    config = SigmaConfiguration()
    f = io.TextIOWrapper(io.BytesIO(request.get('sigma').encode()),
                         encoding='utf-8')
    parser = SigmaCollectionParser(f, config)
    targets = []
    results = []
    for t in sigma_targets:
        backend = getBackend(t)(config, {'rulecomment': False})
        try:
            parser.generate(backend)
            result = backend.finalize()
            if result:
                results.append(result)
                targets.append(t)
        except Exception:
            continue
    d_result = {t: r.strip() for t, r in zip(targets, results)}
    return {
        'results': [{
            'types': mispattributes['output'],
            'values': d_result
        }]
    }
Пример #2
0
def handler(q=False):
    if q is False:
        return False
    request = json.loads(q)
    if not request.get('sigma'):
        misperrors['error'] = 'Sigma rule missing'
        return misperrors
    config = SigmaConfiguration()
    backend_options = BackendOptions(None)
    f = io.TextIOWrapper(io.BytesIO(request.get('sigma').encode()),
                         encoding='utf-8')
    parser = SigmaCollectionParser(f, config, None)
    targets = []
    old_stdout = sys.stdout
    result = io.StringIO()
    sys.stdout = result
    for t in sigma_targets:
        backend = getBackend(t)(config, backend_options, None)
        try:
            parser.generate(backend)
            backend.finalize()
            print("#NEXT")
            targets.append(t)
        except Exception:
            continue
    sys.stdout = old_stdout
    results = result.getvalue()[:-5].split('#NEXT')
    d_result = {t: r.strip() for t, r in zip(targets, results)}
    return {
        'results': [{
            'types': mispattributes['output'],
            'values': d_result
        }]
    }
    def generateSqlQuery(self, sigma_io):
        try:
            # Check if sigma_io can be parsed
            parser = SigmaCollectionParser(sigma_io, self.config, None)
        except Exception as e:
            raise SigmaParseError("Parsing error: {}".format(e))

        # generate sql query
        queries = list(parser.generate(self.SQL))

        # extract parsed rules
        parsed_rules = [parser.parsedyaml for parser in parser.parsers]

        # returns the SQL-Query with the parsed rule
        return list(zip(queries, parsed_rules))
Пример #4
0
def main():
    if len(sys.argv) <= 2:
        return_data(
            {"status": "error", "message": "Missing argument to the Python script"}
        )

    if sys.argv[1] == "check":
        return_data({"status": "success"})

    pattern_type = sys.argv[1]
    indicator_value = sys.argv[2]

    if pattern_type == "stix":
        result = False
        try:
            errors = run_validator(indicator_value)
            if len(errors) == 0:
                result = True
        except:
            result = False
        return_data({"status": "success", "data": result})

    if pattern_type == "yara":
        parser = plyara.Plyara()
        result = False
        try:
            parser.parse_string(indicator_value)
            result = True
        except:
            result = False
        return_data({"status": "success", "data": result})

    if pattern_type == "sigma":
        result = False
        try:
            parser = SigmaCollectionParser(indicator_value)
            result = True
        except:
            result = False
        return_data({"status": "success", "data": result})

    if pattern_type == "snort":
        result = False
        try:
            parsed = Parser(indicator_value).all
            result = True
        except:
            result = False
        return_data({"status": "success", "data": result})

    if pattern_type == "suricata":
        result = False
        try:
            parsed = parse_rules(indicator_value)
            result = True
        except:
            result = False
        return_data({"status": "success", "data": result})

    return_data({"status": "unknown", "data": None})
Пример #5
0
 def __init__(self, pattern=None, **_):
     try:
         SigmaCollectionParser(self.pattern)
     except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e:
         raise ValidationError('{0:s} is not a valid YAML markup: {1!s}'.format(pattern, e))
     except (SigmaParseError, SigmaCollectionParseError) as e:
         raise ValidationError('{0:s} is not a valid Sigma rule: {1!s}'.format(pattern, e))
Пример #6
0
def main():
    if len(sys.argv) <= 2:
        return_data({'status': 'error', 'message': 'Missing argument to the Python script'})

    if sys.argv[1] == 'check':
        return_data({'status': 'success'})

    pattern_type = sys.argv[1]
    indicator_value = sys.argv[2]

    if pattern_type == 'stix':
        result = False
        try:
            errors = run_validator(indicator_value)
            if len(errors) == 0:
                result = True
        except:
            result = False
        return_data({'status': 'success', 'data': result})

    if pattern_type == 'yara':
        parser = plyara.Plyara()
        result = False
        try:
            parser.parse_string(indicator_value)
            result = True
        except:
            result = False
        return_data({'status': 'success', 'data': result})

    if pattern_type == 'sigma':
        result = False
        try:
            parser = SigmaCollectionParser(indicator_value)
            result = True
        except:
            result = False
        return_data({'status': 'success', 'data': result})

    if pattern_type == 'snort':
        result = False
        try:
            parsed = Parser(indicator_value).all
            result = True
        except:
            result = False
        return_data({'status': 'success', 'data': result})

    if pattern_type == 'suricata':
        result = False
        try:
            parsed = parse_rules(indicator_value)
            result = True
        except:
            result = False
        return_data({'status': 'success', 'data': result})

    return_data({'status': 'unknown', 'data': None})
Пример #7
0
    def sigma_to_query(self, sigma_file: str):
        """convert a Sigma rule file to query using the backend/config properties"""

        # initialize config manager to retrieve config
        scm = SigmaConfigurationManager()
        sigmaconfigs = SigmaConfigurationChain()
        sigmaconfig = scm.get(self.config)
        sigmaconfigs.append(sigmaconfig)

        # dynamically grab backend and pair to config
        backend_class = backends.getBackend(self.backend)
        backend = backend_class(sigmaconfigs)

        with open(sigma_file) as f:
            parser = SigmaCollectionParser(f, sigmaconfigs)
            parser.generate(backend)

        return json.loads(backend.finalize())
Пример #8
0
def main():
    backend = SigmaNormalizationBackend(SigmaConfiguration())

    if args.recursive:
        paths = [
            p for pathname in args.inputs
            for p in pathlib.Path(pathname).glob("**/*") if p.is_file()
        ]
    else:
        paths = [pathlib.Path(pathname) for pathname in args.inputs]

    primary_paths = None
    if args.primary:
        with open(args.primary, "r") as f:
            primary_paths = {pathname.strip() for pathname in f.readlines()}

    parsed = {
        str(path): SigmaCollectionParser(path.open().read())
        for path in paths
    }
    converted = {
        str(path): list(sigma_collection.generate(backend))
        for path, sigma_collection in parsed.items()
    }
    converted_flat = ((path, i, normalized)
                      for path, nlist in converted.items()
                      for i, normalized in zip(range(len(nlist)), nlist))
    converted_pairs_iter = itertools.combinations(converted_flat, 2)
    if primary_paths:
        converted_pairs = [
            pair for pair in converted_pairs_iter
            if pair[0][0] in primary_paths or pair[1][0] in paths
        ]
    else:
        converted_pairs = list(converted_pairs_iter)
    similarities = [
        (item1[:2], item2[:2], difflib.SequenceMatcher(None, item1[2],
                                                       item2[2]).ratio())
        for item1, item2 in progressbar.progressbar(converted_pairs)
    ]

    i = 0
    for similarity in sorted(similarities, key=lambda s: s[2], reverse=True):
        if args.min_similarity and similarity[
                2] * 100 < args.min_similarity:  # finish after similarity drops below minimum
            break
        print("{:70} | {:2} | {:70} | {:2} | {:>3.2%}".format(
            *similarity[0], *similarity[1], similarity[2]))
        i += 1
        if args.top and i >= args.top:  # end after $top pairs
            break
Пример #9
0
    def validate(self, detection, expectation):
        config = SigmaConfiguration()

        self.basic_rule["detection"] = detection

        with patch("yaml.safe_load_all", return_value=[self.basic_rule]):
            parser = SigmaCollectionParser("any sigma io", config, None)
            backend = DevoBackend(config, self.table)

            assert len(parser.parsers) == 1

            for p in parser.parsers:
                if isinstance(expectation, str):
                    self.assertEqual(expectation, backend.generate(p))
                elif isinstance(expectation, Exception):
                    self.assertRaises(type(expectation), backend.generate, p)
Пример #10
0
def main():
    argparser = argparse.ArgumentParser(
        description="Convert Sigma rules into SIEM signatures.")
    argparser.add_argument("input", help="Sigma input file")
    cmdargs = argparser.parse_args()

    try:
        f = open(cmdargs.input, "r")
    except IOError as e:
        print("Error while opening input file: %s" % str(e), file=sys.stderr)
        sys.exit(1)

    content = "".join(f.readlines())
    f.close()
    sc = SigmaCollectionParser(content)

    print(yaml.dump_all(sc, default_flow_style=False))
Пример #11
0
    def test_fieldname_mapping(self):
        detection = {"selection": {"fieldname": "test1"},
                     "condition": "selection"}
        expected_result = 'SELECT * FROM {} WHERE mapped_fieldname = "test1"'.format(
            self.table)

        # configure mapping
        config = SigmaConfiguration()
        config.fieldmappings["fieldname"] = FieldMapping(
            "fieldname", "mapped_fieldname")

        self.basic_rule["detection"] = detection

        with patch("yaml.safe_load_all", return_value=[self.basic_rule]):
            parser = SigmaCollectionParser("any sigma io", config, None)
            backend = SQLBackend(config, self.table)

            assert len(parser.parsers) == 1

            for p in parser.parsers:
                self.assertEqual(expected_result, backend.generate(p))
Пример #12
0
    def test_fieldname_mapping(self):
        detection = {"selection": {"fieldname": "test1"}, "condition": "selection"}
        expected_result = 'SELECT json FROM {} ' \
                          'WHERE json_extract(json, \'$.type\') = \'eventlog\' ' \
                          'AND json_extract(json, \'$.mapped_fieldname\') = "test1"'.format(self.table)

        # configure mapping
        config = SigmaConfiguration()
        config.fieldmappings["fieldname"] = FieldMapping(
            "fieldname", "mapped_fieldname")

        self.basic_rule["detection"] = detection

        with patch("yaml.safe_load_all", return_value=[self.basic_rule]):
            parser = SigmaCollectionParser("any sigma io", config, None)
            backend = ForensicStoreBackend(config)

            assert len(parser.parsers) == 1

            for p in parser.parsers:
                self.assertEqual(expected_result.lower(),
                                 backend.generate(p).lower())
Пример #13
0
def main():
    argparser = set_argparser()
    cmdargs = argparser.parse_args()

    scm = SigmaConfigurationManager()

    logger = logging.getLogger(__name__)
    if cmdargs.debug:   # pragma: no cover
        logger.setLevel(logging.DEBUG)

    if cmdargs.lists:
        print("Backends (Targets):")
        list_backends(cmdargs.debug)

        print()
        print("Configurations (Sources):")
        list_configurations(backend=cmdargs.target, scm=scm)

        print()
        print("Modifiers:")
        list_modifiers(modifiers=modifiers)
        sys.exit(0)
    elif len(cmdargs.inputs) == 0:
        print("Nothing to do!")
        argparser.print_usage()
        sys.exit(0)

    if cmdargs.target is None:
        print("No target selected, select one with -t/--target")
        argparser.print_usage()
        sys.exit(ERR_NO_TARGET)

    rulefilter = None
    if cmdargs.filter:
        try:
            rulefilter = SigmaRuleFilter(cmdargs.filter)
        except SigmaRuleFilterParseException as e:
            print("Parse error in Sigma rule filter expression: %s" % str(e), file=sys.stderr)
            sys.exit(ERR_RULE_FILTER_PARSING)

    sigmaconfigs = SigmaConfigurationChain()
    backend_class = backends.getBackend(cmdargs.target)
    if cmdargs.config is None:
        if backend_class.config_required and not cmdargs.shoot_yourself_in_the_foot:
            print("The backend you want to use usually requires a configuration to generate valid results. Please provide one with --config/-c.", file=sys.stderr)
            print("Available choices for this backend (get complete list with --lists/-l):")
            list_configurations(backend=cmdargs.target, scm=scm)
            sys.exit(ERR_CONFIG_REQUIRED)
    if backend_class.default_config is not None:
        cmdargs.config = backend_class.default_config

    if cmdargs.config:
        order = 0
        for conf_name in cmdargs.config:
            try:
                sigmaconfig = scm.get(conf_name)
                if sigmaconfig.order is not None:
                    if sigmaconfig.order <= order and not cmdargs.shoot_yourself_in_the_foot:
                        print("The configurations were provided in the wrong order (order key check in config file)", file=sys.stderr)
                        sys.exit(ERR_CONFIG_ORDER)
                    order = sigmaconfig.order

                try:
                    if cmdargs.target not in sigmaconfig.config["backends"]:
                        print("The configuration '{}' is not valid for backend '{}'. Valid choices are: {}".format(conf_name, cmdargs.target, ", ".join(sigmaconfig.config["backends"])), file=sys.stderr)
                        sys.exit(ERR_CONFIG_ORDER)
                except KeyError:
                    pass

                sigmaconfigs.append(sigmaconfig)
            except OSError as e:
                print("Failed to open Sigma configuration file %s: %s" % (conf_name, str(e)), file=sys.stderr)
                exit(ERR_OPEN_CONFIG_FILE)
            except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e:
                print("Sigma configuration file %s is no valid YAML: %s" % (conf_name, str(e)), file=sys.stderr)
                exit(ERR_CONFIG_INVALID_YAML)
            except SigmaConfigParseError as e:
                print("Sigma configuration parse error in %s: %s" % (conf_name, str(e)), file=sys.stderr)
                exit(ERR_CONFIG_PARSING)

    backend_options = BackendOptions(cmdargs.backend_option, cmdargs.backend_config)
    backend = backend_class(sigmaconfigs, backend_options)

    filename = cmdargs.output
    if filename:
        try:
            out = open(filename, "w", encoding='utf-8')
        except (IOError, OSError) as e:
            print("Failed to open output file '%s': %s" % (filename, str(e)), file=sys.stderr)
            exit(ERR_OUTPUT)
    else:
        out = sys.stdout

    error = 0
    for sigmafile in get_inputs(cmdargs.inputs, cmdargs.recurse):
        logger.debug("* Processing Sigma input %s" % (sigmafile))
        try:
            if cmdargs.inputs == ['-']:
                f = sigmafile
            else:
                f = sigmafile.open(encoding='utf-8')
            parser = SigmaCollectionParser(f, sigmaconfigs, rulefilter)
            results = parser.generate(backend)
            for result in results:
                print(result, file=out)
        except OSError as e:
            print("Failed to open Sigma file %s: %s" % (sigmafile, str(e)), file=sys.stderr)
            error = ERR_OPEN_SIGMA_RULE
        except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e:
            print("Sigma file %s is no valid YAML: %s" % (sigmafile, str(e)), file=sys.stderr)
            error = ERR_INVALID_YAML
            if not cmdargs.defer_abort:
                sys.exit(error)
        except (SigmaParseError, SigmaCollectionParseError) as e:
            print("Sigma parse error in %s: %s" % (sigmafile, str(e)), file=sys.stderr)
            error = ERR_SIGMA_PARSING
            if not cmdargs.defer_abort:
                sys.exit(error)
        except NotSupportedError as e:
            print("The Sigma rule requires a feature that is not supported by the target system: " + str(e), file=sys.stderr)
            if not cmdargs.ignore_backend_errors:
                error = ERR_NOT_SUPPORTED
                if not cmdargs.defer_abort:
                    sys.exit(error)
        except BackendError as e:
            print("Backend error in %s: %s" % (sigmafile, str(e)), file=sys.stderr)
            if not cmdargs.ignore_backend_errors:
                error = ERR_BACKEND
                if not cmdargs.defer_abort:
                    sys.exit(error)
        except (NotImplementedError, TypeError) as e:
            print("An unsupported feature is required for this Sigma rule (%s): " % (sigmafile) + str(e), file=sys.stderr)
            print("Feel free to contribute for fun and fame, this is open source :) -> https://github.com/Neo23x0/sigma", file=sys.stderr)
            if not cmdargs.ignore_backend_errors:
                error = ERR_NOT_IMPLEMENTED
                if not cmdargs.defer_abort:
                    sys.exit(error)
        except PartialMatchError as e:
            print("Partial field match error: %s" % str(e), file=sys.stderr)
            if not cmdargs.ignore_backend_errors:
                error = ERR_PARTIAL_FIELD_MATCH
                if not cmdargs.defer_abort:
                    sys.exit(error)
        except FullMatchError as e:
            print("Full field match error", file=sys.stderr)
            if not cmdargs.ignore_backend_errors:
                error = ERR_FULL_FIELD_MATCH
                if not cmdargs.defer_abort:
                    sys.exit(error)                
        finally:
            try:
                f.close()
            except:
                pass

    result = backend.finalize()
    if result:
        print(result, file=out)
    out.close()

    sys.exit(error)
Пример #14
0
def run(constants):
    output_dir = constants.get('output_dir', DEFAULT_OUTPUT_DIR)
    rules_dir = constants.get('rules_dir', DEFAULT_RULES_DIR)
    backend = constants.get('backend', DEFAULT_BACKEND)
    filename = output_dir / 'query'
    try:
        out = open(filename, 'w', encoding='utf-8')
    except (IOError, OSError) as e:
        print('Failed to open output file "%s": %s' % (str(filename), str(e)), file=sys.stderr)
        sys.exit(1)

    error = 0
    inputs = get_inputs(rules_dir)
    for path in inputs:
        logger.debug('Processing Sigma rule "%s"' % path)
        f = None
        # noinspection PyUnresolvedReferences
        try:
            f = path.open(encoding='utf-8')
            sigma_parser = SigmaCollectionParser(f)
            results = sigma_parser.generate(backend)
            for result in results:
                print(result, file=out)
        except OSError as e:
            print('Failed to open Sigma rule file "%s": %s' % (path, str(e)), file=sys.stderr)
            error = 5
        except (yaml.parser.ParseError, yaml.scanner.ScannerError) as e:
            print('Sigma rule file "%s" is invalid YAML: %s' % (path, str(e)), file=sys.stderr)
            error = 3
            sys.exit(error)
        except (SigmaParseError, SigmaCollectionParseError) as e:
            print('Sigma parse error in "%s": %s' % (path, str(e)), file=sys.stderr)
            error = 4
            sys.exit(error)
        except NotSupportedError as e:
            print('The Sigma rule requires a feature that is not supported by the target system:',
                  str(e), file=sys.stderr)
            error = 9
            sys.exit(error)
        except BackendError as e:
            print('Backend error in "%s": %s' % (path, str(e)), file=sys.stderr)
            error = 8
            sys.exit(error)
        except NotImplementedError as e:
            print('An unsupported feature is required for this Sigma rule "%s": %s' % (path, str(e)), file=sys.stderr)
            error = 42
            sys.exit(error)
        except PartialMatchError as e:
            print('Partial field match error:', str(e), file=sys.stderr)
            error = 80
            sys.exit(error)
        except FullMatchError as e:
            print('Full field match error:', str(e), file=sys.stderr)
            error = 90
            sys.exit(error)
        finally:
            # noinspection PyBroadException
            try:
                f.close()
            except Exception:
                pass

    result = backend.finalize()
    if result:
        print(result, file=out)

    out.close()
    sys.exit(error)
if args.recursive:
    paths = [
        p for pathname in args.inputs
        for p in pathlib.Path(pathname).glob("**/*") if p.is_file()
    ]
else:
    paths = [pathlib.Path(pathname) for pathname in args.inputs]

primary_paths = None
if args.primary:
    with open(args.primary, "r") as f:
        primary_paths = {pathname.strip() for pathname in f.readlines()}

parsed = {
    str(path): SigmaCollectionParser(path.open().read())
    for path in paths
}
converted = {
    str(path): list(sigma_collection.generate(backend))
    for path, sigma_collection in parsed.items()
}
converted_flat = ((path, i, normalized) for path, nlist in converted.items()
                  for i, normalized in zip(range(len(nlist)), nlist))
converted_pairs_iter = itertools.combinations(converted_flat, 2)
if primary_paths:
    converted_pairs = [
        pair for pair in converted_pairs_iter
        if pair[0][0] in primary_paths or pair[1][0] in paths
    ]
else:
Пример #16
0
def main():
    argparser = set_argparser()
    cmdargs = argparser.parse_args()
    scm = SigmaConfigurationManager()

    logger = logging.getLogger(__name__)
    if cmdargs.debug:  # pragma: no cover
        logging.basicConfig(filename='sigmac.log',
                            filemode='w',
                            level=logging.DEBUG)
        logger.setLevel(logging.DEBUG)

    if cmdargs.lists:
        print("Backends (Targets):")
        list_backends(cmdargs.debug)

        print()
        print("Configurations (Sources):")
        list_configurations(backend=cmdargs.target, scm=scm)

        print()
        print("Modifiers:")
        list_modifiers(modifiers=modifiers)
        sys.exit(0)
    elif len(cmdargs.inputs) == 0:
        print("Nothing to do!")
        argparser.print_usage()
        sys.exit(0)

    if cmdargs.target is None:
        print("No target selected, select one with -t/--target")
        argparser.print_usage()
        sys.exit(ERR_NO_TARGET)

    logger.debug("* Target selected %s" % (cmdargs.target))

    rulefilter = None
    if cmdargs.filter:
        try:
            rulefilter = SigmaRuleFilter(cmdargs.filter)
        except SigmaRuleFilterParseException as e:
            print("Parse error in Sigma rule filter expression: %s" % str(e),
                  file=sys.stderr)
            sys.exit(ERR_RULE_FILTER_PARSING)

    sigmaconfigs = SigmaConfigurationChain()
    backend_class = backends.getBackend(cmdargs.target)
    if cmdargs.config is None:
        if backend_class.config_required and not cmdargs.shoot_yourself_in_the_foot:
            print(
                "The backend you want to use usually requires a configuration to generate valid results. Please provide one with --config/-c.",
                file=sys.stderr)
            print(
                "Available choices for this backend (get complete list with --lists/-l):"
            )
            list_configurations(backend=cmdargs.target, scm=scm)
            sys.exit(ERR_CONFIG_REQUIRED)
        if backend_class.default_config is not None:
            cmdargs.config = backend_class.default_config

    if cmdargs.config:
        order = 0
        for conf_name in cmdargs.config:
            try:
                sigmaconfig = scm.get(conf_name)
                if sigmaconfig.order is not None:
                    if sigmaconfig.order <= order and not cmdargs.shoot_yourself_in_the_foot:
                        print(
                            "The configurations were provided in the wrong order (order key check in config file)",
                            file=sys.stderr)
                        sys.exit(ERR_CONFIG_ORDER)
                    order = sigmaconfig.order

                try:
                    if cmdargs.target not in sigmaconfig.config["backends"]:
                        print(
                            "The configuration '{}' is not valid for backend '{}'. Valid choices are: {}"
                            .format(conf_name, cmdargs.target,
                                    ", ".join(sigmaconfig.config["backends"])),
                            file=sys.stderr)
                        sys.exit(ERR_CONFIG_ORDER)
                except KeyError:
                    pass

                sigmaconfigs.append(sigmaconfig)
            except OSError as e:
                print("Failed to open Sigma configuration file %s: %s" %
                      (conf_name, str(e)),
                      file=sys.stderr)
                exit(ERR_OPEN_CONFIG_FILE)
            except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e:
                print("Sigma configuration file %s is no valid YAML: %s" %
                      (conf_name, str(e)),
                      file=sys.stderr)
                exit(ERR_CONFIG_INVALID_YAML)
            except SigmaConfigParseError as e:
                print("Sigma configuration parse error in %s: %s" %
                      (conf_name, str(e)),
                      file=sys.stderr)
                exit(ERR_CONFIG_PARSING)

    if cmdargs.output_fields:
        if cmdargs.output_format:
            output_fields_rejected = [
                field for field in cmdargs.output_fields.split(",")
                if field not in allowed_fields
            ]  # Not allowed fields
            if output_fields_rejected:
                print(
                    "These fields are not allowed (check help for allow field list) : %s"
                    % (", ".join(output_fields_rejected)),
                    file=sys.stderr)
                exit(ERR_OUTPUT_FORMAT)
            else:
                output_fields_filtered = [
                    field for field in cmdargs.output_fields.split(",")
                    if field in allowed_fields
                ]  # Keep only allowed fields
        else:
            print(
                "The '--output-fields' or '-of' arguments must be used with '--output-format' or '-oF' equal to 'json' or 'yaml'",
                file=sys.stderr)
            exit(ERR_OUTPUT_FORMAT)

    backend_options = BackendOptions(cmdargs.backend_option,
                                     cmdargs.backend_config)
    backend = backend_class(sigmaconfigs, backend_options)

    filename_ext = cmdargs.output_extention
    filename = cmdargs.output
    fileprefix = None
    if filename:
        if filename_ext:
            if filename_ext[0] == '.':
                pass
            else:
                filename_ext = '.' + filename_ext
        else:
            filename_ext = '.rule'

        if filename[-1:] in ['_', '/', '\\']:
            fileprefix = filename
        else:
            try:
                out = open(filename, "w", encoding='utf-8')
            except (IOError, OSError) as e:
                print("Failed to open output file '%s': %s" %
                      (filename, str(e)),
                      file=sys.stderr)
                exit(ERR_OUTPUT)
    else:
        out = sys.stdout

    error = 0
    output_array = []
    for sigmafile in get_inputs(cmdargs.inputs, cmdargs.recurse):
        logger.debug("* Processing Sigma input %s" % (sigmafile))
        success = True
        try:
            if cmdargs.inputs == ['-']:
                f = sigmafile
            else:
                f = sigmafile.open(encoding='utf-8')
            parser = SigmaCollectionParser(f, sigmaconfigs, rulefilter,
                                           sigmafile)
            results = parser.generate(backend)

            nb_result = len(list(copy.deepcopy(results)))
            inc_filenane = None if nb_result < 2 else 0

            newline_separator = '\0' if cmdargs.print0 else '\n'

            results = list(
                results
            )  # Since results is an iterator and used twice we convert it a list
            for result in results:
                if not fileprefix == None and not inc_filenane == None:  #yml action
                    try:
                        filename = fileprefix + str(sigmafile.name)
                        filename = filename.replace(
                            '.yml', '_' + str(inc_filenane) + filename_ext)
                        inc_filenane += 1
                        out = open(filename, "w", encoding='utf-8')
                    except (IOError, OSError) as e:
                        print("Failed to open output file '%s': %s" %
                              (filename, str(e)),
                              file=sys.stderr)
                        exit(ERR_OUTPUT)
                elif not fileprefix == None and inc_filenane == None:  # a simple yml
                    try:
                        filename = fileprefix + str(sigmafile.name)
                        filename = filename.replace('.yml', filename_ext)
                        out = open(filename, "w", encoding='utf-8')
                    except (IOError, OSError) as e:
                        print("Failed to open output file '%s': %s" %
                              (filename, str(e)),
                              file=sys.stderr)
                        exit(ERR_OUTPUT)
                if not cmdargs.output_fields:
                    print(result, file=out, end=newline_separator)

            if cmdargs.output_fields:  # Handle output fields
                output = {}
                f.seek(0)
                docs = yaml.load_all(f, Loader=yaml.FullLoader)
                for doc in docs:
                    for k, v in doc.items():
                        if k in output_fields_filtered:
                            output[k] = v
                output['rule'] = [result for result in results]
                if "filename" in output_fields_filtered:
                    output['filename'] = str(sigmafile.name)
                output_array.append(output)

            if nb_result == 0:  # backend get only 1 output
                if not fileprefix == None:  # want a prefix anyway
                    try:
                        filename = "%s%s_mono_output%s" % (
                            fileprefix, cmdargs.target, filename_ext)
                        out = open(filename, "w", encoding='utf-8')
                        fileprefix = None  # no need to open the same file many time
                    except (IOError, OSError) as e:
                        print("Failed to open output file '%s': %s" %
                              (filename, str(e)),
                              file=sys.stderr)
                        exit(ERR_OUTPUT)

        except OSError as e:
            print("Failed to open Sigma file %s: %s" % (sigmafile, str(e)),
                  file=sys.stderr)
            logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile))
            success = False
            error = ERR_OPEN_SIGMA_RULE
        except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e:
            print("Error: Sigma file %s is no valid YAML: %s" %
                  (sigmafile, str(e)),
                  file=sys.stderr)
            logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile))
            success = False
            error = ERR_INVALID_YAML
            if not cmdargs.defer_abort:
                sys.exit(error)
        except (SigmaParseError, SigmaCollectionParseError) as e:
            print("Error: Sigma parse error in %s: %s" % (sigmafile, str(e)),
                  file=sys.stderr)
            logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile))
            success = False
            error = ERR_SIGMA_PARSING
            if not cmdargs.defer_abort:
                sys.exit(error)
        except NotSupportedError as e:
            print(
                "Error: The Sigma rule requires a feature that is not supported by the target system: "
                + str(e),
                file=sys.stderr)
            logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile))
            success = False
            if not cmdargs.ignore_backend_errors:
                error = ERR_NOT_SUPPORTED
                if not cmdargs.defer_abort:
                    sys.exit(error)
        except BackendError as e:
            print("Error: Backend error in %s: %s" % (sigmafile, str(e)),
                  file=sys.stderr)
            logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile))
            success = False
            if not cmdargs.ignore_backend_errors:
                error = ERR_BACKEND
                if not cmdargs.defer_abort:
                    sys.exit(error)
        except (NotImplementedError, TypeError) as e:
            print(
                "An unsupported feature is required for this Sigma rule (%s): "
                % (sigmafile) + str(e),
                file=sys.stderr)
            logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile))
            success = False
            if not cmdargs.ignore_backend_errors:
                error = ERR_NOT_IMPLEMENTED
                if not cmdargs.defer_abort:
                    sys.exit(error)
        except PartialMatchError as e:
            print("Error: Partial field match error: %s" % str(e),
                  file=sys.stderr)
            logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile))
            success = False
            if not cmdargs.ignore_backend_errors:
                error = ERR_PARTIAL_FIELD_MATCH
                if not cmdargs.defer_abort:
                    sys.exit(error)
        except FullMatchError as e:
            print("Error: Full field match error", file=sys.stderr)
            logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile))
            success = False
            if not cmdargs.ignore_backend_errors:
                error = ERR_FULL_FIELD_MATCH
                if not cmdargs.defer_abort:
                    sys.exit(error)
        finally:
            try:
                f.close()
            except:
                pass

        if success:
            logger.debug("* Convertion Sigma input %s SUCCESS" % (sigmafile))

    result = backend.finalize()
    if result:
        print(result, file=out)

    if cmdargs.output_fields:
        if cmdargs.output_format == 'json':
            print(json.dumps(output_array, indent=4, ensure_ascii=False),
                  file=out)
        elif cmdargs.output_format == 'yaml':
            print(ruamel.yaml.round_trip_dump(output_array), file=out)

    out.close()

    sys.exit(error)