From 630516bf3c0982859e2b691f94f2499d32722249 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Fri, 12 Jun 2026 14:15:45 +0200 Subject: [PATCH 1/4] fix: prune header constraints on snapshot restore Ensures that `header_constraints` are correctly cleared and updated when the compiler's state is reverted to a snapshot. This prevents stale constraints from persisting after compilation errors or partial rule additions, maintaining an accurate representation of the active patterns. --- lib/src/compiler/mod.rs | 3 ++ lib/src/tests/mod.rs | 74 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/lib/src/compiler/mod.rs b/lib/src/compiler/mod.rs index 182a41f42..d7b070dd7 100644 --- a/lib/src/compiler/mod.rs +++ b/lib/src/compiler/mod.rs @@ -1236,6 +1236,9 @@ impl Compiler<'_> { self.filesize_bounds .retain(|pattern_id, _| *pattern_id < snapshot.next_pattern_id); + + self.header_constraints + .retain(|pattern_id, _| *pattern_id < snapshot.next_pattern_id); } /// Returns true if the bytes in the slice are all 0x00, 0x90, or 0xff. diff --git a/lib/src/tests/mod.rs b/lib/src/tests/mod.rs index b5c75e1ec..83657562e 100644 --- a/lib/src/tests/mod.rs +++ b/lib/src/tests/mod.rs @@ -3424,6 +3424,80 @@ fn filesize_bounds() { .expect_err("should fail"); } +#[test] +fn header_constraints() { + let rules = crate::compile( + r#" + rule test_1 { + strings: + $a = /foo.*bar/ + condition: + uint16(0) == 0x5a4d and $a + } + rule test_2 { + strings: + $a = /foo.*bar/ + condition: + $a + } + "#, + ) + .unwrap(); + + let mut scanner = crate::scanner::Scanner::new(&rules); + + assert_eq!( + scanner + .scan(b"foobar") + .expect("scan should not fail") + .matching_rules() + .len(), + 1 // test_2 matches, but test_1 do not. + ); + + let rules = crate::compile( + r#" + rule test { + strings: + $a = /foo.*bar/ + condition: + $a and filesize == 6 + } + "#, + ) + .unwrap(); + + let mut scanner = crate::scanner::Scanner::new(&rules); + + assert_eq!( + scanner + .scan(b"foobar") + .expect("scan should not fail") + .matching_rules() + .len(), + 1 + ); + + crate::compile( + r#" + rule test_1 { + strings: + $a = "foo" + $b = /a*/ + condition: + uint16(0) == 0x5a4d and $a and $b + } + rule test_2 { + strings: + $c = "bar" + condition: + uint16(0) == 0x5a4d and $c + } + "#, + ) + .expect_err("should fail"); +} + #[test] fn for_of() { rule_true!( From 1c6bdc34928b8b8c829d3eba3e8d6240b96a4ed3 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Fri, 12 Jun 2026 16:58:33 +0200 Subject: [PATCH 2/4] build: conditionalize ANSI support to Windows only The `enable-ansi-support` crate is only required on Windows to enable ANSI escape codes in the terminal. On other operating systems, this functionality is native. This change restricts the dependency to Windows targets and wraps its usage in a `#[cfg(target_os = "windows")]` block, reducing the dependency footprint for non-Windows builds. --- Cargo.toml | 1 - cli/Cargo.toml | 6 ++++-- cli/src/main.rs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d71ccd4ab..bca0316ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,6 @@ der-parser = "10.0.0" digest = "0.10.7" dsa = "0.6.3" ecdsa = "0.16.9" -enable-ansi-support = "0.3.1" env_logger = "0.11.10" figment = "0.10.19" globwalk = "0.9.1" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 467e3a61a..1d66ef771 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -57,7 +57,6 @@ figment = { workspace = true, features = ["toml"] } globwalk = { workspace = true } home = { workspace = true } itertools = { workspace = true } -enable-ansi-support = { workspace = true } env_logger = { workspace = true, optional = true, features = ["auto-color"] } log = { workspace = true, optional = true } protobuf = { workspace = true } @@ -85,4 +84,7 @@ wild = "2.2.1" [dev-dependencies] assert_cmd = "2.2.2" assert_fs = "1.1.4" -predicates = { workspace = true } \ No newline at end of file +predicates = { workspace = true } + +[target.'cfg(windows)'.dependencies] +enable-ansi-support = "0.3.1" \ No newline at end of file diff --git a/cli/src/main.rs b/cli/src/main.rs index 040c172ea..3c7fe115f 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -29,8 +29,8 @@ const EXIT_ERROR: i32 = 1; const CONFIG_FILE: &str = ".yara-x.toml"; fn main() -> anyhow::Result<()> { - // Enable support for ANSI escape codes in Windows. In other platforms - // this is a no-op. + // Enable support for ANSI escape codes in Windows. + #[cfg(target_os = "windows")] if let Err(err) = enable_ansi_support::enable_ansi_support() { println!("could not enable ANSI support: {err}") } From 1c103081736627d27dee632ddff1d5578d6bd992 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Fri, 12 Jun 2026 17:25:55 +0200 Subject: [PATCH 3/4] refactor: consolidate proto JSON and YAML serialization into yara-x-proto Merges the `yara-x-proto-json` and `yara-x-proto-yaml` crates into the main `yara-x-proto` crate. This streamlines the project structure by reducing the number of top-level crates and logically groups all protobuf-related functionalities under a single crate. JSON and YAML serializers are now exposed as `yara_x_proto::json` and `yara_x_proto::yaml` modules. --- Cargo.lock | 28 +-- Cargo.toml | 4 - cli/Cargo.toml | 3 +- cli/src/commands/dump.rs | 4 +- lib/Cargo.toml | 2 +- lib/src/modules/tests.rs | 2 +- proto-json/Cargo.toml | 29 --- proto-json/build.rs | 13 -- proto-json/src/yara.proto | 204 ------------------ proto-yaml/Cargo.toml | 28 --- proto-yaml/build.rs | 13 -- proto-yaml/src/yara.proto | 204 ------------------ proto/Cargo.toml | 8 + proto/build.rs | 3 + proto-json/src/lib.rs => proto/src/json.rs | 7 +- proto/src/lib.rs | 6 + .../tests/mod.rs => proto/src/tests/json.rs | 6 +- proto/src/tests/mod.rs | 2 + .../src/tests/test_json.proto | 2 +- .../src/tests/test_yaml.proto | 2 +- .../src/tests/testdata_json}/1.in | 0 .../src/tests/testdata_json}/1.out | 0 .../src/tests/testdata_yaml}/1.in | 0 .../src/tests/testdata_yaml}/1.out | 0 .../tests/mod.rs => proto/src/tests/yaml.rs | 6 +- proto-yaml/src/lib.rs => proto/src/yaml.rs | 7 +- py/Cargo.toml | 2 +- py/src/lib.rs | 2 +- 28 files changed, 38 insertions(+), 549 deletions(-) delete mode 100644 proto-json/Cargo.toml delete mode 100644 proto-json/build.rs delete mode 100644 proto-json/src/yara.proto delete mode 100644 proto-yaml/Cargo.toml delete mode 100644 proto-yaml/build.rs delete mode 100644 proto-yaml/src/yara.proto rename proto-json/src/lib.rs => proto/src/json.rs (98%) rename proto-json/src/tests/mod.rs => proto/src/tests/json.rs (75%) create mode 100644 proto/src/tests/mod.rs rename proto-json/src/tests/test.proto => proto/src/tests/test_json.proto (97%) rename proto-yaml/src/tests/test.proto => proto/src/tests/test_yaml.proto (96%) rename {proto-json/src/tests/testdata => proto/src/tests/testdata_json}/1.in (100%) rename {proto-json/src/tests/testdata => proto/src/tests/testdata_json}/1.out (100%) rename {proto-yaml/src/tests/testdata => proto/src/tests/testdata_yaml}/1.in (100%) rename {proto-yaml/src/tests/testdata => proto/src/tests/testdata_yaml}/1.out (100%) rename proto-yaml/src/tests/mod.rs => proto/src/tests/yaml.rs (77%) rename proto-yaml/src/lib.rs => proto/src/yaml.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 14fa731f5..0549eb06b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4417,7 +4417,6 @@ dependencies = [ "yara-x-macros", "yara-x-parser", "yara-x-proto", - "yara-x-proto-yaml", "zip", ] @@ -4467,8 +4466,7 @@ dependencies = [ "yara-x", "yara-x-fmt", "yara-x-parser", - "yara-x-proto-json", - "yara-x-proto-yaml", + "yara-x-proto", ] [[package]] @@ -4567,29 +4565,8 @@ dependencies = [ [[package]] name = "yara-x-proto" version = "1.17.0" -dependencies = [ - "protobuf", - "protobuf-codegen", -] - -[[package]] -name = "yara-x-proto-json" -version = "1.17.0" dependencies = [ "base64", - "globwalk", - "goldenfile", - "itertools 0.14.0", - "protobuf", - "protobuf-codegen", - "yansi", - "yara-x-proto", -] - -[[package]] -name = "yara-x-proto-yaml" -version = "1.17.0" -dependencies = [ "chrono", "globwalk", "goldenfile", @@ -4597,7 +4574,6 @@ dependencies = [ "protobuf", "protobuf-codegen", "yansi", - "yara-x-proto", ] [[package]] @@ -4615,7 +4591,7 @@ dependencies = [ "yara-x", "yara-x-fmt", "yara-x-parser", - "yara-x-proto-json", + "yara-x-proto", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index bca0316ba..b6944bcde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,6 @@ members = [ "macros", "parser", "proto", - "proto-json", - "proto-yaml", "py", "ls", ] @@ -113,8 +111,6 @@ yara-x-fmt = { path = "fmt", version = "1.17.0" } yara-x-macros = { path = "macros", version = "1.17.0" } yara-x-parser = { path = "parser", version = "1.17.0" } yara-x-proto = { path = "proto", version = "1.17.0"} -yara-x-proto-yaml = { path = "proto-yaml", version = "1.17.0" } -yara-x-proto-json = { path = "proto-json", version = "1.17.0" } zip = { version = "8.2.0", default-features = false } simd-adler32 = "0.3.9" simd_cesu8 = "1.1.1" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 1d66ef771..885081aba 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -68,8 +68,7 @@ strum_macros = { workspace = true } yansi = { workspace = true } yara-x = { workspace = true, features = ["parallel-compilation"] } yara-x-parser = { workspace = true } -yara-x-proto-yaml = { workspace = true } -yara-x-proto-json = { workspace = true } +yara-x-proto = { workspace = true } yara-x-fmt = { workspace = true } chardetng = "1.0.0" diff --git a/cli/src/commands/dump.rs b/cli/src/commands/dump.rs index eb50f5c02..5a79732fa 100644 --- a/cli/src/commands/dump.rs +++ b/cli/src/commands/dump.rs @@ -8,8 +8,8 @@ use std::path::PathBuf; use strum_macros::Display; use yara_x::mods::*; -use yara_x_proto_json::Serializer as JsonSerializer; -use yara_x_proto_yaml::Serializer as YamlSerializer; +use yara_x_proto::json::Serializer as JsonSerializer; +use yara_x_proto::yaml::Serializer as YamlSerializer; use crate::help; diff --git a/lib/Cargo.toml b/lib/Cargo.toml index cf95e60db..a0190d4b5 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -350,7 +350,7 @@ goldenfile = { workspace = true } ihex = { workspace = true } pretty_assertions = { workspace = true } rayon = { workspace = true } -yara-x-proto-yaml = { workspace = true } +yara-x-proto = { workspace = true } zip = { workspace = true } [[bench]] diff --git a/lib/src/modules/tests.rs b/lib/src/modules/tests.rs index 912a84df4..7297d43e8 100644 --- a/lib/src/modules/tests.rs +++ b/lib/src/modules/tests.rs @@ -167,7 +167,7 @@ fn test_modules() { let output_file = mint.new_goldenfile(out_path).unwrap(); // Render the module's output as YAML. - let mut yaml = yara_x_proto_yaml::Serializer::new(output_file); + let mut yaml = yara_x_proto::yaml::Serializer::new(output_file); yaml.serialize(output).unwrap(); }); diff --git a/proto-json/Cargo.toml b/proto-json/Cargo.toml deleted file mode 100644 index 3da688afd..000000000 --- a/proto-json/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "yara-x-proto-json" -description = """ -Library the converts protocol buffers into JSON. -""" -version.workspace = true -authors.workspace = true -edition.workspace = true -homepage.workspace = true -readme.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -base64 = { workspace = true } -protobuf = { workspace = true } -itertools = { workspace = true } -yansi = { workspace = true } -yara-x-proto = { workspace = true } - -[dev-dependencies] -goldenfile = { workspace = true } -globwalk = { workspace = true } -protobuf = { workspace = true } - -[build-dependencies] -protobuf = { workspace = true } -protobuf-codegen = { workspace = true } diff --git a/proto-json/build.rs b/proto-json/build.rs deleted file mode 100644 index 83161a511..000000000 --- a/proto-json/build.rs +++ /dev/null @@ -1,13 +0,0 @@ -use protobuf_codegen::Codegen; - -fn main() { - println!("cargo:rerun-if-changed=src"); - Codegen::new() - .pure() - .cargo_out_dir("protos") - .include("src/tests") - .include("src") - .input("src/tests/test.proto") - .input("src/yara.proto") - .run_from_script(); -} diff --git a/proto-json/src/yara.proto b/proto-json/src/yara.proto deleted file mode 100644 index 3f343c5d0..000000000 --- a/proto-json/src/yara.proto +++ /dev/null @@ -1,204 +0,0 @@ -// Protocol buffer that specifies the options that can be used in other protos -// for controlling the generation of YARA modules. - -syntax = "proto2"; - -package yara; - -import "google/protobuf/descriptor.proto"; - -message ModuleOptions { - required string name = 1; - required string root_message = 2; - optional string cargo_feature = 3; -} - -message FieldOptions { - // Name of the field in YARA rules. - // - // By default, the name of the field in YARA rules is the same it has in - // the protobuf, but sometimes it's useful to override this behaviour and - // specify our own name. For instance, suppose we have the following field - // definition: - // - // FileMetadata metadata = 32 [(yara.field_options).name = "meta"]; - // - // The name of the field in the protobuf is "metadata", but this is a - // reserved keyword in YARA, so we use (yara.field_options).name = "meta" - // for specifying a different name. - optional string name = 1; - - // Ignore the field and don't use it in YARA. - // - // This is useful when the protobuf definition has some fields that we don't - // want to expose to YARA rules. For example: - // - // string some_private_data = 32 [(yara.field_options).ignore = true]; - optional bool ignore = 2; - - // Control under which circumstances the field is accessible by YARA rules. - // - // In some cases, a field should only be used in YARA rules when certain - // requirements are satisfied. Consider the following field definition: - // - // uint64 my_field = 1 [ - // (yara.field_options) = { - // acl: [ - // { - // accept_if: ["foo", "FOO"], - // error_title: "foo is required", - // error_label: "this field was used without foo" - // }, - // { - // accept_if: "bar", - // error_title: "bar is required", - // error_label: "this field was used without bar" - // }, - // { - // reject_if: "baz", - // error_title: "baz is forbidden", - // error_label: "this field was used with baz" - // } - // ] - // } - // ]; - // - // The field "my_field" can be used in YARA rules, but only if the features - // "foo" (or "FOO") and "bar" are enabled in the YARA compiler, while "baz" - // must not be enabled. If these conditions are not met, the compiler will - // return an error. For example, if "FOO" and "baz" are enabled, the following - // error will occur: - // - // error[E034]: bar is required - // --> line:5:29 - // | - // 5 | my_module.my_field == 0 - // | ^^^^^^^^ this field was used without bar - // | - // - // Notice that the error message's title and label are derived from the ACL - // entry that was not satisfied. - // - // Also, keep in mind that ACL entries are evaluated sequentially. The first - // entry that fails will trigger the corresponding error message. - repeated AclEntry acl = 3; - - // Indicates that a string field is always lowercase. - // - // This option can be used only with fields of type string. If used with some - // other type YARA will panic. - // - // string some_lowercase_string = 32 [(yara.field_options).lowercase = true]; - optional bool lowercase = 4; - - // Specifies the format of the field when converted to a string. - // - // See: - // https://docs.rs/yara-x-proto-yaml/latest/yara_x_proto_yaml/index.html - optional string fmt = 5; - - // Indicates that the field is deprecated. - // - // This option is used for indicating that a field is deprecated. - optional DeprecationNotice deprecation_notice = 6; -} - -// An entry in a field's ACL. -// -// Each entry in the ACL is a condition that must be met. If some of the -// conditions in the ACL is not met, the compiler shows an error message that -// is defined by the ACL entry itself. -// -// The condition depends on the features listed in "accept_if" and "reject_if". -// Let's see some examples: -// -// accept_if: "foo" -// -// In the example above, the field can be used if the feature "foo" is enabled, -// otherwise the compiler shows an error. -// -// accept_if: ["foo", "bar"] -// -// In the example above, the field can be used if any of the features in the -// list is enabled, otherwise the error is shown. This can be read as: accept -// if "foo" or "bar". -// -// reject_if: ["foo", "bar"] -// -// In the example above, the field can be used if none of the features in the -// list is enabled, otherwise the error is shown. This can be read as: reject -// if "foo" or "bar". -message AclEntry { - required string error_title = 1; - required string error_label = 2; - repeated string accept_if = 3; - repeated string reject_if = 4; -} - -// Used for indicating that a field is deprecated. -// -// For instance, this is the annotation added to a .proto file for indicating that -// the field `foo` is deprecated: -// -// ``` -// uint64 foo = 1 [(yara.field_options).deprecation_notice = { -// text: "deprecated field", -// help: "use `bar` instead", -// replacement: "bar" -// }]; -// -// When the field is used in a rule it produces the following warning: -// -// ``` -// warning[deprecated_field]: field `foo` is deprecated -// --> test.yar:3:24 -// | -//3 | rule t { condition: my_module.foo == 0 } -// | --- deprecated field -// | -//help: use `bar` instead -// | -//3 - rule t { condition: my_module.foo == 0 } -//3 + rule t { condition: my_module.bar == 0 } -// ``` -message DeprecationNotice { - required string text = 1; - optional string help = 2; - optional string replacement = 3; -} - -message MessageOptions { - optional string name = 1; -} - -message EnumOptions { - optional string name = 1; - optional bool inline = 2; -} - -message EnumValueOptions { - oneof value { - int64 i64 = 1; - double f64 = 2; - } -} - -extend google.protobuf.FileOptions { - optional ModuleOptions module_options = 51503; -} - -extend google.protobuf.FieldOptions { - optional FieldOptions field_options = 51504; -} - -extend google.protobuf.MessageOptions { - optional MessageOptions message_options = 51505; -} - -extend google.protobuf.EnumOptions { - optional EnumOptions enum_options = 51506; -} - -extend google.protobuf.EnumValueOptions { - optional EnumValueOptions enum_value = 51507; -} \ No newline at end of file diff --git a/proto-yaml/Cargo.toml b/proto-yaml/Cargo.toml deleted file mode 100644 index 8c236b4c0..000000000 --- a/proto-yaml/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "yara-x-proto-yaml" -description = """ -Library the converts protocol buffers into YAML. -""" -version.workspace = true -authors.workspace = true -edition.workspace = true -homepage.workspace = true -readme.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -chrono = { workspace = true } -protobuf = { workspace = true } -itertools = { workspace = true } -yansi = { workspace = true } -yara-x-proto = { workspace = true } - -[dev-dependencies] -goldenfile = { workspace = true } -globwalk = { workspace = true } - -[build-dependencies] -protobuf = { workspace = true } -protobuf-codegen = { workspace = true } \ No newline at end of file diff --git a/proto-yaml/build.rs b/proto-yaml/build.rs deleted file mode 100644 index 83161a511..000000000 --- a/proto-yaml/build.rs +++ /dev/null @@ -1,13 +0,0 @@ -use protobuf_codegen::Codegen; - -fn main() { - println!("cargo:rerun-if-changed=src"); - Codegen::new() - .pure() - .cargo_out_dir("protos") - .include("src/tests") - .include("src") - .input("src/tests/test.proto") - .input("src/yara.proto") - .run_from_script(); -} diff --git a/proto-yaml/src/yara.proto b/proto-yaml/src/yara.proto deleted file mode 100644 index 3f343c5d0..000000000 --- a/proto-yaml/src/yara.proto +++ /dev/null @@ -1,204 +0,0 @@ -// Protocol buffer that specifies the options that can be used in other protos -// for controlling the generation of YARA modules. - -syntax = "proto2"; - -package yara; - -import "google/protobuf/descriptor.proto"; - -message ModuleOptions { - required string name = 1; - required string root_message = 2; - optional string cargo_feature = 3; -} - -message FieldOptions { - // Name of the field in YARA rules. - // - // By default, the name of the field in YARA rules is the same it has in - // the protobuf, but sometimes it's useful to override this behaviour and - // specify our own name. For instance, suppose we have the following field - // definition: - // - // FileMetadata metadata = 32 [(yara.field_options).name = "meta"]; - // - // The name of the field in the protobuf is "metadata", but this is a - // reserved keyword in YARA, so we use (yara.field_options).name = "meta" - // for specifying a different name. - optional string name = 1; - - // Ignore the field and don't use it in YARA. - // - // This is useful when the protobuf definition has some fields that we don't - // want to expose to YARA rules. For example: - // - // string some_private_data = 32 [(yara.field_options).ignore = true]; - optional bool ignore = 2; - - // Control under which circumstances the field is accessible by YARA rules. - // - // In some cases, a field should only be used in YARA rules when certain - // requirements are satisfied. Consider the following field definition: - // - // uint64 my_field = 1 [ - // (yara.field_options) = { - // acl: [ - // { - // accept_if: ["foo", "FOO"], - // error_title: "foo is required", - // error_label: "this field was used without foo" - // }, - // { - // accept_if: "bar", - // error_title: "bar is required", - // error_label: "this field was used without bar" - // }, - // { - // reject_if: "baz", - // error_title: "baz is forbidden", - // error_label: "this field was used with baz" - // } - // ] - // } - // ]; - // - // The field "my_field" can be used in YARA rules, but only if the features - // "foo" (or "FOO") and "bar" are enabled in the YARA compiler, while "baz" - // must not be enabled. If these conditions are not met, the compiler will - // return an error. For example, if "FOO" and "baz" are enabled, the following - // error will occur: - // - // error[E034]: bar is required - // --> line:5:29 - // | - // 5 | my_module.my_field == 0 - // | ^^^^^^^^ this field was used without bar - // | - // - // Notice that the error message's title and label are derived from the ACL - // entry that was not satisfied. - // - // Also, keep in mind that ACL entries are evaluated sequentially. The first - // entry that fails will trigger the corresponding error message. - repeated AclEntry acl = 3; - - // Indicates that a string field is always lowercase. - // - // This option can be used only with fields of type string. If used with some - // other type YARA will panic. - // - // string some_lowercase_string = 32 [(yara.field_options).lowercase = true]; - optional bool lowercase = 4; - - // Specifies the format of the field when converted to a string. - // - // See: - // https://docs.rs/yara-x-proto-yaml/latest/yara_x_proto_yaml/index.html - optional string fmt = 5; - - // Indicates that the field is deprecated. - // - // This option is used for indicating that a field is deprecated. - optional DeprecationNotice deprecation_notice = 6; -} - -// An entry in a field's ACL. -// -// Each entry in the ACL is a condition that must be met. If some of the -// conditions in the ACL is not met, the compiler shows an error message that -// is defined by the ACL entry itself. -// -// The condition depends on the features listed in "accept_if" and "reject_if". -// Let's see some examples: -// -// accept_if: "foo" -// -// In the example above, the field can be used if the feature "foo" is enabled, -// otherwise the compiler shows an error. -// -// accept_if: ["foo", "bar"] -// -// In the example above, the field can be used if any of the features in the -// list is enabled, otherwise the error is shown. This can be read as: accept -// if "foo" or "bar". -// -// reject_if: ["foo", "bar"] -// -// In the example above, the field can be used if none of the features in the -// list is enabled, otherwise the error is shown. This can be read as: reject -// if "foo" or "bar". -message AclEntry { - required string error_title = 1; - required string error_label = 2; - repeated string accept_if = 3; - repeated string reject_if = 4; -} - -// Used for indicating that a field is deprecated. -// -// For instance, this is the annotation added to a .proto file for indicating that -// the field `foo` is deprecated: -// -// ``` -// uint64 foo = 1 [(yara.field_options).deprecation_notice = { -// text: "deprecated field", -// help: "use `bar` instead", -// replacement: "bar" -// }]; -// -// When the field is used in a rule it produces the following warning: -// -// ``` -// warning[deprecated_field]: field `foo` is deprecated -// --> test.yar:3:24 -// | -//3 | rule t { condition: my_module.foo == 0 } -// | --- deprecated field -// | -//help: use `bar` instead -// | -//3 - rule t { condition: my_module.foo == 0 } -//3 + rule t { condition: my_module.bar == 0 } -// ``` -message DeprecationNotice { - required string text = 1; - optional string help = 2; - optional string replacement = 3; -} - -message MessageOptions { - optional string name = 1; -} - -message EnumOptions { - optional string name = 1; - optional bool inline = 2; -} - -message EnumValueOptions { - oneof value { - int64 i64 = 1; - double f64 = 2; - } -} - -extend google.protobuf.FileOptions { - optional ModuleOptions module_options = 51503; -} - -extend google.protobuf.FieldOptions { - optional FieldOptions field_options = 51504; -} - -extend google.protobuf.MessageOptions { - optional MessageOptions message_options = 51505; -} - -extend google.protobuf.EnumOptions { - optional EnumOptions enum_options = 51506; -} - -extend google.protobuf.EnumValueOptions { - optional EnumValueOptions enum_value = 51507; -} \ No newline at end of file diff --git a/proto/Cargo.toml b/proto/Cargo.toml index 913800410..c0b6a5180 100644 --- a/proto/Cargo.toml +++ b/proto/Cargo.toml @@ -14,6 +14,14 @@ rust-version.workspace = true [dependencies] protobuf = { workspace = true } +base64 = { workspace = true } +chrono = { workspace = true } +itertools = { workspace = true } +yansi = { workspace = true } + +[dev-dependencies] +goldenfile = { workspace = true } +globwalk = { workspace = true } [build-dependencies] protobuf = { workspace = true } diff --git a/proto/build.rs b/proto/build.rs index a473661ca..dc9a9644f 100644 --- a/proto/build.rs +++ b/proto/build.rs @@ -6,6 +6,9 @@ fn main() { .pure() .cargo_out_dir("protos") .input("src/yara.proto") + .input("src/tests/test_yaml.proto") + .input("src/tests/test_json.proto") .include("src") + .include("src/tests") .run_from_script(); } diff --git a/proto-json/src/lib.rs b/proto/src/json.rs similarity index 98% rename from proto-json/src/lib.rs rename to proto/src/json.rs index 79c52d73f..9ab410b30 100644 --- a/proto-json/src/lib.rs +++ b/proto/src/json.rs @@ -31,12 +31,7 @@ use protobuf::reflect::ReflectFieldRef::{Map, Optional, Repeated}; use protobuf::reflect::{FieldDescriptor, MessageRef, ReflectValueRef}; use yansi::{Color, Paint, Style}; -use yara_x_proto::{FieldFormat, get_field_format}; - -#[cfg(test)] -mod tests; - -include!(concat!(env!("OUT_DIR"), "/protos/mod.rs")); +use crate::{FieldFormat, get_field_format}; const INDENTATION: u16 = 4; diff --git a/proto/src/lib.rs b/proto/src/lib.rs index b0f78a778..5ec0bff78 100644 --- a/proto/src/lib.rs +++ b/proto/src/lib.rs @@ -92,3 +92,9 @@ pub fn get_field_format(field_descriptor: &FieldDescriptor) -> FieldFormat { FieldFormat::None } + +pub mod json; +pub mod yaml; + +#[cfg(test)] +mod tests; diff --git a/proto-json/src/tests/mod.rs b/proto/src/tests/json.rs similarity index 75% rename from proto-json/src/tests/mod.rs rename to proto/src/tests/json.rs index 83a292b2d..80e2a0946 100644 --- a/proto-json/src/tests/mod.rs +++ b/proto/src/tests/json.rs @@ -1,21 +1,21 @@ use protobuf::text_format::parse_from_str; use std::fs; -use crate::Serializer; +use crate::json::Serializer; #[test] fn json_serializer() { // Create goldenfile mint. let mut mint = goldenfile::Mint::new("."); - for entry in globwalk::glob("src/tests/testdata/*.in").unwrap().flatten() { + for entry in globwalk::glob("src/tests/testdata_json/*.in").unwrap().flatten() { // Path to the .in file. let in_path = entry.into_path(); // Path to the .out file. let out_path = in_path.with_extension("out"); let input = fs::read_to_string(in_path).expect("Unable to read"); - let test_pb = parse_from_str::(&input).unwrap(); + let test_pb = parse_from_str::(&input).unwrap(); let output_file = mint.new_goldenfile(out_path).unwrap(); let mut serializer = Serializer::new(output_file); diff --git a/proto/src/tests/mod.rs b/proto/src/tests/mod.rs new file mode 100644 index 000000000..1816e89a9 --- /dev/null +++ b/proto/src/tests/mod.rs @@ -0,0 +1,2 @@ +mod json; +mod yaml; diff --git a/proto-json/src/tests/test.proto b/proto/src/tests/test_json.proto similarity index 97% rename from proto-json/src/tests/test.proto rename to proto/src/tests/test_json.proto index dfea5dc39..83bb69182 100644 --- a/proto-json/src/tests/test.proto +++ b/proto/src/tests/test_json.proto @@ -2,7 +2,7 @@ syntax = "proto2"; import "yara.proto"; -package test; +package test_json; message SubMessage { optional int32 i32 = 1; diff --git a/proto-yaml/src/tests/test.proto b/proto/src/tests/test_yaml.proto similarity index 96% rename from proto-yaml/src/tests/test.proto rename to proto/src/tests/test_yaml.proto index 2ec38bcc0..a0d518684 100644 --- a/proto-yaml/src/tests/test.proto +++ b/proto/src/tests/test_yaml.proto @@ -2,7 +2,7 @@ syntax = "proto2"; import "yara.proto"; -package test; +package test_yaml; message SubMessage { optional int32 int32_dec = 1; diff --git a/proto-json/src/tests/testdata/1.in b/proto/src/tests/testdata_json/1.in similarity index 100% rename from proto-json/src/tests/testdata/1.in rename to proto/src/tests/testdata_json/1.in diff --git a/proto-json/src/tests/testdata/1.out b/proto/src/tests/testdata_json/1.out similarity index 100% rename from proto-json/src/tests/testdata/1.out rename to proto/src/tests/testdata_json/1.out diff --git a/proto-yaml/src/tests/testdata/1.in b/proto/src/tests/testdata_yaml/1.in similarity index 100% rename from proto-yaml/src/tests/testdata/1.in rename to proto/src/tests/testdata_yaml/1.in diff --git a/proto-yaml/src/tests/testdata/1.out b/proto/src/tests/testdata_yaml/1.out similarity index 100% rename from proto-yaml/src/tests/testdata/1.out rename to proto/src/tests/testdata_yaml/1.out diff --git a/proto-yaml/src/tests/mod.rs b/proto/src/tests/yaml.rs similarity index 77% rename from proto-yaml/src/tests/mod.rs rename to proto/src/tests/yaml.rs index 410435781..504120afb 100644 --- a/proto-yaml/src/tests/mod.rs +++ b/proto/src/tests/yaml.rs @@ -1,7 +1,7 @@ use protobuf::text_format::parse_from_str; use std::fs; -use crate::Serializer; +use crate::yaml::Serializer; #[test] fn yaml_serializer() { @@ -11,14 +11,14 @@ fn yaml_serializer() { // Create goldenfile mint. let mut mint = goldenfile::Mint::new("."); - for entry in globwalk::glob("src/tests/testdata/*.in").unwrap().flatten() { + for entry in globwalk::glob("src/tests/testdata_yaml/*.in").unwrap().flatten() { // Path to the .in file. let in_path = entry.into_path(); // Path to the .out file. let out_path = in_path.with_extension("out"); let input = fs::read_to_string(in_path).expect("Unable to read"); - let test_pb = parse_from_str::(&input).unwrap(); + let test_pb = parse_from_str::(&input).unwrap(); let output_file = mint.new_goldenfile(out_path).unwrap(); let mut serializer = Serializer::new(output_file); diff --git a/proto-yaml/src/lib.rs b/proto/src/yaml.rs similarity index 99% rename from proto-yaml/src/lib.rs rename to proto/src/yaml.rs index ec93de853..1e5682eba 100644 --- a/proto-yaml/src/lib.rs +++ b/proto/src/yaml.rs @@ -75,12 +75,7 @@ use protobuf::reflect::ReflectFieldRef::{Map, Optional, Repeated}; use protobuf::reflect::{FieldDescriptor, MessageRef, ReflectValueRef}; use yansi::{Color, Paint, Style}; -use yara_x_proto::{FieldFormat, get_field_format}; - -#[cfg(test)] -mod tests; - -include!(concat!(env!("OUT_DIR"), "/protos/mod.rs")); +use crate::{FieldFormat, get_field_format}; const INDENTATION: u16 = 4; diff --git a/py/Cargo.toml b/py/Cargo.toml index 76ee8d809..6be6d325b 100644 --- a/py/Cargo.toml +++ b/py/Cargo.toml @@ -66,7 +66,7 @@ strum = { workspace = true } strum_macros = { workspace = true } yara-x = { workspace = true } -yara-x-proto-json = { workspace = true } +yara-x-proto = { workspace = true } yara-x-fmt = { workspace = true } yara-x-parser = { workspace = true } diff --git a/py/src/lib.rs b/py/src/lib.rs index b131ef2e1..7af16985e 100644 --- a/py/src/lib.rs +++ b/py/src/lib.rs @@ -1496,7 +1496,7 @@ fn proto_to_json<'py>( let mut module_output_json = Vec::new(); let mut serializer = - yara_x_proto_json::Serializer::new(&mut module_output_json); + yara_x_proto::json::Serializer::new(&mut module_output_json); serializer .serialize(proto) From 31e2ca6f8fdd502cadc27800205c52ad117872e9 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Fri, 12 Jun 2026 17:28:02 +0200 Subject: [PATCH 4/4] style: run `cargo fmt`. --- proto/src/tests/json.rs | 7 +++++-- proto/src/tests/yaml.rs | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/proto/src/tests/json.rs b/proto/src/tests/json.rs index 80e2a0946..12cfb03c8 100644 --- a/proto/src/tests/json.rs +++ b/proto/src/tests/json.rs @@ -8,14 +8,17 @@ fn json_serializer() { // Create goldenfile mint. let mut mint = goldenfile::Mint::new("."); - for entry in globwalk::glob("src/tests/testdata_json/*.in").unwrap().flatten() { + for entry in + globwalk::glob("src/tests/testdata_json/*.in").unwrap().flatten() + { // Path to the .in file. let in_path = entry.into_path(); // Path to the .out file. let out_path = in_path.with_extension("out"); let input = fs::read_to_string(in_path).expect("Unable to read"); - let test_pb = parse_from_str::(&input).unwrap(); + let test_pb = + parse_from_str::(&input).unwrap(); let output_file = mint.new_goldenfile(out_path).unwrap(); let mut serializer = Serializer::new(output_file); diff --git a/proto/src/tests/yaml.rs b/proto/src/tests/yaml.rs index 504120afb..cbf36bd60 100644 --- a/proto/src/tests/yaml.rs +++ b/proto/src/tests/yaml.rs @@ -11,14 +11,17 @@ fn yaml_serializer() { // Create goldenfile mint. let mut mint = goldenfile::Mint::new("."); - for entry in globwalk::glob("src/tests/testdata_yaml/*.in").unwrap().flatten() { + for entry in + globwalk::glob("src/tests/testdata_yaml/*.in").unwrap().flatten() + { // Path to the .in file. let in_path = entry.into_path(); // Path to the .out file. let out_path = in_path.with_extension("out"); let input = fs::read_to_string(in_path).expect("Unable to read"); - let test_pb = parse_from_str::(&input).unwrap(); + let test_pb = + parse_from_str::(&input).unwrap(); let output_file = mint.new_goldenfile(out_path).unwrap(); let mut serializer = Serializer::new(output_file);