Skip to main content

yara_x_proto/
lib.rs

1use protobuf::reflect::{EnumDescriptor, FieldDescriptor};
2
3pub use yara::*;
4
5use crate::yara::exts::field_options;
6
7include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
8
9/// Possible formats applied to values in YARA modules.
10///
11/// In the protobufs defining a YARA module, values can be formatted in various
12/// ways. For example, you can define a field that represents a timestamp in the
13/// following way:
14///
15/// ```text
16/// optional uint32 my_timestamp = 1 [(yara.field_options).fmt = "t"];
17/// ```
18///
19/// Or a field that should be formatted as an hexadecimal number:
20///
21/// ```text
22/// optional uint32 my_flags = 2 [(yara.field_options).fmt = "x"];
23/// ```
24///
25/// This enum represents the different formats that can be applied to values in
26/// YARA modules.
27#[derive(Debug, Clone)]
28pub enum FieldFormat {
29    None,
30    Hex,
31    Timestamp,
32    Flags(EnumDescriptor),
33}
34
35/// Given a field descriptor, returns the format that should be applied to its
36/// value, if any.
37pub fn get_field_format(field_descriptor: &FieldDescriptor) -> FieldFormat {
38    let opts = match field_options.get(&field_descriptor.proto().options) {
39        Some(opts) => opts,
40        None => return FieldFormat::None,
41    };
42
43    let fmt = opts.fmt();
44
45    if fmt == "x" {
46        return FieldFormat::Hex;
47    } else if fmt == "t" {
48        return FieldFormat::Timestamp;
49    }
50
51    let msg_descriptor = field_descriptor.containing_message();
52    let file_descriptor = msg_descriptor.file_descriptor();
53
54    // Check if format is something like `flags:ENUM_TYPE`.
55    if let Some(flags_enum) = fmt.strip_prefix("flags:") {
56        if let Some(flags_enum) =
57            file_descriptor.enums().find(|e| e.name() == flags_enum)
58        {
59            return FieldFormat::Flags(flags_enum);
60        } else {
61            panic!(
62                "field `{}` declared as `flags:{}`, but enum `{}` was not found",
63                field_descriptor.full_name(),
64                flags_enum,
65                flags_enum
66            )
67        }
68    }
69
70    // If the format is not "x", "t", or "flags:ENUM_TYPE", and it's not empty,
71    // it could be a custom format string (e.g. "{:#x}"). In this case,
72    // we don't have a specific ValueFormat enum variant, so we treat it as None
73    // for now. The actual formatting will be handled directly where this
74    // function's return value is used, by checking if `fmt` is non-empty.
75    // However, the original panic for unknown simple formats like "x", "t"
76    // should be preserved if fmt is not a flags type and not x or t.
77    // For now, to keep changes minimal, we'll assume that if it's not x, t, or flags,
78    // and it's not empty, it's an invalid *simple* format specifier.
79    // More complex format strings will pass through as ValueFormat::None
80    // and need to be handled by the caller if direct string formatting is desired.
81    if !fmt.is_empty() {
82        // This part of the logic might need refinement if we want to support
83        // arbitrary format strings directly through ValueFormat.
84        // For now, an unknown non-empty fmt that is not "x", "t", or "flags:..."
85        // is considered an error, similar to the original code.
86        panic!(
87            "invalid format option `{}` for field `{}`",
88            fmt,
89            field_descriptor.full_name(),
90        );
91    }
92
93    FieldFormat::None
94}