diff --git a/src/generator/extract_schema.cpp b/src/generator/extract_schema.cpp index f6611b0a0..7e6c35600 100644 --- a/src/generator/extract_schema.cpp +++ b/src/generator/extract_schema.cpp @@ -28,7 +28,7 @@ enum class scalar_type : uint8_t { null = 1, boolean = 2, integer = 4, string = struct node_scalar { scalar_type type{scalar_type::null}; - std::map tags{}; + std::unordered_map tags{}; }; using base_node = std::variant; @@ -63,7 +63,7 @@ struct node_equal { struct node_record { bool truncated{false}; - std::map children{}; + std::unordered_map children{}; }; struct node_array { @@ -130,21 +130,16 @@ bool node_equal::operator()(const node_record_ptr &lhs, const node_record_ptr &r return false; } - auto lhs_it = lhs->children.begin(); - auto lhs_end = lhs->children.end(); - - auto rhs_it = rhs->children.begin(); - auto rhs_end = rhs->children.end(); - - for (; lhs_it != lhs_end && rhs_it != rhs_end; ++lhs_it, ++rhs_it) { - if (rhs_it->first != lhs_it->first) { + for (const auto &[k, v] : lhs->children) { + auto it = rhs->children.find(k); + if (it == rhs->children.end()) { return false; } - if (!std::visit(node_equal{}, lhs_it->second, rhs_it->second)) { + + if (!std::visit(node_equal{}, v, it->second)) { return false; } } - return true; } @@ -162,10 +157,12 @@ struct node_serialize { ddwaf_object node_serialize::operator()(const std::monostate & /*node*/) const noexcept { + static constexpr unsigned unknown_type = 0; + ddwaf_object tmp; ddwaf_object array; ddwaf_object_array(&array); - ddwaf_object_array_add(&array, ddwaf_object_unsigned(&tmp, 0)); + ddwaf_object_array_add(&array, ddwaf_object_unsigned(&tmp, unknown_type)); return array; } @@ -270,6 +267,7 @@ base_node generate_helper(const ddwaf_object *object, std::size_t depth) record->truncated = true; length = extract_schema::max_record_nodes; } + record->children.reserve(length); for (std::size_t i = 0; i < length && depth > 1; i++) { const auto *child = &object->array[i]; if (child->parameterName == nullptr) { @@ -291,6 +289,7 @@ base_node generate_helper(const ddwaf_object *object, std::size_t depth) array->truncated = true; length = extract_schema::max_array_nodes; } + array->children.reserve(length); for (std::size_t i = 0; i < length && depth > 1; i++) { const auto *child = &object->array[i]; auto schema = generate_helper(child, depth - 1); diff --git a/tests/parser_v2_preprocessors_test.cpp b/tests/parser_v2_preprocessors_test.cpp index 815a2490d..3ae03118c 100644 --- a/tests/parser_v2_preprocessors_test.cpp +++ b/tests/parser_v2_preprocessors_test.cpp @@ -12,6 +12,320 @@ using namespace ddwaf; namespace { -TEST(TestParserV2Preprocessors, ParseEmpty) {} +TEST(TestParserV2Preprocessors, ParseNoGenerator) +{ + ddwaf::object_limits limits; + + auto object = yaml_to_object(R"([{id: 1}])"); + + ddwaf::ruleset_info::section_info section; + auto array = static_cast(parameter(object)); + auto preprocessors = parser::v2::parse_preprocessors(array, section, limits); + ddwaf_object_free(&object); + + { + ddwaf::parameter root; + section.to_object(root); + + auto root_map = static_cast(root); + + auto loaded = ddwaf::parser::at(root_map, "loaded"); + EXPECT_EQ(loaded.size(), 0); + + auto failed = ddwaf::parser::at(root_map, "failed"); + EXPECT_EQ(failed.size(), 1); + EXPECT_NE(failed.find("1"), failed.end()); + + auto errors = ddwaf::parser::at(root_map, "errors"); + EXPECT_EQ(errors.size(), 1); + auto it = errors.find("missing key 'generator'"); + EXPECT_NE(it, errors.end()); + + auto error_rules = static_cast(it->second); + EXPECT_EQ(error_rules.size(), 1); + EXPECT_NE(error_rules.find("1"), error_rules.end()); + + ddwaf_object_free(&root); + } + + EXPECT_EQ(preprocessors.size(), 0); +} + +TEST(TestParserV2Preprocessors, ParseNoID) +{ + ddwaf::object_limits limits; + + auto object = yaml_to_object(R"([{}])"); + + ddwaf::ruleset_info::section_info section; + auto array = static_cast(parameter(object)); + auto preprocessors = parser::v2::parse_preprocessors(array, section, limits); + ddwaf_object_free(&object); + + { + ddwaf::parameter root; + section.to_object(root); + + auto root_map = static_cast(root); + + auto loaded = ddwaf::parser::at(root_map, "loaded"); + EXPECT_EQ(loaded.size(), 0); + + auto failed = ddwaf::parser::at(root_map, "failed"); + EXPECT_EQ(failed.size(), 1); + EXPECT_NE(failed.find("index:0"), failed.end()); + + auto errors = ddwaf::parser::at(root_map, "errors"); + EXPECT_EQ(errors.size(), 1); + auto it = errors.find("missing key 'id'"); + EXPECT_NE(it, errors.end()); + + auto error_rules = static_cast(it->second); + EXPECT_EQ(error_rules.size(), 1); + EXPECT_NE(error_rules.find("index:0"), error_rules.end()); + + ddwaf_object_free(&root); + } + + EXPECT_EQ(preprocessors.size(), 0); +} + +TEST(TestParserV2Preprocessors, ParseNoParameters) +{ + ddwaf::object_limits limits; + + auto object = yaml_to_object(R"([{id: 1, generator: extract_schema}])"); + + ddwaf::ruleset_info::section_info section; + auto array = static_cast(parameter(object)); + auto preprocessors = parser::v2::parse_preprocessors(array, section, limits); + ddwaf_object_free(&object); + + { + ddwaf::parameter root; + section.to_object(root); + + auto root_map = static_cast(root); + + auto loaded = ddwaf::parser::at(root_map, "loaded"); + EXPECT_EQ(loaded.size(), 0); + + auto failed = ddwaf::parser::at(root_map, "failed"); + EXPECT_EQ(failed.size(), 1); + EXPECT_NE(failed.find("1"), failed.end()); + + auto errors = ddwaf::parser::at(root_map, "errors"); + EXPECT_EQ(errors.size(), 1); + auto it = errors.find("missing key 'parameters'"); + EXPECT_NE(it, errors.end()); + + auto error_rules = static_cast(it->second); + EXPECT_EQ(error_rules.size(), 1); + EXPECT_NE(error_rules.find("1"), error_rules.end()); + + ddwaf_object_free(&root); + } + + EXPECT_EQ(preprocessors.size(), 0); +} + +TEST(TestParserV2Preprocessors, ParseNoMappings) +{ + ddwaf::object_limits limits; + + auto object = yaml_to_object(R"([{id: 1, generator: extract_schema, parameters: {}}])"); + + ddwaf::ruleset_info::section_info section; + auto array = static_cast(parameter(object)); + auto preprocessors = parser::v2::parse_preprocessors(array, section, limits); + ddwaf_object_free(&object); + + { + ddwaf::parameter root; + section.to_object(root); + + auto root_map = static_cast(root); + + auto loaded = ddwaf::parser::at(root_map, "loaded"); + EXPECT_EQ(loaded.size(), 0); + + auto failed = ddwaf::parser::at(root_map, "failed"); + EXPECT_EQ(failed.size(), 1); + EXPECT_NE(failed.find("1"), failed.end()); + + auto errors = ddwaf::parser::at(root_map, "errors"); + EXPECT_EQ(errors.size(), 1); + auto it = errors.find("missing key 'mappings'"); + EXPECT_NE(it, errors.end()); + + auto error_rules = static_cast(it->second); + EXPECT_EQ(error_rules.size(), 1); + EXPECT_NE(error_rules.find("1"), error_rules.end()); + + ddwaf_object_free(&root); + } + + EXPECT_EQ(preprocessors.size(), 0); +} + +TEST(TestParserV2Preprocessors, ParseEmptyMappings) +{ + ddwaf::object_limits limits; + + auto object = + yaml_to_object(R"([{id: 1, generator: extract_schema, parameters: {mappings: []}}])"); + + ddwaf::ruleset_info::section_info section; + auto array = static_cast(parameter(object)); + auto preprocessors = parser::v2::parse_preprocessors(array, section, limits); + ddwaf_object_free(&object); + + { + ddwaf::parameter root; + section.to_object(root); + + auto root_map = static_cast(root); + + auto loaded = ddwaf::parser::at(root_map, "loaded"); + EXPECT_EQ(loaded.size(), 0); + + auto failed = ddwaf::parser::at(root_map, "failed"); + EXPECT_EQ(failed.size(), 1); + EXPECT_NE(failed.find("1"), failed.end()); + + auto errors = ddwaf::parser::at(root_map, "errors"); + EXPECT_EQ(errors.size(), 1); + auto it = errors.find("empty mappings"); + EXPECT_NE(it, errors.end()); + + auto error_rules = static_cast(it->second); + EXPECT_EQ(error_rules.size(), 1); + EXPECT_NE(error_rules.find("1"), error_rules.end()); + + ddwaf_object_free(&root); + } + + EXPECT_EQ(preprocessors.size(), 0); +} + +TEST(TestParserV2Preprocessors, ParseNoInput) +{ + ddwaf::object_limits limits; + + auto object = + yaml_to_object(R"([{id: 1, generator: extract_schema, parameters: {mappings: [{}]}}])"); + + ddwaf::ruleset_info::section_info section; + auto array = static_cast(parameter(object)); + auto preprocessors = parser::v2::parse_preprocessors(array, section, limits); + ddwaf_object_free(&object); + + { + ddwaf::parameter root; + section.to_object(root); + + auto root_map = static_cast(root); + + auto loaded = ddwaf::parser::at(root_map, "loaded"); + EXPECT_EQ(loaded.size(), 0); + + auto failed = ddwaf::parser::at(root_map, "failed"); + EXPECT_EQ(failed.size(), 1); + EXPECT_NE(failed.find("1"), failed.end()); + + auto errors = ddwaf::parser::at(root_map, "errors"); + EXPECT_EQ(errors.size(), 1); + auto it = errors.find("missing key 'inputs'"); + EXPECT_NE(it, errors.end()); + + auto error_rules = static_cast(it->second); + EXPECT_EQ(error_rules.size(), 1); + EXPECT_NE(error_rules.find("1"), error_rules.end()); + + ddwaf_object_free(&root); + } + + EXPECT_EQ(preprocessors.size(), 0); +} + +TEST(TestParserV2Preprocessors, ParseEmptyInput) +{ + ddwaf::object_limits limits; + + auto object = yaml_to_object( + R"([{id: 1, generator: extract_schema, parameters: {mappings: [{inputs: [], output: out}]}}])"); + + ddwaf::ruleset_info::section_info section; + auto array = static_cast(parameter(object)); + auto preprocessors = parser::v2::parse_preprocessors(array, section, limits); + ddwaf_object_free(&object); + + { + ddwaf::parameter root; + section.to_object(root); + + auto root_map = static_cast(root); + + auto loaded = ddwaf::parser::at(root_map, "loaded"); + EXPECT_EQ(loaded.size(), 0); + + auto failed = ddwaf::parser::at(root_map, "failed"); + EXPECT_EQ(failed.size(), 1); + EXPECT_NE(failed.find("1"), failed.end()); + + auto errors = ddwaf::parser::at(root_map, "errors"); + EXPECT_EQ(errors.size(), 1); + auto it = errors.find("empty preprocessor input mapping"); + EXPECT_NE(it, errors.end()); + + auto error_rules = static_cast(it->second); + EXPECT_EQ(error_rules.size(), 1); + EXPECT_NE(error_rules.find("1"), error_rules.end()); + + ddwaf_object_free(&root); + } + + EXPECT_EQ(preprocessors.size(), 0); +} + +TEST(TestParserV2Preprocessors, ParseNoOutput) +{ + ddwaf::object_limits limits; + + auto object = yaml_to_object( + R"([{id: 1, generator: extract_schema, parameters: {mappings: [{inputs: [{address: in}]}]}}])"); + + ddwaf::ruleset_info::section_info section; + auto array = static_cast(parameter(object)); + auto preprocessors = parser::v2::parse_preprocessors(array, section, limits); + ddwaf_object_free(&object); + + { + ddwaf::parameter root; + section.to_object(root); + + auto root_map = static_cast(root); + + auto loaded = ddwaf::parser::at(root_map, "loaded"); + EXPECT_EQ(loaded.size(), 0); + + auto failed = ddwaf::parser::at(root_map, "failed"); + EXPECT_EQ(failed.size(), 1); + EXPECT_NE(failed.find("1"), failed.end()); + + auto errors = ddwaf::parser::at(root_map, "errors"); + EXPECT_EQ(errors.size(), 1); + auto it = errors.find("missing key 'output'"); + EXPECT_NE(it, errors.end()); + + auto error_rules = static_cast(it->second); + EXPECT_EQ(error_rules.size(), 1); + EXPECT_NE(error_rules.find("1"), error_rules.end()); + + ddwaf_object_free(&root); + } + + EXPECT_EQ(preprocessors.size(), 0); +} } // namespace