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}