From 3b7902ab4f3bec50ba26e0144657cd38e444ec2c Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Wed, 23 Oct 2024 12:20:51 +0000 Subject: [PATCH 1/6] new(libsinsp): add len() filter transformer Signed-off-by: Luca Guerra --- userspace/libsinsp/filter/parser.cpp | 11 ++- userspace/libsinsp/filter/parser.h | 2 +- .../libsinsp/sinsp_filter_transformer.cpp | 77 +++++++++++++++++-- userspace/libsinsp/sinsp_filter_transformer.h | 17 +++- userspace/libsinsp/sinsp_filtercheck.cpp | 15 ++-- userspace/libsinsp/sinsp_filtercheck.h | 2 +- userspace/libsinsp/sinsp_filtercheck_fd.cpp | 2 +- 7 files changed, 103 insertions(+), 23 deletions(-) diff --git a/userspace/libsinsp/filter/parser.cpp b/userspace/libsinsp/filter/parser.cpp index f03cffbe4f..8c6907ba75 100644 --- a/userspace/libsinsp/filter/parser.cpp +++ b/userspace/libsinsp/filter/parser.cpp @@ -90,12 +90,11 @@ static const std::vector s_binary_list_ops = { static constexpr const char* s_field_transformer_val = "val("; -static const std::vector s_field_transformers = { - "tolower(", - "toupper(", - "b64(", - "basename(", -}; +static const std::vector s_field_transformers = {"tolower(", + "toupper(", + "b64(", + "basename(", + "len("}; static inline void update_pos(const char c, ast::pos_info& pos) { pos.col++; diff --git a/userspace/libsinsp/filter/parser.h b/userspace/libsinsp/filter/parser.h index 92f4c9f28e..1191dd705b 100644 --- a/userspace/libsinsp/filter/parser.h +++ b/userspace/libsinsp/filter/parser.h @@ -69,7 +69,7 @@ class RE2; // | 'startswith ' | 'bstartswith ' | 'endswith ' // ListOperator ::= 'intersects' | 'in' | 'pmatch' // FieldTransformerVal ::= 'val(' -// FieldTransformerType ::= 'tolower(' | 'toupper(' | 'b64(' | 'basename(' +// FieldTransformerType ::= 'tolower(' | 'toupper(' | 'b64(' | 'basename(' | 'len(' // // Tokens (Regular Expressions): // Identifier ::= [a-zA-Z]+[a-zA-Z0-9_]* diff --git a/userspace/libsinsp/sinsp_filter_transformer.cpp b/userspace/libsinsp/sinsp_filter_transformer.cpp index 4b32596b82..d57278434a 100644 --- a/userspace/libsinsp/sinsp_filter_transformer.cpp +++ b/userspace/libsinsp/sinsp_filter_transformer.cpp @@ -63,7 +63,8 @@ bool sinsp_filter_transformer::string_transformer(std::vector& return true; } -bool sinsp_filter_transformer::transform_type(ppm_param_type& t) const { +bool sinsp_filter_transformer::transform_type(ppm_param_type& t, uint32_t& flags) const { + bool is_list = flags & EPF_IS_LIST; switch(m_type) { case FTR_TOUPPER: { switch(t) { @@ -71,7 +72,7 @@ bool sinsp_filter_transformer::transform_type(ppm_param_type& t) const { case PT_FSPATH: case PT_FSRELPATH: // for TOUPPER, the transformed type is the same as the input type - return true; + return !is_list; default: return false; } @@ -82,7 +83,7 @@ bool sinsp_filter_transformer::transform_type(ppm_param_type& t) const { case PT_FSPATH: case PT_FSRELPATH: // for TOLOWER, the transformed type is the same as the input type - return true; + return !is_list; default: return false; } @@ -92,7 +93,7 @@ bool sinsp_filter_transformer::transform_type(ppm_param_type& t) const { case PT_CHARBUF: case PT_BYTEBUF: // for BASE64, the transformed type is the same as the input type - return true; + return !is_list; default: return false; } @@ -107,6 +108,23 @@ bool sinsp_filter_transformer::transform_type(ppm_param_type& t) const { case PT_FSPATH: case PT_FSRELPATH: // for BASENAME, the transformed type is the same as the input type + return !is_list; + default: + return false; + } + } + case FTR_LEN: { + if(is_list) { + t = PT_UINT64; + flags = 0; + return true; + } + switch(t) { + case PT_CHARBUF: + case PT_BYTEBUF: + case PT_FSPATH: + case PT_FSRELPATH: + t = PT_UINT64; return true; default: return false; @@ -119,8 +137,11 @@ bool sinsp_filter_transformer::transform_type(ppm_param_type& t) const { } bool sinsp_filter_transformer::transform_values(std::vector& vec, - ppm_param_type& t) { - if(!transform_type(t)) { + ppm_param_type& t, + uint32_t& flags) { + bool is_list = flags & EPF_IS_LIST; + ppm_param_type original_type = t; + if(!transform_type(t, flags)) { throw_type_incompatibility_err(t, filter_transformer_type_str(m_type)); } @@ -180,6 +201,50 @@ bool sinsp_filter_transformer::transform_values(std::vector& ve return true; }); } + case FTR_LEN: { + assert((void("len() type must be PT_UINT64"), t == PT_UINT64)); + if(is_list) { + uint64_t len = static_cast(vec.size()); + auto stored_val = store_scalar(len); + vec.clear(); + vec.push_back(stored_val); + return true; + } + + // not a list: could be string or buffer + bool is_string = false; + switch(original_type) { + case PT_CHARBUF: + case PT_FSPATH: + case PT_FSRELPATH: + is_string = true; + break; + case PT_BYTEBUF: + is_string = false; + break; + default: + return false; + } + + for(std::size_t i = 0; i < vec.size(); i++) { + uint64_t len; + if(vec[i].ptr == nullptr) { + vec[i] = store_scalar(0); + continue; + } + + if(is_string) { + len = static_cast( + strnlen(reinterpret_cast(vec[i].ptr), vec[i].len)); + vec[i] = store_scalar(len); + continue; + } + + len = static_cast(vec[i].len); + vec[i] = store_scalar(len); + } + return true; + } default: throw_unsupported_err(m_type); return false; diff --git a/userspace/libsinsp/sinsp_filter_transformer.h b/userspace/libsinsp/sinsp_filter_transformer.h index 0b9a379501..3c79518dd5 100644 --- a/userspace/libsinsp/sinsp_filter_transformer.h +++ b/userspace/libsinsp/sinsp_filter_transformer.h @@ -28,6 +28,7 @@ enum filter_transformer_type : uint8_t { FTR_BASE64 = 2, FTR_STORAGE = 3, // This transformer is only used internally FTR_BASENAME = 4, + FTR_LEN = 5 }; static inline std::string filter_transformer_type_str(filter_transformer_type m) { @@ -42,6 +43,8 @@ static inline std::string filter_transformer_type_str(filter_transformer_type m) return "storage"; case FTR_BASENAME: return "basename"; + case FTR_LEN: + return "len"; default: throw sinsp_exception("unknown field transfomer id " + std::to_string(m)); } @@ -63,6 +66,9 @@ static inline filter_transformer_type filter_transformer_from_str(const std::str if(str == "basename") { return filter_transformer_type::FTR_BASENAME; } + if(str == "len") { + return filter_transformer_type::FTR_LEN; + } throw sinsp_exception("unknown field transfomer '" + str + "'"); } @@ -72,9 +78,9 @@ class sinsp_filter_transformer { sinsp_filter_transformer(filter_transformer_type t): m_type(t) {}; - bool transform_type(ppm_param_type& t) const; + bool transform_type(ppm_param_type& t, uint32_t& flags) const; - bool transform_values(std::vector& vals, ppm_param_type& t); + bool transform_values(std::vector& vals, ppm_param_type& t, uint32_t& flags); private: using str_transformer_func_t = std::function; @@ -83,6 +89,13 @@ class sinsp_filter_transformer { ppm_param_type t, str_transformer_func_t mod); + template + extract_value_t store_scalar(T value) { + uint8_t* bytes = reinterpret_cast(&value); + storage_t& stored_val = m_storage_values.emplace_back(bytes, bytes + sizeof(T)); + return {static_cast(stored_val.data()), static_cast(stored_val.size())}; + } + filter_transformer_type m_type; std::vector m_storage_values; }; diff --git a/userspace/libsinsp/sinsp_filtercheck.cpp b/userspace/libsinsp/sinsp_filtercheck.cpp index f7a281f4db..d72fbab9e5 100644 --- a/userspace/libsinsp/sinsp_filtercheck.cpp +++ b/userspace/libsinsp/sinsp_filtercheck.cpp @@ -448,7 +448,7 @@ char* sinsp_filter_check::tostring(sinsp_evt* evt) { } auto ftype = get_transformed_field_info()->m_type; - if(m_field->m_flags & EPF_IS_LIST) { + if(get_transformed_field_info()->m_flags & EPF_IS_LIST) { std::string res = "("; for(auto& val : m_extracted_values) { if(res.size() > 1) { @@ -478,7 +478,7 @@ Json::Value sinsp_filter_check::tojson(sinsp_evt* evt) { } auto ftype = get_transformed_field_info()->m_type; - if(m_field->m_flags & EPF_IS_LIST) { + if(get_transformed_field_info()->m_flags & EPF_IS_LIST) { for(auto& val : m_extracted_values) { jsonval.append(rawval_to_json(val.ptr, ftype, m_field->m_print_format, val.len)); } @@ -1045,11 +1045,12 @@ void sinsp_filter_check::add_transformer(filter_transformer_type trtype) { // apply type transformation, both as a feasibility check and // as an information to be returned later on sinsp_filter_transformer tr(trtype); - if(!tr.transform_type(m_transformed_field->m_type)) { + if(!tr.transform_type(m_transformed_field->m_type, m_transformed_field->m_flags)) { throw sinsp_exception("can't add field transformer: type '" + std::string(param_type_to_string(m_transformed_field->m_type)) + "' is not supported by '" + filter_transformer_type_str(trtype) + - "' transformer applied on field '" + + "' transformer applied on " + + (m_transformed_field->is_list() ? "list " : "") + "field '" + std::string(get_field_info()->m_name) + "'"); } @@ -1062,9 +1063,11 @@ void sinsp_filter_check::add_transformer(filter_transformer_type trtype) { } bool sinsp_filter_check::apply_transformers(std::vector& values) { - auto type = get_field_info()->m_type; + const filtercheck_field_info* field_info = get_field_info(); + auto field_type = field_info->m_type; + auto field_flags = field_info->m_flags; for(auto& tr : m_transformers) { - if(!tr.transform_values(values, type)) { + if(!tr.transform_values(values, field_type, field_flags)) { return false; } } diff --git a/userspace/libsinsp/sinsp_filtercheck.h b/userspace/libsinsp/sinsp_filtercheck.h index 1c9d7de1a5..81c77f7de0 100644 --- a/userspace/libsinsp/sinsp_filtercheck.h +++ b/userspace/libsinsp/sinsp_filtercheck.h @@ -137,7 +137,7 @@ class sinsp_filter_check { // // Extract the field from the event. If sanitize_strings is true, any // string values are sanitized to remove nonprintable characters. - // By default, this fills the vector with only one value, retireved by calling the single-result + // By default, this fills the vector with only one value, retrieved by calling the single-result // extract method. // If a NULL value is returned by extract, the vector is emptied. // Subclasses are meant to either override this, or the single-valued extract method. diff --git a/userspace/libsinsp/sinsp_filtercheck_fd.cpp b/userspace/libsinsp/sinsp_filtercheck_fd.cpp index 496f17164c..c7d24f2265 100644 --- a/userspace/libsinsp/sinsp_filtercheck_fd.cpp +++ b/userspace/libsinsp/sinsp_filtercheck_fd.cpp @@ -293,7 +293,7 @@ static const filtercheck_field_info sinsp_filter_check_fd_fields[] = { "FD full name raw. Just like fd.name, but only used if fd is a file path. File path is " "kept raw with limited sanitization and without deriving the absolute path."}, {PT_CHARBUF, - EPF_IS_LIST | EPF_ARG_ALLOWED | EPF_NO_RHS | EPF_NO_TRANSFORMER, + EPF_IS_LIST | EPF_ARG_ALLOWED | EPF_NO_RHS, PF_DEC, "fd.types", "FD Type", From aedb9cd748c8dde896c233482c2396dfb0812726 Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Wed, 23 Oct 2024 12:21:49 +0000 Subject: [PATCH 2/6] new(libsinsp/tests): add tests for len() filter transformer Signed-off-by: Luca Guerra --- userspace/libsinsp/test/filter_parser.ut.cpp | 2 +- .../libsinsp/test/filter_transformer.ut.cpp | 241 +++++++++++++----- 2 files changed, 177 insertions(+), 66 deletions(-) diff --git a/userspace/libsinsp/test/filter_parser.ut.cpp b/userspace/libsinsp/test/filter_parser.ut.cpp index 2ca3282595..12a0b7540a 100644 --- a/userspace/libsinsp/test/filter_parser.ut.cpp +++ b/userspace/libsinsp/test/filter_parser.ut.cpp @@ -87,7 +87,7 @@ TEST(parser, supported_operators) { TEST(parser, supported_field_transformers) { std::string expected_val = "val"; - std::vector expected = {"tolower", "toupper", "b64", "basename"}; + std::vector expected = {"tolower", "toupper", "b64", "basename", "len"}; auto actual = parser::supported_field_transformers(); ASSERT_EQ(actual.size(), expected.size()); diff --git a/userspace/libsinsp/test/filter_transformer.ut.cpp b/userspace/libsinsp/test/filter_transformer.ut.cpp index b4b6d61e47..d66647d427 100644 --- a/userspace/libsinsp/test/filter_transformer.ut.cpp +++ b/userspace/libsinsp/test/filter_transformer.ut.cpp @@ -18,21 +18,22 @@ limitations under the License. #include -#include +#include #include #include #include -static std::unordered_set all_param_types() { - std::unordered_set ret; +static std::set all_param_types() { + std::set ret; for(auto i = PT_NONE; i < PT_MAX; i = (ppm_param_type)((size_t)i + 1)) { ret.insert(i); } return ret; } -static std::string supported_type_msg(ppm_param_type t, bool support_expected) { - return "expected param type to" + std::string((support_expected ? " " : " not ")) + +static std::string supported_type_msg(ppm_param_type t, bool flags, bool support_expected) { + return std::string("expected ") + (flags ? "list " : "") + "param type to" + + std::string((support_expected ? " " : " not ")) + "be supported: " + std::string(param_type_to_string(t)); } @@ -49,13 +50,45 @@ static extract_value_t const_str_to_extract_value(const char *v) { return ret; } +template +static T extract_value_to_scalar(const extract_value_t &val) { + T ret; + EXPECT_EQ(val.len, sizeof(T)); + memcpy(&ret, val.ptr, val.len); + return ret; +} + +static void check_unsupported_types(sinsp_filter_transformer &tr, + std::set> &supported_types, + std::vector sample_vals) { + auto all_types = all_param_types(); + + for(auto t : all_types) { + uint32_t flags = EPF_IS_LIST; + if(supported_types.find({t, flags}) == supported_types.end()) { + auto vals = sample_vals; + EXPECT_FALSE(tr.transform_type(t, flags)) << supported_type_msg(t, flags, false); + EXPECT_ANY_THROW(tr.transform_values(vals, t, flags)) + << supported_type_msg(t, flags, false); + } + + flags = 0; + if(supported_types.find({t, flags}) == supported_types.end()) { + auto vals = sample_vals; + EXPECT_FALSE(tr.transform_type(t, flags)) << supported_type_msg(t, flags, false); + EXPECT_ANY_THROW(tr.transform_values(vals, t, flags)) + << supported_type_msg(t, flags, false); + } + } +} + TEST(sinsp_filter_transformer, toupper) { sinsp_filter_transformer tr(filter_transformer_type::FTR_TOUPPER); auto all_types = all_param_types(); - auto supported_types = - std::unordered_set({PT_CHARBUF, PT_FSPATH, PT_FSRELPATH}); + auto supported_types = std::set>( + {{PT_CHARBUF, false}, {PT_FSPATH, false}, {PT_FSRELPATH, false}}); auto test_cases = std::vector>{ {"hello", "HELLO"}, @@ -70,24 +103,22 @@ TEST(sinsp_filter_transformer, toupper) { sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); } - // check for unsupported types - for(auto t : all_types) { - if(supported_types.find(t) == supported_types.end()) { - auto vals = sample_vals; - EXPECT_FALSE(tr.transform_type(t)) << supported_type_msg(t, false); - EXPECT_ANY_THROW(tr.transform_values(vals, t)) << supported_type_msg(t, false); - } - } + check_unsupported_types(tr, supported_types, sample_vals); // check for supported types for(auto t : supported_types) { - auto original = t; - EXPECT_TRUE(tr.transform_type(t)) << supported_type_msg(original, true); - EXPECT_EQ(original, t); // note: toupper is expected not to alter the type + auto original_type = t.first; + uint32_t flags = t.second ? EPF_IS_LIST : 0; + auto transformed_type = original_type; + EXPECT_TRUE(tr.transform_type(transformed_type, flags)) + << supported_type_msg(original_type, t.second, true); + EXPECT_EQ(original_type, + transformed_type); // note: toupper is expected not to alter the type auto vals = sample_vals; - EXPECT_TRUE(tr.transform_values(vals, t)) << supported_type_msg(original, true); - EXPECT_EQ(original, t); + EXPECT_TRUE(tr.transform_values(vals, transformed_type, flags)) + << supported_type_msg(original_type, t.second, true); + EXPECT_EQ(original_type, transformed_type); EXPECT_EQ(vals.size(), test_cases.size()); for(uint32_t i = 0; i < test_cases.size(); i++) { @@ -103,8 +134,8 @@ TEST(sinsp_filter_transformer, tolower) { auto all_types = all_param_types(); - auto supported_types = - std::unordered_set({PT_CHARBUF, PT_FSPATH, PT_FSRELPATH}); + auto supported_types = std::set>( + {{PT_CHARBUF, false}, {PT_FSPATH, false}, {PT_FSRELPATH, false}}); auto test_cases = std::vector>{ {"HELLO", "hello"}, @@ -119,24 +150,22 @@ TEST(sinsp_filter_transformer, tolower) { sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); } - // check for unsupported types - for(auto t : all_types) { - if(supported_types.find(t) == supported_types.end()) { - auto vals = sample_vals; - EXPECT_FALSE(tr.transform_type(t)) << supported_type_msg(t, false); - EXPECT_ANY_THROW(tr.transform_values(vals, t)) << supported_type_msg(t, false); - } - } + check_unsupported_types(tr, supported_types, sample_vals); // check for supported types for(auto t : supported_types) { - auto original = t; - EXPECT_TRUE(tr.transform_type(t)) << supported_type_msg(original, true); - EXPECT_EQ(original, t); // note: tolower is expected not to alter the type + auto original_type = t.first; + uint32_t flags = t.second ? EPF_IS_LIST : 0; + auto transformed_type = original_type; + EXPECT_TRUE(tr.transform_type(transformed_type, flags)) + << supported_type_msg(original_type, t.second, true); + EXPECT_EQ(original_type, + transformed_type); // note: tolower is expected not to alter the type auto vals = sample_vals; - EXPECT_TRUE(tr.transform_values(vals, t)) << supported_type_msg(original, true); - EXPECT_EQ(original, t); + EXPECT_TRUE(tr.transform_values(vals, transformed_type, flags)) + << supported_type_msg(original_type, t.second, true); + EXPECT_EQ(original_type, transformed_type); EXPECT_EQ(vals.size(), test_cases.size()); for(uint32_t i = 0; i < test_cases.size(); i++) { @@ -152,7 +181,8 @@ TEST(sinsp_filter_transformer, b64) { auto all_types = all_param_types(); - auto supported_types = std::unordered_set({PT_CHARBUF, PT_BYTEBUF}); + auto supported_types = + std::set>({{PT_CHARBUF, false}, {PT_BYTEBUF, false}}); auto test_cases = std::vector>{ {"aGVsbG8=", "hello"}, @@ -167,24 +197,21 @@ TEST(sinsp_filter_transformer, b64) { sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); } - // check for unsupported types - for(auto t : all_types) { - if(supported_types.find(t) == supported_types.end()) { - auto vals = sample_vals; - EXPECT_FALSE(tr.transform_type(t)) << supported_type_msg(t, false); - EXPECT_ANY_THROW(tr.transform_values(vals, t)) << supported_type_msg(t, false); - } - } + check_unsupported_types(tr, supported_types, sample_vals); // check for supported types for(auto t : supported_types) { - auto original = t; - EXPECT_TRUE(tr.transform_type(t)) << supported_type_msg(original, true); - EXPECT_EQ(original, t); // note: tolower is expected not to alter the type + auto original_type = t.first; + uint32_t flags = t.second ? EPF_IS_LIST : 0; + auto transformed_type = original_type; + EXPECT_TRUE(tr.transform_type(transformed_type, flags)) + << supported_type_msg(original_type, t.second, true); + EXPECT_EQ(original_type, transformed_type); // note: b64 is expected not to alter the type auto vals = sample_vals; - EXPECT_TRUE(tr.transform_values(vals, t)) << supported_type_msg(original, true); - EXPECT_EQ(original, t); + EXPECT_TRUE(tr.transform_values(vals, transformed_type, flags)) + << supported_type_msg(original_type, t.second, true); + EXPECT_EQ(original_type, transformed_type); EXPECT_EQ(vals.size(), test_cases.size()); for(uint32_t i = 0; i < test_cases.size(); i++) { @@ -202,7 +229,8 @@ TEST(sinsp_filter_transformer, b64) { // check invalid input being rejected { auto t = PT_CHARBUF; - EXPECT_FALSE(tr.transform_values(invalid_vals, t)); + uint32_t flags = 0; + EXPECT_FALSE(tr.transform_values(invalid_vals, t, flags)); EXPECT_EQ(t, PT_CHARBUF); } } @@ -212,8 +240,8 @@ TEST(sinsp_filter_transformer, basename) { auto all_types = all_param_types(); - auto supported_types = - std::unordered_set({PT_CHARBUF, PT_FSPATH, PT_FSRELPATH}); + auto supported_types = std::set>( + {{PT_CHARBUF, false}, {PT_FSPATH, false}, {PT_FSRELPATH, false}}); auto test_cases = std::vector>{ {"/home/ubuntu/hello.txt", "hello.txt"}, @@ -230,24 +258,22 @@ TEST(sinsp_filter_transformer, basename) { sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); } - // check for unsupported types - for(auto t : all_types) { - if(supported_types.find(t) == supported_types.end()) { - auto vals = sample_vals; - EXPECT_FALSE(tr.transform_type(t)) << supported_type_msg(t, false); - EXPECT_ANY_THROW(tr.transform_values(vals, t)) << supported_type_msg(t, false); - } - } + check_unsupported_types(tr, supported_types, sample_vals); // check for supported types for(auto t : supported_types) { - auto original = t; - EXPECT_TRUE(tr.transform_type(t)) << supported_type_msg(original, true); - EXPECT_EQ(original, t); // note: basename is expected not to alter the type + auto original_type = t.first; + uint32_t flags = t.second ? EPF_IS_LIST : 0; + auto transformed_type = original_type; + EXPECT_TRUE(tr.transform_type(transformed_type, flags)) + << supported_type_msg(original_type, t.second, true); + EXPECT_EQ(original_type, + transformed_type); // note: basename is expected not to alter the type auto vals = sample_vals; - EXPECT_TRUE(tr.transform_values(vals, t)) << supported_type_msg(original, true); - EXPECT_EQ(original, t); + EXPECT_TRUE(tr.transform_values(vals, transformed_type, flags)) + << supported_type_msg(original_type, t.second, true); + EXPECT_EQ(original_type, transformed_type); EXPECT_EQ(vals.size(), test_cases.size()); for(uint32_t i = 0; i < test_cases.size(); i++) { @@ -258,6 +284,50 @@ TEST(sinsp_filter_transformer, basename) { } } +TEST(sinsp_filter_transformer, len_list) { + sinsp_filter_transformer tr(filter_transformer_type::FTR_LEN); + + auto all_types = all_param_types(); + + std::vector list_values = {"value 1", "value 2", "value 3", "value 4"}; + std::vector list; + + for(auto &tc : list_values) { + list.push_back(const_str_to_extract_value(tc.c_str())); + } + + auto original_type = PT_CHARBUF; + uint32_t original_flags = EPF_IS_LIST; + auto type = original_type; + auto flags = original_flags; + EXPECT_TRUE(tr.transform_type(type, flags)) << supported_type_msg(original_type, true, true); + EXPECT_EQ(type, PT_UINT64); + EXPECT_EQ(flags & EPF_IS_LIST, 0); + + type = original_type; + flags = original_flags; + auto vals = list; + EXPECT_TRUE(tr.transform_values(vals, type, flags)) + << supported_type_msg(original_type, true, true); + EXPECT_EQ(type, PT_UINT64); + EXPECT_EQ(flags & EPF_IS_LIST, 0); + ASSERT_EQ(vals.size(), 1); + + EXPECT_EQ(extract_value_to_scalar(vals[0]), list_values.size()); + + std::vector empty_list; + type = original_type; + flags = original_flags; + vals = empty_list; + EXPECT_TRUE(tr.transform_values(vals, type, flags)) + << supported_type_msg(original_type, true, true); + EXPECT_EQ(type, PT_UINT64); + EXPECT_EQ(flags & EPF_IS_LIST, 0); + ASSERT_EQ(vals.size(), 1); + + EXPECT_EQ(extract_value_to_scalar(vals[0]), 0); +} + TEST_F(sinsp_with_test_input, basename_transformer) { add_default_init_thread(); open_inspector(); @@ -281,3 +351,44 @@ TEST_F(sinsp_with_test_input, basename_transformer) { EXPECT_TRUE(eval_filter(evt, "basename(fd.name) = file_to_run")); EXPECT_FALSE(eval_filter(evt, "basename(fd.name) = /tmp/file_to_run")); } + +TEST_F(sinsp_with_test_input, len_transformer) { + add_default_init_thread(); + open_inspector(); + + sinsp_evt *evt; + + int64_t dirfd = 3; + const char *file_to_run = "/tmp/file_to_run"; + + evt = add_event_advance_ts(increasing_ts(), + 1, + PPME_SYSCALL_OPEN_X, + 6, + dirfd, + file_to_run, + 0, + 0, + 0, + (uint64_t)0); + + // fd.types = (file) + EXPECT_TRUE(eval_filter(evt, "len(fd.types) = 1")); + EXPECT_TRUE(eval_filter(evt, "len(fd.types) > 0")); + EXPECT_FALSE(eval_filter(evt, "len(fd.types) = 0")); + + EXPECT_TRUE(eval_filter(evt, "len(fd.name) = 16")); + + int64_t client_fd = 9; + add_event_advance_ts(increasing_ts(), + 1, + PPME_SOCKET_SOCKET_E, + 3, + (uint32_t)PPM_AF_INET, + (uint32_t)SOCK_STREAM, + (uint32_t)0); + evt = add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_SOCKET_X, 1, client_fd); + + // fd.types = (ipv4,file) + EXPECT_TRUE(eval_filter(evt, "len(fd.types) = 2")); +} From e90d345b487deac77c9869f99af634166227b395 Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Wed, 23 Oct 2024 15:24:22 +0200 Subject: [PATCH 3/6] Update userspace/libsinsp/sinsp_filter_transformer.cpp Co-authored-by: Jason Dellaluce Signed-off-by: Luca Guerra --- userspace/libsinsp/sinsp_filter_transformer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userspace/libsinsp/sinsp_filter_transformer.cpp b/userspace/libsinsp/sinsp_filter_transformer.cpp index d57278434a..060b0be56b 100644 --- a/userspace/libsinsp/sinsp_filter_transformer.cpp +++ b/userspace/libsinsp/sinsp_filter_transformer.cpp @@ -116,7 +116,7 @@ bool sinsp_filter_transformer::transform_type(ppm_param_type& t, uint32_t& flags case FTR_LEN: { if(is_list) { t = PT_UINT64; - flags = 0; + flags = flags & ~EPF_IS_LIST; return true; } switch(t) { From ca9683131f126d1efc85767a16e31ad1a4b11edd Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Wed, 30 Oct 2024 15:36:37 +0000 Subject: [PATCH 4/6] update(libsinsp): address review, comment, allow t(list) = (t(list[0]), t(list[i]))... Signed-off-by: Luca Guerra --- .../libsinsp/sinsp_filter_transformer.cpp | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/userspace/libsinsp/sinsp_filter_transformer.cpp b/userspace/libsinsp/sinsp_filter_transformer.cpp index 060b0be56b..9d04cf7770 100644 --- a/userspace/libsinsp/sinsp_filter_transformer.cpp +++ b/userspace/libsinsp/sinsp_filter_transformer.cpp @@ -72,7 +72,7 @@ bool sinsp_filter_transformer::transform_type(ppm_param_type& t, uint32_t& flags case PT_FSPATH: case PT_FSRELPATH: // for TOUPPER, the transformed type is the same as the input type - return !is_list; + return true; default: return false; } @@ -83,7 +83,7 @@ bool sinsp_filter_transformer::transform_type(ppm_param_type& t, uint32_t& flags case PT_FSPATH: case PT_FSRELPATH: // for TOLOWER, the transformed type is the same as the input type - return !is_list; + return true; default: return false; } @@ -93,7 +93,7 @@ bool sinsp_filter_transformer::transform_type(ppm_param_type& t, uint32_t& flags case PT_CHARBUF: case PT_BYTEBUF: // for BASE64, the transformed type is the same as the input type - return !is_list; + return true; default: return false; } @@ -108,7 +108,7 @@ bool sinsp_filter_transformer::transform_type(ppm_param_type& t, uint32_t& flags case PT_FSPATH: case PT_FSRELPATH: // for BASENAME, the transformed type is the same as the input type - return !is_list; + return true; default: return false; } @@ -226,23 +226,31 @@ bool sinsp_filter_transformer::transform_values(std::vector& ve return false; } - for(std::size_t i = 0; i < vec.size(); i++) { - uint64_t len; - if(vec[i].ptr == nullptr) { - vec[i] = store_scalar(0); - continue; - } + if(vec.size() == 0) { + // nothing to do + return true; + } - if(is_string) { - len = static_cast( - strnlen(reinterpret_cast(vec[i].ptr), vec[i].len)); - vec[i] = store_scalar(len); - continue; - } + // we are assuming that if this is not a list then it's a single element + assert((void("non-list elements to transform with len() must be a vector with a single " + "element"), + vec.size() == 1)); + uint64_t len; + if(vec[0].ptr == nullptr) { + vec[0] = store_scalar(0); + return true; + } - len = static_cast(vec[i].len); - vec[i] = store_scalar(len); + if(is_string) { + len = static_cast( + strnlen(reinterpret_cast(vec[0].ptr), vec[0].len)); + vec[0] = store_scalar(len); + return true; } + + // buffer + len = static_cast(vec[0].len); + vec[0] = store_scalar(len); return true; } default: From 5263f40b09da88e73b7dc3951537c08238235679 Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Wed, 30 Oct 2024 15:37:02 +0000 Subject: [PATCH 5/6] update(libsinsp/tests): rewrite transformer test to test list/nonlist cases Signed-off-by: Luca Guerra --- .../libsinsp/test/filter_transformer.ut.cpp | 443 +++++++++--------- 1 file changed, 231 insertions(+), 212 deletions(-) diff --git a/userspace/libsinsp/test/filter_transformer.ut.cpp b/userspace/libsinsp/test/filter_transformer.ut.cpp index d66647d427..d40ce639c9 100644 --- a/userspace/libsinsp/test/filter_transformer.ut.cpp +++ b/userspace/libsinsp/test/filter_transformer.ut.cpp @@ -31,51 +31,80 @@ static std::set all_param_types() { return ret; } +template +static T extract_value_to_scalar(const extract_value_t& val) { + T ret; + EXPECT_EQ(val.len, sizeof(T)); + memcpy(&ret, val.ptr, val.len); + return ret; +} + static std::string supported_type_msg(ppm_param_type t, bool flags, bool support_expected) { return std::string("expected ") + (flags ? "list " : "") + "param type to" + std::string((support_expected ? " " : " not ")) + "be supported: " + std::string(param_type_to_string(t)); } -static std::string eq_test_msg(const std::pair &tc) { - return "expected '" + tc.first + "' (length: " + std::to_string(tc.first.length()) + ")" + - " to be equal to '" + tc.second + "' (length: " + std::to_string(tc.second.length()) + - ")"; +static std::string eq_test_msg(extract_value_t actual, extract_value_t expected) { + return "expected '" + std::string(reinterpret_cast(actual.ptr)) + + "' (length: " + std::to_string(actual.len) + ")" + " to be equal to '" + + std::string(reinterpret_cast(expected.ptr)) + + "' (length: " + std::to_string(expected.len) + ")"; } -static extract_value_t const_str_to_extract_value(const char *v) { - extract_value_t ret; - ret.ptr = (uint8_t *)v; - ret.len = strlen(v) + 1; - return ret; -} +struct ex_value : public extract_value_t { + ex_value(const ex_value& val) { + m_storage = val.m_storage; + len = val.len; -template -static T extract_value_to_scalar(const extract_value_t &val) { - T ret; - EXPECT_EQ(val.len, sizeof(T)); - memcpy(&ret, val.ptr, val.len); - return ret; -} + ptr = (uint8_t*)m_storage.data(); + } + + ex_value(std::string val) { + m_storage = std::vector(val.c_str(), val.c_str() + val.size() + 1); + + len = m_storage.size(); + ptr = (uint8_t*)m_storage.data(); + } -static void check_unsupported_types(sinsp_filter_transformer &tr, - std::set> &supported_types, - std::vector sample_vals) { + ex_value(uint64_t val) { + uint8_t* bytes = reinterpret_cast(&val); + m_storage = std::vector(bytes, bytes + sizeof(uint64_t)); + + len = sizeof(val); + ptr = (uint8_t*)m_storage.data(); + } + + std::vector m_storage; +}; + +struct test_case_entry { + uint32_t flags; + ppm_param_type input_type; + std::vector input; + ppm_param_type expected_type; + std::vector expected; +}; + +static void check_unsupported_types(sinsp_filter_transformer& tr, + std::set& supported_types, + std::set& supported_list_types) { auto all_types = all_param_types(); for(auto t : all_types) { uint32_t flags = EPF_IS_LIST; - if(supported_types.find({t, flags}) == supported_types.end()) { - auto vals = sample_vals; + if(supported_list_types.find(t) == supported_list_types.end()) { EXPECT_FALSE(tr.transform_type(t, flags)) << supported_type_msg(t, flags, false); + // vals is empty for simplicity, should not affect the test + std::vector vals{}; EXPECT_ANY_THROW(tr.transform_values(vals, t, flags)) << supported_type_msg(t, flags, false); } flags = 0; - if(supported_types.find({t, flags}) == supported_types.end()) { - auto vals = sample_vals; + if(supported_types.find(t) == supported_types.end()) { EXPECT_FALSE(tr.transform_type(t, flags)) << supported_type_msg(t, flags, false); + std::vector vals{}; EXPECT_ANY_THROW(tr.transform_values(vals, t, flags)) << supported_type_msg(t, flags, false); } @@ -85,46 +114,46 @@ static void check_unsupported_types(sinsp_filter_transformer &tr, TEST(sinsp_filter_transformer, toupper) { sinsp_filter_transformer tr(filter_transformer_type::FTR_TOUPPER); - auto all_types = all_param_types(); - - auto supported_types = std::set>( - {{PT_CHARBUF, false}, {PT_FSPATH, false}, {PT_FSRELPATH, false}}); - - auto test_cases = std::vector>{ - {"hello", "HELLO"}, - {"world", "WORLD"}, - {"eXcItED", "EXCITED"}, - {"", ""}, + std::set supported_types{PT_CHARBUF, PT_FSPATH, PT_FSRELPATH}; + std::set supported_list_types = supported_types; + + check_unsupported_types(tr, supported_types, supported_list_types); + + std::vector test_cases{ + {0, PT_CHARBUF, {{"hello"}}, PT_CHARBUF, {{"HELLO"}}}, + {0, PT_CHARBUF, {{"WORLD"}}, PT_CHARBUF, {{"WORLD"}}}, + {0, PT_CHARBUF, {{"eXcItED"}}, PT_CHARBUF, {{"EXCITED"}}}, + {0, PT_CHARBUF, {{""}}, PT_CHARBUF, {{""}}}, + {0, + PT_CHARBUF, + {{"hello"}, {"wOrLd"}, {"ONE_1234"}}, + PT_CHARBUF, + {{"HELLO"}, {"WORLD"}, {"ONE_1234"}}}, }; - std::vector sample_vals; - - for(auto &tc : test_cases) { - sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); - } - - check_unsupported_types(tr, supported_types, sample_vals); - - // check for supported types - for(auto t : supported_types) { - auto original_type = t.first; - uint32_t flags = t.second ? EPF_IS_LIST : 0; - auto transformed_type = original_type; + for(auto const& tc : test_cases) { + auto transformed_type = tc.input_type; + uint32_t flags = tc.flags; + bool is_list = flags & EPF_IS_LIST; EXPECT_TRUE(tr.transform_type(transformed_type, flags)) - << supported_type_msg(original_type, t.second, true); - EXPECT_EQ(original_type, - transformed_type); // note: toupper is expected not to alter the type + << supported_type_msg(tc.input_type, is_list, true); + EXPECT_EQ(transformed_type, tc.expected_type); + + std::vector vals{}; + for(auto const& val : tc.input) { + vals.push_back(val); + } - auto vals = sample_vals; + transformed_type = tc.input_type; EXPECT_TRUE(tr.transform_values(vals, transformed_type, flags)) - << supported_type_msg(original_type, t.second, true); - EXPECT_EQ(original_type, transformed_type); - EXPECT_EQ(vals.size(), test_cases.size()); - - for(uint32_t i = 0; i < test_cases.size(); i++) { - EXPECT_EQ(std::string((const char *)vals[i].ptr), test_cases[i].second) - << eq_test_msg(test_cases[i]); - EXPECT_EQ(vals[i].len, test_cases[i].second.length() + 1) << eq_test_msg(test_cases[i]); + << supported_type_msg(tc.input_type, is_list, true); + EXPECT_EQ(vals.size(), tc.expected.size()); + + for(std::vector::size_type i = 0; i < vals.size(); i++) { + std::string actual = (const char*)vals[i].ptr; + std::string expected = (const char*)tc.expected[i].ptr; + EXPECT_EQ(actual, expected) << eq_test_msg(vals[i], tc.expected[i]); + EXPECT_EQ(vals[i].len, tc.expected[i].len) << eq_test_msg(vals[i], tc.expected[i]); } } } @@ -132,46 +161,47 @@ TEST(sinsp_filter_transformer, toupper) { TEST(sinsp_filter_transformer, tolower) { sinsp_filter_transformer tr(filter_transformer_type::FTR_TOLOWER); - auto all_types = all_param_types(); - - auto supported_types = std::set>( - {{PT_CHARBUF, false}, {PT_FSPATH, false}, {PT_FSRELPATH, false}}); - - auto test_cases = std::vector>{ - {"HELLO", "hello"}, - {"world", "world"}, - {"NoT_eXcItED", "not_excited"}, - {"", ""}, + std::set supported_types{PT_CHARBUF, PT_FSPATH, PT_FSRELPATH}; + std::set supported_list_types = supported_types; + + check_unsupported_types(tr, supported_types, supported_list_types); + + std::vector test_cases{ + {0, PT_CHARBUF, {{"HELLO"}}, PT_CHARBUF, {{"hello"}}}, + {0, PT_CHARBUF, {{"world"}}, PT_CHARBUF, {{"world"}}}, + {0, PT_CHARBUF, {{"NoT eXcItED"}}, PT_CHARBUF, {{"not excited"}}}, + {0, PT_CHARBUF, {{""}}, PT_CHARBUF, {{""}}}, + {EPF_IS_LIST, + PT_CHARBUF, + {{"HELLO"}, {"wOrLd"}, {"one_1234"}}, + PT_CHARBUF, + {{"hello"}, {"world"}, {"one_1234"}}}, }; - std::vector sample_vals; + for(auto const& tc : test_cases) { + bool is_list = tc.flags & EPF_IS_LIST; + uint32_t flags = tc.flags; + auto transformed_type = tc.input_type; - for(auto &tc : test_cases) { - sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); - } - - check_unsupported_types(tr, supported_types, sample_vals); - - // check for supported types - for(auto t : supported_types) { - auto original_type = t.first; - uint32_t flags = t.second ? EPF_IS_LIST : 0; - auto transformed_type = original_type; EXPECT_TRUE(tr.transform_type(transformed_type, flags)) - << supported_type_msg(original_type, t.second, true); - EXPECT_EQ(original_type, - transformed_type); // note: tolower is expected not to alter the type + << supported_type_msg(tc.input_type, is_list, true); + EXPECT_EQ(transformed_type, tc.expected_type); - auto vals = sample_vals; + std::vector vals{}; + for(auto const& val : tc.input) { + vals.push_back(val); + } + + transformed_type = tc.input_type; EXPECT_TRUE(tr.transform_values(vals, transformed_type, flags)) - << supported_type_msg(original_type, t.second, true); - EXPECT_EQ(original_type, transformed_type); - EXPECT_EQ(vals.size(), test_cases.size()); - - for(uint32_t i = 0; i < test_cases.size(); i++) { - EXPECT_EQ(std::string((const char *)vals[i].ptr), test_cases[i].second) - << eq_test_msg(test_cases[i]); - EXPECT_EQ(vals[i].len, test_cases[i].second.length() + 1) << eq_test_msg(test_cases[i]); + << supported_type_msg(tc.input_type, is_list, true); + EXPECT_EQ(vals.size(), tc.expected.size()); + + for(std::vector::size_type i = 0; i < vals.size(); i++) { + std::string actual = (const char*)vals[i].ptr; + std::string expected = (const char*)tc.expected[i].ptr; + EXPECT_EQ(actual, expected) << eq_test_msg(vals[i], tc.expected[i]); + EXPECT_EQ(vals[i].len, tc.expected[i].len) << eq_test_msg(vals[i], tc.expected[i]); } } } @@ -179,163 +209,142 @@ TEST(sinsp_filter_transformer, tolower) { TEST(sinsp_filter_transformer, b64) { sinsp_filter_transformer tr(filter_transformer_type::FTR_BASE64); - auto all_types = all_param_types(); + std::set supported_types{PT_CHARBUF, PT_FSPATH, PT_FSRELPATH, PT_BYTEBUF}; + std::set supported_list_types = supported_types; - auto supported_types = - std::set>({{PT_CHARBUF, false}, {PT_BYTEBUF, false}}); + check_unsupported_types(tr, supported_types, supported_list_types); - auto test_cases = std::vector>{ - {"aGVsbG8=", "hello"}, - {"d29ybGQgIQ==", "world !"}, - {"", ""}, + std::vector test_cases{ + {0, PT_CHARBUF, {{"aGVsbG8="}}, PT_CHARBUF, {{"hello"}}}, + {0, PT_CHARBUF, {{"d29ybGQgIQ=="}}, PT_CHARBUF, {{"world !"}}}, + {0, PT_CHARBUF, {{""}}, PT_CHARBUF, {{""}}}, }; - std::vector invalid_test_cases{"!!!"}; + for(auto const& tc : test_cases) { + bool is_list = tc.flags & EPF_IS_LIST; + uint32_t flags = tc.flags; + auto transformed_type = tc.input_type; - std::vector sample_vals; - for(auto &tc : test_cases) { - sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); - } - - check_unsupported_types(tr, supported_types, sample_vals); - - // check for supported types - for(auto t : supported_types) { - auto original_type = t.first; - uint32_t flags = t.second ? EPF_IS_LIST : 0; - auto transformed_type = original_type; EXPECT_TRUE(tr.transform_type(transformed_type, flags)) - << supported_type_msg(original_type, t.second, true); - EXPECT_EQ(original_type, transformed_type); // note: b64 is expected not to alter the type + << supported_type_msg(tc.input_type, is_list, true); + EXPECT_EQ(transformed_type, tc.expected_type); - auto vals = sample_vals; - EXPECT_TRUE(tr.transform_values(vals, transformed_type, flags)) - << supported_type_msg(original_type, t.second, true); - EXPECT_EQ(original_type, transformed_type); - EXPECT_EQ(vals.size(), test_cases.size()); - - for(uint32_t i = 0; i < test_cases.size(); i++) { - EXPECT_EQ(std::string((const char *)vals[i].ptr), test_cases[i].second) - << eq_test_msg(test_cases[i]); - EXPECT_EQ(vals[i].len, test_cases[i].second.length() + 1) << eq_test_msg(test_cases[i]); + std::vector vals{}; + for(auto const& val : tc.input) { + vals.push_back(val); } - } - - std::vector invalid_vals; - for(auto &tc : invalid_test_cases) { - invalid_vals.push_back(const_str_to_extract_value(tc.c_str())); - } - // check invalid input being rejected - { - auto t = PT_CHARBUF; - uint32_t flags = 0; - EXPECT_FALSE(tr.transform_values(invalid_vals, t, flags)); - EXPECT_EQ(t, PT_CHARBUF); + transformed_type = tc.input_type; + EXPECT_TRUE(tr.transform_values(vals, transformed_type, flags)) + << supported_type_msg(tc.input_type, is_list, true); + EXPECT_EQ(vals.size(), tc.expected.size()); + + for(std::vector::size_type i = 0; i < vals.size(); i++) { + std::string actual = (const char*)vals[i].ptr; + std::string expected = (const char*)tc.expected[i].ptr; + EXPECT_EQ(actual, expected) << eq_test_msg(vals[i], tc.expected[i]); + EXPECT_EQ(vals[i].len, tc.expected[i].len) << eq_test_msg(vals[i], tc.expected[i]); + } } } TEST(sinsp_filter_transformer, basename) { sinsp_filter_transformer tr(filter_transformer_type::FTR_BASENAME); - auto all_types = all_param_types(); + std::set supported_types{PT_CHARBUF, PT_FSPATH, PT_FSRELPATH}; + std::set supported_list_types = supported_types; - auto supported_types = std::set>( - {{PT_CHARBUF, false}, {PT_FSPATH, false}, {PT_FSRELPATH, false}}); + check_unsupported_types(tr, supported_types, supported_list_types); - auto test_cases = std::vector>{ - {"/home/ubuntu/hello.txt", "hello.txt"}, - {"/usr/local/bin/cat", "cat"}, - {"/", ""}, - {"", ""}, - {"/hello/", ""}, - {"hello", "hello"}, + std::vector test_cases{ + {0, PT_CHARBUF, {{"/home/ubuntu/hello.txt"}}, PT_CHARBUF, {{"hello.txt"}}}, + {0, PT_FSPATH, {{"/usr/local/bin/cat"}}, PT_FSPATH, {{"cat"}}}, + {0, PT_FSPATH, {{"/"}}, PT_FSPATH, {{""}}}, + {0, PT_CHARBUF, {{"/hello/"}}, PT_CHARBUF, {{""}}}, + {0, PT_CHARBUF, {{"hello"}}, PT_CHARBUF, {{"hello"}}}, + {0, PT_CHARBUF, {{""}}, PT_CHARBUF, {{""}}}, }; - std::vector sample_vals; - - for(auto &tc : test_cases) { - sample_vals.push_back(const_str_to_extract_value(tc.first.c_str())); - } - - check_unsupported_types(tr, supported_types, sample_vals); + for(auto const& tc : test_cases) { + bool is_list = tc.flags & EPF_IS_LIST; + uint32_t flags = tc.flags; + auto transformed_type = tc.input_type; - // check for supported types - for(auto t : supported_types) { - auto original_type = t.first; - uint32_t flags = t.second ? EPF_IS_LIST : 0; - auto transformed_type = original_type; EXPECT_TRUE(tr.transform_type(transformed_type, flags)) - << supported_type_msg(original_type, t.second, true); - EXPECT_EQ(original_type, - transformed_type); // note: basename is expected not to alter the type + << supported_type_msg(tc.input_type, is_list, true); + EXPECT_EQ(transformed_type, tc.expected_type); - auto vals = sample_vals; + std::vector vals{}; + for(auto const& val : tc.input) { + vals.push_back(val); + } + + transformed_type = tc.input_type; EXPECT_TRUE(tr.transform_values(vals, transformed_type, flags)) - << supported_type_msg(original_type, t.second, true); - EXPECT_EQ(original_type, transformed_type); - EXPECT_EQ(vals.size(), test_cases.size()); - - for(uint32_t i = 0; i < test_cases.size(); i++) { - EXPECT_EQ(std::string((const char *)vals[i].ptr), test_cases[i].second) - << eq_test_msg(test_cases[i]); - EXPECT_EQ(vals[i].len, test_cases[i].second.length() + 1) << eq_test_msg(test_cases[i]); + << supported_type_msg(tc.input_type, is_list, true); + EXPECT_EQ(vals.size(), tc.expected.size()); + + for(std::vector::size_type i = 0; i < vals.size(); i++) { + std::string actual = (const char*)vals[i].ptr; + std::string expected = (const char*)tc.expected[i].ptr; + EXPECT_EQ(actual, expected) << eq_test_msg(vals[i], tc.expected[i]); + EXPECT_EQ(vals[i].len, tc.expected[i].len) << eq_test_msg(vals[i], tc.expected[i]); } } } -TEST(sinsp_filter_transformer, len_list) { +TEST(sinsp_filter_transformer, len) { sinsp_filter_transformer tr(filter_transformer_type::FTR_LEN); - auto all_types = all_param_types(); + std::set supported_types{PT_CHARBUF, PT_FSPATH, PT_FSRELPATH, PT_BYTEBUF}; + std::set supported_list_types = all_param_types(); - std::vector list_values = {"value 1", "value 2", "value 3", "value 4"}; - std::vector list; + check_unsupported_types(tr, supported_types, supported_list_types); - for(auto &tc : list_values) { - list.push_back(const_str_to_extract_value(tc.c_str())); - } + std::vector test_cases{ + {0, PT_CHARBUF, {{"/home/ubuntu/hello.txt"}}, PT_UINT64, {{22}}}, + {0, PT_FSPATH, {{"/"}}, PT_UINT64, {{1}}}, + {EPF_IS_LIST, PT_FSPATH, {{"/hello"}}, PT_UINT64, {{1}}}, + {EPF_IS_LIST, PT_CHARBUF, {}, PT_UINT64, {{0}}}, + {EPF_IS_LIST, PT_UINT64, {{1}, {2}, {3}, {4}, {5}}, PT_UINT64, {{5}}}, + }; + + for(auto const& tc : test_cases) { + bool is_list = tc.flags & EPF_IS_LIST; + uint32_t flags = tc.flags; + auto transformed_type = tc.input_type; + + EXPECT_TRUE(tr.transform_type(transformed_type, flags)) + << supported_type_msg(tc.input_type, is_list, true); + EXPECT_EQ(transformed_type, tc.expected_type); + + std::vector vals{}; + for(auto const& val : tc.input) { + vals.push_back(val); + } + + transformed_type = tc.input_type; + flags = tc.flags; + EXPECT_TRUE(tr.transform_values(vals, transformed_type, flags)) + << supported_type_msg(tc.input_type, is_list, true); + EXPECT_EQ(vals.size(), tc.expected.size()); - auto original_type = PT_CHARBUF; - uint32_t original_flags = EPF_IS_LIST; - auto type = original_type; - auto flags = original_flags; - EXPECT_TRUE(tr.transform_type(type, flags)) << supported_type_msg(original_type, true, true); - EXPECT_EQ(type, PT_UINT64); - EXPECT_EQ(flags & EPF_IS_LIST, 0); - - type = original_type; - flags = original_flags; - auto vals = list; - EXPECT_TRUE(tr.transform_values(vals, type, flags)) - << supported_type_msg(original_type, true, true); - EXPECT_EQ(type, PT_UINT64); - EXPECT_EQ(flags & EPF_IS_LIST, 0); - ASSERT_EQ(vals.size(), 1); - - EXPECT_EQ(extract_value_to_scalar(vals[0]), list_values.size()); - - std::vector empty_list; - type = original_type; - flags = original_flags; - vals = empty_list; - EXPECT_TRUE(tr.transform_values(vals, type, flags)) - << supported_type_msg(original_type, true, true); - EXPECT_EQ(type, PT_UINT64); - EXPECT_EQ(flags & EPF_IS_LIST, 0); - ASSERT_EQ(vals.size(), 1); - - EXPECT_EQ(extract_value_to_scalar(vals[0]), 0); + for(std::vector::size_type i = 0; i < vals.size(); i++) { + uint64_t actual = extract_value_to_scalar(vals[i]); + uint64_t expected = extract_value_to_scalar(tc.expected[i]); + EXPECT_EQ(actual, expected); + } + } } TEST_F(sinsp_with_test_input, basename_transformer) { add_default_init_thread(); open_inspector(); - sinsp_evt *evt; + sinsp_evt* evt; int64_t dirfd = 3; - const char *file_to_run = "/tmp/file_to_run"; + const char* file_to_run = "/tmp/file_to_run"; add_event_advance_ts(increasing_ts(), 1, PPME_SYSCALL_OPEN_E, 3, file_to_run, 0, 0); evt = add_event_advance_ts(increasing_ts(), 1, @@ -356,10 +365,10 @@ TEST_F(sinsp_with_test_input, len_transformer) { add_default_init_thread(); open_inspector(); - sinsp_evt *evt; + sinsp_evt* evt; int64_t dirfd = 3; - const char *file_to_run = "/tmp/file_to_run"; + const char* file_to_run = "/tmp/file_to_run"; evt = add_event_advance_ts(increasing_ts(), 1, @@ -391,4 +400,14 @@ TEST_F(sinsp_with_test_input, len_transformer) { // fd.types = (ipv4,file) EXPECT_TRUE(eval_filter(evt, "len(fd.types) = 2")); + + uint8_t read_buf[] = {'\x01', '\x02', '\x03', '\x04', '\x05', '\x06'}; + evt = add_event_advance_ts(increasing_ts(), + 1, + PPME_SYSCALL_READ_X, + 2, + (int64_t)0, + scap_const_sized_buffer{read_buf, sizeof(read_buf)}); + + EXPECT_TRUE(eval_filter(evt, "len(evt.arg.data) == 6")); } From 31f613b37e54a6682cd43fbdae98ccec0eceaedf Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Thu, 31 Oct 2024 10:13:20 +0000 Subject: [PATCH 6/6] update(libsinsp): empty transformer storage before filling len() Signed-off-by: Luca Guerra --- userspace/libsinsp/sinsp_filter_transformer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/userspace/libsinsp/sinsp_filter_transformer.cpp b/userspace/libsinsp/sinsp_filter_transformer.cpp index 9d04cf7770..158d39ce86 100644 --- a/userspace/libsinsp/sinsp_filter_transformer.cpp +++ b/userspace/libsinsp/sinsp_filter_transformer.cpp @@ -203,6 +203,7 @@ bool sinsp_filter_transformer::transform_values(std::vector& ve } case FTR_LEN: { assert((void("len() type must be PT_UINT64"), t == PT_UINT64)); + m_storage_values.clear(); if(is_list) { uint64_t len = static_cast(vec.size()); auto stored_val = store_scalar(len); @@ -227,8 +228,9 @@ bool sinsp_filter_transformer::transform_values(std::vector& ve } if(vec.size() == 0) { - // nothing to do - return true; + // should never happen since there is no way to + // call len() with no arguments + return false; } // we are assuming that if this is not a list then it's a single element