use std::collections::Bound;
use std::fs;
use std::io::Write;
use std::mem::size_of;
use pretty_assertions::assert_eq;
use serde_json::json;
use crate::compiler::{FilesizeBounds, SubPattern, VarStack, linters};
use crate::errors::{SerializationError, VariableError};
use crate::types::Type;
use crate::{Compiler, Rules, Scanner, SourceCode, compile};
#[test]
fn serialization() {
assert!(matches!(
Rules::deserialize([]).err().unwrap(),
SerializationError::InvalidFormat
));
assert!(matches!(
Rules::deserialize(b"YARA-X").err().unwrap(),
SerializationError::InvalidFormat
));
let mut data = Vec::new();
data.extend(b"YARA-X\0\0");
data.extend(1u32.to_le_bytes());
data.extend(b"foo");
assert!(matches!(
Rules::deserialize(&data).err().unwrap(),
SerializationError::DecodeError(_)
));
let mut data = Vec::new();
data.extend(b"YARA-X\0\0");
data.extend(0u32.to_le_bytes());
data.extend(b"foo");
assert!(matches!(
Rules::deserialize(&data).err().unwrap(),
SerializationError::InvalidVersion { expected: _, actual: 0 }
));
let rules = compile(r#"rule test { strings: $a = "foo" condition: $a }"#)
.unwrap()
.serialize()
.unwrap();
let rules = Rules::deserialize(rules).unwrap();
let mut scanner = Scanner::new(&rules);
assert_eq!(
scanner
.scan(b"foo")
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
assert_eq!(size_of::<SubPattern>(), 24);
}
#[test]
fn namespaces() {
let mut compiler = Compiler::new();
assert!(
compiler
.add_source("rule foo {condition: true}")
.unwrap()
.add_source("rule bar {condition: foo}")
.is_ok()
);
let mut compiler = Compiler::new();
assert!(
compiler
.add_source("rule foo {condition: true}")
.unwrap()
.new_namespace("bar")
.add_source("rule bar {condition: foo}")
.is_err()
);
let mut compiler = Compiler::new();
assert!(
compiler
.new_namespace("foo")
.add_source("rule foo {condition: true}")
.unwrap()
.new_namespace("foo")
.add_source("rule bar {condition: foo}")
.is_ok()
);
}
#[test]
fn var_stack() {
let mut stack = VarStack::new();
let mut frame1 = stack.new_frame(4);
let mut frame2 = stack.new_frame(4);
let var = frame1.new_var(Type::Integer);
assert_eq!(var.ty(), Type::Integer);
assert_eq!(var.frame_id(), 1);
assert_eq!(var.index(), 0);
let var = frame1.new_var(Type::String);
assert_eq!(var.ty(), Type::String);
assert_eq!(var.frame_id(), 1);
assert_eq!(var.index(), 1);
let var = frame2.new_var(Type::Integer);
assert_eq!(var.ty(), Type::Integer);
assert_eq!(var.frame_id(), 2);
assert_eq!(var.index(), 4);
let var = frame2.new_var(Type::Bool);
assert_eq!(var.ty(), Type::Bool);
assert_eq!(var.frame_id(), 2);
assert_eq!(var.index(), 5);
stack.unwind(&frame1);
assert_eq!(stack.used(), 0);
}
#[test]
fn snapshots() {
let mut compiler = Compiler::new();
compiler
.add_source(r#"rule test { strings: $a = "foo" condition: $a }"#)
.unwrap();
let snapshot = compiler.take_snapshot();
compiler
.add_source(r#"rule test { strings: $a = /{}/ condition: $a }"#)
.expect_err("compilation should fail");
assert_eq!(compiler.take_snapshot(), snapshot);
}
#[test]
fn globals() {
let mut compiler = Compiler::new();
assert_eq!(
compiler.define_global("#invalid", true).err().unwrap(),
VariableError::InvalidIdentifier("#invalid".to_string())
);
let mut compiler = Compiler::new();
assert_eq!(
compiler
.define_global("a", true)
.unwrap()
.define_global("a", false)
.err()
.unwrap(),
VariableError::AlreadyExists("a".to_string())
);
let mut compiler = Compiler::new();
compiler
.define_global("int_1", 1u8)
.unwrap()
.add_source("rule foo {condition: int_1 == 1}")
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("int_1", 1u16)
.unwrap()
.add_source("rule foo {condition: int_1 == 1}")
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("int_1", 1u32)
.unwrap()
.add_source("rule foo {condition: int_1 == 1}")
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("int_1", 1i8)
.unwrap()
.add_source("rule foo {condition: int_1 == 1}")
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("int_1", 1i16)
.unwrap()
.add_source("rule foo {condition: int_1 == 1}")
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("int_1", 1i32)
.unwrap()
.add_source("rule foo {condition: int_1 == 1}")
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("int_1", 1i64)
.unwrap()
.add_source("rule foo {condition: int_1 == 1}")
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("float_1", 1_f32)
.unwrap()
.add_source("rule foo {condition: float_1 == 1.0}")
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("float_1", 1_f64)
.unwrap()
.add_source("rule foo {condition: float_1 == 1.0}")
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("str_foo", "foo")
.unwrap()
.add_source(r#"rule foo {condition: str_foo == "foo"}"#)
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("bstr_foo", b"\0\0".as_slice())
.unwrap()
.add_source(r#"rule foo {condition: bstr_foo == "\0\0"}"#)
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.define_global("str_foo", "foo".to_string())
.unwrap()
.add_source(r#"rule foo {condition: str_foo == "foo"}"#)
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
let mut compiler = Compiler::new();
compiler
.new_namespace("test")
.add_source(
r#"
rule foo {strings: $a = "foo" condition: $a}
global rule always_true { condition: true }"#,
)
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(b"foo")
.expect("scan should not fail")
.matching_rules()
.len(),
2
);
#[cfg(feature = "test_proto2-module")]
{
let mut compiler = Compiler::new();
compiler
.define_global("str_foo", "foo")
.unwrap()
.add_source(
r#"
import "test_proto2"
rule foo {
condition:
str_foo == "foo" and
for any s in test_proto2.array_string: (s == str_foo)
}"#,
)
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
}
}
#[test]
fn globals_json() {
let mut compiler = Compiler::new();
compiler
.define_global(
"some_struct",
json!({
"some_int": 1,
"some_bool": true,
"some_string": "foo",
"some_int_array": [1,2,3],
"some_float_array": [1.0, 2.0, 3.0]
}),
)
.unwrap()
.add_source(
r#"
rule foo {
condition:
some_struct.some_int == 1 and
some_struct.some_bool and
some_struct.some_string == "foo" and
some_struct.some_int_array[0] == 1 and
some_struct.some_float_array[1] == 2.0
}"#,
)
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
assert_eq!(
Compiler::new()
.define_global("invalid_array", json!([1, "foo", 3]))
.unwrap_err(),
VariableError::InvalidArray
);
assert_eq!(
Compiler::new()
.define_global("invalid_array", json!([1, [2, 3], 4]))
.unwrap_err(),
VariableError::InvalidArray
);
assert_eq!(
Compiler::new()
.define_global("invalid_array", json!([1, null]))
.unwrap_err(),
VariableError::InvalidArray
);
assert_eq!(
Compiler::new()
.define_global("invalid_struct", json!({ "foo": null }))
.unwrap_err(),
VariableError::UnexpectedNull
);
assert_eq!(
Compiler::new()
.define_global("invalid_struct", json!({ "/foo": 1 }))
.unwrap_err(),
VariableError::InvalidIdentifier("/foo".to_string())
);
}
#[test]
fn relaxed_re_syntax() {
let mut compiler = Compiler::new();
compiler.relaxed_re_syntax(true);
compiler
.add_source(r#"rule test_1 { strings: $a = /\X\Y\Z/ condition: $a }"#)
.unwrap()
.add_source(r#"rule test_2 { strings: $a = /xyz{/ condition: $a }"#)
.unwrap()
.add_source(r#"rule test_3 { strings: $a = /xyz[\>]/ condition: $a }"#)
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(b"XYZ")
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
assert_eq!(
Scanner::new(&rules)
.scan(b"xyz{")
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
assert_eq!(
Scanner::new(&rules)
.scan(b"xyz>")
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
}
#[test]
fn unsupported_modules() {
let mut compiler = Compiler::new();
compiler
.ignore_module("foo_module")
.add_source(
r#"
import "foo_module"
// This rule is ignored because it uses an ignored module.
rule ignored_1 { condition: foo_module.some_field == 1 }
// This rule is ignored because it depends on a rule that directly
// depends on an ignored moduled.
rule ignored_2 { condition: ignored_1 }
// This rule is ignored because it depends on a rule that indirectly
// depends on an ignored module.
rule ignored_3 { condition: ignored_2 }
// This rule should match even if the previous ones were ignored.
rule always_true { condition: true }
"#,
)
.unwrap();
let rules = compiler.build();
assert_eq!(
Scanner::new(&rules)
.scan(&[])
.expect("scan should not fail")
.matching_rules()
.len(),
1
);
}
#[cfg(feature = "test_proto2-module")]
#[test]
fn banned_modules() {
let mut compiler = Compiler::new();
assert_eq!(
compiler
.ban_module(
"test_proto2",
"module `test_proto2` can't be used",
"module `test_proto2` is used here",
)
.add_source(
r#"
import "test_proto2"
rule test { condition: test_proto2.int32_zero == 0}
"#,
)
.expect_err("expected error")
.to_string(),
r#"error[E100]: module `test_proto2` can't be used
--> line:2:13
|
2 | import "test_proto2"
| ^^^^^^^^^^^^^^^^^^^^ module `test_proto2` is used here"#
);
assert_eq!(compiler.errors().len(), 1);
}
#[test]
fn linter_tag_list() {
assert!(Compiler::new()
.add_linter(linters::tags_allowed(vec![
"foo".to_string(),
"bar".to_string()
]))
.add_source(
r#"rule test : foo bar { strings: $foo = "foo" condition: $foo }"#
)
.unwrap()
.warnings()
.is_empty());
assert_eq!(
Compiler::new()
.add_linter(linters::tags_allowed(vec![
"foo".to_string(),
"bar".to_string(),
]))
.add_source(
r#"rule test : axs ers { strings: $foo = "foo" condition: $foo }"#
)
.unwrap()
.warnings()
.iter()
.map(|w| w.to_string())
.collect::<Vec<_>>(),
&[r#"warning[unknown_tag]: tag not in allowed list
--> line:1:13
|
1 | rule test : axs ers { strings: $foo = "foo" condition: $foo }
| --- tag `axs` not in allowed list
|
= note: allowed tags: foo, bar"#,
r#"warning[unknown_tag]: tag not in allowed list
--> line:1:17
|
1 | rule test : axs ers { strings: $foo = "foo" condition: $foo }
| --- tag `ers` not in allowed list
|
= note: allowed tags: foo, bar"#,
]
);
assert_eq!(
Compiler::new()
.add_linter(linters::tags_allowed(vec![
"foo".to_string(),
"bar".to_string(),
]).error(true))
.add_source(
r#"rule test : axs ers { strings: $foo = "foo" condition: $foo }"#
)
.expect_err("expected error")
.to_string(),
r#"error[E040]: tag not in allowed list
--> line:1:13
|
1 | rule test : axs ers { strings: $foo = "foo" condition: $foo }
| ^^^ tag `axs` not in allowed list
|
= note: allowed tags: foo, bar"#);
}
#[test]
fn linter_tags_regexp() {
assert!(Compiler::new()
.add_linter(linters::tag_regex("^(foo|bar)").unwrap())
.add_source(
r#"rule test : foo1 bar2 { strings: $foo = "foo" condition: $foo }"#
)
.unwrap()
.warnings()
.is_empty());
assert_eq!(
Compiler::new()
.add_linter(linters::tag_regex("^(foo|bar)").unwrap())
.add_source(
r#"rule test : baz blah { strings: $foo = "foo" condition: $foo }"#
)
.unwrap()
.warnings()
.iter()
.map(|w| w.to_string())
.collect::<Vec<_>>(),
&[r#"warning[invalid_tag]: tag `baz` does not match regex `^(foo|bar)`
--> line:1:13
|
1 | rule test : baz blah { strings: $foo = "foo" condition: $foo }
| --- tag `baz` does not match regex `^(foo|bar)`"#,
r#"warning[invalid_tag]: tag `blah` does not match regex `^(foo|bar)`
--> line:1:17
|
1 | rule test : baz blah { strings: $foo = "foo" condition: $foo }
| ---- tag `blah` does not match regex `^(foo|bar)`"#,
]);
assert_eq!(
Compiler::new()
.add_linter(linters::tag_regex("^(foo|bar)").unwrap().error(true))
.add_source(
r#"rule test : baz blah { strings: $foo = "foo" condition: $foo }"#
)
.expect_err("expected error")
.to_string(),
r#"error[E041]: tag does not match regex `^(foo|bar)`
--> line:1:13
|
1 | rule test : baz blah { strings: $foo = "foo" condition: $foo }
| ^^^ tag `baz` does not match regex `^(foo|bar)`"#);
assert!(linters::tag_regex("(AXS|ERS").is_err());
}
#[test]
fn linter_rule_name() {
assert!(
Compiler::new()
.add_linter(linters::rule_name("r_.+").unwrap())
.add_source(
r#"rule r_foo { strings: $foo = "foo" condition: $foo }"#
)
.unwrap()
.add_source(
r#"rule r_bar { strings: $bar = "bar" condition: $bar }"#
)
.unwrap()
.warnings()
.is_empty()
);
assert_eq!(
Compiler::new()
.add_linter(linters::rule_name("r_.+").unwrap())
.add_source(
r#"rule foo { strings: $foo = "foo" condition: $foo }"#
)
.unwrap()
.warnings()
.iter()
.map(|w| w.to_string())
.collect::<Vec<_>>(),
&[
r#"warning[invalid_rule_name]: rule name does not match regex `r_.+`
--> line:1:6
|
1 | rule foo { strings: $foo = "foo" condition: $foo }
| --- this rule name does not match regex `r_.+`"#
]
);
assert_eq!(
Compiler::new()
.add_linter(linters::rule_name("r_.+").unwrap().error(true))
.add_source(r#"rule foo { condition: true }"#)
.expect_err("expected error")
.to_string(),
"error[E039]: rule name does not match regex `r_.+`
--> line:1:6
|
1 | rule foo { condition: true }
| ^^^ this rule name does not match regex `r_.+`"
);
assert!(linters::rule_name("(AXS|ERS").is_err());
}
#[test]
fn linter_required_metadata() {
assert!(Compiler::new()
.add_linter(linters::metadata("author").required(true))
.add_source(r#"rule r_foo { meta: author = "foo" strings: $foo = "foo" condition: $foo }"#)
.unwrap()
.warnings()
.is_empty());
assert_eq!(
Compiler::new()
.add_linter(linters::metadata("author").required(true))
.add_source(
r#"rule foo { strings: $foo = "foo" condition: $foo }"#
)
.unwrap()
.warnings()
.iter()
.map(|w| w.to_string())
.collect::<Vec<_>>(),
&[r#"warning[missing_metadata]: required metadata is missing
--> line:1:6
|
1 | rule foo { strings: $foo = "foo" condition: $foo }
| --- required metadata `author` not found"#]
);
assert_eq!(
Compiler::new()
.add_linter(linters::metadata("author").required(true).error(true))
.add_source(r#"rule foo { condition: true }"#)
.expect_err("expected error")
.to_string(),
"error[E038]: required metadata is missing
--> line:1:6
|
1 | rule foo { condition: true }
| ^^^ required metadata `author` not found"
);
}
#[cfg(feature = "test_proto2-module")]
#[test]
fn import_modules() {
let mut compiler = Compiler::new();
assert!(
compiler
.add_source(
r#"
import "test_proto2"
rule foo {condition: test_proto2.int32_zero == 0}"#
)
.unwrap()
.add_source(
r#"
import "test_proto2"
rule bar {condition: test_proto2.int32_zero == 0}"#
)
.is_ok()
);
let mut compiler = Compiler::new();
assert!(
compiler
.add_source(
r#"
import "test_proto2"
rule foo {condition: test_proto2.int32_zero == 0}"#
)
.unwrap()
.new_namespace("namespace1")
.add_source(
r#"
import "test_proto2"
rule bar {condition: test_proto2.int32_zero == 0}"#
)
.is_ok()
);
}
#[cfg(feature = "pe-module")]
#[test]
fn wrong_type() {
assert_eq!(
Compiler::new()
.add_source(
r#"
import "pe"
rule test { condition: pe.is_dll }"#
)
.expect_err("expected error")
.to_string(),
"error[E002]: wrong type
--> line:3:36
|
3 | rule test { condition: pe.is_dll }
| ^^^^^^^^^ expression should be `bool`, but it is a function
|
= help: you probably meant pe.is_dll()"
);
assert_eq!(
Compiler::new()
.add_source(
r#"
import "pe"
rule test { condition: pe.sections }"#
)
.expect_err("expected error")
.to_string(),
"error[E002]: wrong type
--> line:3:36
|
3 | rule test { condition: pe.sections }
| ^^^^^^^^^^^ expression should be `bool`, but it is an array"
);
assert_eq!(
Compiler::new()
.add_source(
r#"
import "pe"
rule test { condition: pe.version_info }"#
)
.expect_err("expected error")
.to_string(),
"error[E002]: wrong type
--> line:3:36
|
3 | rule test { condition: pe.version_info }
| ^^^^^^^^^^^^^^^ expression should be `bool`, but it is a map"
);
}
#[test]
fn continue_after_error() {
let mut compiler = Compiler::new();
assert!(
compiler
.add_source(
r#"
rule test {
strings:
$a = "foo"
$b = "bar"
condition:
$a
}"#
)
.is_err()
);
assert!(
compiler
.add_source(
r#"
rule test {
strings:
$a = "foo"
condition:
$a
}"#
)
.is_ok()
);
let rules = compiler.build();
let mut scanner = Scanner::new(&rules);
assert_eq!(scanner.scan(b"foo").unwrap().matching_rules().len(), 1);
let mut compiler = Compiler::new();
compiler.new_namespace("namespace1");
assert!(
compiler
.add_source(
r#"
rule test {
strings:
$a = "foo"
$b = "bar"
condition:
$a
}"#
)
.is_err()
);
compiler.new_namespace("namespace2");
assert!(
compiler
.add_source(
r#"
rule test {
strings:
$a = "foo"
condition:
$a
}"#
)
.is_ok()
);
let rules = compiler.build();
let mut scanner = Scanner::new(&rules);
assert_eq!(scanner.scan(b"foo").unwrap().matching_rules().len(), 1);
}
#[test]
fn conflicting_identifiers_error() {
assert_eq!(
Compiler::new()
.define_global("foo", 1)
.unwrap()
.add_source("rule foo {condition: true}")
.unwrap_err()
.to_string(),
"error[E013]: rule `foo` conflicts with an existing identifier
--> line:1:6
|
1 | rule foo {condition: true}
| ^^^ identifier already in use by a module or global variable"
);
}
#[test]
fn duplicate_rule_error() {
assert_eq!(
Compiler::new()
.add_source("rule foo : first {condition: true}")
.unwrap()
.add_source("rule foo : second {condition: true}")
.unwrap_err()
.to_string(),
"error[E012]: duplicate rule `foo`
--> line:1:6
|
1 | rule foo : second {condition: true}
| ^^^ duplicate declaration of `foo`
|
::: line:1:6
|
1 | rule foo : first {condition: true}
| --- `foo` declared here for the first time"
);
}
#[cfg(feature = "constant-folding")]
#[test]
fn number_out_of_range_error() {
assert_eq!(
Compiler::new()
.add_source(
"rule test {
condition:
9223372036854775807 + 1000000000 == 0
}"
)
.unwrap_err()
.to_string(),
"error[E007]: number out of range
--> line:3:4
|
3 | 9223372036854775807 + 1000000000 == 0
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this number is out of the allowed range [-9223372036854775808-9223372036854775807]"
);
}
#[test]
fn utf8_errors() {
let mut src =
"rule test {condition: true}".to_string().as_bytes().to_vec();
src.insert(4, 0xff);
let mut compiler = Compiler::new();
let err = compiler
.add_source(src.as_slice())
.expect_err("expected error")
.to_string();
assert_eq!(
err,
"error[E032]: invalid UTF-8
--> line:1:5
|
1 | rule� test {condition: true}
| ^ invalid UTF-8 character"
);
assert_eq!(
compiler.errors.first().expect("expected error").to_string(),
err
);
}
#[test]
fn errors_serialization() {
let err = Compiler::new()
.add_source(
SourceCode::from("rule test {condition: foo}")
.with_origin("test.yar"),
)
.err()
.unwrap();
let json_error = serde_json::to_string(&err).unwrap();
let expected = json!({
"type": "UnknownIdentifier",
"code": "E009",
"title": "unknown identifier `foo`",
"line": 1,
"column": 23,
"labels":[
{
"level": "error",
"code_origin": "test.yar",
"line": 1,
"column": 23,
"span": { "start": 22, "end": 25 },
"text": "this identifier has not been declared"
}
],
"footers": [],
"text": r#"error[E009]: unknown identifier `foo`
--> test.yar:1:23
|
1 | rule test {condition: foo}
| ^^^ this identifier has not been declared"#
});
assert_eq!(json_error, expected.to_string());
}
#[test]
fn test_includes() {
let mut compiler = Compiler::new();
compiler
.add_include_dir("src/compiler/tests/testdata/includes")
.add_source(r#"include "included_ok.yar""#)
.unwrap();
let rules = compiler.build();
let mut scanner = Scanner::new(&rules);
assert_eq!(scanner.scan(b"").unwrap().matching_rules().len(), 1);
}
#[test]
fn test_circular_includes() {
let mut compiler = Compiler::new();
let err = compiler
.add_include_dir("src/compiler/tests/testdata/includes")
.add_source(r#"include "included_circular.yar""#)
.unwrap_err()
.to_string();
assert!(err.contains(r#"error[E046]: circular include dependencies"#));
#[cfg(target_family = "unix")]
assert!(err.contains(
r"src/compiler/tests/testdata/includes/included_circular.yar"
));
#[cfg(target_family = "windows")]
assert!(err.contains(
r#"src\compiler\tests\testdata\includes\included_circular.yar"#
));
}
#[test]
fn test_relative_includes() {
let mut compiler = Compiler::new();
compiler
.add_include_dir("src/compiler/tests/testdata/includes")
.add_source(r#"include "included_relative.yar""#)
.unwrap();
let rules = compiler.build();
let mut scanner = Scanner::new(&rules);
assert_eq!(scanner.scan(b"").unwrap().matching_rules().len(), 1);
}
#[test]
fn test_disable_includes() {
let mut compiler = Compiler::new();
compiler
.enable_includes(false)
.add_include_dir("src/compiler/tests/testdata/includes");
assert_eq!(
compiler
.add_source(r#"include "included_ok.yar""#)
.unwrap_err()
.to_string(),
r#"error[E044]: include statements not allowed
--> line:1:1
|
1 | include "included_ok.yar"
| ^^^^^^^^^^^^^^^^^^^^^^^^^ includes are disabled for this compilation"#
);
}
#[test]
fn test_switch_warnings() {
let mut compiler = Compiler::new();
compiler
.switch_warning("invariant_expr", false)
.unwrap()
.add_source(
r#"
rule test {
condition: true
}
"#,
)
.unwrap();
assert_eq!(compiler.warnings().len(), 0);
}
#[test]
fn test_switch_all_warnings() {
let mut compiler = Compiler::new();
compiler
.switch_all_warnings(false)
.add_source(
r#"
rule test {
condition: true
}
"#,
)
.unwrap();
assert_eq!(compiler.warnings().len(), 0);
}
#[test]
fn test_errors() {
let mut mint = goldenfile::Mint::new(".");
for entry in globwalk::glob("src/compiler/tests/testdata/errors/*.in")
.unwrap()
.flatten()
{
let in_path = entry.into_path();
println!("{in_path:?}");
let out_path = in_path.with_extension("out");
let mut src = String::new();
let rules = fs::read_to_string(&in_path).expect("unable to read");
#[cfg(not(feature = "constant-folding"))]
if rules.starts_with("// constant-folding required") {
continue;
}
#[cfg(not(feature = "test_proto2-module"))]
if rules.starts_with("// test_proto2-module required") {
continue;
}
src.push_str(rules.as_str());
let err = compile(src.as_str()).expect_err(
format!("file {in_path:?} should have failed").as_str(),
);
let mut output_file = mint.new_goldenfile(out_path).unwrap();
output_file
.write_all(err.to_string().as_bytes())
.expect("unable to write")
}
}
#[test]
fn test_warnings() {
let mut mint = goldenfile::Mint::new(".");
for entry in globwalk::glob("src/compiler/tests/testdata/warnings/*.in")
.unwrap()
.flatten()
{
let in_path = entry.into_path();
println!("{in_path:?}");
let out_path = in_path.with_extension("out");
let mut src = String::new();
let rules = fs::read_to_string(&in_path).expect("unable to read");
#[cfg(not(feature = "constant-folding"))]
if rules.starts_with("// constant-folding required") {
continue;
}
#[cfg(not(feature = "test_proto2-module"))]
if rules.starts_with("// test_proto2-module required") {
continue;
}
let mut compiler = Compiler::new();
src.push_str(rules.as_str());
compiler.ignore_module("unsupported_module");
compiler.add_source(src.as_str()).unwrap();
let mut output_file = mint.new_goldenfile(out_path).unwrap();
for w in compiler.warnings() {
let mut s = w.to_string();
s.push('\n');
output_file.write_all(s.as_bytes()).expect("unable to write");
}
}
}
#[test]
fn test_filesize_bounds() {
let mut bounds = FilesizeBounds::default();
assert_eq!(bounds, FilesizeBounds::from(..));
bounds.min_end(Bound::Included(1000));
assert_eq!(bounds, FilesizeBounds::from(..=1000));
bounds.min_end(Bound::Excluded(1000));
assert_eq!(bounds, FilesizeBounds::from(..1000));
bounds.min_end(Bound::Excluded(2000));
assert_eq!(bounds, FilesizeBounds::from(..1000));
bounds.max_start(Bound::Included(1));
assert_eq!(bounds, FilesizeBounds::from(1..1000));
bounds.max_start(Bound::Excluded(1));
assert_eq!(
bounds,
FilesizeBounds::from((Bound::Excluded(1), Bound::Excluded(1000)))
);
}