yara_x/
variables.rs

1/*! This module implements the [`Variable`] type.
2
3[`Variable`] is just a wrapper around [`TypeValue`]. Instead of exposing
4the internal [`TypeValue`] type in the public API we expose [`Variable`]
5instead, decoupling the API from internal implementation details.
6
7API functions like [`crate::Compiler::define_global`] expect Rust types that
8implement the [`Into<Variable>`] trait. This module implements the trait for
9multiple commonly used types like `bool`, `i64`, `&str`, etc.
10 */
11use std::rc::Rc;
12
13use bstr::BString;
14use thiserror::Error;
15
16use crate::types;
17use crate::types::{Array, TypeValue};
18
19/// Represents a YARA variable.
20///
21/// Functions like [`crate::Compiler::define_global`] expect types that
22/// implement [`Into<Variable>`].
23pub struct Variable(TypeValue);
24
25/// Error returned while defining or setting variables.
26#[derive(Error, Debug, Eq, PartialEq)]
27pub enum VariableError {
28    /// The variable has not being defined. Before calling
29    /// [`crate::Scanner::set_global`] the variable must be defined with a
30    /// call to [`crate::Compiler::define_global`].
31    #[error("variable `{0}` not defined")]
32    Undefined(String),
33
34    /// A variable with the same name already exists.
35    #[error("variable `{0}` already exists")]
36    AlreadyExists(String),
37
38    /// The identifier is not valid. Identifiers can only contain alphanumeric
39    /// characters and underscores, and can't start with a digit.
40    #[error("invalid variable identifier `{0}`")]
41    InvalidIdentifier(String),
42
43    /// The value of a variable cannot be null. This may happen when using a
44    /// [`serde_json::Value`], as JSON values can be null.
45    #[error("null values are not accepted")]
46    UnexpectedNull,
47
48    /// Invalid array. Arrays can't be empty, and all items must be non-null
49    /// and have the same type.
50    #[error("arrays can't be empty and all items must be non-null and the same type")]
51    InvalidArray,
52
53    /// Integer value is out of range.
54    #[error("integer value is out of range")]
55    IntegerOutOfRange,
56
57    /// A variable has been previously defined with a different type. You can
58    /// not call [`crate::Scanner::set_global`] and pass a value that don't
59    /// match the already defined type.
60    #[error(
61        "invalid type for `{variable}`, expecting `{expected_type}`, got `{actual_type}"
62    )]
63    InvalidType {
64        /// Variable name.
65        variable: String,
66        /// Name of the expected type.
67        expected_type: String,
68        /// Name of the actual type.
69        actual_type: String,
70    },
71}
72
73impl TryFrom<bool> for Variable {
74    type Error = VariableError;
75    fn try_from(value: bool) -> Result<Self, Self::Error> {
76        Ok(Variable(TypeValue::var_bool_from(value)))
77    }
78}
79
80impl TryFrom<i64> for Variable {
81    type Error = VariableError;
82    fn try_from(value: i64) -> Result<Self, Self::Error> {
83        Ok(Variable(TypeValue::var_integer_from(value)))
84    }
85}
86
87impl TryFrom<i32> for Variable {
88    type Error = VariableError;
89    fn try_from(value: i32) -> Result<Self, Self::Error> {
90        Ok(Variable(TypeValue::var_integer_from(value)))
91    }
92}
93
94impl TryFrom<i16> for Variable {
95    type Error = VariableError;
96    fn try_from(value: i16) -> Result<Self, Self::Error> {
97        Ok(Variable(TypeValue::var_integer_from(value)))
98    }
99}
100
101impl TryFrom<i8> for Variable {
102    type Error = VariableError;
103    fn try_from(value: i8) -> Result<Self, Self::Error> {
104        Ok(Variable(TypeValue::var_integer_from(value)))
105    }
106}
107
108impl TryFrom<u32> for Variable {
109    type Error = VariableError;
110    fn try_from(value: u32) -> Result<Self, Self::Error> {
111        Ok(Variable(TypeValue::var_integer_from(value)))
112    }
113}
114
115impl TryFrom<u16> for Variable {
116    type Error = VariableError;
117    fn try_from(value: u16) -> Result<Self, Self::Error> {
118        Ok(Variable(TypeValue::var_integer_from(value)))
119    }
120}
121
122impl TryFrom<u8> for Variable {
123    type Error = VariableError;
124    fn try_from(value: u8) -> Result<Self, Self::Error> {
125        Ok(Variable(TypeValue::var_integer_from(value)))
126    }
127}
128
129impl TryFrom<f64> for Variable {
130    type Error = VariableError;
131    fn try_from(value: f64) -> Result<Self, Self::Error> {
132        Ok(Variable(TypeValue::var_float_from(value)))
133    }
134}
135
136impl TryFrom<f32> for Variable {
137    type Error = VariableError;
138    fn try_from(value: f32) -> Result<Self, Self::Error> {
139        Ok(Variable(TypeValue::var_float_from(value)))
140    }
141}
142
143impl TryFrom<&str> for Variable {
144    type Error = VariableError;
145    fn try_from(value: &str) -> Result<Self, Self::Error> {
146        Ok(Variable(TypeValue::var_string_from(value)))
147    }
148}
149
150impl TryFrom<&[u8]> for Variable {
151    type Error = VariableError;
152    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
153        Ok(Variable(TypeValue::var_string_from(value)))
154    }
155}
156
157impl TryFrom<String> for Variable {
158    type Error = VariableError;
159    fn try_from(value: String) -> Result<Self, Self::Error> {
160        Ok(Variable(TypeValue::var_string_from(value)))
161    }
162}
163
164impl TryFrom<serde_json::Value> for Variable {
165    type Error = VariableError;
166    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
167        Variable::try_from(&value)
168    }
169}
170
171impl TryFrom<&serde_json::Value> for Variable {
172    type Error = VariableError;
173    fn try_from(value: &serde_json::Value) -> Result<Self, Self::Error> {
174        match value {
175            serde_json::Value::Null => Err(VariableError::UnexpectedNull),
176            serde_json::Value::Bool(b) => {
177                Ok(Variable(TypeValue::var_bool_from(*b)))
178            }
179            serde_json::Value::Number(n) => {
180                if let Some(n) = n.as_u64() {
181                    let n: i64 = n
182                        .try_into()
183                        .map_err(|_| VariableError::IntegerOutOfRange)?;
184                    Ok(Variable(TypeValue::var_integer_from(n)))
185                } else if let Some(n) = n.as_i64() {
186                    Ok(Variable(TypeValue::var_integer_from(n)))
187                } else if let Some(n) = n.as_f64() {
188                    Ok(Variable(TypeValue::var_float_from(n)))
189                } else {
190                    unreachable!()
191                }
192            }
193            serde_json::Value::String(s) => {
194                Ok(Variable(TypeValue::var_string_from(s)))
195            }
196            serde_json::Value::Array(values) => {
197                let mut array = None;
198                // Try to determine the type of the array by looking at the
199                // type of the first non-null item.
200                for v in values {
201                    if v.is_boolean() {
202                        array = Some(Array::Bools(Vec::new()));
203                        break;
204                    } else if v.is_i64() {
205                        array = Some(Array::Integers(Vec::new()));
206                        break;
207                    } else if v.is_f64() {
208                        array = Some(Array::Floats(Vec::new()));
209                        break;
210                    } else if v.is_string() {
211                        array = Some(Array::Strings(Vec::new()));
212                        break;
213                    } else if v.is_object() {
214                        array = Some(Array::Structs(Vec::new()));
215                        break;
216                    } else if v.is_array() {
217                        // Arrays can't be nested.
218                        return Err(VariableError::InvalidArray);
219                    }
220                }
221
222                // If the array is empty or all the items are null we can't
223                // determine the type of the array, and that's not allowed.
224                if array.is_none() {
225                    return Err(VariableError::InvalidArray);
226                }
227
228                let mut array = array.unwrap();
229
230                match array {
231                    Array::Integers(ref mut integers) => {
232                        for v in values {
233                            match v.as_i64() {
234                                Some(v) => {
235                                    integers.push(v);
236                                }
237                                None => {
238                                    return Err(VariableError::InvalidArray);
239                                }
240                            };
241                        }
242                    }
243                    Array::Floats(ref mut floats) => {
244                        for v in values {
245                            match v.as_f64() {
246                                Some(v) => {
247                                    floats.push(v);
248                                }
249                                None => {
250                                    return Err(VariableError::InvalidArray);
251                                }
252                            };
253                        }
254                    }
255                    Array::Bools(ref mut bools) => {
256                        for v in values {
257                            match v.as_bool() {
258                                Some(v) => {
259                                    bools.push(v);
260                                }
261                                None => {
262                                    return Err(VariableError::InvalidArray);
263                                }
264                            };
265                        }
266                    }
267                    Array::Strings(ref mut strings) => {
268                        for v in values {
269                            match v.as_str() {
270                                Some(v) => {
271                                    strings.push(BString::from(v).into());
272                                }
273                                None => {
274                                    return Err(VariableError::InvalidArray);
275                                }
276                            };
277                        }
278                    }
279                    Array::Structs(ref mut structs) => {
280                        for v in values {
281                            match v.as_object() {
282                                Some(v) => {
283                                    let mut s = types::Struct::new();
284                                    for (key, value) in v {
285                                        s.add_field(
286                                            key,
287                                            TypeValue::from(
288                                                Variable::try_from(value)?,
289                                            ),
290                                        );
291                                    }
292                                    structs.push(Rc::new(s));
293                                }
294                                None => {
295                                    return Err(VariableError::InvalidArray);
296                                }
297                            };
298                        }
299                    }
300                }
301                Ok(Variable(TypeValue::Array(Rc::new(array))))
302            }
303            serde_json::Value::Object(obj) => {
304                let mut s = types::Struct::new();
305                for (key, value) in obj {
306                    s.add_field(
307                        key,
308                        TypeValue::from(Variable::try_from(value)?),
309                    );
310                }
311                Ok(Variable(TypeValue::Struct(Rc::new(s))))
312            }
313        }
314    }
315}
316
317impl From<Variable> for TypeValue {
318    fn from(value: Variable) -> Self {
319        value.0
320    }
321}
322
323/// Returns true if the given identifier is a valid one.
324///
325/// Valid identifiers are composed of letters, digits, and the underscore (_)
326/// character, but they can't start with a digit.
327pub(crate) fn is_valid_identifier(ident: &str) -> bool {
328    let mut chars = ident.chars();
329
330    if let Some(first) = chars.next() {
331        // The first character must be a letter or underscore.
332        if !first.is_alphabetic() && first != '_' {
333            return false;
334        }
335    } else {
336        // No first char, ident is empty.
337        return false;
338    }
339
340    // The remaining characters must be letters, numbers, or underscores.
341    chars.all(|c| c.is_alphanumeric() || c == '_')
342}
343
344#[cfg(test)]
345mod test {
346    #[test]
347    fn is_valid_identifier() {
348        // Valid identifiers
349        assert!(super::is_valid_identifier("a"));
350        assert!(super::is_valid_identifier("_"));
351        assert!(super::is_valid_identifier("foo"));
352        assert!(super::is_valid_identifier("_foo"));
353
354        // Invalid identifiers
355        assert!(!super::is_valid_identifier("123"));
356        assert!(!super::is_valid_identifier("1foo"));
357        assert!(!super::is_valid_identifier("foo|"));
358        assert!(!super::is_valid_identifier("foo.bar"));
359    }
360}