Skip to content

[high] Re-apply #98: exit non-zero and keep converting when a rule fails with --output-dir (reverted with #105) - #110

Open
elhoim wants to merge 1 commit into
SigmaHQ:mainfrom
elhoim:reland/output-dir-conversion-errors
Open

elhoim wants to merge 1 commit into
SigmaHQ:mainfrom
elhoim:reland/output-dir-conversion-errors

Conversation

@elhoim

@elhoim elhoim commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

BLUF

Priority: high

What happened

main no longer contains #98, although it was merged:

Probably nobody intended to revert #98 and #99; they were collateral of undoing a broken conflict resolution.

Original description (#98)

BLUF

  • Problem: with --output-dir, any conversion error is downgraded to a warning and the command exits 0. Every rule after the failing one is silently dropped.
  • Impact: sigma convert -t <backend> -od out/ rules/ in a CI job reports success while out/ is incomplete, and can even be empty. The same input without -od exits 1.
  • Fix: convert rule by rule in write_separate_files(), report each failing rule, keep writing the others, and exit 1 when any rule failed.
  • Tests: two new CliRunner tests. The first (fail rule + good rule → exit 1, good rule still written) fails on main and passes with the fix. The second guards --skip-unsupported (still exit 0, reported under "Ignored errors").

Priority: high

Details

Root cause: sigma/cli/convert.py:192-195

try:
    backend.convert(rule_collection, format, correlation_method, callback=write_callback)
except Exception as e:
    click.echo(f"Warning: Failed to convert rules: {e}", err=True)

Backend.convert() is a single list comprehension over all rules. The first exception aborts it, so the callback never runs for later rules. The exception is then swallowed. The single-output path (convert.py:590-601) turns the same SigmaError / NotImplementedError into Error while converting: ... with exit status 1.

The fix does what Backend.convert() does: init_processing_pipeline() and resolve_rule_references(), then convert_rule() / convert_correlation_rule() in collection order with the existing callback. The difference is that SigmaError / NotImplementedError is caught per rule. finalize() is not called, but the old code discarded its return value anyway. After the files are written, a ClickException reports the number of failed rules.

-s/--skip-unsupported still works as before. In that mode the backend collects SigmaErrors itself (collect_errors=True), and they are listed under "Ignored errors" with exit 0.

Before (rule a.yml uses an unresolvable |expand placeholder, b.yml is fine):

$ sigma convert -t text_query_test -od out rules/
Warning: Failed to convert rules: Attempt to convert unhandled placeholder 'var' into query.
Wrote 0 file(s) to out
$ echo $?
0

After:

Error: Failed to convert rule /tmp/demo/rules/a.yml: Attempt to convert unhandled placeholder 'var' into query.
Wrote 1 file(s) to out
Error: 1 rule(s) failed to convert, see errors above.
$ echo $?
1

This PR is independent of the other --output-dir PR, which changes the write loop of the same function. Whichever lands second, I'll rebase it.

Testing

  • New tests: test_convert_output_dir_conversion_error_fails_and_continues (fails on main, passes here) and test_convert_output_dir_conversion_error_skip_unsupported.
  • Full suite, run the way CI runs it (Python 3.12, dependencies pinned to poetry.lock: pySigma 1.4.0, click 8.4.2, pyparsing 3.3.2): pytest --cov=sigma → 120 passed, 1 skipped.

🤖 Generated with Claude Code

Re-applies SigmaHQ#98, which was merged but then undone on main: the merge of SigmaHQ#105
(8effb4c) brought in cdd5436, a revert of a conflict-resolution merge
(86d3041) that had carried SigmaHQ#98 and SigmaHQ#99 into the SigmaHQ#105 branch.

Adapted to SigmaHQ#105's write_separate_files(): rules are converted one by one
without a callback, so a failing rule is reported and counted instead of
aborting the conversion of every later rule behind a warning. Each successful
conversion stores its finalized result on the rule, which SigmaHQ#105 then collects
via get_output_rules()/get_conversion_result(). The command exits 1 when any
rule failed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant