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);
}
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");
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");
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);
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();
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);
}
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);
+ }
}
#include "framework/base_api.h"
#include "framework/module.h"
+#include "helpers/json_stream.h"
#include "helpers/markup.h"
#include "log/messages.h"
#include "main/modules.h"
cout << endl << Markup::head(3) << name << endl << endl;
if ( const char* h = m->get_help() )
- cout << endl << "What: " << h << endl;
+ cout << endl << "Help: " << h << endl;
cout << endl << "Type: " << mod_type(mh->api) << endl;
cout << endl << "Usage: " << mod_use(m->get_usage()) << endl;
{
switch ( ct )
{
- case CountType::SUM: return " (sum)";
- case CountType::NOW: return " (now)";
- case CountType::MAX: return " (max)";
+ case CountType::SUM: return "sum";
+ case CountType::NOW: return "now";
+ case CountType::MAX: return "max";
default: break;
}
assert(false);
cout << "." << pegs->name;
cout << Markup::emphasis_off();
cout << ": " << pegs->help;
- cout << peg_op(pegs->type);
+ cout << " (" << peg_op(pegs->type) << ")";
cout << endl;
++pegs;
}
cout << "no match" << endl;
}
+//--------------------------------------------------------------------------
+// JSON dumpers
+//--------------------------------------------------------------------------
+
+static void dump_param_range_json(JsonStream& json, const Parameter* p)
+{
+ const char* range = p->get_range();
+
+ if ( !range )
+ json.put("range");
+ else
+ {
+ switch ( p->type )
+ {
+ case Parameter::PT_INT:
+ case Parameter::PT_PORT:
+ {
+ std::string tr = range;
+ const char* d = strchr(range, ':');
+ if ( *range == 'm' )
+ {
+ if ( d )
+ {
+ tr = std::to_string(Parameter::get_int(range)) +
+ tr.substr(tr.find(":"));
+ }
+ else
+ tr = std::to_string(Parameter::get_int(range));
+ }
+ if ( d and *++d == 'm' )
+ {
+ tr = tr.substr(0, tr.find(":") + 1) +
+ std::to_string(Parameter::get_int(d));
+ }
+ json.put("range", tr);
+ break;
+ }
+
+ default:
+ json.put("range", p->get_range());
+ }
+ }
+}
+
+static void dump_param_default_json(JsonStream& json, const Parameter* p)
+{
+ const char* def = p->deflt;
+
+ if ( !def )
+ json.put("default");
+ else
+ {
+ switch ( p->type )
+ {
+ case Parameter::PT_INT:
+ case Parameter::PT_PORT:
+ json.put("default", std::stol(def));
+ break;
+
+ case Parameter::PT_REAL:
+ {
+ const char* dot = strchr(def, '.');
+ if ( dot )
+ json.put("default", std::stod(def), strlen(dot) - 1);
+ else
+ json.put("default", std::stod(def));
+
+ break;
+ }
+
+ case Parameter::PT_BOOL:
+ !strcmp(def, "true") ? json.put_true("default") : json.put_false("default");
+ break;
+
+ default:
+ json.put("default", def);
+ }
+ }
+}
+
+static void dump_params_tree_json(JsonStream& json, const Parameter* p)
+{
+ while ( p and p->type != Parameter::PT_MAX )
+ {
+ assert(p->name);
+
+ json.open();
+ json.put("option", p->name);
+ json.put("type", p->get_type());
+ if ( p->is_table() and p->range )
+ {
+ json.open_array("sub_options");
+ dump_params_tree_json(json, (const Parameter*)p->range);
+ json.close_array();
+ }
+ else
+ dump_param_range_json(json, p);
+
+ dump_param_default_json(json, p);
+ if ( p->help )
+ json.put("help", p->help);
+ else
+ json.put("help");
+
+ json.close();
+
+ ++p;
+ }
+}
+
+static void dump_configs_json(JsonStream& json, const Module* mod)
+{
+ const Parameter* params = mod->get_parameters();
+
+ json.open_array("configuration");
+ dump_params_tree_json(json, params);
+ json.close_array();
+}
+
+static void dump_commands_json(JsonStream& json, const Module* mod)
+{
+ const Command* cmds = mod->get_commands();
+
+ json.open_array("commands");
+
+ while ( cmds and cmds->name )
+ {
+ json.open();
+
+ json.put("name", cmds->name);
+
+ json.open_array("params");
+ if ( cmds->params )
+ dump_params_tree_json(json, cmds->params);
+
+ json.close_array();
+
+ if ( cmds->help )
+ json.put("help", cmds->help);
+ else
+ json.put("help");
+
+ json.close();
+
+ ++cmds;
+ }
+
+ json.close_array();
+}
+
+static void dump_rules_json(JsonStream& json, const Module* mod)
+{
+ auto rules = get_rules(mod->get_name(), true);
+
+ json.open_array("rules");
+ for ( const auto& rp : rules )
+ {
+ json.open();
+
+ json.put("gid", rp.mod->get_gid());
+ json.put("sid", rp.rule->sid);
+ json.put("msg", rp.rule->msg);
+
+ json.close();
+ }
+ json.close_array();
+}
+
+static void dump_pegs_json(JsonStream& json, const Module* mod)
+{
+ const PegInfo* pegs = mod->get_pegs();
+
+ json.open_array("peg_counts");
+ while ( pegs and pegs->type != CountType::END )
+ {
+ json.open();
+ json.put("type", peg_op(pegs->type));
+
+ assert(pegs->name);
+ json.put("name", pegs->name);
+
+ if ( pegs->help )
+ json.put("help", pegs->help);
+ else
+ json.put("help");
+
+ json.close();
+
+ ++pegs;
+ }
+ json.close_array();
+}
+
+void ModuleManager::show_modules_json()
+{
+ auto mod_hooks = get_all_modhooks();
+ mod_hooks.sort(comp_mods);
+ JsonStream json(std::cout);
+
+ json.open_array();
+ for ( const auto* mh : mod_hooks )
+ {
+ const Module* mod = mh->mod;
+ assert(mod);
+
+ std::string name = "";
+ if ( const char* n = mod->get_name() )
+ name = n;
+
+ assert(!name.empty());
+
+ std::string help = "";
+ if ( const char* h = mod->get_help() )
+ help = h;
+
+ const char* type = mod_type(mh->api);
+ const char* usage = mod_use(mod->get_usage());
+
+ json.open();
+ json.put("module", name);
+ json.put("help", help);
+ json.put("type", type);
+ json.put("usage", usage);
+ if ( mh->api and (mh->api->type == PT_INSPECTOR) )
+ json.put("instance_type", mod_bind(mod));
+
+ dump_configs_json(json, mod);
+ dump_commands_json(json, mod);
+ dump_rules_json(json, mod);
+ dump_pegs_json(json, mod);
+ json.close();
+ }
+ json.close_array();
+}
+
+#ifdef UNIT_TEST
+
+#include <catch/snort_catch.h>
+
+TEST_CASE("param range JSON dumper", "[ModuleManager]")
+{
+ std::stringstream ss;
+ JsonStream json(ss);
+
+ SECTION("null")
+ {
+ const Parameter p("string", Parameter::PT_STRING, nullptr, nullptr, "help");
+ dump_param_range_json(json, &p);
+ std::string x = R"-("range": null)-";
+ CHECK(ss.str() == x);
+ }
+
+ SECTION("common string")
+ {
+ const Parameter p("enum", Parameter::PT_ENUM, "one | two | three", nullptr, "help");
+ dump_param_range_json(json, &p);
+ std::string x = R"-("range": "one | two | three")-";
+ CHECK(ss.str() == x);
+ }
+
+ SECTION("number string")
+ {
+ const Parameter i_max("int_max", Parameter::PT_INT, "255", nullptr, "help");
+ dump_param_range_json(json, &i_max);
+ std::string x = R"-("range": "255")-";
+ CHECK(ss.str() == x);
+ ss.str("");
+
+ const Parameter i_min("int_min", Parameter::PT_INT, "255:", nullptr, "help");
+ dump_param_range_json(json, &i_min);
+ x = R"-(, "range": "255:")-";
+ CHECK(ss.str() == x);
+ ss.str("");
+
+ const Parameter i_exp_max("int_exp_max", Parameter::PT_INT, ":255", nullptr, "help");
+ dump_param_range_json(json, &i_exp_max);
+ x = R"-(, "range": ":255")-";
+ CHECK(ss.str() == x);
+ ss.str("");
+
+ const Parameter p_min_max("int_min_max", Parameter::PT_PORT, "0:65535", nullptr, "help");
+ dump_param_range_json(json, &p_min_max);
+ x = R"-(, "range": "0:65535")-";
+ CHECK(ss.str() == x);
+ ss.str("");
+
+ const Parameter i_hex("int_in_hex", Parameter::PT_INT, "0x5:0xFF", nullptr, "help");
+ dump_param_range_json(json, &i_hex);
+ x = R"-(, "range": "0x5:0xFF")-";
+ CHECK(ss.str() == x);
+ }
+
+ SECTION("number string with maxN")
+ {
+ const Parameter i_max("int_max", Parameter::PT_INT, "max32", nullptr, "help");
+ dump_param_range_json(json, &i_max);
+ std::string x = R"-("range": "4294967295")-";
+ CHECK(ss.str() == x);
+ ss.str("");
+
+ const Parameter i_min("int_min", Parameter::PT_INT, "max32:", nullptr, "help");
+ dump_param_range_json(json, &i_min);
+ x = R"-(, "range": "4294967295:")-";
+ CHECK(ss.str() == x);
+ ss.str("");
+
+ const Parameter i_exp_max("int_exp_max", Parameter::PT_INT, ":max32", nullptr, "help");
+ dump_param_range_json(json, &i_exp_max);
+ x = R"-(, "range": ":4294967295")-";
+ CHECK(ss.str() == x);
+ ss.str("");
+
+ const Parameter p_min_max("int_min_max", Parameter::PT_INT, "max31:max32", nullptr, "help");
+ dump_param_range_json(json, &p_min_max);
+ x = R"-(, "range": "2147483647:4294967295")-";
+ CHECK(ss.str() == x);
+ }
+}
+
+TEST_CASE("param default JSON dumper", "[ModuleManager]")
+{
+ std::stringstream ss;
+ JsonStream json(ss);
+
+ SECTION("null")
+ {
+ const Parameter p("int", Parameter::PT_INT, nullptr, nullptr, "help");
+ dump_param_default_json(json, &p);
+ std::string x = R"-("default": null)-";
+ CHECK(ss.str() == x);
+ }
+
+ SECTION("string")
+ {
+ const Parameter p("multi", Parameter::PT_MULTI, "one | two | three", "one two", "help");
+ dump_param_default_json(json, &p);
+ std::string x = R"-("default": "one two")-";
+ CHECK(ss.str() == x);
+ }
+
+ SECTION("integer")
+ {
+ const Parameter p("int", Parameter::PT_INT, nullptr, "5", "help");
+ dump_param_default_json(json, &p);
+ std::string x = R"-("default": 5)-";
+ CHECK(ss.str() == x);
+ }
+
+ SECTION("real")
+ {
+ const Parameter p("real", Parameter::PT_REAL, nullptr, "12.345", "help");
+ dump_param_default_json(json, &p);
+ std::string x = R"-("default": 12.345)-";
+ CHECK(ss.str() == x);
+ }
+
+ SECTION("boolean")
+ {
+ const Parameter t("bool_true", Parameter::PT_BOOL, nullptr, "true", "help");
+ dump_param_default_json(json, &t);
+ std::string x = R"-("default": true)-";
+ CHECK(ss.str() == x);
+ ss.str("");
+
+ const Parameter f("bool_false", Parameter::PT_BOOL, nullptr, "false", "help");
+ dump_param_default_json(json, &f);
+ x = R"-(, "default": false)-";
+ CHECK(ss.str() == x);
+ }
+}
+
+#endif // UNIT_TEST
+