Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new: add possibility to retrieve details about rules with filter_details_visitor #2544

Merged
merged 10 commits into from
May 19, 2023
Merged
49 changes: 49 additions & 0 deletions unit_tests/engine/test_filter_details_resolver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
Copyright (C) 2023 The Falco Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless ASSERT_EQd by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <gtest/gtest.h>
#include <engine/filter_details_resolver.h>


TEST(DetailsResolver, resolve_ast)
{
std::string cond = "(spawned_process or evt.type = open) and (proc.name icontains cat or proc.name in (known_procs, ps))";
auto ast = libsinsp::filter::parser(cond).parse();
filter_details details;
details.known_macros.insert("spawned_process");
details.known_lists.insert("known_procs");
filter_details_resolver resolver;
resolver.run(ast.get(), details);

// Assert fields
ASSERT_EQ(details.fields.size(), 2);
ASSERT_NE(details.fields.find("evt.type"), details.fields.end());
ASSERT_NE(details.fields.find("proc.name"), details.fields.end());

// Assert macros
ASSERT_EQ(details.macros.size(), 1);
ASSERT_NE(details.macros.find("spawned_process"), details.macros.end());

// Assert operators
ASSERT_EQ(details.operators.size(), 3);
ASSERT_NE(details.operators.find("="), details.operators.end());
ASSERT_NE(details.operators.find("icontains"), details.operators.end());
ASSERT_NE(details.operators.find("in"), details.operators.end());

// Assert lists
ASSERT_EQ(details.lists.size(), 1);
ASSERT_NE(details.lists.find("known_procs"), details.lists.end());
}
1 change: 1 addition & 0 deletions userspace/engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ set(FALCO_ENGINE_SOURCE_FILES
json_evt.cpp
evttype_index_ruleset.cpp
formats.cpp
filter_details_resolver.cpp
filter_macro_resolver.cpp
filter_warning_resolver.cpp
stats_manager.cpp
Expand Down
295 changes: 284 additions & 11 deletions userspace/engine/falco_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ limitations under the License.
#include <fstream>
#include <functional>
#include <utility>
#include <vector>

#include <nlohmann/json.hpp>

#include <sinsp.h>
#include <plugin.h>
Expand All @@ -42,6 +45,7 @@ limitations under the License.
#include "utils.h"
#include "banned.h" // This raises a compilation error when certain functions are used
#include "evttype_index_ruleset.h"
#include "filter_details_resolver.h"

const std::string falco_engine::s_default_ruleset = "falco-default-ruleset";

Expand Down Expand Up @@ -427,27 +431,296 @@ std::size_t falco_engine::add_source(const std::string &source,
return m_sources.insert(src, source);
}

void falco_engine::describe_rule(std::string *rule) const
void falco_engine::describe_rule(std::string *rule, bool json) const
loresuso marked this conversation as resolved.
Show resolved Hide resolved
{
static const char* rule_fmt = "%-50s %s\n";
fprintf(stdout, rule_fmt, "Rule", "Description");
fprintf(stdout, rule_fmt, "----", "-----------");
if (!rule)
if(!json)
{
for (auto &r : m_rules)
static const char *rule_fmt = "%-50s %s\n";
fprintf(stdout, rule_fmt, "Rule", "Description");
fprintf(stdout, rule_fmt, "----", "-----------");
if(!rule)
{
auto str = falco::utils::wrap_text(r.description, 51, 110) + "\n";
fprintf(stdout, rule_fmt, r.name.c_str(), str.c_str());
for(auto &r : m_rules)
{
auto str = falco::utils::wrap_text(r.description, 51, 110) + "\n";
fprintf(stdout, rule_fmt, r.name.c_str(), str.c_str());
}
}
else
{
auto r = m_rules.at(*rule);
if(r == nullptr)
{
return;
}
auto str = falco::utils::wrap_text(r->description, 51, 110) + "\n";
fprintf(stdout, rule_fmt, r->name.c_str(), str.c_str());
}

return;
}

std::unique_ptr<sinsp> insp(new sinsp());
Json::FastWriter writer;
std::string json_str;

if(!rule)
{
// In this case we build json information about
// all rules, macros and lists
Json::Value output;

// Store information about rules
Json::Value rules_array = Json::arrayValue;
for(const auto& r : m_rules)
{
auto ri = m_rule_collector.rules().at(r.name);
Json::Value rule;
get_json_details(r, *ri, insp.get(), rule);

// Append to rule array
rules_array.append(rule);
}
output["rules"] = rules_array;

// Store information about macros
Json::Value macros_array;
for(const auto &m : m_rule_collector.macros())
{
Json::Value macro;
get_json_details(m, macro);
macros_array.append(macro);
}
output["macros"] = macros_array;

// Store information about lists
Json::Value lists_array = Json::arrayValue;
for(const auto &l : m_rule_collector.lists())
{
Json::Value list;
get_json_details(l, list);
lists_array.append(list);
}
output["lists"] = lists_array;

json_str = writer.write(output);
}
else
{
auto r = m_rules.at(*rule);
auto str = falco::utils::wrap_text(r->description, 51, 110) + "\n";
fprintf(stdout, rule_fmt, r->name.c_str(), str.c_str());
// build json information for just the specified rule
auto ri = m_rule_collector.rules().at(*rule);
if(ri == nullptr)
{
throw falco_exception("Rule \"" + *rule + "\" is not loaded");
}
auto r = m_rules.at(ri->name);
Json::Value rule;
get_json_details(*r, *ri, insp.get(), rule);
json_str = writer.write(rule);
}

fprintf(stdout, "%s", json_str.c_str());
}

void falco_engine::get_json_details(const falco_rule &r,
const rule_loader::rule_info &ri,
sinsp *insp,
Json::Value &rule) const
{
Json::Value rule_info;

// Fill general rule information
rule_info["name"] = r.name;
rule_info["condition"] = ri.cond;
rule_info["priority"] = format_priority(r.priority, false);
rule_info["output"] = r.output;
rule_info["description"] = r.description;
rule_info["enabled"] = ri.enabled;
rule_info["source"] = r.source;
Json::Value tags = Json::arrayValue;
for(const auto &t : ri.tags)
{
tags.append(t);
}
rule_info["tags"] = tags;
rule["info"] = rule_info;

// Parse rule condition and build the AST
// Assumption: no exception because rules have already been loaded.
auto ast = libsinsp::filter::parser(ri.cond).parse();
Json::Value json_details;
get_json_details(ast.get(), json_details);
rule["details"] = json_details;

// Get fields from output string
sinsp_evt_formatter fmt(insp, r.output);
std::vector<std::string> out_fields;
fmt.get_field_names(out_fields);
Json::Value outputFields = Json::arrayValue;
for(const auto &of : out_fields)
{
outputFields.append(of);
}
rule["details"]["output_fields"] = outputFields;

// Get fields from exceptions
Json::Value exception_fields = Json::arrayValue;
for(const auto &f : r.exception_fields)
{
exception_fields.append(f);
}
rule["details"]["exception_fields"] = exception_fields;

// Get operators from exceptions
Json::Value exception_operators = Json::arrayValue;
for(const auto &e : ri.exceptions)
{
if(e.comps.is_list)
{
for(const auto& c : e.comps.items)
{
if(c.is_list)
{
// considering max two levels of lists
for(const auto& i : c.items)
{
exception_operators.append(i.item);
}
}
else
{
exception_operators.append(c.item);
}
}
}
else
{
exception_operators.append(e.comps.item);
}
}
rule["details"]["exception_operators"] = exception_operators;

if(ri.source == falco_common::syscall_source)
{
// Store event types
Json::Value events;
get_json_evt_types(ast.get(), events);
rule["details"]["events"] = events;
}
}

void falco_engine::get_json_details(const rule_loader::macro_info& m,
Json::Value& macro) const
{
Json::Value macro_info;

macro_info["name"] = m.name;
macro_info["condition"] = m.cond;
macro["info"] = macro_info;

// Assumption: no exception because rules have already been loaded.
auto ast = libsinsp::filter::parser(m.cond).parse();

Json::Value json_details;
get_json_details(ast.get(), json_details);
macro["details"] = json_details;

// Store event types
Json::Value events;
get_json_evt_types(ast.get(), events);
macro["details"]["events"] = events;
}

void falco_engine::get_json_details(const rule_loader::list_info& l,
Json::Value& list) const
{
Json::Value list_info;
list_info["name"] = l.name;

Json::Value items = Json::arrayValue;
Json::Value lists = Json::arrayValue;
for(const auto &i : l.items)
{
if(m_rule_collector.lists().at(i) != nullptr)
{
lists.append(i);
continue;
}
items.append(i);
}

list_info["items"] = items;
list["info"] = list_info;
list["details"]["lists"] = lists;
}

void falco_engine::get_json_details(libsinsp::filter::ast::expr* ast,
Json::Value& output) const
{
filter_details details;
for(const auto &m : m_rule_collector.macros())
{
details.known_macros.insert(m.name);
}

for(const auto &l : m_rule_collector.lists())
{
details.known_lists.insert(l.name);
}

// Resolve the AST details
filter_details_resolver resolver;
resolver.run(ast, details);

Json::Value macros = Json::arrayValue;
for(const auto &m : details.macros)
{
macros.append(m);
}
output["macros"] = macros;

Json::Value operators = Json::arrayValue;
for(const auto &o : details.operators)
{
operators.append(o);
}
output["operators"] = operators;

Json::Value condition_fields = Json::arrayValue;
for(const auto &f : details.fields)
{
condition_fields.append(f);
}
output["condition_fields"] = condition_fields;

Json::Value lists = Json::arrayValue;
for(const auto &l : details.lists)
{
lists.append(l);
}
output["lists"] = lists;

details.reset();
}

void falco_engine::get_json_evt_types(libsinsp::filter::ast::expr* ast,
Json::Value& output) const
{
output = Json::arrayValue;
auto evtcodes = libsinsp::filter::ast::ppm_event_codes(ast);
if(evtcodes.size() != libsinsp::events::all_event_set().size())
{
auto syscodes = libsinsp::filter::ast::ppm_sc_codes(ast);
auto syscodes_to_evt_names = libsinsp::events::sc_set_to_event_names(syscodes);
auto evtcodes_to_evt_names = libsinsp::events::event_set_to_names(evtcodes, false);
for (const auto& n : unordered_set_union(syscodes_to_evt_names, evtcodes_to_evt_names))
{
output.append(n);
}
}
}


void falco_engine::print_stats() const
{
std::string out;
Expand Down
Loading