diff --git a/sigma/cli/rules.py b/sigma/cli/rules.py index e4c0cd8..1a8879c 100644 --- a/sigma/cli/rules.py +++ b/sigma/cli/rules.py @@ -1,7 +1,9 @@ from pathlib import Path from sys import stderr import click +import yaml from sigma.collection import SigmaCollection +from sigma.exceptions import SigmaCollectionError def load_rules(input, file_pattern): @@ -10,27 +12,31 @@ def load_rules(input, file_pattern): """ rule_collection = SigmaCollection([], []) - for path in list(input): - if path == Path("-"): - rule_collection = SigmaCollection.merge([ - rule_collection, - SigmaCollection.from_yaml(click.get_text_stream("stdin")) - ]) - else: - rule_paths = SigmaCollection.resolve_paths( - [path], - recursion_pattern="**/" + file_pattern, - ) - with click.progressbar( - list(rule_paths), label="Parsing Sigma rules", file=stderr - ) as progress_rule_paths: + try: + for path in list(input): + if path == Path("-"): rule_collection = SigmaCollection.merge([ rule_collection, - SigmaCollection.load_ruleset( - progress_rule_paths, - collect_errors=True, - ) + SigmaCollection.from_yaml(click.get_text_stream("stdin")) ]) + else: + rule_paths = SigmaCollection.resolve_paths( + [path], + recursion_pattern="**/" + file_pattern, + ) + with click.progressbar( + list(rule_paths), label="Parsing Sigma rules", file=stderr + ) as progress_rule_paths: + rule_collection = SigmaCollection.merge([ + rule_collection, + SigmaCollection.load_ruleset( + progress_rule_paths, + collect_errors=True, + ) + ]) + except yaml.YAMLError as e: + # Report YAML syntax errors like other Sigma errors instead of crashing with a traceback. + raise SigmaCollectionError(f"YAML syntax error: {e}") from e rule_collection.resolve_rule_references() diff --git a/tests/test_check.py b/tests/test_check.py index ad514f3..f3105b3 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -151,6 +151,22 @@ def test_check_cli_generates_junitxml(tmp_path): assert tree.getroot().tag == "testsuites" +def test_check_yaml_syntax_error_junitxml(tmp_path): + """A YAML syntax error is reported as check error and still produces the JUnit report.""" + rules = tmp_path / "rules" + rules.mkdir() + (rules / "broken.yml").write_text("title: [unclosed\n") + out = tmp_path / "report.xml" + cli = CliRunner() + result = cli.invoke(check, ["--junitxml", str(out), str(rules)]) + assert result.exit_code == 1 + assert "Check error: YAML syntax error" in result.output + assert "broken.yml" in result.output + tree = ET.parse(str(out)) + failures = tree.getroot().findall(".//failure") + assert any("YAML syntax error" in (f.text or "") for f in failures) + + def test_check_unknown_collection_action(tmp_path): """Collection-level errors (not attached to any rule) must be reported and fail the check.""" (tmp_path / "typo.yml").write_text("action: repaet\ntitle: typo in action keyword\n") diff --git a/tests/test_convert.py b/tests/test_convert.py index c5848ce..3f4a535 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -49,6 +49,15 @@ def test_convert_invalid_rule(): assert "at least one condition" in result.stderr +def test_convert_yaml_syntax_error(tmp_path): + (tmp_path / "broken.yml").write_text("title: [unclosed\n") + cli = CliRunner() + result = cli.invoke(convert, ["-t", "text_query_test", str(tmp_path)]) + assert result.exit_code == 1 + assert "Error while converting: YAML syntax error" in result.stderr + assert "broken.yml" in result.stderr + + def test_convert_stdin(): cli = CliRunner() with open("tests/files/valid/sigma_rule.yml", "rt") as yml_file: