From: Michael Friedrich Date: Wed, 11 Feb 2015 12:12:08 +0000 (+0100) Subject: Add macro config validator for command args, env, custom attr, perfdata templates X-Git-Tag: v2.3.0~234 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8ca57cba03a4fb11561daa326d4db827d9edabdd;p=icinga2 Add macro config validator for command args, env, custom attr, perfdata templates fixes #7311 --- diff --git a/lib/base/utility.cpp b/lib/base/utility.cpp index bcd1d7be8..27eb9bcd9 100644 --- a/lib/base/utility.cpp +++ b/lib/base/utility.cpp @@ -1256,3 +1256,23 @@ String Utility::UnescapeString(const String& s) return result.str(); } + +bool Utility::ValidateMacroString(const String& macro) +{ + if (macro.IsEmpty()) + return true; + + size_t pos_first, pos_second, offset; + offset = 0; + + while((pos_first = macro.FindFirstOf("$", offset)) != String::NPos) { + pos_second = macro.FindFirstOf("$", pos_first + 1); + + if (pos_second == String::NPos) + return false; + + offset = pos_second + 1; + } + + return true; +} diff --git a/lib/base/utility.hpp b/lib/base/utility.hpp index 6409d698e..25a91a63c 100644 --- a/lib/base/utility.hpp +++ b/lib/base/utility.hpp @@ -118,6 +118,8 @@ public: static String EscapeString(const String& s, const String& chars); static String UnescapeString(const String& s); + static bool ValidateMacroString(const String& macro); + static void SetThreadName(const String& name, bool os = true); static String GetThreadName(void); diff --git a/lib/icinga/command.cpp b/lib/icinga/command.cpp index 0159a0d60..a3071dac7 100644 --- a/lib/icinga/command.cpp +++ b/lib/icinga/command.cpp @@ -20,11 +20,15 @@ #include "icinga/command.hpp" #include "base/function.hpp" #include "base/exception.hpp" +#include "base/objectlock.hpp" +#include using namespace icinga; REGISTER_TYPE(Command); REGISTER_SCRIPTFUNCTION(ValidateCommandAttributes, &Command::ValidateAttributes); +REGISTER_SCRIPTFUNCTION(ValidateCommandArguments, &Command::ValidateArguments); +REGISTER_SCRIPTFUNCTION(ValidateEnvironmentVariables, &Command::ValidateEnvironmentVariables); int Command::GetModifiedAttributes(void) const { @@ -52,3 +56,55 @@ void Command::ValidateAttributes(const String& location, const Command::Ptr& obj } } +void Command::ValidateArguments(const String& location, const Command::Ptr& object) +{ + Dictionary::Ptr arguments = object->GetArguments(); + + if (!arguments) + return; + + ObjectLock olock(arguments); + BOOST_FOREACH(const Dictionary::Pair& kv, arguments) { + const Value& arginfo = kv.second; + Value argval; + + if (arginfo.IsObjectType()) { + Dictionary::Ptr argdict = arginfo; + + if (argdict->Contains("value")) + argval = argdict->Get("value"); + } else + argval = arginfo; + + if (argval.IsEmpty()) + continue; + + String argstr = argval; + + if(!Utility::ValidateMacroString(argstr)) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Closing $ not found in macro format string '" + argstr + "'.", object->GetDebugInfo())); + } + } +} + +void Command::ValidateEnvironmentVariables(const String& location, const Command::Ptr& object) +{ + Dictionary::Ptr env = object->GetEnv(); + + if (!env) + return; + + ObjectLock olock(env); + BOOST_FOREACH(const Dictionary::Pair& kv, env) { + const Value& envval = kv.second; + + if (!envval.IsString() || envval.IsEmpty()) + continue; + + if(!Utility::ValidateMacroString(envval)) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Closing $ not found in macro format string '" + envval + "'.", object->GetDebugInfo())); + } + } +} diff --git a/lib/icinga/command.hpp b/lib/icinga/command.hpp index a45f689dc..4a23c4619 100644 --- a/lib/icinga/command.hpp +++ b/lib/icinga/command.hpp @@ -40,6 +40,8 @@ public: //virtual Dictionary::Ptr Execute(const Object::Ptr& context) = 0; static void ValidateAttributes(const String& location, const Command::Ptr& object); + static void ValidateArguments(const String& location, const Command::Ptr& object); + static void ValidateEnvironmentVariables(const String& location, const Command::Ptr& object); int GetModifiedAttributes(void) const; void SetModifiedAttributes(int flags, const MessageOrigin& origin = MessageOrigin()); diff --git a/lib/icinga/customvarobject.cpp b/lib/icinga/customvarobject.cpp index 5ee6a308d..ca7e4bbe7 100644 --- a/lib/icinga/customvarobject.cpp +++ b/lib/icinga/customvarobject.cpp @@ -19,10 +19,15 @@ #include "icinga/customvarobject.hpp" #include "base/logger.hpp" +#include "base/function.hpp" +#include "base/exception.hpp" +#include "base/objectlock.hpp" +#include using namespace icinga; REGISTER_TYPE(CustomVarObject); +REGISTER_SCRIPTFUNCTION(ValidateCustomAttributes, &CustomVarObject::ValidateCustomAttributes); boost::signals2::signal CustomVarObject::OnVarsChanged; @@ -61,3 +66,57 @@ bool CustomVarObject::IsVarOverridden(const String& name) const return vars_override->Contains(name); } + +void CustomVarObject::ValidateCustomAttributes(const String& location, const CustomVarObject::Ptr& object) +{ + Dictionary::Ptr vars = object->GetVars(); + + if (!vars) + return; + + /* string, array, dictionary */ + ObjectLock olock(vars); + BOOST_FOREACH(const Dictionary::Pair& kv, vars) { + const Value& varval = kv.second; + + if (varval.IsObjectType()) { + /* only one dictonary level */ + Dictionary::Ptr varval_dict = varval; + + ObjectLock xlock(varval_dict); + BOOST_FOREACH(const Dictionary::Pair& kv_var, varval_dict) { + if(kv_var.second.IsEmpty()) + continue; + + if(!Utility::ValidateMacroString(kv_var.second)) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Closing $ not found in macro format string '" + kv_var.second + "'.", object->GetDebugInfo())); + } + } + } else if (varval.IsObjectType()) { + /* check all array entries */ + Array::Ptr varval_arr = varval; + + ObjectLock ylock (varval_arr); + BOOST_FOREACH(const Value& arrval, varval_arr) { + if (arrval.IsEmpty()) + continue; + + if(!Utility::ValidateMacroString(arrval)) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Closing $ not found in macro format string '" + arrval + "'.", object->GetDebugInfo())); + } + } + } else { + if (varval.IsEmpty()) + continue; + + String varstr = varval; + + if(!Utility::ValidateMacroString(varstr)) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Closing $ not found in macro format string '" + varstr + "'.", object->GetDebugInfo())); + } + } + } +} \ No newline at end of file diff --git a/lib/icinga/customvarobject.hpp b/lib/icinga/customvarobject.hpp index 7243f620a..36f97dd93 100644 --- a/lib/icinga/customvarobject.hpp +++ b/lib/icinga/customvarobject.hpp @@ -61,6 +61,8 @@ public: static boost::signals2::signal OnVarsChanged; + static void ValidateCustomAttributes(const String& location, const CustomVarObject::Ptr& object); + Dictionary::Ptr GetVars(void) const; void SetVars(const Dictionary::Ptr& vars, const MessageOrigin& origin = MessageOrigin()); diff --git a/lib/icinga/icinga-type.conf b/lib/icinga/icinga-type.conf index c3cda24d7..18d4c9f85 100644 --- a/lib/icinga/icinga-type.conf +++ b/lib/icinga/icinga-type.conf @@ -26,6 +26,7 @@ } %type CustomVarObject { + %validator "ValidateCustomAttributes", %attribute %dictionary "vars", } @@ -193,6 +194,8 @@ %type Command %inherits CustomVarObject { %validator "ValidateCommandAttributes", + %validator "ValidateCommandArguments", + %validator "ValidateEnvironmentVariables", %require "execute", %attribute %function "execute", diff --git a/lib/perfdata/graphitewriter.cpp b/lib/perfdata/graphitewriter.cpp index 695344b12..f4790a7e8 100644 --- a/lib/perfdata/graphitewriter.cpp +++ b/lib/perfdata/graphitewriter.cpp @@ -43,6 +43,7 @@ using namespace icinga; REGISTER_TYPE(GraphiteWriter); +REGISTER_SCRIPTFUNCTION(ValidateNameTemplates, &GraphiteWriter::ValidateNameTemplates); REGISTER_STATSFUNCTION(GraphiteWriterStats, &GraphiteWriter::StatsFunc); @@ -146,7 +147,7 @@ void GraphiteWriter::SendPerfdata(const String& prefix, const CheckResult::Ptr& ObjectLock olock(perfdata); BOOST_FOREACH(const Value& val, perfdata) { PerfdataValue::Ptr pdv; - + if (val.IsObjectType()) pdv = val; else { @@ -158,7 +159,7 @@ void GraphiteWriter::SendPerfdata(const String& prefix, const CheckResult::Ptr& continue; } } - + String escaped_key = EscapeMetric(pdv->GetLabel()); boost::algorithm::replace_all(escaped_key, "::", "."); @@ -230,3 +231,15 @@ Value GraphiteWriter::EscapeMacroMetric(const Value& value) } else return EscapeMetric(value); } + +void GraphiteWriter::ValidateNameTemplates(const String& location, const GraphiteWriter::Ptr& object) +{ + if(!Utility::ValidateMacroString(object->GetHostNameTemplate())) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Closing $ not found in macro format string '" + object->GetHostNameTemplate() + "'.", object->GetDebugInfo())); + } + if (!Utility::ValidateMacroString(object->GetServiceNameTemplate())) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Closing $ not found in macro format string '" + object->GetServiceNameTemplate() + "'.", object->GetDebugInfo())); + } +} diff --git a/lib/perfdata/graphitewriter.hpp b/lib/perfdata/graphitewriter.hpp index 82a136be3..dc9392b4d 100644 --- a/lib/perfdata/graphitewriter.hpp +++ b/lib/perfdata/graphitewriter.hpp @@ -43,6 +43,8 @@ public: static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); + static void ValidateNameTemplates(const String& location, const GraphiteWriter::Ptr& object); + protected: virtual void Start(void); diff --git a/lib/perfdata/perfdata-type.conf b/lib/perfdata/perfdata-type.conf index 42938b4f1..a07cd29af 100644 --- a/lib/perfdata/perfdata-type.conf +++ b/lib/perfdata/perfdata-type.conf @@ -18,6 +18,8 @@ ******************************************************************************/ %type PerfdataWriter { + %validator "ValidateFormatTemplates", + %attribute %string "host_perfdata_path", %attribute %string "service_perfdata_path", %attribute %string "host_temp_path", @@ -28,6 +30,8 @@ } %type GraphiteWriter { + %validator "ValidateNameTemplates", + %attribute %string "host", %attribute %string "port", %attribute %string "host_name_template", @@ -44,4 +48,3 @@ %attribute %string "host", %attribute %string "port", } - diff --git a/lib/perfdata/perfdatawriter.cpp b/lib/perfdata/perfdatawriter.cpp index 5f2d625db..902d77829 100644 --- a/lib/perfdata/perfdatawriter.cpp +++ b/lib/perfdata/perfdatawriter.cpp @@ -27,12 +27,14 @@ #include "base/convert.hpp" #include "base/utility.hpp" #include "base/context.hpp" +#include "base/exception.hpp" #include "base/application.hpp" #include "base/statsfunction.hpp" using namespace icinga; REGISTER_TYPE(PerfdataWriter); +REGISTER_SCRIPTFUNCTION(ValidateFormatTemplates, &PerfdataWriter::ValidateFormatTemplates); REGISTER_STATSFUNCTION(PerfdataWriterStats, &PerfdataWriter::StatsFunc); @@ -138,3 +140,14 @@ void PerfdataWriter::RotationTimerHandler(void) RotateFile(m_HostOutputFile, GetHostTempPath(), GetHostPerfdataPath()); } +void PerfdataWriter::ValidateFormatTemplates(const String& location, const PerfdataWriter::Ptr& object) +{ + if(!Utility::ValidateMacroString(object->GetHostFormatTemplate())) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Closing $ not found in macro format string '" + object->GetHostFormatTemplate() + "'.", object->GetDebugInfo())); + } + if (!Utility::ValidateMacroString(object->GetServiceFormatTemplate())) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Closing $ not found in macro format string '" + object->GetHostFormatTemplate() + "'.", object->GetDebugInfo())); + } +} \ No newline at end of file diff --git a/lib/perfdata/perfdatawriter.hpp b/lib/perfdata/perfdatawriter.hpp index b0ced3728..7edd334a9 100644 --- a/lib/perfdata/perfdatawriter.hpp +++ b/lib/perfdata/perfdatawriter.hpp @@ -42,6 +42,8 @@ public: static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); + static void ValidateFormatTemplates(const String& location, const PerfdataWriter::Ptr& object); + protected: virtual void Start(void); diff --git a/test/icinga-macros.cpp b/test/icinga-macros.cpp index f222f7210..3fceefc66 100644 --- a/test/icinga-macros.cpp +++ b/test/icinga-macros.cpp @@ -51,6 +51,17 @@ BOOST_AUTO_TEST_CASE(simple) Array::Ptr result = MacroProcessor::ResolveMacros("$testD$", resolvers); BOOST_CHECK(result->GetLength() == 2); + /* verify the config validator macro checks */ + BOOST_CHECK(Utility::ValidateMacroString("$host.address") == false); + BOOST_CHECK(Utility::ValidateMacroString("host.vars.test$") == false); + + BOOST_CHECK(Utility::ValidateMacroString("host.vars.test$") == false); + BOOST_CHECK(Utility::ValidateMacroString("$template::test$abc$") == false); + + BOOST_CHECK(Utility::ValidateMacroString("$$test $host.vars.test$") == true); + + BOOST_CHECK(Utility::ValidateMacroString("test $host.vars.test$") == true); + } BOOST_AUTO_TEST_SUITE_END()