cleanup(userspace,unit_tests): moved rule schema under engine.

Also, moved yaml_helper under engine/ folder.
Ported rule json schema validation in the engine.

Also, updated rule_loader tests to check for validation.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
This commit is contained in:
Federico Di Pierro
2024-09-06 14:52:11 +02:00
committed by poiana
parent 895e50d3a0
commit 5bd2d5a63e
15 changed files with 295 additions and 136 deletions

View File

@@ -1,6 +1,9 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "../test_falco_engine.h" #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_ruleset = "sample-ruleset";
std::string s_sample_source = falco_common::syscall_source; std::string s_sample_source = falco_common::syscall_source;
@@ -24,6 +27,7 @@ TEST_F(test_falco_engine, list_append)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; 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))"); ASSERT_EQ(get_compiled_rule_condition("legit_rule"),"(evt.type = open and proc.name in (ash, bash, csh, ksh, sh, tcsh, zsh, dash, pwsh))");
} }
@@ -48,6 +52,7 @@ TEST_F(test_falco_engine, condition_append)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; 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))"); 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))");
} }
@@ -72,6 +77,7 @@ TEST_F(test_falco_engine, rule_override_append)
std::string rule_name = "legit_rule"; std::string rule_name = "legit_rule";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; 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. // Here we don't use the deprecated `append` flag, so we don't expect the warning.
ASSERT_FALSE(check_warning_message(WARNING_APPEND)); ASSERT_FALSE(check_warning_message(WARNING_APPEND));
@@ -102,6 +108,7 @@ TEST_F(test_falco_engine, rule_append)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; 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. // We should have at least one warning because the 'append' flag is deprecated.
ASSERT_TRUE(check_warning_message(WARNING_APPEND)); ASSERT_TRUE(check_warning_message(WARNING_APPEND));
@@ -128,6 +135,7 @@ TEST_F(test_falco_engine, rule_override_replace)
std::string rule_name = "legit_rule"; std::string rule_name = "legit_rule";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; 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, {}); auto rule_description = m_engine->describe_rule(&rule_name, {});
ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get<std::string>(), ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get<std::string>(),
@@ -161,6 +169,7 @@ TEST_F(test_falco_engine, rule_override_append_replace)
std::string rule_name = "legit_rule"; std::string rule_name = "legit_rule";
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; 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, {}); auto rule_description = m_engine->describe_rule(&rule_name, {});
ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get<std::string>(), ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get<std::string>(),
@@ -196,6 +205,7 @@ TEST_F(test_falco_engine, rule_incorrect_override_type)
)END"; )END";
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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(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); ASSERT_TRUE(std::string(m_load_result_json["errors"][0]["context"]["snippet"]).find("priority: append") != std::string::npos);
} }
@@ -219,6 +229,7 @@ TEST_F(test_falco_engine, rule_incorrect_append_override)
)END"; )END";
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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. // We should have at least one warning because the 'append' flag is deprecated.
ASSERT_TRUE(check_warning_message(WARNING_APPEND)); ASSERT_TRUE(check_warning_message(WARNING_APPEND));
@@ -248,6 +259,7 @@ TEST_F(test_falco_engine, macro_override_append_before_macro_definition)
// We cannot define a macro override before the macro definition. // We cannot define a macro override before the macro definition.
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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)); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_MACRO));
} }
@@ -273,6 +285,7 @@ TEST_F(test_falco_engine, macro_override_replace_before_macro_definition)
// The first override defines a macro that is overridden by the second macro definition // The first override defines a macro that is overridden by the second macro definition
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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)"); ASSERT_EQ(get_compiled_rule_condition("test_rule"),"evt.type in (open, openat)");
} }
@@ -297,6 +310,7 @@ TEST_F(test_falco_engine, macro_append_before_macro_definition)
// We cannot define a macro override before the macro definition. // We cannot define a macro override before the macro definition.
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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)); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_MACRO));
} }
@@ -322,6 +336,7 @@ TEST_F(test_falco_engine, macro_override_append_after_macro_definition)
// We cannot define a macro override before the macro definition. // We cannot define a macro override before the macro definition.
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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)"); ASSERT_EQ(get_compiled_rule_condition("test_rule"),"(evt.type in (open, openat) or evt.type = openat2)");
} }
@@ -346,6 +361,7 @@ TEST_F(test_falco_engine, macro_append_after_macro_definition)
// We cannot define a macro override before the macro definition. // We cannot define a macro override before the macro definition.
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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)"); ASSERT_EQ(get_compiled_rule_condition("test_rule"),"(evt.type in (open, openat) or evt.type = openat2)");
} }
@@ -366,6 +382,7 @@ TEST_F(test_falco_engine, rule_override_append_before_rule_definition)
)END"; )END";
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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)); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_RULE_APPEND));
} }
@@ -386,6 +403,7 @@ TEST_F(test_falco_engine, rule_override_replace_before_rule_definition)
)END"; )END";
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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)); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_RULE_REPLACE));
} }
@@ -405,6 +423,7 @@ TEST_F(test_falco_engine, rule_append_before_rule_definition)
)END"; )END";
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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)); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_RULE_APPEND));
} }
@@ -424,6 +443,7 @@ TEST_F(test_falco_engine, rule_override_append_after_rule_definition)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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)"); ASSERT_EQ(get_compiled_rule_condition("test_rule"),"(evt.type in (open, openat) and proc.name = cat)");
} }
@@ -442,6 +462,7 @@ TEST_F(test_falco_engine, rule_append_after_rule_definition)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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)"); ASSERT_EQ(get_compiled_rule_condition("test_rule"),"(evt.type in (open, openat) and proc.name = cat)");
} }
@@ -470,6 +491,7 @@ TEST_F(test_falco_engine, list_override_append_wrong_key)
// considered. so in this situation, we are defining the list 2 times. The // considered. so in this situation, we are defining the list 2 times. The
// second one overrides the first one. // second one overrides the first one.
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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))"); ASSERT_EQ(get_compiled_rule_condition("test_rule"),"(evt.type = execve and proc.name in (blkid))");
} }
@@ -494,6 +516,7 @@ TEST_F(test_falco_engine, list_override_append_before_list_definition)
// We cannot define a list override before the list definition. // We cannot define a list override before the list definition.
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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)); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_LIST));
} }
@@ -518,6 +541,7 @@ TEST_F(test_falco_engine, list_override_replace_before_list_definition)
// With override replace we define a first list that then is overridden by the second one. // 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_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))"); ASSERT_EQ(get_compiled_rule_condition("test_rule"),"(evt.type = execve and proc.name in (blkid))");
} }
@@ -541,6 +565,7 @@ TEST_F(test_falco_engine, list_append_before_list_definition)
// We cannot define a list append before the list definition. // We cannot define a list append before the list definition.
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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)); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_LIST));
} }
@@ -564,6 +589,7 @@ TEST_F(test_falco_engine, list_override_append_after_list_definition)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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))"); ASSERT_EQ(get_compiled_rule_condition("test_rule"),"(evt.type = execve and proc.name in (blkid, csi-provisioner, csi-attacher))");
} }
@@ -585,6 +611,7 @@ TEST_F(test_falco_engine, list_append_after_list_definition)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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))"); ASSERT_EQ(get_compiled_rule_condition("test_rule"),"(evt.type = execve and proc.name in (blkid, csi-provisioner, csi-attacher))");
} }
@@ -605,6 +632,7 @@ TEST_F(test_falco_engine, rule_override_without_field)
)END"; )END";
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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")); ASSERT_TRUE(check_error_message("An append override for 'condition' was specified but 'condition' is not defined"));
} }
@@ -627,6 +655,7 @@ TEST_F(test_falco_engine, rule_override_extra_field)
)END"; )END";
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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'")); ASSERT_TRUE(check_error_message("Unexpected key 'priority'"));
} }
@@ -651,6 +680,7 @@ TEST_F(test_falco_engine, missing_enabled_key_with_override)
// In the rule override we miss `enabled: true` // In the rule override we miss `enabled: true`
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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")); ASSERT_TRUE(check_error_message("'enabled' was specified but 'enabled' is not defined"));
} }
@@ -675,6 +705,7 @@ TEST_F(test_falco_engine, rule_override_with_enabled)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(load_rules(rules_content, "rules.yaml"));
ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation();
ASSERT_FALSE(has_warnings()); ASSERT_FALSE(has_warnings());
// The rule should be enabled at the end. // The rule should be enabled at the end.
EXPECT_EQ(num_rules_for_ruleset(), 1); EXPECT_EQ(num_rules_for_ruleset(), 1);
@@ -712,6 +743,7 @@ TEST_F(test_falco_engine, rule_override_exceptions_required_fields)
)END"; )END";
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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_FALSE(has_warnings());
ASSERT_TRUE(check_error_message("Item has no mapping for key 'fields'")) << m_load_result_json.dump(); ASSERT_TRUE(check_error_message("Item has no mapping for key 'fields'")) << m_load_result_json.dump();
} }
@@ -728,6 +760,7 @@ TEST_F(test_falco_engine, rule_not_enabled)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(load_rules(rules_content, "rules.yaml"));
ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation();
ASSERT_FALSE(has_warnings()); ASSERT_FALSE(has_warnings());
EXPECT_EQ(num_rules_for_ruleset(), 0); EXPECT_EQ(num_rules_for_ruleset(), 0);
} }
@@ -747,6 +780,7 @@ TEST_F(test_falco_engine, rule_enabled_warning)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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)); ASSERT_TRUE(check_warning_message(WARNING_ENABLED));
// The rule should be enabled at the end. // The rule should be enabled at the end.
EXPECT_EQ(num_rules_for_ruleset(), 1); EXPECT_EQ(num_rules_for_ruleset(), 1);
@@ -772,6 +806,7 @@ TEST_F(test_falco_engine, rule_enabled_is_ignored_by_append)
// 'enabled' is ignored by the append, this syntax is not supported // 'enabled' is ignored by the append, this syntax is not supported
// so the rule is not enabled. // so the rule is not enabled.
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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); EXPECT_EQ(num_rules_for_ruleset(), 0);
} }
@@ -797,6 +832,7 @@ TEST_F(test_falco_engine, rewrite_rule)
// The above syntax is not supported, we cannot override the content // The above syntax is not supported, we cannot override the content
// of a rule in this way. // of a rule in this way.
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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. // In this case the rule is completely overridden but this syntax is not supported.
EXPECT_EQ(num_rules_for_ruleset(), 1); EXPECT_EQ(num_rules_for_ruleset(), 1);
ASSERT_EQ(get_compiled_rule_condition("test_rule"),"proc.name = cat"); ASSERT_EQ(get_compiled_rule_condition("test_rule"),"proc.name = cat");
@@ -817,6 +853,7 @@ TEST_F(test_falco_engine, required_engine_version_semver)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(load_rules(rules_content, "rules.yaml"));
ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation();
ASSERT_FALSE(has_warnings()); ASSERT_FALSE(has_warnings());
} }
@@ -835,6 +872,7 @@ TEST_F(test_falco_engine, required_engine_version_not_semver)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(load_rules(rules_content, "rules.yaml"));
ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation();
ASSERT_FALSE(has_warnings()); ASSERT_FALSE(has_warnings());
} }
@@ -853,6 +891,7 @@ TEST_F(test_falco_engine, required_engine_version_invalid)
)END"; )END";
ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); 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")); ASSERT_TRUE(check_error_message("Unable to parse engine version"));
} }
@@ -865,22 +904,23 @@ TEST_F(test_falco_engine, list_value_with_escaping)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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->successful());
ASSERT_TRUE(m_load_result->has_warnings()); // a warning for the unused list ASSERT_TRUE(m_load_result->has_warnings()); // a warning for the unused list
auto rule_description = m_engine->describe_rule(nullptr, {}); auto rule_description = m_engine->describe_rule(nullptr, {});
ASSERT_TRUE(m_load_result->successful()); ASSERT_TRUE(m_load_result->successful());
ASSERT_EQ(rule_description["rules"].size(), 0); ASSERT_EQ(rule_description["rules"].size(), 0);
ASSERT_EQ(rule_description["macros"].size(), 0); ASSERT_EQ(rule_description["macros"].size(), 0);
ASSERT_EQ(rule_description["lists"].size(), 1); ASSERT_EQ(rule_description["lists"].size(), 1);
// escaped values must not be interpreted as list refs by mistake // escaped values must not be interpreted as list refs by mistake
ASSERT_EQ(rule_description["lists"][0]["details"]["lists"].size(), 0); ASSERT_EQ(rule_description["lists"][0]["details"]["lists"].size(), 0);
// values should be escaped correctly // 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"].size(), 2);
ASSERT_EQ(rule_description["lists"][0]["details"]["items_compiled"][0].template get<std::string>(), "non_escaped_val"); ASSERT_EQ(rule_description["lists"][0]["details"]["items_compiled"][0].template get<std::string>(), "non_escaped_val");
ASSERT_EQ(rule_description["lists"][0]["details"]["items_compiled"][1].template get<std::string>(), "escaped val"); ASSERT_EQ(rule_description["lists"][0]["details"]["items_compiled"][1].template get<std::string>(), "escaped val");
} }
TEST_F(test_falco_engine, exceptions_condition) TEST_F(test_falco_engine, exceptions_condition)
@@ -900,6 +940,7 @@ TEST_F(test_falco_engine, exceptions_condition)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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\")"); 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\")");
} }
@@ -911,6 +952,7 @@ TEST_F(test_falco_engine, macro_name_invalid)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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")); ASSERT_TRUE(check_warning_message("Macro has an invalid name. Macro names should match a regular expression"));
} }
@@ -930,6 +972,7 @@ TEST_F(test_falco_engine, list_name_invalid)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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")); ASSERT_TRUE(check_warning_message("List has an invalid name. List names should match a regular expression"));
} }
@@ -958,6 +1001,7 @@ TEST_F(test_falco_engine, exceptions_append_no_values)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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")); ASSERT_TRUE(check_warning_message("Overriding/appending exception with no values"));
} }
@@ -985,6 +1029,7 @@ TEST_F(test_falco_engine, exceptions_override_no_values)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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")); ASSERT_TRUE(check_warning_message("Overriding/appending exception with no values"));
} }
@@ -1010,6 +1055,7 @@ TEST_F(test_falco_engine, exceptions_names_not_unique)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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")); ASSERT_TRUE(check_warning_message("Multiple definitions of exception"));
} }
@@ -1033,6 +1079,7 @@ TEST_F(test_falco_engine, exceptions_values_rhs_field_ambiguous)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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_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")); EXPECT_TRUE(check_warning_message("'proc.pname' may be a valid field misused as a const string value"));
} }
@@ -1049,6 +1096,7 @@ TEST_F(test_falco_engine, exceptions_values_rhs_field_ambiguous_quoted)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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_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")); EXPECT_TRUE(check_warning_message("'proc.pname' may be a valid field misused as a const string value"));
} }
@@ -1065,6 +1113,7 @@ TEST_F(test_falco_engine, exceptions_values_rhs_field_ambiguous_space_quoted)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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_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")); EXPECT_TRUE(check_warning_message("'proc.pname ' may be a valid field misused as a const string value"));
} }
@@ -1081,6 +1130,7 @@ TEST_F(test_falco_engine, exceptions_values_rhs_transformer)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = toupper(proc.pname))");
} }
@@ -1096,6 +1146,7 @@ TEST_F(test_falco_engine, exceptions_values_transformer_value_quoted)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = toupper(proc.pname))");
} }
@@ -1111,6 +1162,7 @@ TEST_F(test_falco_engine, exceptions_values_transformer_space)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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_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")); EXPECT_TRUE(check_warning_message("'toupper( proc.pname)' may be a valid field transformer misused as a const string value"));
} }
@@ -1127,6 +1179,7 @@ TEST_F(test_falco_engine, exceptions_values_transformer_space_quoted)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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_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")); EXPECT_TRUE(check_warning_message("'toupper( proc.pname)' may be a valid field transformer misused as a const string value"));
} }
@@ -1143,6 +1196,7 @@ TEST_F(test_falco_engine, exceptions_fields_transformer)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); 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_FALSE(has_warnings());
EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)"); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)");
} }
@@ -1159,6 +1213,7 @@ TEST_F(test_falco_engine, exceptions_fields_transformer_quoted)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(load_rules(rules_content, "rules.yaml"));
ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation();
ASSERT_FALSE(has_warnings()); ASSERT_FALSE(has_warnings());
EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)"); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)");
} }
@@ -1175,6 +1230,7 @@ TEST_F(test_falco_engine, exceptions_fields_transformer_space_quoted)
)END"; )END";
ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(load_rules(rules_content, "rules.yaml"));
ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation();
ASSERT_FALSE(has_warnings()); ASSERT_FALSE(has_warnings());
EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)"); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)");
} }

