From 911bd165563551600eabc8594f8843f5858fce5d Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Mon, 28 Feb 2022 17:14:29 +0000 Subject: [PATCH] update(engine): create a lua helper for rule filter manipulation The lua_filter_helper class is a simple Lua wrapper that can be used in the Lua rule loader to parse/compile rule filters, and manipulate them to resolve/replace list and macro references. Signed-off-by: Jason Dellaluce --- userspace/engine/CMakeLists.txt | 4 +- userspace/engine/falco_engine.cpp | 3 +- userspace/engine/json_evt.cpp | 2 +- userspace/engine/json_evt.h | 5 +- userspace/engine/lua_filter_helper.cpp | 217 +++++++++++++++++++++++++ userspace/engine/lua_filter_helper.h | 37 +++++ 6 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 userspace/engine/lua_filter_helper.cpp create mode 100644 userspace/engine/lua_filter_helper.h diff --git a/userspace/engine/CMakeLists.txt b/userspace/engine/CMakeLists.txt index 40a8d106..1c46ccb6 100644 --- a/userspace/engine/CMakeLists.txt +++ b/userspace/engine/CMakeLists.txt @@ -19,7 +19,9 @@ set(FALCO_ENGINE_SOURCE_FILES falco_utils.cpp json_evt.cpp ruleset.cpp - formats.cpp) + formats.cpp + filter_macro_resolver.cpp + lua_filter_helper.cpp) add_library(falco_engine STATIC ${FALCO_ENGINE_SOURCE_FILES}) add_dependencies(falco_engine njson lyaml string-view-lite) diff --git a/userspace/engine/falco_engine.cpp b/userspace/engine/falco_engine.cpp index c9934c60..9b2b427f 100644 --- a/userspace/engine/falco_engine.cpp +++ b/userspace/engine/falco_engine.cpp @@ -28,8 +28,8 @@ limitations under the License. #include "formats.h" +#include "lua_filter_helper.h" extern "C" { -#include "lpeg.h" #include "lyaml.h" } @@ -53,6 +53,7 @@ falco_engine::falco_engine(bool seed_rng) falco_common::init(); falco_rules::init(m_ls); + lua_filter_helper::init(m_ls); m_required_plugin_versions.clear(); diff --git a/userspace/engine/json_evt.cpp b/userspace/engine/json_evt.cpp index e10ade59..26d0e2b4 100644 --- a/userspace/engine/json_evt.cpp +++ b/userspace/engine/json_evt.cpp @@ -1418,7 +1418,7 @@ json_event_filter_check *k8s_audit_filter_check::allocate_new() return (json_event_filter_check *)chk; } -json_event_filter::json_event_filter() +json_event_filter::json_event_filter(): sinsp_filter(NULL) { } diff --git a/userspace/engine/json_evt.h b/userspace/engine/json_evt.h index 75ed0dd6..4c18a8e8 100644 --- a/userspace/engine/json_evt.h +++ b/userspace/engine/json_evt.h @@ -27,7 +27,7 @@ limitations under the License. #include #include "prefix_search.h" -#include "gen_filter.h" +#include class json_event : public gen_event { @@ -383,7 +383,8 @@ public: json_event_filter_check &jchk); }; -class json_event_filter : public gen_event_filter + +class json_event_filter : public sinsp_filter { public: json_event_filter(); diff --git a/userspace/engine/lua_filter_helper.cpp b/userspace/engine/lua_filter_helper.cpp new file mode 100644 index 00000000..c8571b08 --- /dev/null +++ b/userspace/engine/lua_filter_helper.cpp @@ -0,0 +1,217 @@ +/* +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 +#include "lua_filter_helper.h" +#include "filter_macro_resolver.h" +#include "filter_list_resolver.h" +#include "rules.h" + +using namespace std; +using namespace libsinsp::filter; + +// The code below implements the Lua wrapper. +// todo(jasondellaluce): remove this once Lua is removed from Falco +extern "C" { + #include "lua.h" + #include "lualib.h" + #include "lauxlib.h" +} + +const static struct luaL_Reg ll_filter_helper[] = +{ + {"compile_filter", &lua_filter_helper::compile_filter}, + {"parse_filter", &lua_filter_helper::parse_filter}, + {"expand_list", &lua_filter_helper::expand_list}, + {"expand_macro", &lua_filter_helper::expand_macro}, + {"find_unknown_macro", &lua_filter_helper::find_unknown_macro}, + {"clone_ast", &lua_filter_helper::clone_ast}, + {"delete_ast", &lua_filter_helper::delete_ast}, + {NULL, NULL} +}; + +void lua_filter_helper::init(lua_State *ls) +{ + luaL_openlib(ls, "filter_helper", ll_filter_helper, 0); +} + +int lua_filter_helper::parse_filter(lua_State *ls) +{ + if (! lua_isstring(ls, -1)) + { + lua_pushstring(ls, "invalid argument passed to parse_filter()"); + lua_error(ls); + } + + string filter_str = lua_tostring(ls, -1); + + parser p(filter_str); + p.set_max_depth(1000); + try + { + auto filter = p.parse(); + lua_pushboolean(ls, true); + lua_pushlightuserdata(ls, filter); + } + catch (const sinsp_exception& e) + { + string err = to_string(p.get_pos().col) + ": " + e.what(); + lua_pushboolean(ls, false); + lua_pushstring(ls, err.c_str()); + } + return 2; +} + +int lua_filter_helper::compile_filter(lua_State *ls) +{ + if (! lua_islightuserdata(ls, -4) || + ! lua_islightuserdata(ls, -3) || + ! lua_isstring(ls, -2) || + ! lua_isnumber(ls, -1)) + { + lua_pushstring(ls, "invalid argument passed to compile_filter()"); + lua_error(ls); + } + + falco_rules *rules = (falco_rules *) lua_topointer(ls, -4); + ast::expr* ast = (ast::expr*) lua_topointer(ls, -3); + std::string source = lua_tostring(ls, -2); + int32_t check_id = (int32_t) luaL_checkinteger(ls, -1); + + try + { + sinsp_filter_compiler compiler(rules->get_filter_factory(source), ast); + compiler.set_check_id(check_id); + gen_event_filter* filter = compiler.compile(); + lua_pushboolean(ls, true); + lua_pushlightuserdata(ls, filter); + } + catch (const sinsp_exception& e) + { + lua_pushboolean(ls, false); + lua_pushstring(ls, e.what()); + } + catch (const falco_exception& e) + { + lua_pushboolean(ls, false); + lua_pushstring(ls, e.what()); + } + return 2; +} + +int lua_filter_helper::expand_list(lua_State *ls) +{ + if (! lua_islightuserdata(ls, -3) || // ast + ! lua_isstring(ls, -2) || // name + ! lua_istable(ls, -1)) // values + { + lua_pushstring(ls, "invalid arguments passed to expand_list()"); + lua_error(ls); + } + + ast::expr* ast = (ast::expr*) lua_topointer(ls, -3); + std::string name = lua_tostring(ls, -2); + vector values; + // first key + lua_pushnil(ls); + while (lua_next(ls, -2) != 0) { + // key is at index -2, value is at index + // -1. We want the values. + values.push_back(lua_tostring(ls, -1)); + // Remove value, keep key for next iteration + lua_pop(ls, 1); + } + + filter_list_resolver resolver; + resolver.define_list(name, values); + resolver.process(ast); + lua_pushboolean(ls, !resolver.get_resolved_lists().empty()); + lua_pushlightuserdata(ls, ast); + return 2; +} + +int lua_filter_helper::expand_macro(lua_State *ls) +{ + if (! lua_islightuserdata(ls, -3) || // ast + ! lua_isstring(ls, -2) || // name + ! lua_islightuserdata(ls, -1)) // macro + { + lua_pushstring(ls, "invalid arguments passed to expand_macro()"); + lua_error(ls); + } + + ast::expr* ast = (ast::expr*) lua_topointer(ls, -3); + std::string name = lua_tostring(ls, -2); + ast::expr* macro = (ast::expr*) lua_topointer(ls, -1); + + filter_macro_resolver resolver; + resolver.define_macro(name, macro); + resolver.process(ast); + lua_pushboolean(ls, !resolver.get_resolved_macros().empty()); + lua_pushlightuserdata(ls, ast); + return 2; +} + +int lua_filter_helper::find_unknown_macro(lua_State *ls) +{ + if (! lua_islightuserdata(ls, -1)) // ast + { + lua_pushstring(ls, "invalid arguments passed to find_unknown_macro()"); + lua_error(ls); + } + + ast::expr* ast = (ast::expr*) lua_topointer(ls, -1); + + filter_macro_resolver resolver; + resolver.process(ast); + if (!resolver.get_unknown_macros().empty()) + { + lua_pushboolean(ls, true); + lua_pushstring(ls, resolver.get_unknown_macros().begin()->c_str()); + } + else + { + lua_pushboolean(ls, false); + lua_pushstring(ls, ""); + } + return 2; +} + +int lua_filter_helper::clone_ast(lua_State *ls) +{ + if (! lua_islightuserdata(ls, -1)) // ast + { + lua_pushstring(ls, "Invalid arguments passed to clone_ast()"); + lua_error(ls); + } + + ast::expr* ast = (ast::expr*) lua_topointer(ls, -1); + ast::expr* cloned_ast = ast::clone(ast); + lua_pushlightuserdata(ls, cloned_ast); + return 1; +} + +int lua_filter_helper::delete_ast(lua_State *ls) +{ + if (! lua_islightuserdata(ls, -1)) // ptr + { + lua_pushstring(ls, "Invalid arguments passed to delete_ast()"); + lua_error(ls); + } + + delete (ast::expr*) lua_topointer(ls, -1); + return 0; +} \ No newline at end of file diff --git a/userspace/engine/lua_filter_helper.h b/userspace/engine/lua_filter_helper.h new file mode 100644 index 00000000..8f80f868 --- /dev/null +++ b/userspace/engine/lua_filter_helper.h @@ -0,0 +1,37 @@ +/* +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 + +// todo(jasondellaluce): remove this once Lua is removed from Falco +typedef struct lua_State lua_State; + +/*! + \brief This is a Lua helper for filter-related operations + todo(jasondellaluce): remove this once Lua is removed from Falco +*/ +class lua_filter_helper +{ +public: + static void init(lua_State *ls); + static int compile_filter(lua_State *ls); + static int parse_filter(lua_State *ls); + static int expand_list(lua_State *ls); + static int expand_macro(lua_State *ls); + static int find_unknown_macro(lua_State *ls); + static int clone_ast(lua_State *ls); + static int delete_ast(lua_State *ls); +};