Language bindings
YARA-X is written in Rust, but you do not have to write
Rust to use it. Besides the yr command-line tool, the project maintains
official bindings for several languages, all backed by the same engine. This means you
can compile rules and scan data in-process — without spawning the CLI — from Python, Go,
C/C++, Rust, or JavaScript running on WebAssembly.
All of the bindings expose the same core concepts you already know from the rule
language and CLI: a compiler turns rule source into compiled Rules, and a
scanner runs those rules over a block of data. Only the surface API differs between
languages.
Available bindings
The table below lists each official binding, where its source lives in the VirusTotal/yara-x repository, and how you install or depend on it.
| Language | Source directory | Package / crate | Install or import |
|---|---|---|---|
| Python | py/ | yara-x (PyPI) | pip install yara-x → import yara_x |
| Go | go/ | github.com/VirusTotal/yara-x/go | go get github.com/VirusTotal/yara-x/go |
| C / C++ | capi/ | yara-x-capi (crate) | build with cargo cinstall, then #include <yara_x.h> |
| Rust | lib/ | yara-x (crates.io) | cargo add yara-x |
| JavaScript / WASM | js-wasm/ | @virustotal/yara-x (npm) | npm install @virustotal/yara-x |
The Python package is published on PyPI as yara-x (with a hyphen), but the module
you import in code is yara_x (with an underscore). This is a common Python naming
convention — the distribution name and the import name differ.
Python
The Python bindings are installed with pip install yara-x and imported as yara_x.
Wheels are published for Linux, macOS, and Windows, so no Rust toolchain is needed to
install them. See the Python API docs
for the full surface, including the Compiler, Rules, and Scanner classes.
Go
The Go bindings live under github.com/VirusTotal/yara-x/go and use the C API under the
hood via cgo.
Because the Go bindings wrap the C library through cgo, you must build and install the
C library first before the Go package can be compiled. Follow the
C/C++ instructions to install it,
then run go get github.com/VirusTotal/yara-x/go.
C / C++
The C API is built with cargo-c. Running
cargo cinstall builds both static and dynamic libraries, generates the yara_x.h
header (via cbindgen), and writes a pkg-config .pc file so other build systems can
locate the library. The public functions are prefixed with yrx_, for example
yrx_compile. This is the same C library the Go bindings depend on.
Rust
To use YARA-X as a native Rust library, add the yara-x
crate with cargo add yara-x. API documentation is published on
docs.rs/yara-x.
JavaScript / WebAssembly
The js-wasm/ directory provides JavaScript bindings built on WebAssembly, published to
npm as @virustotal/yara-x. The whole yara-x crate can be compiled to WASM, which
is what powers the online
YARA-X playground. The JavaScript API
exposes Compiler, Rules, and Scanner objects, and uses explicit .free() calls
(or the using keyword) to release WASM memory.
Python compile-and-scan example
The snippet below is the canonical way to compile a rule and scan a byte string with the
Python bindings. The simplest path is yara_x.compile(), which returns a Rules object
you can call .scan() on directly.
The following is a minimal, illustrative example intended to show the shape of the API. Adapt the rule and the data you scan to your own use case.
import yara_x
# Compile a single rule from a source string.
rules = yara_x.compile('''
rule test {
strings:
$a = "foobar"
condition:
$a
}
''')
# Scan an in-memory byte string.
results = rules.scan(b"foobar")
# Inspect what matched.
matched = results.matching_rules[0]
print(matched.identifier) # -> "test"
pattern = matched.patterns[0]
print(pattern.identifier) # -> "$a"
print(pattern.matches[0].offset, pattern.matches[0].length) # -> 0 6
For more advanced scenarios — multiple namespaces, global variables, or reusing compiled
rules across many scans — build the rules with a Compiler and run them through a
Scanner:
import yara_x
compiler = yara_x.Compiler()
compiler.new_namespace("foo")
compiler.add_source('rule a { condition: true }')
compiler.new_namespace("bar")
compiler.add_source('rule b { condition: false }')
rules = compiler.build()
scanner = yara_x.Scanner(rules)
scanner.set_timeout(60)
results = scanner.scan(b"some data")
yara_x.compile() is convenient for one-off cases, but a Scanner is the right choice
when you scan many inputs against the same rules — you compile once and reuse the
compiled Rules, which avoids paying the compilation cost on every scan.