View File

@@ -47,6 +47,153 @@ limitations under the License.
const std::string falco_engine::s_default_ruleset = "falco-default-ruleset"; const std::string falco_engine::s_default_ruleset = "falco-default-ruleset";
static const std::string rule_schema_string = R"(
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "array",
"items": {
"$ref": "#/definitions/FalcoRule"
},
"definitions": {
"FalcoRule": {
"type": "object",
"additionalProperties": false,
"properties": {
"required_engine_version": {
"type": "string"
},
"macro": {
"type": "string"
},
"condition": {
"type": "string"
},
"list": {
"type": "string"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/Item"
}
},
"rule": {
"type": "string"
},
"desc": {
"type": "string"
},
"enabled": {
"type": "boolean"
},
"output": {
"type": "string"
},
"append": {
"type": "boolean"
},
"priority": {
"$ref": "#/definitions/Priority"
},
"exceptions": {
"type": "array",
"items": {
"$ref": "#/definitions/Exception"
}
},
"override": {
"$ref": "#/definitions/Override"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [],
"title": "FalcoRule"
},
"Item": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"title": "Item"
},
"Exception": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"fields": {},
"comps": {},
"values": {}
},
"required": [
"name",
"values"
],
"title": "Exception"
},
"Priority": {
"type": "string",
"enum": [
"WARNING",
"NOTICE",
"INFO",
"ERROR",
"CRITICAL"
],
"title": "Priority"
},
"OverriddenItem": {
"type": "string",
"enum": [
"append",
"replace"
],
"title": "Priority"
},
"Override": {
"type": "object",
"additionalProperties": false,
"properties": {
"items": {
"$ref": "#/definitions/OverriddenItem"
},
"desc": {
"$ref": "#/definitions/OverriddenItem"
},
"condition": {
"$ref": "#/definitions/OverriddenItem"
},
"output": {
"$ref": "#/definitions/OverriddenItem"
},
"priority": {
"$ref": "#/definitions/OverriddenItem"
},
"enabled": {
"$ref": "#/definitions/OverriddenItem"
},
"exceptions": {
"$ref": "#/definitions/OverriddenItem"
}
},
"minProperties":1,
"title": "Override"
}
}
}
)";
using namespace falco; using namespace falco;
falco_engine::falco_engine(bool seed_rng) falco_engine::falco_engine(bool seed_rng)
@@ -67,6 +214,8 @@ falco_engine::falco_engine(bool seed_rng)
m_default_ruleset_id = find_ruleset_id(s_default_ruleset); m_default_ruleset_id = find_ruleset_id(s_default_ruleset);
fill_engine_state_funcs(m_engine_state); fill_engine_state_funcs(m_engine_state);
m_rule_schema = nlohmann::json::parse(rule_schema_string);
} }
falco_engine::~falco_engine() falco_engine::~falco_engine()
@@ -198,7 +347,7 @@ std::unique_ptr<load_result> falco_engine::load_rules(const std::string &rules_c
cfg.extra_output_fields = m_extra_output_fields; cfg.extra_output_fields = m_extra_output_fields;
// read rules YAML file and collect its definitions // read rules YAML file and collect its definitions
if(m_rule_reader->read(cfg, *m_rule_collector)) if(m_rule_reader->read(cfg, *m_rule_collector, m_rule_schema))
{ {
// compile the definitions (resolve macro/list refs, exceptions, ...) // compile the definitions (resolve macro/list refs, exceptions, ...)
m_last_compile_output = m_rule_compiler->new_compile_output(); m_last_compile_output = m_rule_compiler->new_compile_output();

View File

@@ -355,6 +355,8 @@ public:
const std::vector<plugin_version_requirement>& plugins, const std::vector<plugin_version_requirement>& plugins,
std::string& err) const; std::string& err) const;
nlohmann::json m_rule_schema;
private: private:
// Create a ruleset using the provided factory and set the // Create a ruleset using the provided factory and set the
// engine state funcs for it. // engine state funcs for it.

View File

@@ -87,6 +87,9 @@ public:
// has_warnings() can both be true if there were only warnings. // has_warnings() can both be true if there were only warnings.
virtual bool has_warnings() = 0; virtual bool has_warnings() = 0;
// Return json schema validation status.
virtual std::string schema_validation() = 0;
// This represents a set of rules contents as a mapping from // This represents a set of rules contents as a mapping from
// rules content name (usually filename) to rules content. The // rules content name (usually filename) to rules content. The
// rules content is actually a reference to the actual string // rules content is actually a reference to the actual string

View File

@@ -18,6 +18,7 @@ limitations under the License.
#include <string> #include <string>
#include "rule_loader.h" #include "rule_loader.h"
#include "yaml_helper.h"
static const std::string item_type_strings[] = { static const std::string item_type_strings[] = {
@@ -282,7 +283,8 @@ std::string rule_loader::context::snippet(const falco::load_result::rules_conten
rule_loader::result::result(const std::string &name) rule_loader::result::result(const std::string &name)
: name(name), : name(name),
success(true) success(true),
schema_validation_str(yaml_helper::validation_none)
{ {
} }
@@ -296,6 +298,11 @@ bool rule_loader::result::has_warnings()
return (warnings.size() > 0); return (warnings.size() > 0);
} }
std::string rule_loader::result::schema_validation()
{
return schema_validation_str;
}
void rule_loader::result::add_error(load_result::error_code ec, const std::string& msg, const context& ctx) void rule_loader::result::add_error(load_result::error_code ec, const std::string& msg, const context& ctx)
{ {
error err = {ec, msg, ctx}; error err = {ec, msg, ctx};
@@ -311,6 +318,11 @@ void rule_loader::result::add_warning(load_result::warning_code wc, const std::s
warnings.push_back(warn); warnings.push_back(warn);
} }
void rule_loader::result::set_schema_validation_status(const std::string& status)
{
schema_validation_str = status;
}
const std::string& rule_loader::result::as_string(bool verbose, const rules_contents_t& contents) const std::string& rule_loader::result::as_string(bool verbose, const rules_contents_t& contents)
{ {
if(verbose) if(verbose)

View File

@@ -247,12 +247,16 @@ namespace rule_loader
void add_warning(falco::load_result::warning_code ec, void add_warning(falco::load_result::warning_code ec,
const std::string& msg, const std::string& msg,
const context& ctx); const context& ctx);
void set_schema_validation_status(const std::string& status);
std::string schema_validation();
protected: protected:
const std::string& as_summary_string(); const std::string& as_summary_string();
const std::string& as_verbose_string(const falco::load_result::rules_contents_t& contents); const std::string& as_verbose_string(const falco::load_result::rules_contents_t& contents);
std::string name; std::string name;
bool success; bool success;
std::string schema_validation_str;
std::vector<error> errors; std::vector<error> errors;
std::vector<warning> warnings; std::vector<warning> warnings;

View File

@@ -23,6 +23,7 @@ limitations under the License.
#include "rule_loader_reader.h" #include "rule_loader_reader.h"
#include "falco_engine_version.h" #include "falco_engine_version.h"
#include "rule_loading_messages.h" #include "rule_loading_messages.h"
#include "yaml_helper.h"
#include <libsinsp/logger.h> #include <libsinsp/logger.h>
#include <re2/re2.h> #include <re2/re2.h>
@@ -783,13 +784,15 @@ void rule_loader::reader::read_item(
} }
} }
bool rule_loader::reader::read(rule_loader::configuration& cfg, collector& collector) bool rule_loader::reader::read(rule_loader::configuration& cfg, collector& collector, const nlohmann::json& schema)
{ {
std::vector<YAML::Node> docs; std::vector<YAML::Node> docs;
yaml_helper reader;
std::string schema_validation;
rule_loader::context ctx(cfg.name); rule_loader::context ctx(cfg.name);
try try
{ {
docs = YAML::LoadAll(cfg.content); docs = reader.loadall_from_string(cfg.content, schema, &schema_validation);
} }
catch (YAML::ParserException& e) catch (YAML::ParserException& e)
{ {
@@ -807,7 +810,7 @@ bool rule_loader::reader::read(rule_loader::configuration& cfg, collector& colle
cfg.res->add_error(falco::load_result::LOAD_ERR_YAML_PARSE, "unknown YAML parsing error", ctx); cfg.res->add_error(falco::load_result::LOAD_ERR_YAML_PARSE, "unknown YAML parsing error", ctx);
return false; return false;
} }
cfg.res->set_schema_validation_status(schema_validation);
for (auto doc = docs.begin(); doc != docs.end(); doc++) for (auto doc = docs.begin(); doc != docs.end(); doc++)
{ {
if (doc->IsDefined() && !doc->IsNull()) if (doc->IsDefined() && !doc->IsNull())

View File

@@ -43,7 +43,7 @@ public:
\brief Reads the contents of a ruleset and uses a collector to store \brief Reads the contents of a ruleset and uses a collector to store
thew new definitions thew new definitions
*/ */
virtual bool read(configuration& cfg, collector& loader); virtual bool read(configuration& cfg, collector& loader, const nlohmann::json& schema={});
/*! /*!
\brief Engine version used to be represented as a simple progressive \brief Engine version used to be represented as a simple progressive

View File

@@ -41,10 +41,10 @@ limitations under the License.
#include <valijson/schema_parser.hpp> #include <valijson/schema_parser.hpp>
#include <valijson/validator.hpp> #include <valijson/validator.hpp>
#include "config_falco.h" //#include "config_falco.h"
#include "event_drops.h" //#include "event_drops.h"
#include "falco_outputs.h" //#include "falco_outputs.h"
class yaml_helper; class yaml_helper;
@@ -89,7 +89,37 @@ public:
inline static const std::string configs_key = "config_files"; inline static const std::string configs_key = "config_files";
inline static const std::string validation_ok = "ok"; inline static const std::string validation_ok = "ok";
inline static const std::string validation_failed = "failed"; inline static const std::string validation_failed = "failed";
inline static const std::string validation_none = "schema not provided"; inline static const std::string validation_none = "none";
/**
* Load all the YAML document represented by the input string.
* Since this is used by rule loader, does not process env vars.
*/
std::vector<YAML::Node> loadall_from_string(const std::string& input, const nlohmann::json& schema={}, std::string *validation=nullptr)
{
auto nodes = YAML::LoadAll(input);
if (validation)
{
if(!schema.empty())
{
// Validate each node.
for (const auto& node : nodes)
{
*validation = validate_node(node, schema);
if (*validation != validation_ok)
{
// Return first error
break;
}
}
}
else
{
*validation = validation_none;
}
}
return nodes;
}
/** /**
* Load the YAML document represented by the input string. * Load the YAML document represented by the input string.

View File

@@ -26,9 +26,6 @@ namespace falco {
namespace app { namespace app {
namespace actions { namespace actions {
// Map that holds { rule filename | validation status } for each rule file read.
typedef std::map<std::string, std::string> rule_read_res;
bool check_rules_plugin_requirements(falco::app::state& s, std::string& err); bool check_rules_plugin_requirements(falco::app::state& s, std::string& err);
void print_enabled_event_sources(falco::app::state& s); void print_enabled_event_sources(falco::app::state& s);
void activate_interesting_kernel_tracepoints(falco::app::state& s, std::unique_ptr<sinsp>& inspector); void activate_interesting_kernel_tracepoints(falco::app::state& s, std::unique_ptr<sinsp>& inspector);
@@ -43,14 +40,10 @@ falco::app::run_result open_live_inspector(
const std::string& source); const std::string& source);
template<class InputIterator> template<class InputIterator>
rule_read_res read_files(InputIterator begin, InputIterator end, void read_files(InputIterator begin, InputIterator end,
std::vector<std::string>& rules_contents, std::vector<std::string>& rules_contents,
falco::load_result::rules_contents_t& rc, falco::load_result::rules_contents_t& rc)
const nlohmann::json& schema={})
{ {
rule_read_res res;
yaml_helper reader;
std::string validation;
// Read the contents in a first pass // Read the contents in a first pass
for(auto it = begin; it != end; it++) for(auto it = begin; it != end; it++)
{ {
@@ -65,8 +58,6 @@ rule_read_res read_files(InputIterator begin, InputIterator end,
std::string rules_content((std::istreambuf_iterator<char>(is)), std::string rules_content((std::istreambuf_iterator<char>(is)),
std::istreambuf_iterator<char>()); std::istreambuf_iterator<char>());
reader.load_from_string(rules_content, schema, &validation);
res[filename] = validation;
rules_contents.emplace_back(std::move(rules_content)); rules_contents.emplace_back(std::move(rules_content));
} }
@@ -85,8 +76,6 @@ rule_read_res read_files(InputIterator begin, InputIterator end,
{ {
throw falco_exception("Unexpected mismatch in rules content name/rules content sets?"); throw falco_exception("Unexpected mismatch in rules content name/rules content sets?");
} }
return res;
} }

View File

@@ -54,12 +54,11 @@ falco::app::run_result falco::app::actions::load_rules_files(falco::app::state&
std::vector<std::string> rules_contents; std::vector<std::string> rules_contents;
falco::load_result::rules_contents_t rc; falco::load_result::rules_contents_t rc;
rule_read_res validation_res;
try { try {
validation_res = read_files(s.config->m_loaded_rules_filenames.begin(), read_files(s.config->m_loaded_rules_filenames.begin(),
s.config->m_loaded_rules_filenames.end(), s.config->m_loaded_rules_filenames.end(),
rules_contents, rules_contents,
rc, s.config->m_rule_schema); rc);
} }
catch(falco_exception& e) catch(falco_exception& e)
{ {
@@ -70,12 +69,11 @@ falco::app::run_result falco::app::actions::load_rules_files(falco::app::state&
falco_logger::log(falco_logger::level::INFO, "Loading rules from:\n"); falco_logger::log(falco_logger::level::INFO, "Loading rules from:\n");
for(auto &filename : s.config->m_loaded_rules_filenames) for(auto &filename : s.config->m_loaded_rules_filenames)
{ {
auto validation = validation_res[filename];
auto priority = validation == yaml_helper::validation_ok ? falco_logger::level::INFO : falco_logger::level::WARNING;
falco_logger::log(priority, std::string(" ") + filename + " | schema validation: " + validation + "\n");
std::unique_ptr<falco::load_result> res; std::unique_ptr<falco::load_result> res;
res = s.engine->load_rules(rc.at(filename), filename); res = s.engine->load_rules(rc.at(filename), filename);
auto priority = res->schema_validation() == yaml_helper::validation_ok ? falco_logger::level::INFO : falco_logger::level::WARNING;
falco_logger::log(priority, std::string(" ") + filename + " | schema validation: " + res->schema_validation() + "\n");
if(!res->successful()) if(!res->successful())
{ {

View File

@@ -24,7 +24,7 @@ falco::app::run_result falco::app::actions::print_rule_schema(falco::app::state
{ {
if(s.options.print_rule_schema) if(s.options.print_rule_schema)
{ {
printf("%s", s.config->m_rule_schema.dump(2).c_str()); printf("%s", s.engine->m_rule_schema.dump(2).c_str());
return run_result::exit(); return run_result::exit();
} }
return run_result::ok(); return run_result::ok();

View File

@@ -33,12 +33,11 @@ falco::app::run_result falco::app::actions::validate_rules_files(falco::app::sta
std::vector<std::string> rules_contents; std::vector<std::string> rules_contents;
falco::load_result::rules_contents_t rc; falco::load_result::rules_contents_t rc;
rule_read_res validation_res;
try { try {
validation_res = read_files(s.options.validate_rules_filenames.begin(), read_files(s.options.validate_rules_filenames.begin(),
s.options.validate_rules_filenames.end(), s.options.validate_rules_filenames.end(),
rules_contents, rules_contents,
rc,s.config->m_rule_schema); rc);
} }
catch(falco_exception& e) catch(falco_exception& e)
{ {
@@ -69,24 +68,21 @@ falco::app::run_result falco::app::actions::validate_rules_files(falco::app::sta
// printed when verbose is true. // printed when verbose is true.
std::string summary; std::string summary;
falco_logger::log(falco_logger::level::INFO, "Validating rules file(s):\n");
for(const auto& file : s.options.validate_rules_filenames)
{
auto validation = validation_res[file];
auto priority = validation == yaml_helper::validation_ok ? falco_logger::level::INFO : falco_logger::level::WARNING;
falco_logger::log(priority, std::string(" ") + file + " | schema validation: " + validation + "\n");
}
// The json output encompasses all files so the // The json output encompasses all files so the
// validation result is a single json object. // validation result is a single json object.
std::string err = ""; std::string err = "";
nlohmann::json results = nlohmann::json::array(); nlohmann::json results = nlohmann::json::array();
falco_logger::log(falco_logger::level::INFO, "Validating rules file(s):\n");
for(auto &filename : s.options.validate_rules_filenames) for(auto &filename : s.options.validate_rules_filenames)
{ {
std::unique_ptr<falco::load_result> res; std::unique_ptr<falco::load_result> res;
res = s.engine->load_rules(rc.at(filename), filename); res = s.engine->load_rules(rc.at(filename), filename);
auto priority = res->schema_validation() == yaml_helper::validation_ok ? falco_logger::level::INFO : falco_logger::level::WARNING;
falco_logger::log(priority, std::string(" ") + filename + " | schema validation: " + res->schema_validation() + "\n");
if (!check_rules_plugin_requirements(s, err)) if (!check_rules_plugin_requirements(s, err))
{ {
return run_result::fatal(err); return run_result::fatal(err);

File diff suppressed because one or more lines are too long

View File

@@ -212,7 +212,6 @@ public:
yaml_helper m_config; yaml_helper m_config;
nlohmann::json m_config_schema; nlohmann::json m_config_schema;
nlohmann::json m_rule_schema;
private: private:
void merge_config_files(const std::string& config_name, config_loaded_res &res); void merge_config_files(const std::string& config_name, config_loaded_res &res);