#include #include "../test_falco_engine.h" #include "yaml_helper.h" #define ASSERT_VALIDATION_STATUS(status) \ ASSERT_TRUE(sinsp_utils::startswith(m_load_result->schema_validation(), status)) std::string s_sample_ruleset = "sample-ruleset"; std::string s_sample_source = falco_common::syscall_source; TEST_F(test_falco_engine, list_append) { std::string rules_content = R"END( - list: shell_binaries items: [ash, bash, csh, ksh, sh, tcsh, zsh, dash] - rule: legit_rule desc: legit rule description condition: evt.type=open and proc.name in (shell_binaries) output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - list: shell_binaries items: [pwsh] override: items: append )END"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ( get_compiled_rule_condition("legit_rule"), "(evt.type = open and proc.name in (ash, bash, csh, ksh, sh, tcsh, zsh, dash, pwsh))"); } TEST_F(test_falco_engine, condition_append) { std::string rules_content = R"END( - macro: interactive condition: > ((proc.aname=sshd and proc.name != sshd) or proc.name=systemd-logind or proc.name=login) - rule: legit_rule desc: legit rule description condition: evt.type=open and interactive output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - macro: interactive condition: or proc.name = ssh override: condition: append )END"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("legit_rule"), "(evt.type = open and (((proc.aname = sshd and proc.name != sshd) or proc.name = " "systemd-logind or proc.name = login) or proc.name = ssh))"); } TEST_F(test_falco_engine, rule_override_append) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: legit_rule desc: with append condition: and proc.name = cat output: proc=%proc.name override: desc: append condition: append output: append )END"; std::string rule_name = "legit_rule"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); // Here we don't use the deprecated `append` flag, so we don't expect the warning. ASSERT_FALSE(check_warning_message(WARNING_APPEND)); auto rule_description = m_engine->describe_rule(&rule_name, {}); ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get(), "evt.type=open and proc.name = cat"); ASSERT_EQ(rule_description["rules"][0]["info"]["output"].template get(), "user=%user.name command=%proc.cmdline file=%fd.name proc=%proc.name"); ASSERT_EQ(rule_description["rules"][0]["info"]["description"].template get(), "legit rule description with append"); } TEST_F(test_falco_engine, rule_append) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: legit_rule condition: and proc.name = cat append: true )END"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); // We should have at least one warning because the 'append' flag is deprecated. ASSERT_TRUE(check_warning_message(WARNING_APPEND)); ASSERT_EQ(get_compiled_rule_condition("legit_rule"), "(evt.type = open and proc.name = cat)"); } TEST_F(test_falco_engine, rule_override_replace) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: legit_rule desc: a replaced legit description condition: evt.type = close override: desc: replace condition: replace )END"; std::string rule_name = "legit_rule"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); auto rule_description = m_engine->describe_rule(&rule_name, {}); ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get(), "evt.type = close"); ASSERT_EQ(rule_description["rules"][0]["info"]["output"].template get(), "user=%user.name command=%proc.cmdline file=%fd.name"); ASSERT_EQ(rule_description["rules"][0]["info"]["description"].template get(), "a replaced legit description"); } TEST_F(test_falco_engine, rule_override_append_replace) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: legit_rule desc: a replaced legit description condition: and proc.name = cat priority: WARNING override: desc: replace condition: append priority: replace )END"; std::string rule_name = "legit_rule"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); auto rule_description = m_engine->describe_rule(&rule_name, {}); ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get(), "evt.type = close and proc.name = cat"); ASSERT_EQ(rule_description["rules"][0]["info"]["output"].template get(), "user=%user.name command=%proc.cmdline file=%fd.name"); ASSERT_EQ(rule_description["rules"][0]["info"]["description"].template get(), "a replaced legit description"); ASSERT_EQ(rule_description["rules"][0]["info"]["priority"].template get(), "Warning"); } TEST_F(test_falco_engine, rule_incorrect_override_type) { std::string rules_content = R"END( - rule: failing_rule desc: legit rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: failing_rule desc: an appended incorrect field condition: and proc.name = cat priority: WARNING override: desc: replace condition: append priority: append )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message("Key 'priority' cannot be appended to, use 'replace' instead")); ASSERT_TRUE(std::string(m_load_result_json["errors"][0]["context"]["snippet"]) .find("priority: append") != std::string::npos); } TEST_F(test_falco_engine, rule_incorrect_append_override) { std::string rules_content = R"END( - rule: failing_rule desc: legit rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: failing_rule desc: an appended incorrect field condition: and proc.name = cat append: true override: desc: replace condition: append )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); // We should have at least one warning because the 'append' flag is deprecated. ASSERT_TRUE(check_warning_message(WARNING_APPEND)); ASSERT_TRUE(check_error_message(ERROR_OVERRIDE_APPEND)); } TEST_F(test_falco_engine, macro_override_append_before_macro_definition) { std::string rules_content = R"END( - macro: open_simple condition: or evt.type = openat2 override: condition: append - macro: open_simple condition: evt.type in (open,openat) - rule: test_rule desc: simple rule condition: open_simple output: command=%proc.cmdline priority: INFO )END"; // We cannot define a macro override before the macro definition. ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_MACRO)); } TEST_F(test_falco_engine, macro_override_replace_before_macro_definition) { std::string rules_content = R"END( - macro: open_simple condition: or evt.type = openat2 override: condition: replace - macro: open_simple condition: evt.type in (open,openat) - rule: test_rule desc: simple rule condition: open_simple output: command=%proc.cmdline priority: INFO )END"; // The first override defines a macro that is overridden by the second macro definition ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "evt.type in (open, openat)"); } TEST_F(test_falco_engine, macro_append_before_macro_definition) { std::string rules_content = R"END( - macro: open_simple condition: or evt.type = openat2 append: true - macro: open_simple condition: evt.type in (open,openat) - rule: test_rule desc: simple rule condition: open_simple output: command=%proc.cmdline priority: INFO )END"; // We cannot define a macro override before the macro definition. ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_MACRO)); } TEST_F(test_falco_engine, macro_override_append_after_macro_definition) { std::string rules_content = R"END( - macro: open_simple condition: evt.type in (open,openat) - macro: open_simple condition: or evt.type = openat2 override: condition: append - rule: test_rule desc: simple rule condition: open_simple output: command=%proc.cmdline priority: INFO )END"; // We cannot define a macro override before the macro definition. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type in (open, openat) or evt.type = openat2)"); } TEST_F(test_falco_engine, macro_append_after_macro_definition) { std::string rules_content = R"END( - macro: open_simple condition: evt.type in (open,openat) - macro: open_simple condition: or evt.type = openat2 append: true - rule: test_rule desc: simple rule condition: open_simple output: command=%proc.cmdline priority: INFO )END"; // We cannot define a macro override before the macro definition. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type in (open, openat) or evt.type = openat2)"); } TEST_F(test_falco_engine, rule_override_append_before_rule_definition) { std::string rules_content = R"END( - rule: test_rule condition: and proc.name = cat override: condition: append - rule: test_rule desc: simple rule condition: evt.type in (open,openat) output: command=%proc.cmdline priority: INFO )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_RULE_APPEND)); } TEST_F(test_falco_engine, rule_override_replace_before_rule_definition) { std::string rules_content = R"END( - rule: test_rule condition: and proc.name = cat override: condition: replace - rule: test_rule desc: simple rule condition: evt.type in (open,openat) output: command=%proc.cmdline priority: INFO )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_RULE_REPLACE)); } TEST_F(test_falco_engine, rule_append_before_rule_definition) { std::string rules_content = R"END( - rule: test_rule condition: and proc.name = cat append: true - rule: test_rule desc: simple rule condition: evt.type in (open,openat) output: command=%proc.cmdline priority: INFO )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_RULE_APPEND)); } TEST_F(test_falco_engine, rule_override_append_after_rule_definition) { std::string rules_content = R"END( - rule: test_rule desc: simple rule condition: evt.type in (open,openat) output: command=%proc.cmdline priority: INFO - rule: test_rule condition: and proc.name = cat override: condition: append )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type in (open, openat) and proc.name = cat)"); } TEST_F(test_falco_engine, rule_append_after_rule_definition) { std::string rules_content = R"END( - rule: test_rule desc: simple rule condition: evt.type in (open,openat) output: command=%proc.cmdline priority: INFO - rule: test_rule condition: and proc.name = cat append: true )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type in (open, openat) and proc.name = cat)"); } TEST_F(test_falco_engine, list_override_append_wrong_key) { // todo: maybe we want to manage some non-existent keys // Please note how the non-existent key 'non-existent keys' is ignored. std::string rules_content = R"END( - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] override_written_wrong: items: append - list: dev_creation_binaries items: [blkid] - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; // Since there is a wrong key in the first list definition the `override` is not // considered. so in this situation, we are defining the list 2 times. The // second one overrides the first one. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_failed) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = execve and proc.name in (blkid))"); } TEST_F(test_falco_engine, list_override_append_before_list_definition) { std::string rules_content = R"END( - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] override: items: append - list: dev_creation_binaries items: [blkid] - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; // We cannot define a list override before the list definition. ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_LIST)); } TEST_F(test_falco_engine, list_override_replace_before_list_definition) { std::string rules_content = R"END( - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] override: items: replace - list: dev_creation_binaries items: [blkid] - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; // With override replace we define a first list that then is overridden by the second one. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = execve and proc.name in (blkid))"); } TEST_F(test_falco_engine, list_append_before_list_definition) { std::string rules_content = R"END( - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] append: true - list: dev_creation_binaries items: [blkid] - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; // We cannot define a list append before the list definition. ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_LIST)); } TEST_F(test_falco_engine, list_override_append_after_list_definition) { std::string rules_content = R"END( - list: dev_creation_binaries items: [blkid] - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] override: items: append - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = execve and proc.name in (blkid, csi-provisioner, csi-attacher))"); } TEST_F(test_falco_engine, list_append_after_list_definition) { std::string rules_content = R"END( - list: dev_creation_binaries items: [blkid] - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] append: true - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = execve and proc.name in (blkid, csi-provisioner, csi-attacher))"); } TEST_F(test_falco_engine, rule_override_without_field) { std::string rules_content = R"END( - rule: failing_rule desc: legit rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: failing_rule desc: an appended incorrect field override: desc: replace condition: append )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message( "An append override for 'condition' was specified but 'condition' is not defined")); } TEST_F(test_falco_engine, rule_override_extra_field) { std::string rules_content = R"END( - rule: failing_rule desc: legit rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: failing_rule desc: an appended incorrect field condition: and proc.name = cat priority: WARNING override: desc: replace condition: append )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message("Unexpected key 'priority'")); } TEST_F(test_falco_engine, missing_enabled_key_with_override) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false - rule: test_rule desc: missing enabled key condition: and proc.name = cat override: desc: replace condition: append enabled: replace )END"; // In the rule override we miss `enabled: true` ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message("'enabled' was specified but 'enabled' is not defined")); } TEST_F(test_falco_engine, rule_override_with_enabled) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false - rule: test_rule desc: correct override condition: and proc.name = cat enabled: true override: desc: replace condition: append enabled: replace )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); // The rule should be enabled at the end. EXPECT_EQ(num_rules_for_ruleset(), 1); } TEST_F(test_falco_engine, rule_override_exceptions_required_fields) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO exceptions: - name: test_exception fields: proc.name comps: in values: ["cat"] # when appending, it's fine to provide partial exception definitions - rule: test_rule exceptions: - name: test_exception values: [echo] override: exceptions: append # when replacing, we don't allow partial exception definitions - rule: test_rule exceptions: - name: test_exception values: [id] override: exceptions: replace )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); ASSERT_TRUE(check_error_message("Item has no mapping for key 'fields'")) << m_load_result_json.dump(); } TEST_F(test_falco_engine, rule_not_enabled) { std::string rules_content = R"END( - rule: test_rule desc: rule not enabled condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); EXPECT_EQ(num_rules_for_ruleset(), 0); } TEST_F(test_falco_engine, rule_enabled_warning) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false - rule: test_rule enabled: true )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message(WARNING_ENABLED)); // The rule should be enabled at the end. EXPECT_EQ(num_rules_for_ruleset(), 1); } // todo!: Probably we shouldn't allow this syntax TEST_F(test_falco_engine, rule_enabled_is_ignored_by_append) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false - rule: test_rule condition: and proc.name = cat append: true enabled: true )END"; // 'enabled' is ignored by the append, this syntax is not supported // so the rule is not enabled. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(num_rules_for_ruleset(), 0); } // todo!: Probably we shouldn't allow this syntax TEST_F(test_falco_engine, rewrite_rule) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false - rule: test_rule desc: redefined rule syntax condition: proc.name = cat output: user=%user.name command=%proc.cmdline file=%fd.name priority: WARNING enabled: true )END"; // The above syntax is not supported, we cannot override the content // of a rule in this way. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); // In this case the rule is completely overridden but this syntax is not supported. EXPECT_EQ(num_rules_for_ruleset(), 1); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "proc.name = cat"); } TEST_F(test_falco_engine, required_engine_version_semver) { std::string rules_content = R"END( - required_engine_version: 0.26.0 - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); } TEST_F(test_falco_engine, required_engine_version_not_semver) { std::string rules_content = R"END( - required_engine_version: 26 - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); } TEST_F(test_falco_engine, required_engine_version_invalid) { std::string rules_content = R"END( - required_engine_version: seven - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message("Unable to parse engine version")); } // checks for issue described in https://github.com/falcosecurity/falco/pull/3028 TEST_F(test_falco_engine, list_value_with_escaping) { std::string rules_content = R"END( - list: my_list items: [non_escaped_val, "escaped val"] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(m_load_result->successful()); ASSERT_TRUE(m_load_result->has_warnings()); // a warning for the unused list auto rule_description = m_engine->describe_rule(nullptr, {}); ASSERT_TRUE(m_load_result->successful()); ASSERT_EQ(rule_description["rules"].size(), 0); ASSERT_EQ(rule_description["macros"].size(), 0); ASSERT_EQ(rule_description["lists"].size(), 1); // escaped values must not be interpreted as list refs by mistake ASSERT_EQ(rule_description["lists"][0]["details"]["lists"].size(), 0); // values should be escaped correctly ASSERT_EQ(rule_description["lists"][0]["details"]["items_compiled"].size(), 2); ASSERT_EQ(rule_description["lists"][0]["details"]["items_compiled"][0] .template get(), "non_escaped_val"); ASSERT_EQ(rule_description["lists"][0]["details"]["items_compiled"][1] .template get(), "escaped val"); } TEST_F(test_falco_engine, exceptions_condition) { std::string rules_content = R"END( - rule: test_rule desc: test rule condition: proc.cmdline contains curl or proc.cmdline contains wget output: command=%proc.cmdline priority: INFO exceptions: - name: test_exception fields: [proc.cmdline] comps: [contains] values: - [curl 127.0.0.1] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "((proc.cmdline contains curl or proc.cmdline contains wget) and not proc.cmdline " "contains \"curl 127.0.0.1\")"); } TEST_F(test_falco_engine, macro_name_invalid) { std::string rules_content = R"END( - macro: test-macro condition: evt.type = close )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message( "Macro has an invalid name. Macro names should match a regular expression")); } TEST_F(test_falco_engine, list_name_invalid) { std::string rules_content = R"END( - list: test list items: [open, openat, openat2] - rule: test_rule desc: test rule description condition: evt.type in (test list) output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message( "List has an invalid name. List names should match a regular expression")); } // The appended exception has a purposely miswritten field (value), // simulating a typo or an incorrect usage. TEST_F(test_falco_engine, exceptions_append_no_values) { std::string rules_content = R"END( - rule: test_rule desc: test rule condition: proc.cmdline contains curl output: command=%proc.cmdline priority: INFO exceptions: - name: test_exception fields: [proc.cmdline] comps: [contains] values: - [curl 127.0.0.1] - rule: test_rule exceptions: - name: test_exception value: curl 1.1.1.1 append: true )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_failed) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message("Overriding/appending exception with no values")); } TEST_F(test_falco_engine, exceptions_override_no_values) { std::string rules_content = R"END( - rule: test_rule desc: test rule condition: proc.cmdline contains curl output: command=%proc.cmdline priority: INFO exceptions: - name: test_exception fields: [proc.cmdline] comps: [contains] values: - [curl 127.0.0.1] - rule: test_rule exceptions: - name: test_exception value: curl 1.1.1.1 override: exceptions: append )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_failed) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message("Overriding/appending exception with no values")); } TEST_F(test_falco_engine, exceptions_names_not_unique) { std::string rules_content = R"END( - rule: test_rule desc: test rule condition: proc.cmdline contains curl output: command=%proc.cmdline priority: INFO exceptions: - name: test_exception fields: [proc.cmdline] comps: [contains] values: - [curl 127.0.0.1] - name: test_exception fields: [proc.cmdline] comps: [endswith] values: - [curl 127.0.0.1] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message("Multiple definitions of exception")); } static std::string s_exception_values_rule_base = R"END( - rule: test_rule desc: test rule condition: evt.type = open output: command=%proc.cmdline priority: INFO )END"; TEST_F(test_falco_engine, exceptions_values_rhs_field_ambiguous) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - [proc.pname] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = proc.pname)"); EXPECT_TRUE(check_warning_message( "'proc.pname' may be a valid field misused as a const string value")); } TEST_F(test_falco_engine, exceptions_values_rhs_field_ambiguous_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - ["proc.pname"] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = proc.pname)"); EXPECT_TRUE(check_warning_message( "'proc.pname' may be a valid field misused as a const string value")); } TEST_F(test_falco_engine, exceptions_values_rhs_field_ambiguous_space_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - ["proc.pname "] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = \"proc.pname \")"); EXPECT_TRUE(check_warning_message( "'proc.pname ' may be a valid field misused as a const string value")); } TEST_F(test_falco_engine, exceptions_values_rhs_transformer) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - [toupper(proc.pname)] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = toupper(proc.pname))"); } TEST_F(test_falco_engine, exceptions_values_transformer_value_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - ["toupper(proc.pname)"] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = toupper(proc.pname))"); } TEST_F(test_falco_engine, exceptions_values_transformer_space) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - [toupper( proc.pname)] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = \"toupper( proc.pname)\")"); EXPECT_TRUE( check_warning_message("'toupper( proc.pname)' may be a valid field transformer misused " "as a const string value")); } TEST_F(test_falco_engine, exceptions_values_transformer_space_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - ["toupper( proc.pname)"] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = \"toupper( proc.pname)\")"); EXPECT_TRUE( check_warning_message("'toupper( proc.pname)' may be a valid field transformer misused " "as a const string value")); } TEST_F(test_falco_engine, exceptions_fields_transformer) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [tolower(proc.name)] comps: [=] values: - [test] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_FALSE(has_warnings()); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)"); } TEST_F(test_falco_engine, exceptions_fields_transformer_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: ["tolower(proc.name)"] comps: [=] values: - [test] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)"); } TEST_F(test_falco_engine, exceptions_fields_transformer_space_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: ["tolower( proc.name)"] comps: [=] values: - [test] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)"); } TEST_F(test_falco_engine, redefine_rule_different_source) { auto rules_content = R"END( - rule: LD_PRELOAD trick desc: Some desc condition: ka.verb = GET output: some output priority: INFO source: k8s_audit - rule: LD_PRELOAD trick desc: Some desc condition: and 1 = 2 output: Some output priority: INFO source: syscall )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(check_error_message("Rule has been re-defined with a different source")); } TEST_F(test_falco_engine, append_across_sources) { auto rules_content = R"END( - rule: LD_PRELOAD trick desc: Some desc condition: ka.verb = GET output: some output priority: INFO source: k8s_audit - rule: LD_PRELOAD trick desc: Some desc condition: and 1 = 2 output: Some output priority: INFO source: syscall append: true )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(check_error_message("Rule has been re-defined with a different source")); } TEST_F(test_falco_engine, selective_replace_across_sources) { auto rules_content = R"END( - rule: LD_PRELOAD trick desc: Some desc condition: ka.verb = GET output: some output priority: INFO source: k8s_audit - rule: LD_PRELOAD trick condition: 1 = 2 override: condition: replace source: syscall )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(check_error_message("Rule has been re-defined with a different source")); } TEST_F(test_falco_engine, empty_source_addl_rule) { auto rules_content = R"END( - rule: LD_PRELOAD trick desc: Some desc condition: evt.type=execve output: some output priority: INFO source: syscall - rule: LD_PRELOAD trick desc: Some desc condition: and proc.name=apache output: Some output priority: INFO source: append: true )END"; EXPECT_TRUE(load_rules(rules_content, "rules.yaml")); } TEST_F(test_falco_engine, empty_string_source_addl_rule) { auto rules_content = R"END( - rule: LD_PRELOAD trick desc: Some desc condition: evt.type=execve output: some output priority: INFO source: syscall - rule: LD_PRELOAD trick desc: Some desc condition: and proc.name=apache output: Some output priority: INFO source: "" append: true )END"; EXPECT_TRUE(load_rules(rules_content, "rules.yaml")); }