boreal_parser/lib.rs
1//! Parser for YARA rules.
2//!
3//! This crate is designed to be used by the [`boreal` crate](https://docs.rs/boreal/%2A/boreal/).
4//!
5//! It exposes a main entrypoint function, [`parse`], which parses the contents of a YARA file.
6//!
7//! ```rust
8//! use boreal_parser::*;
9//! use boreal_parser::expression::*;
10//! use boreal_parser::file::*;
11//! use boreal_parser::rule::*;
12//!
13//! let file = parse(r#"
14//! import "pe"
15//!
16//! private rule b : tag1 {
17//! meta:
18//! a = true
19//! strings:
20//! $b = "\\mspaint.exe" wide
21//! condition:
22//! pe.is_dll() and all of them
23//! }"#)?;
24//!
25//! assert_eq!(
26//! file.components[0],
27//! YaraFileComponent::Import(Import {
28//! name: "pe".to_owned(),
29//! span: 1..12,
30//! })
31//! );
32//! assert_eq!(
33//! file.components[1],
34//! YaraFileComponent::Rule(Box::new(Rule {
35//! name: "b".to_owned(),
36//! name_span: 27..28,
37//! tags: vec![RuleTag {
38//! tag: "tag1".to_owned(),
39//! span: 31..35
40//! }],
41//! metadatas: vec![Metadata {
42//! name: "a".to_owned(),
43//! value: MetadataValue::Boolean(true)
44//! }],
45//! variables: vec![VariableDeclaration {
46//! name: "b".to_owned(),
47//! value: VariableDeclarationValue::Bytes(b"\\mspaint.exe".to_vec()),
48//! modifiers: VariableModifiers {
49//! wide: true,
50//! ..Default::default()
51//! },
52//! span: 86..111,
53//! }],
54//!
55//! condition: Expression {
56//! expr: ExpressionKind::And(vec![
57//! Expression {
58//! expr: ExpressionKind::Identifier(Identifier {
59//! name: "pe".to_owned(),
60//! name_span: 135..137,
61//! operations: vec![
62//! IdentifierOperation {
63//! op: IdentifierOperationType::Subfield(
64//! "is_dll".to_owned()
65//! ),
66//! span: 137..144,
67//! },
68//! IdentifierOperation {
69//! op: IdentifierOperationType::FunctionCall(vec![]),
70//! span: 144..146,
71//! }
72//! ],
73//! }),
74//! span: 135..146,
75//! },
76//! Expression {
77//! expr: ExpressionKind::For {
78//! selection: ForSelection::All,
79//! set: VariableSet { elements: vec![] },
80//!
81//! body: None,
82//! },
83//! span: 151..162,
84//! }
85//! ]),
86//! span: 135..162
87//! },
88//! is_private: true,
89//! is_global: false,
90//! }))
91//! );
92//!
93//! # Ok::<(), boreal_parser::error::Error>(())
94//! ```
95
96// Parsing uses the [`nom`] crate, adapted for textual parsing.
97//
98// All of the parsing functions, unless otherwise indicated, depends on the
99// following invariants:
100// - The received input has already been left-trimmed
101// - The returned input is right-trimmed
102// The [`nom_recipes::rtrim`] function is provided to make this easier.
103
104pub mod error;
105pub mod expression;
106pub mod file;
107pub mod hex_string;
108mod nom_recipes;
109mod number;
110pub mod regex;
111pub mod rule;
112mod string;
113mod types;
114pub use types::Params;
115
116/// Parse a YARA file.
117///
118/// This uses default values for a few parameters, see [`Params`].
119/// To modify those parameters, use [`parse_with_params`].
120///
121/// # Errors
122///
123/// Returns an error if the parsing fails, or if there are
124/// trailing data in the file that has not been parsed.
125pub fn parse(input: &str) -> Result<file::YaraFile, error::Error> {
126 parse_with_params(input, Params::default())
127}
128
129/// Parse a YARA file with the provided parameters.
130///
131/// # Errors
132///
133/// Returns an error if the parsing fails, or if there are
134/// trailing data in the file that has not been parsed.
135pub fn parse_with_params(input: &str, params: Params) -> Result<file::YaraFile, error::Error> {
136 use nom::Finish;
137
138 let input = types::Input::with_params(input, params);
139 let (_, rules) = file::parse_yara_file(input).finish()?;
140
141 Ok(rules)
142}
143
144#[cfg(test)]
145mod test_helpers;
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_parse_str() {
153 assert!(parse(" global rule c { condition: false }").is_ok());
154 assert!(parse(" global rule c { condtion: false }").is_err());
155 }
156}