new(engine): add rule filter macro-resolver

This is a first step towards porting the rule filter building logic that is currently implemented in Lua.
filter_macro_resolver uses the newly introduced AST constructs from libsinsp, and
allow manipulating filter ASTs to resolve/replace macro references. This is meant to be used
at boot time by the rule loader (which we still want to maintain implemented in Lua for now).

Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
This commit is contained in:
Jason Dellaluce
2022-02-28 17:11:57 +00:00
committed by poiana
parent 0a132f453a
commit c5818e6273
2 changed files with 223 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
/*
Copyright (C) 2022 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 required 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 "filter_macro_resolver.h"
using namespace std;
using namespace libsinsp::filter;
bool filter_macro_resolver::run(libsinsp::filter::ast::expr*& filter)
{
m_unknown_macros.clear();
m_resolved_macros.clear();
m_last_node_changed = false;
m_last_node = filter;
filter->accept(this);
if (m_last_node_changed)
{
delete filter;
filter = m_last_node;
}
return !m_resolved_macros.empty();
}
void filter_macro_resolver::define_macro(
string name,
shared_ptr<libsinsp::filter::ast::expr> macro)
{
auto it = m_macros.find(name);
m_macros[name] = macro;
}
set<string>& filter_macro_resolver::get_unknown_macros()
{
return m_unknown_macros;
}
set<string>& filter_macro_resolver::get_resolved_macros()
{
return m_resolved_macros;
}
void filter_macro_resolver::visit(ast::and_expr* e)
{
for (size_t i = 0; i < e->children.size(); i++)
{
e->children[i]->accept(this);
if (m_last_node_changed)
{
delete e->children[i];
e->children[i] = m_last_node;
}
}
m_last_node = e;
m_last_node_changed = false;
}
void filter_macro_resolver::visit(ast::or_expr* e)
{
for (size_t i = 0; i < e->children.size(); i++)
{
e->children[i]->accept(this);
if (m_last_node_changed)
{
delete e->children[i];
e->children[i] = m_last_node;
}
}
m_last_node = e;
m_last_node_changed = false;
}
void filter_macro_resolver::visit(ast::not_expr* e)
{
e->child->accept(this);
if (m_last_node_changed)
{
delete e->child;
e->child = m_last_node;
}
m_last_node = e;
m_last_node_changed = false;
}
void filter_macro_resolver::visit(ast::list_expr* e)
{
m_last_node = e;
m_last_node_changed = false;
}
void filter_macro_resolver::visit(ast::binary_check_expr* e)
{
// avoid exploring checks, so that we can be sure that each
// value_expr* node visited is a macro identifier
m_last_node = e;
m_last_node_changed = false;
}
void filter_macro_resolver::visit(ast::unary_check_expr* e)
{
m_last_node = e;
m_last_node_changed = false;
}
void filter_macro_resolver::visit(ast::value_expr* e)
{
// we are supposed to get here only in case
// of identier-only children from either a 'not',
// an 'and' or an 'or'.
auto macro = m_macros.find(e->value);
if (macro != m_macros.end())
{
// todo(jasondellaluce): should we visit down the new resolved AST too?
m_last_node = ast::clone((*macro).second.get());
m_last_node_changed = true;
m_resolved_macros.insert(e->value);
}
else
{
m_last_node = e;
m_last_node_changed = false;
m_unknown_macros.insert(e->value);
}
}

View File

@@ -0,0 +1,87 @@
/*
Copyright (C) 2022 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 required 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.
*/
#pragma once
#include <filter/parser.h>
#include <string>
#include <set>
#include <map>
#include <memory>
/*!
\brief Helper class for substituting and resolving macro
refereces in parsed filters.
*/
class filter_macro_resolver: private libsinsp::filter::ast::expr_visitor
{
public:
/*!
\brief Visits a filter AST and substitutes macro references
according with all the definitions added through define_macro(),
by replacing the reference with a clone of the macro AST.
\param filter The filter AST to be processed. Note that the pointer
is passed by reference and be modified in order to apply
the substutions. In that case, the old pointer is owned by this
class and is deleted automatically.
\return true if at least one of the defined macros is resolved
*/
bool run(libsinsp::filter::ast::expr*& filter);
/*!
\brief Defines a new macro to be substituted in filters. If called
multiple times for the same macro name, the previous definition
gets overridden.
\param name The name of the macro.
\param macro The AST of the macro.
*/
void define_macro(
std::string name,
std::shared_ptr<libsinsp::filter::ast::expr> macro);
/*!
\brief Returns a set containing the names of all the macros
substituted during the last invocation of run(). Should be
non-empty if the last invocation of run() returned true.
*/
std::set<std::string>& get_resolved_macros();
/*!
\brief Returns a set containing the names of all the macros
that remained unresolved during the last invocation of run().
A macro remains unresolved if it is found inside the processed
filter but it was not defined with define_macro();
*/
std::set<std::string>& get_unknown_macros();
private:
void visit(libsinsp::filter::ast::and_expr* e) override;
void visit(libsinsp::filter::ast::or_expr* e) override;
void visit(libsinsp::filter::ast::not_expr* e) override;
void visit(libsinsp::filter::ast::value_expr* e) override;
void visit(libsinsp::filter::ast::list_expr* e) override;
void visit(libsinsp::filter::ast::unary_check_expr* e) override;
void visit(libsinsp::filter::ast::binary_check_expr* e) override;
bool m_last_node_changed;
libsinsp::filter::ast::expr* m_last_node;
std::set<std::string> m_unknown_macros;
std::set<std::string> m_resolved_macros;
std::map<
std::string,
std::shared_ptr<libsinsp::filter::ast::expr>
> m_macros;
};