diff --git a/userspace/engine/falco_engine.cpp b/userspace/engine/falco_engine.cpp index e01ad61f..fab6a9be 100644 --- a/userspace/engine/falco_engine.cpp +++ b/userspace/engine/falco_engine.cpp @@ -313,11 +313,13 @@ unique_ptr falco_engine::process_event(std::size_t so } unique_ptr res(new rule_result()); - auto rule = m_rule_loader.rules().at(ev->get_check_id()); + // note: indexes are 0-based, whereas check_ids are not + auto rule_idx = ev->get_check_id() - 1; + auto rule = m_rule_loader.rules().at(rule_idx); if (!rule) { throw falco_exception("populate_rule_result error: unknown rule id " - + to_string(ev->get_check_id())); + + to_string(rule_idx)); } res->evt = ev; res->rule = rule->name; @@ -326,7 +328,7 @@ unique_ptr falco_engine::process_event(std::size_t so res->priority_num = rule->priority; res->tags = rule->tags; res->exception_fields = rule->exception_fields; - m_rule_stats_manager.on_event(m_rule_loader.rules(), ev->get_check_id()); + m_rule_stats_manager.on_event(m_rule_loader.rules(), rule_idx); return res; } catch(std::out_of_range const &exc) @@ -390,7 +392,7 @@ void falco_engine::describe_rule(string *rule) void falco_engine::print_stats() { string out; - m_rule_stats_manager.format_stats(m_rule_loader.rules(), out); + m_rule_stats_manager.format(m_rule_loader.rules(), out); // todo(jasondellaluce): introduce a logging callback in Falco fprintf(stdout, "%s", out.c_str()); } diff --git a/userspace/engine/indexed_vector.h b/userspace/engine/indexed_vector.h index cafb17e9..e36d50e9 100644 --- a/userspace/engine/indexed_vector.h +++ b/userspace/engine/indexed_vector.h @@ -22,7 +22,7 @@ limitations under the License. /*! \brief Simple wrapper of std::vector that allows random access - through both numeric and string indexes with O(1) complexity. + through both numeric and string indexes with O(1) complexity */ template class indexed_vector @@ -31,7 +31,7 @@ public: /*! \brief Returns the number of elements */ - virtual inline uint32_t size() + virtual inline size_t size() { return m_entries.size(); } @@ -64,18 +64,18 @@ public: is returned. \param entry Element to add in the vector \param index String index of the element to be added in the vector - \return The numeric index of the element + \return The numeric index assigned to the element */ - virtual inline uint32_t insert(T& entry, const std::string& index) + virtual inline size_t insert(T& entry, const std::string& index) { - uint32_t id; + size_t id; auto prev = m_index.find(index); if (prev != m_index.end()) { id = prev->second; - m_entries[id - 1] = entry; + m_entries[id] = entry; return id; } - id = m_entries.size() + 1; + id = m_entries.size(); m_entries.push_back(entry); m_index[index] = id; return id; @@ -85,11 +85,11 @@ public: \brief Returns a pointer to the element at the given numeric index, or nullptr if no element exists at the given index. */ - virtual inline T* at(uint32_t id) + virtual inline T* at(size_t id) const { if (id <= m_entries.size()) { - return &*(m_entries.begin() + id - 1);; + return (T* const) &m_entries[id]; } return nullptr; } @@ -98,7 +98,7 @@ public: \brief Returns a pointer to the element at the given string index, or nullptr if no element exists at the given index. */ - virtual inline T* at(const std::string& index) + virtual inline T* at(const std::string& index) const { auto it = m_index.find(index); if (it != m_index.end()) { @@ -129,5 +129,5 @@ public: private: std::vector m_entries; - std::map m_index; + std::map m_index; }; diff --git a/userspace/engine/rule_loader.cpp b/userspace/engine/rule_loader.cpp index 4cce148a..2fed30a2 100644 --- a/userspace/engine/rule_loader.cpp +++ b/userspace/engine/rule_loader.cpp @@ -67,7 +67,7 @@ static bool is_field_defined(falco_engine *engine, string source, string field) return false; } -// todo: this should be in libsinsp +// todo(jasondellaluce): add an helper in libsinsp for this static bool is_operator_defined(std::string op) { static vector ops = {"=", "==", "!=", "<=", ">=", "<", ">", @@ -76,13 +76,14 @@ static bool is_operator_defined(std::string op) return find(ops.begin(), ops.end(), op) != ops.end(); } -// todo: this should be in libsinsp +// todo(jasondellaluce): add an helper in libsinsp for this static bool is_operator_for_list(std::string op) { return op == "in" || op == "intersects" || op == "pmatch"; } -static bool is_format_valid(falco_engine* e, string src, string fmt, string& err) +static bool is_format_valid( + falco_engine* e, string src, string fmt, string& err) { try { @@ -141,7 +142,7 @@ static void yaml_decode_seq(const YAML::Node& item, vector& out) for(const YAML::Node& v : item) { THROW(!v.IsScalar() || !YAML::convert::decode(v, value), - "Can't decode YAML sequence value"); + "Can't decode YAML sequence value: " + YAML::Dump(v)); out.push_back(value); } } @@ -153,13 +154,13 @@ static void yaml_decode_seq(const YAML::Node& item, set& out) for(const YAML::Node& v : item) { THROW(!v.IsScalar() || !YAML::convert::decode(v, value), - "Can't decode YAML sequence value"); + "Can't decode YAML sequence value: " + YAML::Dump(v)); out.insert(value); } } // todo(jasondellaluce): this breaks string escaping in lists -static bool resolve_list(string& cnd, YAML::Node& list) +static bool resolve_list(string& cnd, const YAML::Node& list) { static string blanks = " \t\n\r"; static string delims = blanks + "(),="; @@ -230,14 +231,13 @@ static bool resolve_list(string& cnd, YAML::Node& list) } static void resolve_macros( - indexed_vector>>& macros, + const indexed_vector>>& macros, map& used_macros, shared_ptr& ast, uint32_t index_visibility, string on_unknown_err_prefix) { filter_macro_resolver macro_resolver; - for (auto &ref : macros) { if (ref.first["index"].as() < index_visibility) @@ -246,9 +246,10 @@ static void resolve_macros( } } macro_resolver.run(ast); - THROW(!macro_resolver.get_unknown_macros().empty(), on_unknown_err_prefix - + "Undefined macro '" + *macro_resolver.get_unknown_macros().begin() - + "' used in filter."); + THROW(!macro_resolver.get_unknown_macros().empty(), + on_unknown_err_prefix + "Undefined macro '" + + *macro_resolver.get_unknown_macros().begin() + + "' used in filter."); for (auto &resolved : macro_resolver.get_resolved_macros()) { used_macros[resolved] = true; @@ -257,7 +258,7 @@ static void resolve_macros( // note: there is no visibility order between filter conditions and lists static shared_ptr parse_condition(string cnd, - std::map& used_lists, indexed_vector& lists) + std::map& used_lists, const indexed_vector& lists) { for (auto &l : lists) { @@ -341,19 +342,19 @@ static void validate_rule_exception( else { THROW(!yaml_is_type(ex["fields"]) - || !yaml_is_type(ex["comps"]), + || !yaml_is_type(ex["comps"]), "Rule exception item " + ex["name"].as() + ": fields and comps must both be strings"); } THROW(!is_field_defined( - engine, source, ex["fields"].as()), + engine, source, ex["fields"].as()), "Rule exception item " + ex["name"].as() + ": field name " + ex["fields"].as() + " is not a supported filter field"); THROW(!is_operator_defined(ex["comps"].as()), "Rule exception item " + ex["name"].as() - + ": comparison operator " + ex["comps"].as() - + " is not a supported comparison operator"); + + ": comparison operator " + ex["comps"].as() + + " is not a supported comparison operator"); break; case YAML::NodeType::Sequence: if (!ex["comps"].IsDefined()) @@ -373,20 +374,22 @@ static void validate_rule_exception( for (auto field : ex["fields"]) { THROW(!yaml_is_type(field), - "Rule exception item " + ex["name"].as() + ": fields must strings "); + "Rule exception item " + ex["name"].as() + + ": fields must strings "); THROW(!is_field_defined(engine, source, field.as()), - "Rule exception item " + ex["name"].as() + ": field name " - + field.as() + " is not a supported filter field"); + "Rule exception item " + ex["name"].as() + + ": field name " + field.as() + + " is not a supported filter field"); } for (auto comp : ex["comps"]) { THROW(!yaml_is_type(comp), "Rule exception item " + ex["name"].as() - + ": comps must strings "); + + ": comps must strings "); THROW(!is_operator_defined(comp.as()), "Rule exception item " + ex["name"].as() - + ": comparison operator " + comp.as() - + " is not a supported comparison operator"); + + ": comparison operator " + comp.as() + + " is not a supported comparison operator"); } break; default: @@ -409,10 +412,10 @@ static void build_rule_exception_infos( { THROW(!yaml_is_type(val), "Expected values array for item " - + exname + " to contain a list of strings"); + + exname + " to contain a list of strings"); icond += icond.empty() - ? "(" + ex["fields"].as() + " " - + ex["comps"].as() + " (" + ? ("(" + ex["fields"].as() + " " + + ex["comps"].as() + " (") : ", "; exception_fields.insert(ex["fields"].as()); value = val.as(); @@ -428,7 +431,7 @@ static void build_rule_exception_infos( { THROW(ex["fields"].size() != values.size(), "Exception item " + exname - + ": fields and values lists must have equal length"); + + ": fields and values lists must have equal length"); icond += icond == "(" ? "" : " or "; icond += "("; uint32_t k = 0; @@ -488,7 +491,7 @@ void rule_loader::clear() m_required_plugin_versions.clear(); } -indexed_vector& rule_loader::rules() +const indexed_vector& rule_loader::rules() { return m_rules; } @@ -531,7 +534,7 @@ bool rule_loader::is_plugin_compatible( return true; } -void rule_loader::apply_output_replacements(std::string& out) +void rule_loader::apply_output_substitutions(std::string& out) { if (out.find(s_container_info_fmt) != string::npos) { @@ -611,36 +614,36 @@ bool rule_loader::read(const string &content, falco_engine* engine, } void rule_loader::read_item( - falco_engine* engine, YAML::Node& item, vector& warn) + falco_engine* engine, YAML::Node& item, vector& warnings) { if (item["required_engine_version"].IsDefined()) { - read_required_engine_version(engine, item, warn); + read_required_engine_version(engine, item, warnings); } else if(item["required_plugin_versions"].IsDefined()) { - read_required_plugin_versions(engine, item, warn); + read_required_plugin_versions(engine, item, warnings); } else if(item["macro"].IsDefined()) { - read_macro(engine, item, warn); + read_macro(engine, item, warnings); } else if(item["list"].IsDefined()) { - read_list(engine, item, warn); + read_list(engine, item, warnings); } else if(item["rule"].IsDefined()) { - read_rule(engine, item, warn); + read_rule(engine, item, warnings); } else { - warn.push_back("Unknown top level object"); + warnings.push_back("Unknown top level object"); } } void rule_loader::read_required_engine_version( - falco_engine* engine, YAML::Node& item, vector& warn) + falco_engine* engine, YAML::Node& item, vector& warnings) { uint32_t v = 0; THROW(!YAML::convert::decode(item["required_engine_version"], v), @@ -651,7 +654,7 @@ void rule_loader::read_required_engine_version( } void rule_loader::read_required_plugin_versions( - falco_engine* engine, YAML::Node& item, vector& warn) + falco_engine* engine, YAML::Node& item, vector& warnings) { string name, ver; THROW(!item["required_plugin_versions"].IsSequence(), @@ -671,7 +674,7 @@ void rule_loader::read_required_plugin_versions( } void rule_loader::read_list( - falco_engine* engine, YAML::Node& item, vector& warn) + falco_engine* engine, YAML::Node& item, vector& warnings) { string name; THROW(!YAML::convert::decode(item["list"], name) || name.empty(), @@ -696,7 +699,7 @@ void rule_loader::read_list( } void rule_loader::read_macro( - falco_engine* engine, YAML::Node& item, vector& warn) + falco_engine* engine, YAML::Node& item, vector& warnings) { string name, cnd; THROW(!YAML::convert::decode(item["macro"], name) || name.empty(), @@ -714,7 +717,7 @@ void rule_loader::read_macro( } if (!engine->is_source_valid(item["source"].as())) { - warn.push_back("Macro " + name + warnings.push_back("Macro " + name + ": warning (unknown-source): unknown source " + item["source"].as() + ", skipping"); return; @@ -733,7 +736,7 @@ void rule_loader::read_macro( } void rule_loader::read_rule( - falco_engine* engine, YAML::Node& item, vector& warn) + falco_engine* engine, YAML::Node& item, vector& warnings) { string name; falco_common::priority_type priority; @@ -762,7 +765,7 @@ void rule_loader::read_rule( } if (!engine->is_source_valid(item["source"].as())) { - warn.push_back("Rule " + name + warnings.push_back("Rule " + name + ": warning (unknown-source): unknown source " + item["source"].as() + ", skipping"); return; @@ -810,7 +813,8 @@ void rule_loader::read_rule( } THROW(!yaml_is_type(item["priority"]) - || !falco_common::parse_priority(item["priority"].as(), priority), + || !falco_common::parse_priority( + item["priority"].as(), priority), "Invalid priority"); item["priority_num"] = (uint32_t) priority; @@ -891,10 +895,7 @@ bool rule_loader::expand(falco_engine* engine, vector& warnings, vector& errors) { indexed_vector lists; - indexed_vector // todo: maybe remove pair - >> macros; + indexed_vector macros; map used_lists; map used_macros; @@ -975,10 +976,10 @@ void rule_loader::expand_list_infos( // note: there is a visibility ordering between macros void rule_loader::expand_macro_infos( - indexed_vector& lists, + const indexed_vector& lists, map& used_lists, map& used_macros, - indexed_vector>>& out) + indexed_vector& out) { for (auto m : m_macro_infos) { @@ -1000,7 +1001,7 @@ void rule_loader::expand_macro_infos( resolve_macros(out, used_macros, m.second, m.first["index_visibility"].as(), "Compilation error when compiling \"" - + m.first["condition"].as() + "\": "); + + m.first["condition"].as() + "\": "); } catch (exception& e) { @@ -1012,11 +1013,11 @@ void rule_loader::expand_macro_infos( void rule_loader::expand_rule_infos( falco_engine* engine, - indexed_vector& lists, - indexed_vector>>& macros, + const indexed_vector& lists, + const indexed_vector& macros, map& used_lists, map& used_macros, - vector& warn) + vector& warnings) { string err; for (auto r : m_rule_infos) @@ -1038,13 +1039,13 @@ void rule_loader::expand_rule_infos( } auto ast = parse_condition(condition, used_lists, lists); - + resolve_macros(macros, used_macros, ast, MAX_VISIBILITY, ""); string output = r["output"].as(); if (r["source"].as() == falco_common::syscall_source) { - apply_output_replacements(output); + apply_output_substitutions(output); } THROW(!is_format_valid(engine, r["source"].as(), output, err), @@ -1059,14 +1060,15 @@ void rule_loader::expand_rule_infos( rule.exception_fields = exception_fields; yaml_decode_seq(r["tags"], rule.tags); - auto rule_id = m_rules.insert(rule, rule.name); + // note: indexes are 0-based, but 0 is not an acceptable rule_id + auto rule_id = m_rules.insert(rule, rule.name) + 1; auto filter = compile_condition(engine, rule_id, ast, rule.source, err); if (!filter) { if (r["skip-if-unknown-filter"].as() && err.find("nonexistent field") != string::npos) { - warn.push_back( + warnings.push_back( "Rule " + rule.name + ": warning (unknown-field):"); continue; } @@ -1082,7 +1084,7 @@ void rule_loader::expand_rule_infos( auto evttypes = filter->evttypes(); if (evttypes.size() == 0 || evttypes.size() > 100) { - warn.push_back( + warnings.push_back( "Rule " + rule.name + ": warning (no-evttype):\n" + + " matches too many evt.type values.\n" + " This has a significant performance penalty."); diff --git a/userspace/engine/rule_loader.h b/userspace/engine/rule_loader.h index a396f4d1..3c2ec54a 100644 --- a/userspace/engine/rule_loader.h +++ b/userspace/engine/rule_loader.h @@ -42,7 +42,7 @@ public: /*! \brief Returns the rules loaded after the last invocation of load() */ - virtual indexed_vector& rules(); + virtual const indexed_vector& rules(); /*! \brief Configures the loader. The changes will influence the next @@ -57,47 +57,59 @@ public: output does not contain "%container.info", then this flag has no effect and the extra string is appended at the end of the rule output anyways. */ - virtual void configure(falco_common::priority_type min_priority, - bool replace_container_info, const std::string& extra); + virtual void configure( + falco_common::priority_type min_priority, + bool replace_container_info, + const std::string& extra); /*! \brief Returns true if the given plugin name and version are compatible with the loaded rulesets. If false is returned, required_version is filled with the required plugin version that didn't match. */ - virtual bool is_plugin_compatible(const std::string& name, - const std::string& version, std::string& required_version); + virtual bool is_plugin_compatible( + const std::string& name, + const std::string& version, + std::string& required_version); /*! \brief Parses the content of a ruleset. This should be called multiple - times to load different ruleset files. The internal state (e.g. loaded + times to load different rulesets. The internal state (e.g. loaded rules, plugin version requirements, etc...) gets updated at each invocation of the load() method. - \param rules_content The contents of the ruleset file + \param content The contents of the ruleset \param engine The instance of falco_engine used to add rule filters \param warnings Filled-out with warnings \param warnings Filled-out with errors \return true if the ruleset content is loaded successfully */ - virtual bool load(const std::string& rules_content, falco_engine* engine, - std::vector& warnings, std::vector& errors); + virtual bool load( + const std::string& content, + falco_engine* engine, + std::vector& warnings, + std::vector& errors); private: + typedef pair< + YAML::Node, + shared_ptr + > macro_node; + bool read( const std::string& content, falco_engine* engine, std::vector& warnings, std::vector& errors); void read_item( - falco_engine* engine, YAML::Node& item, vector& warn); + falco_engine* engine, YAML::Node& item, vector& warnings); void read_required_engine_version( - falco_engine* engine, YAML::Node& item, vector& warn); + falco_engine* engine, YAML::Node& item, vector& warnings); void read_required_plugin_versions( - falco_engine* engine, YAML::Node& item, vector& warn); + falco_engine* engine, YAML::Node& item, vector& warnings); void read_macro( - falco_engine* engine, YAML::Node& item, vector& warn); + falco_engine* engine, YAML::Node& item, vector& warnings); void read_list( - falco_engine* engine, YAML::Node& item, vector& warn); + falco_engine* engine, YAML::Node& item, vector& warnings); void read_rule( - falco_engine* engine, YAML::Node& item, vector& warn); + falco_engine* engine, YAML::Node& item, vector& warnings); void read_rule_exceptions( falco_engine* engine, YAML::Node& item, bool append); bool expand(falco_engine* engine, @@ -105,18 +117,18 @@ private: void expand_list_infos( std::map& used, indexed_vector& out); void expand_macro_infos( - indexed_vector& lists, + const indexed_vector& lists, std::map& used_lists, std::map& used_macros, - indexed_vector>>& out); + indexed_vector& out); void expand_rule_infos( falco_engine* engine, - indexed_vector& lists, - indexed_vector>>& macros, + const indexed_vector& lists, + const indexed_vector& macros, std::map& used_lists, std::map& used_macros, vector& warnings); - void apply_output_replacements(std::string& output); + void apply_output_substitutions(std::string& output); uint32_t m_cur_index; std::string m_extra; diff --git a/userspace/engine/stats_manager.cpp b/userspace/engine/stats_manager.cpp index 441ace4a..cbd74bb1 100644 --- a/userspace/engine/stats_manager.cpp +++ b/userspace/engine/stats_manager.cpp @@ -26,7 +26,9 @@ void stats_manager::clear() m_by_priority.clear(); } -void stats_manager::format_stats(indexed_vector& rules, string& out) +void stats_manager::format( + const indexed_vector& rules, + string& out) { string fmt; string name; @@ -54,7 +56,7 @@ void stats_manager::format_stats(indexed_vector& rules, string& out) } void stats_manager::on_event( - indexed_vector& rules, + const indexed_vector& rules, uint32_t rule_id) { auto *rule = rules.at(rule_id); diff --git a/userspace/engine/stats_manager.h b/userspace/engine/stats_manager.h index 9b533851..8ad40811 100644 --- a/userspace/engine/stats_manager.h +++ b/userspace/engine/stats_manager.h @@ -36,13 +36,15 @@ public: \brief Callback for when a rule with a given index matches an event */ virtual void on_event( - indexed_vector& rules, uint32_t index); + const indexed_vector& rules, + uint32_t index); /*! \brief Formats the internal statistics into the out sring */ - virtual void format_stats( - indexed_vector& rules, std::string& out); + virtual void format( + const indexed_vector& rules, + std::string& out); private: uint64_t m_total;