diff --git a/userspace/engine/filter_macro_resolver.cpp b/userspace/engine/filter_macro_resolver.cpp new file mode 100644 index 00000000..d481483c --- /dev/null +++ b/userspace/engine/filter_macro_resolver.cpp @@ -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 macro) +{ + auto it = m_macros.find(name); + m_macros[name] = macro; +} + +set& filter_macro_resolver::get_unknown_macros() +{ + return m_unknown_macros; +} + +set& 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); + } +} diff --git a/userspace/engine/filter_macro_resolver.h b/userspace/engine/filter_macro_resolver.h new file mode 100644 index 00000000..c6912933 --- /dev/null +++ b/userspace/engine/filter_macro_resolver.h @@ -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 +#include +#include +#include +#include + +/*! + \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 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& 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& 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 m_unknown_macros; + std::set m_resolved_macros; + std::map< + std::string, + std::shared_ptr + > m_macros; +};