Open a GitHub issue for anything that is not itself sensitive. For a finding you would rather not publish first, use GitHub's private vulnerability reporting on this repository.
Please include the input that triggered it — a rule file, a log line, a pipeline, a config — reduced as far as you can. Nullfire is a parser fed untrusted files, so a reproducer is usually the whole diagnosis.
Nullfire is an offline analysis tool. It has no server, no authentication and no credentials, and it opens no sockets. The interesting attack surface is therefore input parsing and output handling.
(pySigma, our one direct dependency, pulls in requests for plugin
installation machinery that Nullfire never invokes. The no-networking
guarantee is about Nullfire's own behaviour, not about the dependency tree.)
In scope:
- Crashes, hangs or unbounded memory growth from a crafted Sigma rule, JSONL log file, processing pipeline or configuration file.
- Any path by which rule content or log content is executed rather than analysed.
- Reading files outside the rules directory (symlink or traversal escapes).
- Leaking log content or host paths into reports beyond what is documented.
- Incorrect verdicts that overstate confidence — in particular anything
that makes Nullfire report a healthy rule as
NULLFIRE, or an operational failure as a detection finding. These are treated as security-relevant because people act on them.
Out of scope:
- A compromised Python environment or a malicious pySigma release. Pin your dependencies.
- Sandboxing pySigma. The per-rule exception guard limits a pySigma failure to one rule; it does not contain arbitrary code execution inside the library.
- Denial of service requiring inputs far larger than the documented ceilings.
These are properties, not aspirations, and each is covered by a test:
- No rule or log content is ever executed. No shell, no subprocess, no
eval, noexec, no dynamic import driven by input. - No regex from a rule file is ever compiled. Sigma's
|reis reported undecidable; Sigma wildcards are matched by a hand-written two-pointer matcher with O(n·m) worst case.reappears in exactly one module, for two fixed anchored patterns that recognise ATT&CK tag formats. - YAML is parsed with
safe_loadonly, in Nullfire and in pySigma. Pipelines are loaded withallow_template_vars=Falseandallow_external_sources=Falsepassed explicitly, so a change to pySigma's defaults cannot quietly widen this. - Every input axis is bounded: file streaming, per-line size, per-value length, distinct values per field, distinct field paths, nesting depth, constraint-tree depth and node count.
- Symlinks are not followed and every candidate path is checked for containment under the rules root.
- Reports do not leak the host. Paths are relativized; a path outside the known roots degrades to its file name, never to an absolute path.
- Malformed log lines are never quoted in error messages.
- Logs stay local. No telemetry, no upload, no update check, no sockets opened by Nullfire itself.
Full analysis: docs/threat-model.md.
Nullfire is built to be pointed at real telemetry:
nullfire analyze --config nullfire.yml --redact--redact (or redact_values: true) withholds every observed value. The
report keeps field names, occupancy figures, verdicts and reasons — enough to
act on, safe to attach to a ticket.
The policy is precisely: redaction protects log-derived content. A rule's
own expected literals and the generated fixtures are retained, because they
originate in the rule files the reader already has. Fixture generation never
reads the data profile, so a fixture cannot contain log content — a property
asserted by tests/test_security_properties.py.
Without redaction, reports contain a capped, truncated sample of observed
values (example_limit, default 5). They never contain whole records. Review
before publishing anyway: field names alone can be sensitive in some
environments.
Pre-1.0. Fixes land on main; there are no maintained release branches yet.