yara_x/
models.rs

1use std::ops::Range;
2use std::slice::Iter;
3
4use bstr::{BStr, ByteSlice};
5use serde::{Deserialize, Serialize};
6
7use crate::compiler::{IdentId, PatternId, PatternInfo, RuleInfo};
8use crate::scanner::{ScanContext, ScanState};
9use crate::{compiler, scanner, Rules};
10
11/// Kinds of patterns.
12#[derive(Serialize, Deserialize, Clone, Copy)]
13pub enum PatternKind {
14    /// The pattern is a plain text string.
15    Text,
16    /// The pattern is a hex pattern (e.g: { 01 02 03 })
17    Hex,
18    /// The pattern is a regular expression.
19    Regexp,
20}
21
22/// A structure that describes a rule.
23pub struct Rule<'a, 'r> {
24    pub(crate) ctx: Option<&'a ScanContext<'r, 'a>>,
25    pub(crate) rules: &'r Rules,
26    pub(crate) rule_info: &'r RuleInfo,
27}
28
29impl<'a, 'r> Rule<'a, 'r> {
30    /// Returns the rule's name.
31    pub fn identifier(&self) -> &'r str {
32        self.rules.ident_pool().get(self.rule_info.ident_id).unwrap()
33    }
34
35    /// Returns the rule's namespace.
36    pub fn namespace(&self) -> &'r str {
37        self.rules.ident_pool().get(self.rule_info.namespace_ident_id).unwrap()
38    }
39
40    /// Returns the metadata associated to this rule.
41    pub fn metadata(&self) -> Metadata<'a, 'r> {
42        Metadata {
43            rules: self.rules,
44            iterator: self.rule_info.metadata.iter(),
45            len: self.rule_info.metadata.len(),
46        }
47    }
48
49    /// Returns true if the rule is global.
50    pub fn is_global(&self) -> bool {
51        self.rule_info.is_global
52    }
53
54    /// Returns true if the rule is private.
55    pub fn is_private(&self) -> bool {
56        self.rule_info.is_private
57    }
58
59    /// Returns the tags associated to this rule.
60    pub fn tags(&self) -> Tags<'a, 'r> {
61        Tags {
62            rules: self.rules,
63            iterator: self.rule_info.tags.iter(),
64            len: self.rule_info.tags.len(),
65        }
66    }
67
68    /// Returns an iterator over the patterns defined for this rule.
69    ///
70    /// By default, the iterator yields only public patterns. Use
71    /// [`Patterns::include_private`] if you want to include private patterns
72    /// as well.
73    pub fn patterns(&self) -> Patterns<'a, 'r> {
74        Patterns {
75            ctx: self.ctx,
76            rules: self.rules,
77            include_private: false,
78            iterator: self.rule_info.patterns.iter(),
79            len_non_private: self.rule_info.patterns.len()
80                - self.rule_info.num_private_patterns,
81            len_private: self.rule_info.num_private_patterns,
82        }
83    }
84}
85
86/// A metadata value.
87#[derive(Debug, PartialEq, Serialize)]
88#[serde(untagged)]
89pub enum MetaValue<'r> {
90    /// Integer value.
91    Integer(i64),
92    /// Float value.
93    Float(f64),
94    /// Bool value.
95    Bool(bool),
96    /// A valid UTF-8 string.
97    String(&'r str),
98    /// An arbitrary string. Used when the value contains invalid UTF-8
99    /// characters.
100    Bytes(&'r BStr),
101}
102
103/// Iterator that returns the metadata associated to a rule.
104///
105/// The iterator returns (`&str`, [`MetaValue`]) pairs, where the first item
106/// is the identifier, and the second one the metadata value.
107pub struct Metadata<'a, 'r> {
108    rules: &'r Rules,
109    iterator: Iter<'a, (IdentId, compiler::MetaValue)>,
110    len: usize,
111}
112
113impl<'r> Metadata<'_, 'r> {
114    /// Returns the metadata as a [`serde_json::Value`].
115    ///
116    /// The returned value is an array of tuples `(ident, value)` with all
117    /// the metadata associated to the rule.
118    ///
119    /// ```rust
120    /// # use yara_x;
121    /// let rules = yara_x::compile(r#"
122    /// rule test {
123    ///   meta:
124    ///     some_int = 1
125    ///     some_bool = true
126    ///     some_str = "foo"
127    ///     some_bytes = "\x01\x02\x03"
128    ///   condition:
129    ///     true
130    /// }
131    /// "#).unwrap();
132    ///
133    /// let mut scanner = yara_x::Scanner::new(&rules);
134    ///
135    /// let scan_results = scanner
136    ///     .scan(&[])
137    ///     .unwrap();
138    ///
139    /// let matching_rule = scan_results
140    ///     .matching_rules()
141    ///     .next()
142    ///     .unwrap();
143    ///
144    /// assert_eq!(
145    ///     matching_rule.metadata().into_json(),
146    ///     serde_json::json!([
147    ///         ("some_int", 1),
148    ///         ("some_bool", true),
149    ///         ("some_str", "foo"),
150    ///         ("some_bytes", [0x01, 0x02, 0x03]),
151    ///     ])
152    /// );
153    /// ```
154    pub fn into_json(self) -> serde_json::Value {
155        let v: Vec<(&'r str, MetaValue<'r>)> = self.collect();
156        serde_json::value::to_value(v).unwrap()
157    }
158
159    /// Returns `true` if the rule doesn't have any metadata.
160    #[inline]
161    pub fn is_empty(&self) -> bool {
162        self.iterator.len() == 0
163    }
164}
165
166impl<'r> Iterator for Metadata<'_, 'r> {
167    type Item = (&'r str, MetaValue<'r>);
168
169    fn next(&mut self) -> Option<Self::Item> {
170        let (ident_id, value) = self.iterator.next()?;
171
172        let ident = self.rules.ident_pool().get(*ident_id).unwrap();
173
174        let value = match value {
175            compiler::MetaValue::Bool(b) => MetaValue::Bool(*b),
176            compiler::MetaValue::Integer(i) => MetaValue::Integer(*i),
177            compiler::MetaValue::Float(f) => MetaValue::Float(*f),
178            compiler::MetaValue::String(id) => {
179                let s = self.rules.lit_pool().get(*id).unwrap();
180                // We can be sure that s is a valid UTF-8 string, because
181                // the type of meta is MetaValue::String.
182                let s = unsafe { s.to_str_unchecked() };
183                MetaValue::String(s)
184            }
185            compiler::MetaValue::Bytes(id) => {
186                MetaValue::Bytes(self.rules.lit_pool().get(*id).unwrap())
187            }
188        };
189
190        Some((ident, value))
191    }
192}
193
194impl ExactSizeIterator for Metadata<'_, '_> {
195    #[inline]
196    fn len(&self) -> usize {
197        self.len
198    }
199}
200
201/// An iterator that returns the tags defined by a rule.
202pub struct Tags<'a, 'r> {
203    rules: &'r Rules,
204    iterator: Iter<'a, IdentId>,
205    len: usize,
206}
207
208impl Tags<'_, '_> {
209    /// Returns `true` if the rule doesn't have any tags.
210    #[inline]
211    pub fn is_empty(&self) -> bool {
212        self.iterator.len() == 0
213    }
214}
215
216impl<'r> Iterator for Tags<'_, 'r> {
217    type Item = Tag<'r>;
218
219    fn next(&mut self) -> Option<Self::Item> {
220        let ident_id = self.iterator.next()?;
221        Some(Tag { rules: self.rules, ident_id: *ident_id })
222    }
223}
224
225impl ExactSizeIterator for Tags<'_, '_> {
226    #[inline]
227    fn len(&self) -> usize {
228        self.len
229    }
230}
231
232/// Represents a tag defined by a rule.
233pub struct Tag<'r> {
234    rules: &'r Rules,
235    ident_id: IdentId,
236}
237
238impl<'r> Tag<'r> {
239    /// Returns the tag's identifier.
240    pub fn identifier(&self) -> &'r str {
241        self.rules.ident_pool().get(self.ident_id).unwrap()
242    }
243}
244
245/// An iterator that returns the patterns defined by a rule.
246///
247/// By default, the iterator yields only public patterns. Use
248/// [`Patterns::include_private`] if you want to include private patterns
249/// as well.
250pub struct Patterns<'a, 'r> {
251    ctx: Option<&'a ScanContext<'r, 'a>>,
252    rules: &'r Rules,
253    iterator: Iter<'a, PatternInfo>,
254    /// True if the iterator should yield all patterns, including the
255    /// private ones. If false, only the non-private patterns are
256    /// yielded.
257    include_private: bool,
258    /// Number of private patterns that remain to be yielded.
259    len_private: usize,
260    /// Number of non-private patterns that remain to be yielded.
261    len_non_private: usize,
262}
263
264impl Patterns<'_, '_> {
265    /// Specifies whether the iterator should yield private patterns.
266    ///
267    /// This does not reset the iterator to its initial state, the iterator will
268    /// continue from its current position.
269    pub fn include_private(mut self, yes: bool) -> Self {
270        self.include_private = yes;
271        self
272    }
273}
274
275impl ExactSizeIterator for Patterns<'_, '_> {
276    #[inline]
277    fn len(&self) -> usize {
278        if self.include_private {
279            self.len_non_private + self.len_private
280        } else {
281            self.len_non_private
282        }
283    }
284}
285
286impl<'a, 'r> Iterator for Patterns<'a, 'r> {
287    type Item = Pattern<'a, 'r>;
288
289    fn next(&mut self) -> Option<Self::Item> {
290        loop {
291            let pattern = self.iterator.next()?;
292
293            if pattern.is_private {
294                self.len_private -= 1;
295            } else {
296                self.len_non_private -= 1;
297            }
298
299            if self.include_private || !pattern.is_private {
300                return Some(Pattern {
301                    ctx: self.ctx,
302                    rules: self.rules,
303                    ident_id: pattern.ident_id,
304                    pattern_id: pattern.pattern_id,
305                    kind: pattern.kind,
306                    is_private: pattern.is_private,
307                });
308            }
309        }
310    }
311}
312
313/// Represents a pattern defined by a rule.
314pub struct Pattern<'a, 'r> {
315    ctx: Option<&'a ScanContext<'r, 'a>>,
316    rules: &'r Rules,
317    ident_id: IdentId,
318    pattern_id: PatternId,
319    kind: PatternKind,
320    is_private: bool,
321}
322
323impl<'a, 'r> Pattern<'a, 'r> {
324    /// Returns the pattern's identifier (e.g: $a, $b).
325    pub fn identifier(&self) -> &'r str {
326        self.rules.ident_pool().get(self.ident_id).unwrap()
327    }
328
329    /// Returns the kind of this pattern.
330    #[inline]
331    pub fn kind(&self) -> PatternKind {
332        self.kind
333    }
334
335    /// Returns true if the pattern is private.
336    #[inline]
337    pub fn is_private(&self) -> bool {
338        self.is_private
339    }
340
341    /// Returns the matches found for this pattern.
342    pub fn matches(&self) -> Matches<'a, 'r> {
343        Matches {
344            ctx: self.ctx,
345            iterator: self.ctx.and_then(|ctx| {
346                ctx.pattern_matches
347                    .get(self.pattern_id)
348                    .map(|matches| matches.iter())
349            }),
350        }
351    }
352}
353
354/// Iterator that returns the matches for a pattern.
355pub struct Matches<'a, 'r> {
356    ctx: Option<&'a ScanContext<'r, 'a>>,
357    iterator: Option<Iter<'a, scanner::Match>>,
358}
359
360impl<'a, 'r> Iterator for Matches<'a, 'r> {
361    type Item = Match<'a, 'r>;
362
363    fn next(&mut self) -> Option<Self::Item> {
364        let iter = self.iterator.as_mut()?;
365        Some(Match { ctx: self.ctx?, inner: iter.next()? })
366    }
367}
368
369impl ExactSizeIterator for Matches<'_, '_> {
370    fn len(&self) -> usize {
371        self.iterator.as_ref().map_or(0, |it| it.len())
372    }
373}
374
375/// Represents a match.
376pub struct Match<'a, 'r> {
377    ctx: &'a ScanContext<'r, 'a>,
378    inner: &'a scanner::Match,
379}
380
381impl<'a> Match<'a, '_> {
382    /// Range within the original data where the match occurred.
383    #[inline]
384    pub fn range(&self) -> Range<usize> {
385        self.inner.range.clone()
386    }
387
388    /// Slice containing the data that matched.
389    #[inline]
390    pub fn data(&self) -> &'a [u8] {
391        let data = match &self.ctx.scan_state {
392            ScanState::Finished(snippets) => snippets.get(self.range()),
393            _ => None,
394        };
395
396        data.unwrap()
397    }
398
399    /// XOR key used for decrypting the data if the pattern had the `xor`
400    /// modifier, or `None` if otherwise.
401    #[inline]
402    pub fn xor_key(&self) -> Option<u8> {
403        self.inner.xor_key
404    }
405}