Compiling, formatting & checking
YARA-X is a Rust reimplementation of YARA. Its command-line tool, yr, does more than just scan: it can compile rules ahead of time into a serialized file, reformat rule source for a consistent style, and lint rules against your team's conventions. This page covers yr compile, scanning with a compiled file, yr fmt, yr check, and how namespaces fit in.
The flags shown here come from the upstream CLI reference (linked in Sources). Run yr <command> --help for the authoritative list on your installed version, since options can change between releases.
Why compile?
When you run yr scan against source .yar files, YARA-X compiles them in memory every time before scanning. If you scan many targets with the same rule set, that repeated compilation is wasted work. Compiling once to a serialized rules file lets you skip parsing and compilation on every subsequent scan, which matters when a rule set is large or a scan runs frequently in a pipeline.
yr compile
yr compile parses and compiles one or more rule sources and writes the result to a single serialized file.
yr compile [OPTIONS] <[NAMESPACE:]RULES_PATH>...
Each RULES_PATH is a YARA source file or a directory of source files. By default the output is written to output.yarc; use --output to choose a different path.
| Flag | Purpose |
|---|---|
--output <OUTPUT_PATH> | Path for the compiled rules file (default: output.yarc) |
--relaxed-re-syntax | Use lenient regular-expression parsing for compatibility with rules written for the original YARA |
--path-as-namespace | Use each rule file's path as its namespace, isolating rules per source file |
A typical compile step:
yr compile --output rules.yarc my_rules/
This walks the my_rules/ directory, compiles every *.yar and *.yara file it finds, and serializes the whole set into rules.yarc.
Compiling is also a quick way to fail fast in CI. If any rule has a syntax error, yr compile reports it and exits non-zero before you ship a broken .yarc to production.
Scanning with a compiled file
To scan with a serialized file instead of source rules, pass it to yr scan together with the --compiled-rules (-C) flag.
yr scan --compiled-rules rules.yarc /path/to/target
The general form of the scan command is:
yr scan [OPTIONS] <[NAMESPACE:]RULES_PATH>... <TARGET_PATH>
When --compiled-rules is set, the RULES_PATH argument is interpreted as a previously compiled file rather than source. A few options that pair well with scanning:
| Flag | Purpose |
|---|---|
--compiled-rules, -C | Treat the rules argument as a compiled (.yarc) file |
--negate, -n | Print rules that do not match, instead of those that do |
--threads <NUM_THREADS> | Number of threads used for scanning |
--recursive[=<MAX_DEPTH>] | Recursively scan a target directory, optionally limited to a depth |
--output-format <FORMAT> | Output as text, json, or ndjson |
--print-strings[=<N>] | Show matched pattern data, truncated to N characters |
A compiled file is tied to the YARA-X version that produced it. If you upgrade yr, recompile your rules — a .yarc from an older version may not load cleanly into a newer scanner.
yr fmt — the formatter
yr fmt rewrites YARA source files into a consistent style, the way gofmt or rustfmt do for their languages. By default it formats the given files in place. Unlike compile, scan, and check, it operates on the individual files you name rather than walking a directory, so point it at specific files (a shell glob like rules/*.yar works well).
yr fmt <FILE>...
# Reformat a file in place
yr fmt my_rules.yar
| Flag | Purpose |
|---|---|
--check, -c | Check whether files are already formatted, without changing them |
--tab-size <NUM_SPACES>, -t | Number of spaces per indentation level (default: 4) |
The --check flag makes yr fmt suitable for CI: it leaves files untouched and signals through its exit status whether anything would change, so a pull request can be rejected if its rules are not formatted.
# Fail the build if any rule file is not formatted
yr fmt --check rules/*.yar
Formatting behavior is configurable through a .yara-x.toml file (defaulting to ${HOME}/.yara-x.toml). The [fmt] section controls things like indentation and alignment:
# Illustrative example — a .yara-x.toml [fmt] section
[fmt]
rule.indent_spaces = 4
rule.indent_section_headers = true
rule.indent_section_contents = true
meta.align_values = true
patterns.align_values = true
The TOML snippet above is a representative configuration to show the shape of the [fmt] section. Consult the upstream config-file reference in Sources for the full, current set of keys.
yr check — linting rules
yr check lints rule files against conventions you define, going beyond the syntax errors that compilation already catches. The rules it enforces live in the [check.*] sections of the same .yara-x.toml configuration file — [check.rule_name], [check.tags], and [check.metadata]. You can require that rule names match a naming pattern, restrict the tags rules may use, and require specific metadata fields with specific types.
# Illustrative example — a .yara-x.toml check configuration
[check.rule_name]
regexp = "^(APT|CRIME)_[A-Z][a-zA-Z0-9_]+$"
error = true
[check.tags]
allowed = ["APT", "CRIME", "test"]
[check.metadata]
author = { type = "string", required = true }
description = { type = "string", required = true }
hash = { type = "hash" }
Metadata fields can be declared with a type and an optional required flag. Supported types include "string", "integer", "float", "bool", "md5", "sha1", "sha256", and "hash". Setting error = true on a check escalates a violation from a warning to an error.
Pair yr fmt --check and yr check in the same CI job to enforce both style and content rules. Together they keep a shared rule repository readable and consistent without manual review of every change.
The [warnings] section of the config file complements this by letting you disable individual compiler warnings globally, for example:
# Illustrative example
[warnings]
text_as_hex = { disabled = true }
Namespaces
Namespaces group rules and prevent name collisions when you combine rule sets from different sources — two files can each define a rule called dropper as long as they live in different namespaces. A namespace is attached to a rules path with a namespace: prefix, and the syntax works for both compile and scan:
yr compile <[NAMESPACE:]RULES_PATH>...
yr scan <[NAMESPACE:]RULES_PATH>... <TARGET_PATH>
For example, to place one vendor's rules under acme and another's under contoso:
yr compile --output combined.yarc acme:acme_rules/ contoso:contoso_rules/
If you would rather derive namespaces automatically from file paths instead of naming them by hand, pass --path-as-namespace. Each source file then gets its own namespace based on its path, which is a convenient way to isolate a directory tree of rules collected from many origins.
yr compile --path-as-namespace --output combined.yarc third_party_rules/
End-to-end example
A small workflow that ties the pieces together — format, lint, compile, then scan:
# 1. Normalize style and lint conventions
yr fmt rules/*.yar
yr check rules/
# 2. Compile the rule set once
yr compile --output rules.yarc rules/
# 3. Scan many targets with the compiled file
yr scan --compiled-rules rules.yarc /data/samples/