mirror of
https://github.com/falcosecurity/falco.git
synced 2025-09-06 01:00:36 +00:00
Rule loader changes to support result objects
Changes to the rule loader to support result objects: - Instead of throwing falco_exception on internal error, throw a rule_load_exception instead, which contains distinct error/message/context information. - A context object contains a chain of location structs chaining from the document root to the object where the error occurred. Each location has a file position (as a YAML::Mark), an item type (e.g. "rule", "list", "exception"), and an item name (e.g. "Write Below Etc"). This will allow showing the exact location of an error (e.g. list item/exception field) while also remembering the item that contained it. - All the _info structs now contain a context so errors that occur after yaml parsing can still point to the original location in the yaml file. - rule_loader::result is an implementation of the abstract class defined in falco_load_result. The implementation keeps track of a list of errors/warnigns that used to be in the configuration object, - Clean up compile_ methods to just throw rule_load_exceptions or return nothing, and ensure that all rule_load_exceptions are caught in compile(). When caught, errors are added to the result object. Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
This commit is contained in:
@@ -21,9 +21,13 @@ limitations under the License.
|
|||||||
#include "filter_evttype_resolver.h"
|
#include "filter_evttype_resolver.h"
|
||||||
#include "filter_warning_resolver.h"
|
#include "filter_warning_resolver.h"
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#define MAX_VISIBILITY ((uint32_t) -1)
|
#define MAX_VISIBILITY ((uint32_t) -1)
|
||||||
#define THROW(cond, err) { if (cond) { throw falco_exception(err); } }
|
|
||||||
|
#define THROW(cond, err, ctx) { if ((cond)) { throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_VALIDATE, (err), (ctx)); } }
|
||||||
|
|
||||||
|
using namespace falco;
|
||||||
|
|
||||||
static string s_container_info_fmt = "%container.info";
|
static string s_container_info_fmt = "%container.info";
|
||||||
static string s_default_extra_fmt = "%container.name (id=%container.id)";
|
static string s_default_extra_fmt = "%container.name (id=%container.id)";
|
||||||
@@ -31,6 +35,399 @@ static string s_default_extra_fmt = "%container.name (id=%container.id)";
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace libsinsp::filter;
|
using namespace libsinsp::filter;
|
||||||
|
|
||||||
|
rule_loader::context::context(const std::string& name)
|
||||||
|
: name(name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::context::context(const YAML::Node &item,
|
||||||
|
const std::string item_type,
|
||||||
|
const std::string item_name,
|
||||||
|
const context& parent)
|
||||||
|
: name(parent.name)
|
||||||
|
{
|
||||||
|
// Copy parent locations
|
||||||
|
m_locs = parent.m_locs;
|
||||||
|
|
||||||
|
// Add current item to back
|
||||||
|
location loc = {item.Mark(), item_type, item_name};
|
||||||
|
m_locs.push_back(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rule_loader::context::as_string()
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
// If no locations (can happen for initial file-level
|
||||||
|
// context), just note it was somewhere in the file
|
||||||
|
if(m_locs.empty())
|
||||||
|
{
|
||||||
|
os << "In " << name << ":" << std::endl;
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
for(auto& loc : m_locs)
|
||||||
|
{
|
||||||
|
os << (first ? "In " : " ");
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
os << loc.item_type;
|
||||||
|
if(!loc.item_name.empty())
|
||||||
|
{
|
||||||
|
os << " '" << loc.item_name << "'";
|
||||||
|
}
|
||||||
|
os << ": ";
|
||||||
|
|
||||||
|
os << "("
|
||||||
|
<< name << ":"
|
||||||
|
<< loc.mark.line << ":"
|
||||||
|
<< loc.mark.column
|
||||||
|
<< ")" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json rule_loader::context::as_json()
|
||||||
|
{
|
||||||
|
nlohmann::json ret;
|
||||||
|
|
||||||
|
ret["locations"] = nlohmann::json::array();
|
||||||
|
|
||||||
|
if(m_locs.empty())
|
||||||
|
{
|
||||||
|
nlohmann::json jloc, jpos;
|
||||||
|
|
||||||
|
jloc["item_type"] = "file";
|
||||||
|
jloc["item_name"] = "";
|
||||||
|
|
||||||
|
jpos["filename"] = name;
|
||||||
|
jpos["line"] = 0;
|
||||||
|
jpos["column"] = 0;
|
||||||
|
jpos["offset"] = 0;
|
||||||
|
|
||||||
|
jloc["position"] = jpos;
|
||||||
|
|
||||||
|
ret["locations"].push_back(jloc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(auto& loc : m_locs)
|
||||||
|
{
|
||||||
|
nlohmann::json jloc, jpos;
|
||||||
|
|
||||||
|
jloc["item_type"] = loc.item_type;
|
||||||
|
jloc["item_name"] = loc.item_name;
|
||||||
|
|
||||||
|
jpos["filename"] = name;
|
||||||
|
jpos["line"] = loc.mark.line;
|
||||||
|
jpos["column"] = loc.mark.column;
|
||||||
|
jpos["offset"] = loc.mark.pos;
|
||||||
|
|
||||||
|
jloc["position"] = jpos;
|
||||||
|
|
||||||
|
ret["locations"].push_back(jloc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rule_loader::context::snippet(const std::string& content) const
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
|
||||||
|
if(m_locs.empty() || content.size() == 0)
|
||||||
|
{
|
||||||
|
return "<No context available>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::context::location loc = m_locs.back();
|
||||||
|
|
||||||
|
size_t from = loc.mark.pos;
|
||||||
|
|
||||||
|
// In some cases like this, where the content ends with a
|
||||||
|
// dangling property value:
|
||||||
|
// tags:
|
||||||
|
// The YAML::Mark position can be past the end of the file.
|
||||||
|
for(; from > 0 && from >= content.size(); from--);
|
||||||
|
|
||||||
|
// Add the line that includes the mark and a marker
|
||||||
|
// at the column number.
|
||||||
|
size_t to = from;
|
||||||
|
|
||||||
|
for(; from > 0 && content.at(from) != '\n'; from--);
|
||||||
|
for(; to < content.size()-1 && content.at(to) != '\n'; to++);
|
||||||
|
|
||||||
|
// Don't include the newlines
|
||||||
|
if(content.at(from) == '\n')
|
||||||
|
{
|
||||||
|
from++;
|
||||||
|
}
|
||||||
|
if(content.at(to) == '\n')
|
||||||
|
{
|
||||||
|
to--;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = content.substr(from, to-from+1) + "\n";
|
||||||
|
|
||||||
|
// Add a blank line with a marker at the column number
|
||||||
|
ret += std::string(loc.mark.column, ' ') + '^' + "\n";
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::result::result(const std::string &name)
|
||||||
|
: name(name),
|
||||||
|
success(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rule_loader::result::successful()
|
||||||
|
{
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rule_loader::result::has_warnings()
|
||||||
|
{
|
||||||
|
return (warnings.size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rule_loader::result::add_error(load_result::error_code ec, const std::string& msg, const context& ctx, const std::string& rules_content)
|
||||||
|
{
|
||||||
|
error err = {ec, msg, ctx, ctx.snippet(rules_content)};
|
||||||
|
success = false;
|
||||||
|
|
||||||
|
errors.push_back(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rule_loader::result::add_warning(load_result::warning_code wc, const std::string& msg, const context& ctx, const std::string& rules_content)
|
||||||
|
{
|
||||||
|
warning warn = {wc, msg, ctx, ctx.snippet(rules_content)};
|
||||||
|
|
||||||
|
warnings.push_back(warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& rule_loader::result::as_string(bool verbose)
|
||||||
|
{
|
||||||
|
if(verbose)
|
||||||
|
{
|
||||||
|
return as_verbose_string();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return as_summary_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& rule_loader::result::as_summary_string()
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
if(!res_summary_string.empty())
|
||||||
|
{
|
||||||
|
return res_summary_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!name.empty())
|
||||||
|
{
|
||||||
|
os << name << ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
os << (success ? "Ok" : "Invalid");
|
||||||
|
|
||||||
|
if(!errors.empty())
|
||||||
|
{
|
||||||
|
os << std::endl;
|
||||||
|
|
||||||
|
os << " " << errors.size() << " errors: [";
|
||||||
|
bool first = true;
|
||||||
|
for(auto &err : errors)
|
||||||
|
{
|
||||||
|
if(!first)
|
||||||
|
{
|
||||||
|
os << " ";
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
os << load_result::error_code_str(err.ec)
|
||||||
|
<< " (" << load_result::error_str(err.ec) << ")";
|
||||||
|
}
|
||||||
|
os << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!warnings.empty())
|
||||||
|
{
|
||||||
|
os << std::endl;
|
||||||
|
|
||||||
|
os << " " << warnings.size() << " warnings: [";
|
||||||
|
bool first = true;
|
||||||
|
for(auto &warn : warnings)
|
||||||
|
{
|
||||||
|
if(!first)
|
||||||
|
{
|
||||||
|
os << " ";
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
os << load_result::warning_code_str(warn.wc)
|
||||||
|
<< " (" << load_result::warning_str(warn.wc) << ")";
|
||||||
|
}
|
||||||
|
os << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
res_summary_string = os.str();
|
||||||
|
return res_summary_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& rule_loader::result::as_verbose_string()
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
if(!res_verbose_string.empty())
|
||||||
|
{
|
||||||
|
return res_verbose_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!name.empty())
|
||||||
|
{
|
||||||
|
os << name << ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
os << (success ? "Ok" : "Invalid");
|
||||||
|
|
||||||
|
if (!errors.empty())
|
||||||
|
{
|
||||||
|
os << std::endl;
|
||||||
|
|
||||||
|
os << errors.size()
|
||||||
|
<< " Errors:" << std::endl;
|
||||||
|
|
||||||
|
for(auto &err : errors)
|
||||||
|
{
|
||||||
|
os << err.ctx.as_string();
|
||||||
|
|
||||||
|
os << "------" << std::endl;
|
||||||
|
os << err.snippet;
|
||||||
|
os << "------" << std::endl;
|
||||||
|
|
||||||
|
os << load_result::error_code_str(err.ec)
|
||||||
|
<< " (" << load_result::error_str(err.ec) << "): "
|
||||||
|
<< err.msg
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!warnings.empty())
|
||||||
|
{
|
||||||
|
os << std::endl;
|
||||||
|
|
||||||
|
os << warnings.size()
|
||||||
|
<< " Warnings:" << std::endl;
|
||||||
|
|
||||||
|
for(auto &warn : warnings)
|
||||||
|
{
|
||||||
|
os << warn.ctx.as_string();
|
||||||
|
|
||||||
|
os << "------" << std::endl;
|
||||||
|
os << warn.snippet;
|
||||||
|
os << "------" << std::endl;
|
||||||
|
|
||||||
|
os << load_result::warning_code_str(warn.wc)
|
||||||
|
<< " (" << load_result::warning_str(warn.wc) << "): "
|
||||||
|
<< warn.msg;
|
||||||
|
os << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res_verbose_string = os.str();
|
||||||
|
return res_verbose_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nlohmann::json& rule_loader::result::as_json()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
|
||||||
|
if(!res_json.empty())
|
||||||
|
{
|
||||||
|
return res_json;
|
||||||
|
}
|
||||||
|
|
||||||
|
j["name"] = name;
|
||||||
|
j["successful"] = success;
|
||||||
|
|
||||||
|
j["errors"] = nlohmann::json::array();
|
||||||
|
|
||||||
|
for(auto &err : errors)
|
||||||
|
{
|
||||||
|
nlohmann::json jerr;
|
||||||
|
|
||||||
|
jerr["context"] = err.ctx.as_json();
|
||||||
|
jerr["context"]["snippet"] = err.snippet;
|
||||||
|
|
||||||
|
jerr["code"] = load_result::error_code_str(err.ec);
|
||||||
|
jerr["codedesc"] = load_result::error_desc(err.ec);
|
||||||
|
jerr["message"] = err.msg;
|
||||||
|
|
||||||
|
j["errors"].push_back(jerr);
|
||||||
|
}
|
||||||
|
|
||||||
|
j["warnings"] = nlohmann::json::array();
|
||||||
|
|
||||||
|
for(auto &warn : warnings)
|
||||||
|
{
|
||||||
|
nlohmann::json jwarn;
|
||||||
|
|
||||||
|
jwarn["context"] = warn.ctx.as_json();
|
||||||
|
jwarn["context"]["snippet"] = warn.snippet;
|
||||||
|
|
||||||
|
jwarn["code"] = load_result::warning_code_str(warn.wc);
|
||||||
|
jwarn["codedesc"] = load_result::warning_desc(warn.wc);
|
||||||
|
jwarn["message"] = warn.msg;
|
||||||
|
|
||||||
|
j["warnings"].push_back(jwarn);
|
||||||
|
}
|
||||||
|
|
||||||
|
res_json = j;
|
||||||
|
return res_json;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::engine_version_info::engine_version_info(context &ctx)
|
||||||
|
: ctx(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::plugin_version_info::plugin_version_info()
|
||||||
|
: ctx("no-filename-given")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::plugin_version_info::plugin_version_info(context &ctx)
|
||||||
|
: ctx(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::list_info::list_info(context &ctx)
|
||||||
|
: ctx(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::macro_info::macro_info(context &ctx)
|
||||||
|
: ctx(ctx), cond_ctx(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::rule_exception_info::rule_exception_info(context &ctx)
|
||||||
|
: ctx(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::rule_info::rule_info(context &ctx)
|
||||||
|
: ctx(ctx), cond_ctx(ctx), output_ctx(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
// todo(jasondellaluce): this breaks string escaping in lists and exceptions
|
// todo(jasondellaluce): this breaks string escaping in lists and exceptions
|
||||||
static void quote_item(string& e)
|
static void quote_item(string& e)
|
||||||
{
|
{
|
||||||
@@ -97,7 +494,6 @@ template <typename T>
|
|||||||
static inline void append_info(T* prev, T& info, uint32_t id)
|
static inline void append_info(T* prev, T& info, uint32_t id)
|
||||||
{
|
{
|
||||||
prev->visibility = id;
|
prev->visibility = id;
|
||||||
prev->ctx.append(info.ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void validate_exception_info(
|
static void validate_exception_info(
|
||||||
@@ -115,19 +511,19 @@ static void validate_exception_info(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
THROW(ex.fields.items.size() != ex.comps.items.size(),
|
THROW(ex.fields.items.size() != ex.comps.items.size(),
|
||||||
"Rule exception item " + ex.name
|
"Fields and comps lists must have equal length",
|
||||||
+ ": fields and comps lists must have equal length");
|
ex.ctx);
|
||||||
for (auto &v : ex.comps.items)
|
for (auto &v : ex.comps.items)
|
||||||
{
|
{
|
||||||
THROW(!is_operator_defined(v.item),
|
THROW(!is_operator_defined(v.item),
|
||||||
"Rule exception item " + ex.name + ": comparison operator "
|
std::string("'") + v.item + "' is not a supported comparison operator",
|
||||||
+ v.item + " is not a supported comparison operator");
|
ex.ctx);
|
||||||
}
|
}
|
||||||
for (auto &v : ex.fields.items)
|
for (auto &v : ex.fields.items)
|
||||||
{
|
{
|
||||||
THROW(!source.is_field_defined(v.item),
|
THROW(!source.is_field_defined(v.item),
|
||||||
"Rule exception item " + ex.name + ": field name "
|
std::string("'") + v.item + "' is not a supported filter field",
|
||||||
+ v.item + " is not a supported filter field");
|
ex.ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -137,14 +533,15 @@ static void validate_exception_info(
|
|||||||
ex.comps.is_list = false;
|
ex.comps.is_list = false;
|
||||||
ex.comps.item = "in";
|
ex.comps.item = "in";
|
||||||
}
|
}
|
||||||
THROW(ex.comps.is_list, "Rule exception item "
|
THROW(ex.comps.is_list,
|
||||||
+ ex.name + ": fields and comps must both be strings");
|
"Fields and comps must both be strings",
|
||||||
THROW(!is_operator_defined(ex.comps.item),
|
ex.ctx);
|
||||||
"Rule exception item " + ex.name + ": comparison operator "
|
THROW((ex.comps.item != "in" && ex.comps.item != "pmatch" && ex.comps.item != "intersects"),
|
||||||
+ ex.comps.item + " is not a supported comparison operator");
|
"When fields is a single value, comps must be one of (in, pmatch, intersects)",
|
||||||
|
ex.ctx);
|
||||||
THROW(!source.is_field_defined(ex.fields.item),
|
THROW(!source.is_field_defined(ex.fields.item),
|
||||||
"Rule exception item " + ex.name + ": field name "
|
std::string("'") + ex.fields.item + "' is not a supported filter field",
|
||||||
+ ex.fields.item + " is not a supported filter field");
|
ex.ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,8 +558,9 @@ static void build_rule_exception_infos(
|
|||||||
{
|
{
|
||||||
for (auto &val : ex.values)
|
for (auto &val : ex.values)
|
||||||
{
|
{
|
||||||
THROW(val.is_list, "Expected values array for item "
|
THROW(val.is_list,
|
||||||
+ ex.name + " to contain a list of strings");
|
"Expected values array to contain a list of strings",
|
||||||
|
ex.ctx)
|
||||||
icond += icond.empty()
|
icond += icond.empty()
|
||||||
? ("(" + ex.fields.item + " "
|
? ("(" + ex.fields.item + " "
|
||||||
+ ex.comps.item + " (")
|
+ ex.comps.item + " (")
|
||||||
@@ -180,8 +578,8 @@ static void build_rule_exception_infos(
|
|||||||
for (auto &values : ex.values)
|
for (auto &values : ex.values)
|
||||||
{
|
{
|
||||||
THROW(ex.fields.items.size() != values.items.size(),
|
THROW(ex.fields.items.size() != values.items.size(),
|
||||||
"Exception item " + ex.name
|
"Fields and values lists must have equal length",
|
||||||
+ ": fields and values lists must have equal length");
|
ex.ctx);
|
||||||
icond += icond == "(" ? "" : " or ";
|
icond += icond == "(" ? "" : " or ";
|
||||||
icond += "(";
|
icond += "(";
|
||||||
uint32_t k = 0;
|
uint32_t k = 0;
|
||||||
@@ -305,7 +703,7 @@ static void resolve_macros(
|
|||||||
indexed_vector<rule_loader::macro_info>& macros,
|
indexed_vector<rule_loader::macro_info>& macros,
|
||||||
shared_ptr<ast::expr>& ast,
|
shared_ptr<ast::expr>& ast,
|
||||||
uint32_t visibility,
|
uint32_t visibility,
|
||||||
const string& on_unknown_err_prefix)
|
const rule_loader::context &ctx)
|
||||||
{
|
{
|
||||||
filter_macro_resolver macro_resolver;
|
filter_macro_resolver macro_resolver;
|
||||||
for (auto &m : macros)
|
for (auto &m : macros)
|
||||||
@@ -316,10 +714,14 @@ static void resolve_macros(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
macro_resolver.run(ast);
|
macro_resolver.run(ast);
|
||||||
|
|
||||||
|
// Note: only complaining about the first unknown macro
|
||||||
THROW(!macro_resolver.get_unknown_macros().empty(),
|
THROW(!macro_resolver.get_unknown_macros().empty(),
|
||||||
on_unknown_err_prefix + "Undefined macro '"
|
std::string("Undefined macro '")
|
||||||
+ *macro_resolver.get_unknown_macros().begin()
|
+ *macro_resolver.get_unknown_macros().begin()
|
||||||
+ "' used in filter.");
|
+ "' used in filter.",
|
||||||
|
ctx);
|
||||||
|
|
||||||
for (auto &m : macro_resolver.get_resolved_macros())
|
for (auto &m : macro_resolver.get_resolved_macros())
|
||||||
{
|
{
|
||||||
macros.at(m)->used = true;
|
macros.at(m)->used = true;
|
||||||
@@ -329,7 +731,8 @@ static void resolve_macros(
|
|||||||
// note: there is no visibility order between filter conditions and lists
|
// note: there is no visibility order between filter conditions and lists
|
||||||
static shared_ptr<ast::expr> parse_condition(
|
static shared_ptr<ast::expr> parse_condition(
|
||||||
string condition,
|
string condition,
|
||||||
indexed_vector<rule_loader::list_info>& lists)
|
indexed_vector<rule_loader::list_info>& lists,
|
||||||
|
const rule_loader::context &ctx)
|
||||||
{
|
{
|
||||||
for (auto &l : lists)
|
for (auto &l : lists)
|
||||||
{
|
{
|
||||||
@@ -347,8 +750,10 @@ static shared_ptr<ast::expr> parse_condition(
|
|||||||
}
|
}
|
||||||
catch (const sinsp_exception& e)
|
catch (const sinsp_exception& e)
|
||||||
{
|
{
|
||||||
throw falco_exception("Compilation error when compiling \""
|
throw rule_loader::rule_load_exception(
|
||||||
+ condition + "\": " + to_string(p.get_pos().col) + ": " + e.what());
|
load_result::LOAD_ERR_COMPILE_CONDITION,
|
||||||
|
e.what(),
|
||||||
|
ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,14 +791,16 @@ void rule_loader::define(configuration& cfg, engine_version_info& info)
|
|||||||
{
|
{
|
||||||
auto v = falco_engine::engine_version();
|
auto v = falco_engine::engine_version();
|
||||||
THROW(v < info.version, "Rules require engine version "
|
THROW(v < info.version, "Rules require engine version "
|
||||||
+ to_string(info.version) + ", but engine version is " + to_string(v));
|
+ to_string(info.version) + ", but engine version is " + to_string(v),
|
||||||
|
info.ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rule_loader::define(configuration& cfg, plugin_version_info& info)
|
void rule_loader::define(configuration& cfg, plugin_version_info& info)
|
||||||
{
|
{
|
||||||
sinsp_version plugin_version(info.version);
|
sinsp_version plugin_version(info.version);
|
||||||
THROW(!plugin_version.m_valid, "Invalid required version '" + info.version
|
THROW(!plugin_version.m_valid, "Invalid required version '" + info.version
|
||||||
+ "' for plugin '" + info.name + "'");
|
+ "' for plugin '" + info.name + "'",
|
||||||
|
info.ctx);
|
||||||
m_required_plugin_versions[info.name].insert(info.version);
|
m_required_plugin_versions[info.name].insert(info.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,8 +812,9 @@ void rule_loader::define(configuration& cfg, list_info& info)
|
|||||||
void rule_loader::append(configuration& cfg, list_info& info)
|
void rule_loader::append(configuration& cfg, list_info& info)
|
||||||
{
|
{
|
||||||
auto prev = m_list_infos.at(info.name);
|
auto prev = m_list_infos.at(info.name);
|
||||||
THROW(!prev, "List " + info.name +
|
THROW(!prev,
|
||||||
" has 'append' key but no list by that name already exists");
|
"List has 'append' key but no list by that name already exists",
|
||||||
|
info.ctx);
|
||||||
prev->items.insert(prev->items.end(), info.items.begin(), info.items.end());
|
prev->items.insert(prev->items.end(), info.items.begin(), info.items.end());
|
||||||
append_info(prev, info, m_cur_index++);
|
append_info(prev, info, m_cur_index++);
|
||||||
}
|
}
|
||||||
@@ -419,8 +827,9 @@ void rule_loader::define(configuration& cfg, macro_info& info)
|
|||||||
void rule_loader::append(configuration& cfg, macro_info& info)
|
void rule_loader::append(configuration& cfg, macro_info& info)
|
||||||
{
|
{
|
||||||
auto prev = m_macro_infos.at(info.name);
|
auto prev = m_macro_infos.at(info.name);
|
||||||
THROW(!prev, "Macro " + info.name
|
THROW(!prev,
|
||||||
+ " has 'append' key but no macro by that name already exists");
|
"Macro has 'append' key but no macro by that name already exists",
|
||||||
|
info.ctx);
|
||||||
prev->cond += " ";
|
prev->cond += " ";
|
||||||
prev->cond += info.cond;
|
prev->cond += info.cond;
|
||||||
append_info(prev, info, m_cur_index++);
|
append_info(prev, info, m_cur_index++);
|
||||||
@@ -431,20 +840,23 @@ void rule_loader::define(configuration& cfg, rule_info& info)
|
|||||||
auto source = cfg.sources.at(info.source);
|
auto source = cfg.sources.at(info.source);
|
||||||
if (!source)
|
if (!source)
|
||||||
{
|
{
|
||||||
cfg.warnings.push_back("Rule " + info.name
|
cfg.res->add_warning(load_result::LOAD_UNKNOWN_SOURCE,
|
||||||
+ ": warning (unknown-source): unknown source "
|
"Unknown source " + info.source + ", skipping",
|
||||||
+ info.source + ", skipping");
|
info.ctx,
|
||||||
|
cfg.content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto prev = m_rule_infos.at(info.name);
|
auto prev = m_rule_infos.at(info.name);
|
||||||
THROW(prev && prev->source != info.source,
|
THROW(prev && prev->source != info.source,
|
||||||
"Rule " + info.name + " has been re-defined with a different source");
|
"Rule has been re-defined with a different source",
|
||||||
|
info.ctx);
|
||||||
|
|
||||||
for (auto &ex : info.exceptions)
|
for (auto &ex : info.exceptions)
|
||||||
{
|
{
|
||||||
THROW(!ex.fields.is_valid(), "Rule exception item "
|
THROW(!ex.fields.is_valid(),
|
||||||
+ ex.name + ": must have fields property with a list of fields");
|
"Rule exception item must have fields property with a list of fields",
|
||||||
|
ex.ctx);
|
||||||
validate_exception_info(*source, ex);
|
validate_exception_info(*source, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,15 +866,19 @@ void rule_loader::define(configuration& cfg, rule_info& info)
|
|||||||
void rule_loader::append(configuration& cfg, rule_info& info)
|
void rule_loader::append(configuration& cfg, rule_info& info)
|
||||||
{
|
{
|
||||||
auto prev = m_rule_infos.at(info.name);
|
auto prev = m_rule_infos.at(info.name);
|
||||||
THROW(!prev, "Rule " + info.name
|
|
||||||
+ " has 'append' key but no rule by that name already exists");
|
THROW(!prev,
|
||||||
|
"Rule has 'append' key but no rule by that name already exists",
|
||||||
|
info.ctx);
|
||||||
THROW(info.cond.empty() && info.exceptions.empty(),
|
THROW(info.cond.empty() && info.exceptions.empty(),
|
||||||
"Appended rule must have exceptions or condition property");
|
"Appended rule must have exceptions or condition property",
|
||||||
|
info.ctx);
|
||||||
|
|
||||||
auto source = cfg.sources.at(prev->source);
|
auto source = cfg.sources.at(prev->source);
|
||||||
// note: this is not supposed to happen
|
// note: this is not supposed to happen
|
||||||
THROW(!source, "Rule " + prev->name
|
THROW(!source,
|
||||||
+ ": error (unknown-source): unknown source " + prev->source);
|
std::string("Unknown source ") + prev->source,
|
||||||
|
info.ctx);
|
||||||
|
|
||||||
if (!info.cond.empty())
|
if (!info.cond.empty())
|
||||||
{
|
{
|
||||||
@@ -477,19 +893,23 @@ void rule_loader::append(configuration& cfg, rule_info& info)
|
|||||||
{ return i.name == ex.name; });
|
{ return i.name == ex.name; });
|
||||||
if (prev_ex == prev->exceptions.end())
|
if (prev_ex == prev->exceptions.end())
|
||||||
{
|
{
|
||||||
THROW(!ex.fields.is_valid(), "Rule exception new item "
|
THROW(!ex.fields.is_valid(),
|
||||||
+ ex.name + ": must have fields property with a list of fields");
|
"Rule exception must have fields property with a list of fields",
|
||||||
THROW(ex.values.empty(), "Rule exception new item "
|
ex.ctx);
|
||||||
+ ex.name + ": must have fields property with a list of values");
|
THROW(ex.values.empty(),
|
||||||
|
"Rule exception must have values property with a list of values",
|
||||||
|
ex.ctx);
|
||||||
validate_exception_info(*source, ex);
|
validate_exception_info(*source, ex);
|
||||||
prev->exceptions.push_back(ex);
|
prev->exceptions.push_back(ex);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
THROW(ex.fields.is_valid(),
|
THROW(ex.fields.is_valid(),
|
||||||
"Can not append exception fields to existing rule, only values");
|
"Can not append exception fields to existing exception, only values",
|
||||||
|
ex.ctx);
|
||||||
THROW(ex.comps.is_valid(),
|
THROW(ex.comps.is_valid(),
|
||||||
"Can not append exception comps to existing rule, only values");
|
"Can not append exception comps to existing exception, only values",
|
||||||
|
ex.ctx);
|
||||||
prev_ex->values.insert(
|
prev_ex->values.insert(
|
||||||
prev_ex->values.end(), ex.values.begin(), ex.values.end());
|
prev_ex->values.end(), ex.values.begin(), ex.values.end());
|
||||||
}
|
}
|
||||||
@@ -500,18 +920,34 @@ void rule_loader::append(configuration& cfg, rule_info& info)
|
|||||||
void rule_loader::enable(configuration& cfg, rule_info& info)
|
void rule_loader::enable(configuration& cfg, rule_info& info)
|
||||||
{
|
{
|
||||||
auto prev = m_rule_infos.at(info.name);
|
auto prev = m_rule_infos.at(info.name);
|
||||||
THROW(!prev, "Rule " + info.name
|
THROW(!prev,
|
||||||
+ " has 'enabled' key but no rule by that name already exists");
|
"Rule has 'enabled' key but no rule by that name already exists",
|
||||||
|
info.ctx);
|
||||||
prev->enabled = info.enabled;
|
prev->enabled = info.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rule_loader::rule_load_exception::rule_load_exception(load_result::error_code ec, std::string msg, const context& ctx)
|
||||||
|
: ec(ec), msg(msg), ctx(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_loader::rule_load_exception::~rule_load_exception()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* rule_loader::rule_load_exception::what()
|
||||||
|
{
|
||||||
|
errstr = load_result::error_code_str(ec) + ": "
|
||||||
|
+ msg.c_str();
|
||||||
|
|
||||||
|
return errstr.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
void rule_loader::compile_list_infos(configuration& cfg, indexed_vector<list_info>& out) const
|
void rule_loader::compile_list_infos(configuration& cfg, indexed_vector<list_info>& out) const
|
||||||
{
|
{
|
||||||
string tmp;
|
string tmp;
|
||||||
vector<string> used;
|
vector<string> used;
|
||||||
for (auto &list : m_list_infos)
|
for (auto &list : m_list_infos)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
list_info v = list;
|
list_info v = list;
|
||||||
v.items.clear();
|
v.items.clear();
|
||||||
@@ -537,11 +973,6 @@ void rule_loader::compile_list_infos(configuration& cfg, indexed_vector<list_inf
|
|||||||
v.used = false;
|
v.used = false;
|
||||||
out.insert(v, v.name);
|
out.insert(v, v.name);
|
||||||
}
|
}
|
||||||
catch (exception& e)
|
|
||||||
{
|
|
||||||
throw falco_exception(list.ctx.error(e.what()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto &v : used)
|
for (auto &v : used)
|
||||||
{
|
{
|
||||||
out.at(v)->used = true;
|
out.at(v)->used = true;
|
||||||
@@ -554,27 +985,18 @@ void rule_loader::compile_macros_infos(
|
|||||||
indexed_vector<list_info>& lists,
|
indexed_vector<list_info>& lists,
|
||||||
indexed_vector<macro_info>& out) const
|
indexed_vector<macro_info>& out) const
|
||||||
{
|
{
|
||||||
const context* info_ctx = NULL;
|
set<string> used;
|
||||||
try
|
|
||||||
{
|
|
||||||
for (auto &m : m_macro_infos)
|
for (auto &m : m_macro_infos)
|
||||||
{
|
{
|
||||||
info_ctx = &m.ctx;
|
|
||||||
macro_info entry = m;
|
macro_info entry = m;
|
||||||
entry.cond_ast = parse_condition(m.cond, lists);
|
entry.cond_ast = parse_condition(m.cond, lists, m.cond_ctx);
|
||||||
entry.used = false;
|
entry.used = false;
|
||||||
out.insert(entry, m.name);
|
out.insert(entry, m.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &m : out)
|
for (auto &m : out)
|
||||||
{
|
{
|
||||||
info_ctx = &m.ctx;
|
resolve_macros(out, m.cond_ast, m.visibility, m.ctx);
|
||||||
resolve_macros(out, m.cond_ast, m.visibility,
|
|
||||||
"Compilation error when compiling \"" + m.cond + "\": ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (exception& e)
|
|
||||||
{
|
|
||||||
throw falco_exception(info_ctx->error(e.what()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,11 +1008,9 @@ void rule_loader::compile_rule_infos(
|
|||||||
indexed_vector<falco_rule>& out) const
|
indexed_vector<falco_rule>& out) const
|
||||||
{
|
{
|
||||||
string err, condition;
|
string err, condition;
|
||||||
set<string> warn_codes;
|
set<load_result::warning_code> warn_codes;
|
||||||
filter_warning_resolver warn_resolver;
|
filter_warning_resolver warn_resolver;
|
||||||
for (auto &r : m_rule_infos)
|
for (auto &r : m_rule_infos)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
// skip the rule if below the minimum priority
|
// skip the rule if below the minimum priority
|
||||||
if (r.priority > cfg.min_priority)
|
if (r.priority > cfg.min_priority)
|
||||||
@@ -600,8 +1020,10 @@ void rule_loader::compile_rule_infos(
|
|||||||
|
|
||||||
auto source = cfg.sources.at(r.source);
|
auto source = cfg.sources.at(r.source);
|
||||||
// note: this is not supposed to happen
|
// note: this is not supposed to happen
|
||||||
THROW(!source, "Rule " + r.name
|
|
||||||
+ ": error (unknown-source): unknown source " + r.source);
|
THROW(!source,
|
||||||
|
std::string("Unknown source ") + r.source,
|
||||||
|
r.ctx);
|
||||||
|
|
||||||
// build filter AST by parsing the condition, building exceptions,
|
// build filter AST by parsing the condition, building exceptions,
|
||||||
// and resolving lists and macros
|
// and resolving lists and macros
|
||||||
@@ -613,8 +1035,8 @@ void rule_loader::compile_rule_infos(
|
|||||||
build_rule_exception_infos(
|
build_rule_exception_infos(
|
||||||
r.exceptions, rule.exception_fields, condition);
|
r.exceptions, rule.exception_fields, condition);
|
||||||
}
|
}
|
||||||
auto ast = parse_condition(condition, lists);
|
auto ast = parse_condition(condition, lists, r.cond_ctx);
|
||||||
resolve_macros(macros, ast, MAX_VISIBILITY, "");
|
resolve_macros(macros, ast, MAX_VISIBILITY, r.ctx);
|
||||||
|
|
||||||
// check for warnings in the filtering condition
|
// check for warnings in the filtering condition
|
||||||
warn_codes.clear();
|
warn_codes.clear();
|
||||||
@@ -622,9 +1044,7 @@ void rule_loader::compile_rule_infos(
|
|||||||
{
|
{
|
||||||
for (auto &w : warn_codes)
|
for (auto &w : warn_codes)
|
||||||
{
|
{
|
||||||
cfg.warnings.push_back(
|
cfg.res->add_warning(w, "", r.ctx, cfg.content);
|
||||||
"Rule " + r.name + ": warning (" + w + "):\n "
|
|
||||||
+ falco::utils::wrap_text(warn_resolver.format(w), 4, 50));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,8 +1055,13 @@ void rule_loader::compile_rule_infos(
|
|||||||
apply_output_substitutions(cfg, rule.output);
|
apply_output_substitutions(cfg, rule.output);
|
||||||
}
|
}
|
||||||
|
|
||||||
THROW(!is_format_valid(*cfg.sources.at(r.source), rule.output, err),
|
if(!is_format_valid(*cfg.sources.at(r.source), rule.output, err))
|
||||||
"Invalid output format '" + rule.output + "': '" + err + "'");
|
{
|
||||||
|
throw rule_load_exception(
|
||||||
|
load_result::LOAD_ERR_COMPILE_OUTPUT,
|
||||||
|
err,
|
||||||
|
r.output_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
// construct rule definition and compile it to a filter
|
// construct rule definition and compile it to a filter
|
||||||
rule.name = r.name;
|
rule.name = r.name;
|
||||||
@@ -644,11 +1069,39 @@ void rule_loader::compile_rule_infos(
|
|||||||
rule.description = r.desc;
|
rule.description = r.desc;
|
||||||
rule.priority = r.priority;
|
rule.priority = r.priority;
|
||||||
rule.tags = r.tags;
|
rule.tags = r.tags;
|
||||||
try
|
|
||||||
{
|
|
||||||
auto rule_id = out.insert(rule, rule.name);
|
auto rule_id = out.insert(rule, rule.name);
|
||||||
out.at(rule_id)->id = rule_id;
|
out.at(rule_id)->id = rule_id;
|
||||||
|
|
||||||
|
// This also compiles the filter, and might throw a
|
||||||
|
// falco_exception with details on the compilation
|
||||||
|
// failure.
|
||||||
|
try {
|
||||||
source->ruleset->add(*out.at(rule_id), ast);
|
source->ruleset->add(*out.at(rule_id), ast);
|
||||||
|
}
|
||||||
|
catch (const falco_exception& e)
|
||||||
|
{
|
||||||
|
// Allow errors containing "nonexistent field" if
|
||||||
|
// skip_if_unknown_filter is true
|
||||||
|
std::string err = e.what();
|
||||||
|
|
||||||
|
if (err.find("nonexistent field") != string::npos &&
|
||||||
|
r.skip_if_unknown_filter)
|
||||||
|
{
|
||||||
|
cfg.res->add_warning(
|
||||||
|
load_result::LOAD_UNKNOWN_FIELD,
|
||||||
|
e.what(),
|
||||||
|
r.cond_ctx,
|
||||||
|
cfg.content);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw rule_loader::rule_load_exception(
|
||||||
|
load_result::LOAD_ERR_COMPILE_CONDITION,
|
||||||
|
e.what(),
|
||||||
|
r.cond_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// By default rules are enabled/disabled for the default ruleset
|
// By default rules are enabled/disabled for the default ruleset
|
||||||
if(r.enabled)
|
if(r.enabled)
|
||||||
@@ -659,22 +1112,6 @@ void rule_loader::compile_rule_infos(
|
|||||||
{
|
{
|
||||||
source->ruleset->disable(rule.name, true, cfg.default_ruleset_id);
|
source->ruleset->disable(rule.name, true, cfg.default_ruleset_id);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (falco_exception& e)
|
|
||||||
{
|
|
||||||
string err = e.what();
|
|
||||||
if (err.find("nonexistent field") != string::npos
|
|
||||||
&& r.skip_if_unknown_filter)
|
|
||||||
{
|
|
||||||
cfg.warnings.push_back(
|
|
||||||
"Rule " + rule.name + ": warning (unknown-field):");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw falco_exception("Rule " + rule.name + ": error " + err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// populate set of event types and emit an special warning
|
// populate set of event types and emit an special warning
|
||||||
set<uint16_t> evttypes = { ppm_event_type::PPME_PLUGINEVENT_E };
|
set<uint16_t> evttypes = { ppm_event_type::PPME_PLUGINEVENT_E };
|
||||||
@@ -685,21 +1122,17 @@ void rule_loader::compile_rule_infos(
|
|||||||
if ((evttypes.empty() || evttypes.size() > 100)
|
if ((evttypes.empty() || evttypes.size() > 100)
|
||||||
&& r.warn_evttypes)
|
&& r.warn_evttypes)
|
||||||
{
|
{
|
||||||
cfg.warnings.push_back(
|
cfg.res->add_warning(
|
||||||
"Rule " + rule.name + ": warning (no-evttype):\n" +
|
load_result::LOAD_NO_EVTTYPE,
|
||||||
+ " matches too many evt.type values.\n"
|
"Rule matches too many evt.type values. This has a significant performance penalty.",
|
||||||
+ " This has a significant performance penalty.");
|
r.ctx,
|
||||||
|
cfg.content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (exception& e)
|
|
||||||
{
|
|
||||||
throw falco_exception(r.ctx.error(e.what()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rule_loader::compile(configuration& cfg, indexed_vector<falco_rule>& out) const
|
void rule_loader::compile(configuration& cfg, indexed_vector<falco_rule>& out) const
|
||||||
{
|
{
|
||||||
indexed_vector<list_info> lists;
|
indexed_vector<list_info> lists;
|
||||||
indexed_vector<macro_info> macros;
|
indexed_vector<macro_info> macros;
|
||||||
@@ -711,10 +1144,9 @@ bool rule_loader::compile(configuration& cfg, indexed_vector<falco_rule>& out) c
|
|||||||
compile_macros_infos(cfg, lists, macros);
|
compile_macros_infos(cfg, lists, macros);
|
||||||
compile_rule_infos(cfg, lists, macros, out);
|
compile_rule_infos(cfg, lists, macros, out);
|
||||||
}
|
}
|
||||||
catch (exception& e)
|
catch(rule_load_exception &e)
|
||||||
{
|
{
|
||||||
cfg.errors.push_back(e.what());
|
cfg.res->add_error(e.ec, e.msg, e.ctx, cfg.content);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// print info on any dangling lists or macros that were not used anywhere
|
// print info on any dangling lists or macros that were not used anywhere
|
||||||
@@ -722,17 +1154,22 @@ bool rule_loader::compile(configuration& cfg, indexed_vector<falco_rule>& out) c
|
|||||||
{
|
{
|
||||||
if (!m.used)
|
if (!m.used)
|
||||||
{
|
{
|
||||||
cfg.warnings.push_back("macro " + m.name
|
cfg.res->add_warning(
|
||||||
+ " not referred to by any rule/macro");
|
load_result::LOAD_UNUSED_MACRO,
|
||||||
|
"Macro not referred to by any other rule/macro",
|
||||||
|
m.ctx,
|
||||||
|
cfg.content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto &l : lists)
|
for (auto &l : lists)
|
||||||
{
|
{
|
||||||
if (!l.used)
|
if (!l.used)
|
||||||
{
|
{
|
||||||
cfg.warnings.push_back("list " + l.name
|
cfg.res->add_warning(
|
||||||
+ " not referred to by any rule/macro/list");
|
load_result::LOAD_UNUSED_LIST,
|
||||||
|
"List not referred to by any other rule/macro",
|
||||||
|
l.ctx,
|
||||||
|
cfg.content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
@@ -20,8 +20,10 @@ limitations under the License.
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <yaml-cpp/yaml.h>
|
#include <yaml-cpp/yaml.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
#include "falco_rule.h"
|
#include "falco_rule.h"
|
||||||
#include "falco_source.h"
|
#include "falco_source.h"
|
||||||
|
#include "falco_load_result.h"
|
||||||
#include "indexed_vector.h"
|
#include "indexed_vector.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -31,34 +33,111 @@ limitations under the License.
|
|||||||
class rule_loader
|
class rule_loader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/*!
|
class context
|
||||||
\brief Represents a section of text from which a certain info
|
|
||||||
struct has been decoded
|
|
||||||
*/
|
|
||||||
struct context
|
|
||||||
{
|
{
|
||||||
std::string content;
|
public:
|
||||||
|
struct location
|
||||||
|
{
|
||||||
|
// The original location in the document
|
||||||
|
YAML::Mark mark;
|
||||||
|
|
||||||
|
// The kind of item at this location
|
||||||
|
// (e.g. "list", "macro", "rule", "exception", etc)
|
||||||
|
std::string item_type;
|
||||||
|
|
||||||
|
// The name of this item (e.g. "Write Below Etc",
|
||||||
|
// etc).
|
||||||
|
std::string item_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
context(const std::string& name);
|
||||||
|
context(const YAML::Node& mark,
|
||||||
|
const std::string item_type,
|
||||||
|
const std::string item_name,
|
||||||
|
const context& parent);
|
||||||
|
virtual ~context() = default;
|
||||||
|
|
||||||
|
// Return a snippet of the provided rules content
|
||||||
|
// corresponding to this context.
|
||||||
|
std::string snippet(const std::string& content) const;
|
||||||
|
|
||||||
|
std::string as_string();
|
||||||
|
nlohmann::json as_json();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
// A chain of locations from the current item, its
|
||||||
|
// parent, possibly older ancestors.
|
||||||
|
std::vector<location> m_locs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct warning
|
||||||
|
{
|
||||||
|
falco::load_result::warning_code wc;
|
||||||
|
std::string msg;
|
||||||
|
context ctx;
|
||||||
|
std::string snippet;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct error
|
||||||
|
{
|
||||||
|
falco::load_result::error_code ec;
|
||||||
|
std::string msg;
|
||||||
|
context ctx;
|
||||||
|
std::string snippet;
|
||||||
|
};
|
||||||
|
|
||||||
|
class rule_load_exception : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
rule_load_exception(falco::load_result::error_code ec, std::string msg, const context& ctx);
|
||||||
|
virtual ~rule_load_exception();
|
||||||
|
const char* what();
|
||||||
|
|
||||||
|
falco::load_result::error_code ec;
|
||||||
|
std::string msg;
|
||||||
|
context ctx;
|
||||||
|
|
||||||
|
std::string errstr;
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Wraps an error by adding info about the text section
|
\brief Contains the result of loading rule definitions
|
||||||
*/
|
*/
|
||||||
inline std::string error(std::string err) const
|
class result : public falco::load_result
|
||||||
{
|
{
|
||||||
std::string cnt = content;
|
public:
|
||||||
err += "\n---\n";
|
result(const std::string &name);
|
||||||
err += trim(cnt);
|
virtual ~result() = default;
|
||||||
err += "\n---";
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
virtual bool successful() override;
|
||||||
\brief Appends another text section info to this one
|
virtual bool has_warnings() override;
|
||||||
*/
|
virtual const std::string& as_string(bool verbose) override;
|
||||||
inline void append(context& m)
|
virtual const nlohmann::json& as_json() override;
|
||||||
{
|
|
||||||
content += "\n\n";
|
void add_error(falco::load_result::error_code ec,
|
||||||
content += m.content;
|
const std::string& msg,
|
||||||
}
|
const context& ctx,
|
||||||
|
const std::string& rules_content);
|
||||||
|
|
||||||
|
void add_warning(falco::load_result::warning_code ec,
|
||||||
|
const std::string& msg,
|
||||||
|
const context& ctx,
|
||||||
|
const std::string& rules_content);
|
||||||
|
protected:
|
||||||
|
|
||||||
|
const std::string& as_summary_string();
|
||||||
|
const std::string& as_verbose_string();
|
||||||
|
std::string name;
|
||||||
|
bool success;
|
||||||
|
|
||||||
|
std::vector<error> errors;
|
||||||
|
std::vector<warning> warnings;
|
||||||
|
|
||||||
|
std::string res_summary_string;
|
||||||
|
std::string res_verbose_string;
|
||||||
|
nlohmann::json res_json;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -68,13 +147,17 @@ public:
|
|||||||
{
|
{
|
||||||
explicit configuration(
|
explicit configuration(
|
||||||
const std::string& cont,
|
const std::string& cont,
|
||||||
const indexed_vector<falco_source>& srcs)
|
const indexed_vector<falco_source>& srcs,
|
||||||
: content(cont), sources(srcs) {}
|
std::string name)
|
||||||
|
: content(cont), sources(srcs), name(name)
|
||||||
|
{
|
||||||
|
res.reset(new result(name));
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& content;
|
const std::string& content;
|
||||||
const indexed_vector<falco_source>& sources;
|
const indexed_vector<falco_source>& sources;
|
||||||
std::vector<std::string> errors;
|
std::string name;
|
||||||
std::vector<std::string> warnings;
|
std::unique_ptr<result> res;
|
||||||
std::string output_extra;
|
std::string output_extra;
|
||||||
uint16_t default_ruleset_id;
|
uint16_t default_ruleset_id;
|
||||||
bool replace_output_container_info;
|
bool replace_output_container_info;
|
||||||
@@ -86,6 +169,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
struct engine_version_info
|
struct engine_version_info
|
||||||
{
|
{
|
||||||
|
engine_version_info(context &ctx);
|
||||||
|
~engine_version_info() = default;
|
||||||
|
|
||||||
|
context ctx;
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -94,6 +181,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
struct plugin_version_info
|
struct plugin_version_info
|
||||||
{
|
{
|
||||||
|
// This differs from the other _info structs by having
|
||||||
|
// a default constructor. This allows it to be used
|
||||||
|
// by falco_engine, which aliases the type.
|
||||||
|
plugin_version_info();
|
||||||
|
plugin_version_info(context &ctx);
|
||||||
|
~plugin_version_info() = default;
|
||||||
|
|
||||||
|
context ctx;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string version;
|
std::string version;
|
||||||
};
|
};
|
||||||
@@ -103,6 +198,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
struct list_info
|
struct list_info
|
||||||
{
|
{
|
||||||
|
list_info(context &ctx);
|
||||||
|
~list_info() = default;
|
||||||
|
|
||||||
context ctx;
|
context ctx;
|
||||||
bool used;
|
bool used;
|
||||||
size_t index;
|
size_t index;
|
||||||
@@ -116,7 +214,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
struct macro_info
|
struct macro_info
|
||||||
{
|
{
|
||||||
|
macro_info(context &ctx);
|
||||||
|
~macro_info() = default;
|
||||||
|
|
||||||
context ctx;
|
context ctx;
|
||||||
|
context cond_ctx;
|
||||||
bool used;
|
bool used;
|
||||||
size_t index;
|
size_t index;
|
||||||
size_t visibility;
|
size_t visibility;
|
||||||
@@ -130,6 +232,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
struct rule_exception_info
|
struct rule_exception_info
|
||||||
{
|
{
|
||||||
|
rule_exception_info(context &ctx);
|
||||||
|
~rule_exception_info() = default;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief This is necessary due to the dynamic-typed nature of
|
\brief This is necessary due to the dynamic-typed nature of
|
||||||
exceptions. Each of fields, comps, and values, can either be a
|
exceptions. Each of fields, comps, and values, can either be a
|
||||||
@@ -148,6 +253,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
context ctx;
|
||||||
std::string name;
|
std::string name;
|
||||||
entry fields;
|
entry fields;
|
||||||
entry comps;
|
entry comps;
|
||||||
@@ -159,7 +265,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
struct rule_info
|
struct rule_info
|
||||||
{
|
{
|
||||||
|
rule_info(context &ctx);
|
||||||
|
~rule_info() = default;
|
||||||
|
|
||||||
context ctx;
|
context ctx;
|
||||||
|
context cond_ctx;
|
||||||
|
context output_ctx;
|
||||||
size_t index;
|
size_t index;
|
||||||
size_t visibility;
|
size_t visibility;
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -185,7 +296,7 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
\brief Uses the internal state to compile a list of falco_rules
|
\brief Uses the internal state to compile a list of falco_rules
|
||||||
*/
|
*/
|
||||||
virtual bool compile(configuration& cfg, indexed_vector<falco_rule>& out) const;
|
virtual void compile(configuration& cfg, indexed_vector<falco_rule>& out) const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Returns the set of all required versions for each plugin according
|
\brief Returns the set of all required versions for each plugin according
|
||||||
|
Reference in New Issue
Block a user