Skip to main content

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