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}