]> granicus.if.org Git - icinga2/commitdiff
Add macro config validator for command args, env, custom attr, perfdata templates
authorMichael Friedrich <michael.friedrich@netways.de>
Wed, 11 Feb 2015 12:12:08 +0000 (13:12 +0100)
committerMichael Friedrich <michael.friedrich@netways.de>
Wed, 11 Feb 2015 14:32:10 +0000 (15:32 +0100)
fixes #7311

13 files changed:
lib/base/utility.cpp
lib/base/utility.hpp
lib/icinga/command.cpp
lib/icinga/command.hpp
lib/icinga/customvarobject.cpp
lib/icinga/customvarobject.hpp
lib/icinga/icinga-type.conf
lib/perfdata/graphitewriter.cpp
lib/perfdata/graphitewriter.hpp
lib/perfdata/perfdata-type.conf
lib/perfdata/perfdatawriter.cpp
lib/perfdata/perfdatawriter.hpp
test/icinga-macros.cpp

index bcd1d7be89e8ff2d8e57439de7ff592b0beb73a1..27eb9bcd94dfde944183a970d84fc2b9df081c08 100644 (file)
@@ -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;
+}
index 6409d698eea955589a1a35971187394510cd3fe6..25a91a63c1910bbd72fd44b8f3cc0e71eb4b6ff9 100644 (file)
@@ -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);
 
index 0159a0d60b946c3f2afe6e63462a2b2784d21ab4..a3071dac741533ff7c326948bf7781bcdc23c884 100644 (file)
 #include "icinga/command.hpp"
 #include "base/function.hpp"
 #include "base/exception.hpp"
+#include "base/objectlock.hpp"
+#include <boost/foreach.hpp>
 
 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>()) {
+                       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()));
+               }
+       }
+}
index a45f689dceacbd28f255db4b2c0e380ddc9e028b..4a23c461973ea3d5c75b7f4daa92ddb586735118 100644 (file)
@@ -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());
index 5ee6a308d2ca7ed0975bcbe0a1efc4dd7cb07478..ca7e4bbe716522cc8807bed642b05f175c1afc11 100644 (file)
 
 #include "icinga/customvarobject.hpp"
 #include "base/logger.hpp"
+#include "base/function.hpp"
+#include "base/exception.hpp"
+#include "base/objectlock.hpp"
+#include <boost/foreach.hpp>
 
 using namespace icinga;
 
 REGISTER_TYPE(CustomVarObject);
+REGISTER_SCRIPTFUNCTION(ValidateCustomAttributes, &CustomVarObject::ValidateCustomAttributes);
 
 boost::signals2::signal<void (const CustomVarObject::Ptr&, const Dictionary::Ptr& vars, const MessageOrigin&)> 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<Dictionary>()) {
+                       /* 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<Array>()) {
+                       /* 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
index 7243f620a26d034b71a7ba3b8b6236470f394758..36f97dd93079dd1b081d48e0ab348df852307259 100644 (file)
@@ -61,6 +61,8 @@ public:
 
        static boost::signals2::signal<void (const CustomVarObject::Ptr&, const Dictionary::Ptr& vars, const MessageOrigin&)> 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());
 
index c3cda24d78abdd825994d777856289cd0bc723d4..18d4c9f85b944497decd2823518ebe84fa0e7b12 100644 (file)
@@ -26,6 +26,7 @@
 }
 
 %type CustomVarObject {
+       %validator "ValidateCustomAttributes",
        %attribute %dictionary "vars",
 }
 
 
 %type Command %inherits CustomVarObject {
        %validator "ValidateCommandAttributes",
+       %validator "ValidateCommandArguments",
+       %validator "ValidateEnvironmentVariables",
 
        %require "execute",
        %attribute %function "execute",
index 695344b124ea151ce5d09a68d6d9b423df84b81e..f4790a7e84600209dae8e3774d0db2471693c974 100644 (file)
@@ -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<PerfdataValue>())
                        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()));
+       }
+}
index 82a136be3b29c7dcf755be98c1eb3fcac6a9b9c4..dc9392b4d050478c66615ffbb03c20e85e58a19e 100644 (file)
@@ -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);
 
index 42938b4f100ab9d3b206780f2ca5262302b848f7..a07cd29afae351a10075b63ebef72d3d6b0980fe 100644 (file)
@@ -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",
 }
-
index 5f2d625db5fb252b59f023021f142ee52ba4e3c9..902d7782976eff59291d2bdc935e4c84d35a5071 100644 (file)
 #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
index b0ced37281ee85a0e18cb6f9cef37b796a516046..7edd334a94ad31b08820c3bba6b17d445119c173 100644 (file)
@@ -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);
 
index f222f72105476ac544e46504d0c20ab3385bc729..3fceefc66d03310445f50ab33d0b962d75627d81 100644 (file)
@@ -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()