From: Bhagya Tholpady (bbantwal) Date: Mon, 10 Aug 2020 15:33:37 +0000 (+0000) Subject: Merge pull request #2373 in SNORT/snort3 from ~OSERHIIE/snort3:help_modules_json... X-Git-Tag: 3.0.2-5~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f990379d638b3087308e403c9ce7543b3961732b;p=thirdparty%2Fsnort3.git Merge pull request #2373 in SNORT/snort3 from ~OSERHIIE/snort3:help_modules_json to master Squashed commit of the following: commit 87484e324090b3d0baa60e5a51bb4f8bc0743890 Author: Oleksandr Serhiienko Date: Mon Aug 3 23:00:45 2020 +0300 cip: fix the trailing parameter for the module commit 31bdafe40d97c6b6a37b23fda5c140ebc3c170f8 Author: Oleksandr Serhiienko Date: Tue Jul 28 23:34:13 2020 +0300 main: add printing modules help in JSON format * main: new CLI option '--help-modules-json' is presented * main: add support for HelpType HT_HMO_JSON * framework: add API to expand maxN literals for ranges in Parameter * managers: add JSON dumpers into ModuleManager * managers: rename 'What' -> 'Help' in the output of '--help-module' CLI option commit 87139f76fa31f68a1b796206e6201afb752d9bb6 Author: Oleksandr Serhiienko Date: Tue Jul 28 17:18:54 2020 +0300 helpers: extend printed JSON syntax * anonymous arrays (root arrays) * JSON keywords (null, true, false) * printing values of floating point type --- diff --git a/src/framework/parameter.cc b/src/framework/parameter.cc index 2471b7f1a..ec15144ff 100644 --- a/src/framework/parameter.cc +++ b/src/framework/parameter.cc @@ -99,7 +99,7 @@ static size_t split(const string& txt, vector& strs) return strs.size(); } -static int64_t get_int(const char* r) +int64_t Parameter::get_int(const char* r) { if ( *r == 'm' ) { @@ -153,7 +153,7 @@ static bool valid_int(Value& v, const char* r) if ( *r != ':' ) { - int64_t low = get_int(r); + int64_t low = Parameter::get_int(r); if ( d < low ) return false; @@ -164,7 +164,7 @@ static bool valid_int(Value& v, const char* r) if ( t and *++t ) { - int64_t hi = get_int(t); + int64_t hi = Parameter::get_int(t); if ( d > hi ) return false; @@ -693,14 +693,14 @@ TEST_CASE("string", "[Parameter]") TEST_CASE("max", "[Parameter]") { - CHECK(get_int("max31") == 2147483647); - CHECK(get_int("max32") == 4294967295); - CHECK(get_int("max53") == 9007199254740992); + CHECK(Parameter::get_int("max31") == 2147483647); + CHECK(Parameter::get_int("max32") == 4294967295); + CHECK(Parameter::get_int("max53") == 9007199254740992); if ( sizeof(size_t) == 4 ) - CHECK(get_int("maxSZ") == 4294967295); + CHECK(Parameter::get_int("maxSZ") == 4294967295); else - CHECK(get_int("maxSZ") == 9007199254740992); + CHECK(Parameter::get_int("maxSZ") == 9007199254740992); } #endif diff --git a/src/framework/parameter.h b/src/framework/parameter.h index bfe8a5f81..439a021e2 100644 --- a/src/framework/parameter.h +++ b/src/framework/parameter.h @@ -96,6 +96,9 @@ struct SO_PUBLIC Parameter // 0-based; -1 if not found; list is | delimited static int index(const char* list, const char* key); + + // convert string to long (including 'maxN' literals) + static int64_t get_int(const char*); }; } #endif diff --git a/src/helpers/json_stream.cc b/src/helpers/json_stream.cc index be554bdbf..545c37ebf 100644 --- a/src/helpers/json_stream.cc +++ b/src/helpers/json_stream.cc @@ -41,9 +41,10 @@ void JsonStream::open(const char* key) void JsonStream::close() { out << " }"; + sep = true; assert(level > 0); - if ( --level == 0 ) + if ( --level == 0 and !level_array ) { out << std::endl; sep = false; @@ -53,14 +54,36 @@ void JsonStream::close() void JsonStream::open_array(const char* key) { split(); - out << std::quoted(key) << ": [ "; + + if ( key ) + out << std::quoted(key) << ": "; + + out << "[ "; sep = false; + level_array++; } void JsonStream::close_array() { out << " ]"; sep = true; + assert(level_array > 0); + + if ( --level_array == 0 and !level ) + { + out << std::endl; + sep = false; + } +} + +void JsonStream::put(const char* key) +{ + split(); + + if ( key ) + out << std::quoted(key) << ": "; + + out << "null"; } void JsonStream::put(const char* key, long val) @@ -86,6 +109,37 @@ void JsonStream::put(const char* key, const std::string& val) out << std::quoted(val); } +void JsonStream::put(const char* key, double val, int precision) +{ + split(); + + if ( key ) + out << std::quoted(key) << ": "; + + out.precision(precision); + out << std::fixed << val; +} + +void JsonStream::put_true(const char* key) +{ + split(); + + if ( key ) + out << std::quoted(key) << ": "; + + out << "true"; +} + +void JsonStream::put_false(const char* key) +{ + split(); + + if ( key ) + out << std::quoted(key) << ": "; + + out << "false"; +} + void JsonStream::split() { if ( sep ) diff --git a/src/helpers/json_stream.h b/src/helpers/json_stream.h index 7a4cf8656..211656035 100644 --- a/src/helpers/json_stream.h +++ b/src/helpers/json_stream.h @@ -33,11 +33,16 @@ public: void open(const char* key = nullptr); void close(); - void open_array(const char* key); + void open_array(const char* key = nullptr); void close_array(); + void put(const char* key); // null void put(const char* key, long val); void put(const char* key, const std::string& val); + void put(const char* key, double val, int precision); + + void put_true(const char* key); + void put_false(const char* key); private: void split(); @@ -46,6 +51,7 @@ private: std::ostream& out; bool sep = false; unsigned level = 0; + unsigned level_array = 0; }; #endif diff --git a/src/helpers/test/json_stream_test.cc b/src/helpers/test/json_stream_test.cc index be07e6671..e59c278ae 100644 --- a/src/helpers/test/json_stream_test.cc +++ b/src/helpers/test/json_stream_test.cc @@ -39,11 +39,52 @@ TEST_CASE("basic", "[json_stream]") CHECK(ss.str() == "{ }\n"); } + SECTION("empty root array") + { + js.open_array(); + js.close_array(); + const char* x = R"-([ ])-" "\n"; + CHECK(ss.str() == x); + } + + SECTION("empty object") + { + js.open(); + js.open("o"); + js.close(); + js.close(); + const char* x = R"-({ "o": { } })-" "\n"; + CHECK(ss.str() == x); + } + SECTION("empty array") { + js.open(); js.open_array("a"); js.close_array(); - const char* x = R"-("a": [ ])-"; + js.close(); + const char* x = R"-({ "a": [ ] })-" "\n"; + CHECK(ss.str() == x); + } + + SECTION("null") + { + js.put("n"); + const char* x = R"-("n": null)-"; + CHECK(ss.str() == x); + } + + SECTION("bool true") + { + js.put_true("b"); + const char* x = R"-("b": true)-"; + CHECK(ss.str() == x); + } + + SECTION("bool false") + { + js.put_false("b"); + const char* x = R"-("b": false)-"; CHECK(ss.str() == x); } @@ -54,6 +95,13 @@ TEST_CASE("basic", "[json_stream]") CHECK(ss.str() == x); } + SECTION("real") + { + js.put("r", 2.5, 2); + const char* x = R"-("r": 2.50)-"; + CHECK(ss.str() == x); + } + SECTION("string") { js.put("s", "yo"); @@ -68,12 +116,36 @@ TEST_CASE("basic", "[json_stream]") CHECK(ss.str() == ""); } + SECTION("null item") + { + js.put(nullptr); + CHECK(ss.str() == "null"); + } + + SECTION("bool true item") + { + js.put_true(nullptr); + CHECK(ss.str() == "true"); + } + + SECTION("bool false item") + { + js.put_false(nullptr); + CHECK(ss.str() == "false"); + } + SECTION("int item") { js.put(nullptr, 1); CHECK(ss.str() == "1"); } + SECTION("real item") + { + js.put(nullptr, 2.5, 2); + CHECK(ss.str() == "2.50"); + } + SECTION("string item") { js.put(nullptr, "it"); @@ -89,6 +161,22 @@ TEST_CASE("basic", "[json_stream]") CHECK(ss.str() == x); } + SECTION("null list") + { + js.put("i"); + js.put("j"); + const char* x = R"-("i": null, "j": null)-"; + CHECK(ss.str() == x); + } + + SECTION("bool list") + { + js.put_true("i"); + js.put_false("j"); + const char* x = R"-("i": true, "j": false)-"; + CHECK(ss.str() == x); + } + SECTION("int list") { js.put("i", 2); @@ -118,6 +206,32 @@ TEST_CASE("basic", "[json_stream]") CHECK(ss.str() == x); } + SECTION("null array") + { + js.open(); + js.open_array("n"); + js.put(nullptr); + js.put(nullptr); + js.put(nullptr); + js.close_array(); + js.close(); + const char* x = R"-({ "n": [ null, null, null ] })-" "\n"; + CHECK(ss.str() == x); + } + + SECTION("bool array") + { + js.open(); + js.open_array("b"); + js.put_true(nullptr); + js.put_false(nullptr); + js.put_true(nullptr); + js.close_array(); + js.close(); + const char* x = R"-({ "b": [ true, false, true ] })-" "\n"; + CHECK(ss.str() == x); + } + SECTION("int array") { js.open(); @@ -131,27 +245,27 @@ TEST_CASE("basic", "[json_stream]") CHECK(ss.str() == x); } - SECTION("string array") + SECTION("real array") { js.open(); - js.open_array("v"); - js.put(nullptr, "long"); - js.put(nullptr, "road"); + js.open_array("r"); + js.put(nullptr, 2.556, 3); + js.put(nullptr, 3.7778, 4); js.close_array(); js.close(); - const char* x = R"-({ "v": [ "long", "road" ] })-" "\n"; + const char* x = R"-({ "r": [ 2.556, 3.7778 ] })-" "\n"; CHECK(ss.str() == x); } - SECTION("array list") + SECTION("string array") { js.open(); - js.open_array("m"); - js.close_array(); - js.open_array("n"); + js.open_array("v"); + js.put(nullptr, "long"); + js.put(nullptr, "road"); js.close_array(); js.close(); - const char* x = R"-({ "m": [ ], "n": [ ] })-" "\n"; + const char* x = R"-({ "v": [ "long", "road" ] })-" "\n"; CHECK(ss.str() == x); } @@ -178,5 +292,72 @@ TEST_CASE("basic", "[json_stream]") const char* x = R"-({ "c": "Snort", "n": [ ], "d": "++" })-" "\n"; CHECK(ss.str() == x); } + + SECTION("root array of objects") + { + js.open_array(); + js.open(); + js.close(); + js.open(); + js.close(); + js.open(); + js.close(); + js.close_array(); + const char* x = R"-([ { }, { }, { } ])-" "\n"; + CHECK(ss.str() == x); + } + + SECTION("root array of objects with nested arrays of objects") + { + js.open_array(); + js.open(); + js.put("i", 1); + js.open_array("array_1"); + js.open(); + js.put("str", "Snort"); + js.close(); + js.open(); + js.put("str", "++"); + js.close(); + js.close_array(); + js.put("j", 2); + js.close(); + js.open(); + js.put("i", 3); + js.open_array("array_2"); + js.open(); + js.put("str", "IPS"); + js.close(); + js.open(); + js.put("str", "IDS"); + js.close(); + js.close_array(); + js.put("j", 4); + js.close(); + js.close_array(); + const char* x = R"-([ { "i": 1, "array_1": [ { "str": "Snort" }, { "str": "++" } ],)-" + R"-( "j": 2 }, { "i": 3, "array_2": [ { "str": "IPS" }, { "str": "IDS" } ],)-" + R"-( "j": 4 } ])-" "\n"; + CHECK(ss.str() == x); + } + + SECTION("root object with nested objects") + { + js.open(); + js.open_array("keys"); + js.put(nullptr, "name"); + js.put(nullptr, "version"); + js.close_array(); + js.open("name"); + js.put("value", "Snort"); + js.close(); + js.open("version"); + js.put("value", "3.0.0"); + js.close(); + js.close(); + const char* x = R"-({ "keys": [ "name", "version" ], "name": { "value": "Snort" },)-" + R"-( "version": { "value": "3.0.0" } })-" "\n"; + CHECK(ss.str() == x); + } } diff --git a/src/main/help.cc b/src/main/help.cc index 0743f76b9..ac0116b59 100644 --- a/src/main/help.cc +++ b/src/main/help.cc @@ -55,6 +55,7 @@ static constexpr const char* snort_help = "--help-limits print the int upper bounds denoted by max*\n" "--help-module output description of given module\n" "--help-modules list all available modules with brief help\n" + "--help-modules-json dump description of all available modules in JSON format\n" "--help-plugins list all available plugins with brief help\n" "--help-options [