From b673c6000cd22e7026142aa20ac7dc249d2c2aa1 Mon Sep 17 00:00:00 2001
From: "Hahn Axel (hahn)" <axel.hahn@unibe.ch>
Date: Thu, 20 Mar 2025 17:23:54 +0100
Subject: [PATCH] v0.10   detect errors in ini meta section incl. checks

---
 docs/30_Usage.md                              |  8 +++--
 inc_functions.php                             | 25 ++++++++-----
 src/amcli.php                                 | 35 +++++++++++++++++--
 tests/040_detect_wrong_metainfos.php          | 19 ++++++++++
 .../validate_error_meta_unknown_key.ini       | 33 +++++++++++++++++
 .../validate_error_meta_wrong_type.ini        | 29 +++++++++++++++
 .../validate_error_notify_wrong_email.ini     | 32 +++++++++++++++++
 7 files changed, 169 insertions(+), 12 deletions(-)
 create mode 100644 tests/040_detect_wrong_metainfos.php
 create mode 100644 tests/configs/validate_error_meta_unknown_key.ini
 create mode 100644 tests/configs/validate_error_meta_wrong_type.ini
 create mode 100644 tests/configs/validate_error_notify_wrong_email.ini

diff --git a/docs/30_Usage.md b/docs/30_Usage.md
index 4cf2676..1c26f06 100644
--- a/docs/30_Usage.md
+++ b/docs/30_Usage.md
@@ -7,11 +7,11 @@
   |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
   |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
   |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
-                           |_| |_|             CLI client v0.8
+                           |_| |_|            CLI client v0.10
 
 
 This client performs appmonitor checks and puts the results as JSON to stdout.
-It contains all checks that are available in the PHP appmonitor client v0.155.
+It contains all checks that are available in the PHP appmonitor client v0.156.
 You can use the compiled binary to monitor any non PHP webapp without 
 implementing the checks for your programming language.
 
@@ -42,6 +42,8 @@ You find example snippets in the source code of this project in tests/config/.
     -v, --verbose     Enable verbose output (written to STDERR)
 
     -b, --buildinfos  show build information and exit
+    -e, --explain <CHECK>
+                      Explain a check and exit
     -l, --list        list available checks and exit
     -m, --modules     list available Php modules in this binary and exit
     -V, --version     Show version and exit
@@ -86,6 +88,8 @@ The binary could stop and exit with non zero exitcode.
 | 5        | Given Ini file cannot be parsed
 | 6        | Ini value `params=` is no JSON
 | 7        | The key 'function' is not set in the given check. It defines the check function to call.
+| 8        | The section 'meta' has a wrong key or wrong value
+| 9        | The section 'notifications' has a wrong key or wrong value
 
 ### appmonitor-client class
 
diff --git a/inc_functions.php b/inc_functions.php
index 74a3269..98f016b 100644
--- a/inc_functions.php
+++ b/inc_functions.php
@@ -20,7 +20,7 @@ $aCol=[
     "light_magenta" => "\e[95m",
 ];
 
-function _h1($s): void
+function _h1(string $s): void
 {
     global $aCol;
     echo "\n$aCol[magenta]>>>>>>>>>> $s$aCol[reset]\n";
@@ -32,7 +32,7 @@ function _h1($s): void
     echo str_repeat(".", 79)."\n\n";
     */
 }
-function _abort($sMessage, $iExitcode = 1): never
+function _abort(string $sMessage, int $iExitcode = 1): never
 {
     global $aCol;
     echo "❌ $aCol[red]$sMessage$aCol[reset]\n";
@@ -48,7 +48,7 @@ function _skip(string $sMessage=""): void
     echo "🔹 SKIP: $sMessage\n";
 }
 
-function _chdir($sDir): void
+function _chdir(string $sDir): void
 {
     global $aCol;
     if (!is_dir($sDir)) {
@@ -60,10 +60,11 @@ function _chdir($sDir): void
 
 /**
  * Execute shell command and abort if it fails
- * @param mixed $cmd
- * @return void
+ * @param string $cmd
+ * 
+ * @return int
  */
-function _exec($cmd): void
+function _exec(string $cmd, bool $bAbortOnError=true): int
 {
     global $aCol;
     echo "$aCol[blue]cmd > $cmd$aCol[reset]\n";
@@ -108,16 +109,24 @@ function _exec($cmd): void
         $rc=proc_close($process);
         $iEnd=microtime(true);
     } else {
-        _abort("Unable to execute command.");
+        if($bAbortOnError){
+            _abort("Unable to execute command.");            
+        }
+        _skip("Unable to execute command.");
     }
     
 
     $sTime= "... ⏱️ Time: " . round($iEnd-$iStart, 3) . "s\n";
     if ($rc != 0) {
         echo "rc=$rc $sTime";
-        _abort("Command failed. Aborting.", $rc);
+        if($bAbortOnError){
+            _abort("Command failed. Aborting.", $rc);
+        }
+        _skip("Unable to execute command.");
+        
     }
     _ok($sTime);
+    return $rc;
 }
 
 function _mkdir(string $sMyDir): void
diff --git a/src/amcli.php b/src/amcli.php
index e145aad..bfb1822 100755
--- a/src/amcli.php
+++ b/src/amcli.php
@@ -21,6 +21,7 @@
  * 2025-03-17  v0.7    update help
  * 2025-03-17  v0.8    check missing 'function' in check. get php client version
  * 2025-03-20  v0.9    add --explain; hardening using more intensive validation
+ * 2025-03-20  v0.10   detect errors in ini meta section
  * ======================================================================
  */
 
@@ -29,11 +30,22 @@ $argc = $_SERVER['argc'];
 $argv = $_SERVER['argv'];
 
 $FLAG_DEBUG = 0;
-$VERSION = "0.9";
+$VERSION = "0.10";
 $PHPCLIENTVERSION = "not-detected";
 
 $AMCLI_BUILD_DATE = "never";
 
+$aMetaValidation=[
+    'host' => ['type' => 'string'],
+    'ttl' =>  ['type' => 'int'],
+    'website' => ['type' => 'string'],
+    'tags' => ['type' => 'array'],
+];
+$aNotificationsValidation=[
+    'email' => ['type' => 'array'],
+    'slack' => ['type' => 'array'],
+];
+
 // ---MARK---INCLUDE-CHECKS---START---
 
 @include "amcli__build.php";
@@ -111,7 +123,7 @@ function _showHelp(): void
   |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
   |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
   |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
-                           |_| |_|             \e[1mCLI client v$VERSION\e[0m
+                           |_| |_|            \e[1mCLI client v$VERSION\e[0m
 
 
 This client performs appmonitor checks and puts the results as JSON to stdout.
@@ -351,6 +363,25 @@ $aIni['meta']['tags']=_json2array($aIni['meta']['tags'] ?? null);
 $aIni['notifications']['email']=_json2array($aIni['notifications']['email'] ?? null);
 $aIni['notifications']['slack']=_json2array($aIni['notifications']['slack'] ?? null);
 
+$oVal=new validateparam();
+$aErrors=$oVal->validateArray($aMetaValidation, $aIni['meta']??[]);
+if(count($aErrors)){
+    echo "❌ ERROR: [meta] section contains errors: \n" 
+        . print_r($aErrors, 1)."\n"
+        ."[meta] is ".print_r($aIni['meta']??[], 1)."\n"
+        ;
+    exit(8);
+}
+
+$aErrors=$oVal->validateArray($aNotificationsValidation, $aIni['notifications']??[]);
+if(count($aErrors)){
+    echo "❌ ERROR: [notifications] section contains errors: \n" 
+        . print_r($aErrors, 1)."\n"
+        ."[notifications] is ".print_r($aIni['notifications']??[], 1)."\n"
+        ;
+    exit(9);
+}
+
 // ----------------------------------------------------------------------
 
 // set metadata
diff --git a/tests/040_detect_wrong_metainfos.php b/tests/040_detect_wrong_metainfos.php
new file mode 100644
index 0000000..3b910ef
--- /dev/null
+++ b/tests/040_detect_wrong_metainfos.php
@@ -0,0 +1,19 @@
+#!/usr/bin/env php
+<?php
+
+echo "
+WHAT: Run find wrong meta infos
+";
+
+foreach([
+    "validate_error_meta_wrong_type.ini",
+    "validate_error_meta_unknown_key.ini",
+    "validate_error_notify_wrong_email.ini"
+
+] as $myIni){
+
+    $rc=_exec("$AMCLI --ini='".__DIR__."/configs/$myIni'", false);
+    if($rc==0){
+        _abort("Should have failed. Wrong meta info wasn't detected.");
+    }
+}
diff --git a/tests/configs/validate_error_meta_unknown_key.ini b/tests/configs/validate_error_meta_unknown_key.ini
new file mode 100644
index 0000000..8e18785
--- /dev/null
+++ b/tests/configs/validate_error_meta_unknown_key.ini
@@ -0,0 +1,33 @@
+; =======================================================================
+;
+; APPMONITOR CLI CLIENT
+;
+; for the checks see its parameters
+; <https://os-docs.iml.unibe.ch/appmonitor/PHP_client/Plugins/Checks/index.html>
+;
+; =======================================================================
+
+
+[meta]
+host = "www.example.com"
+website = "Company website"
+ttl = "300"
+; tags[]="monitoring"
+tags='["monitoring"]'
+
+; DETECT THIS
+i_am_the_new_key="sdfsdf"
+
+[notifications]
+email[]="support@example.com"
+slack["#support-channel2"]="https://hooks.slack.com/services/XXXXXX/YYYYYY/ZZZZZ"
+
+
+["hello plugin"]
+description="I sust wann say hello"
+function="hello"
+params='{
+"message": "Here I am"
+}'
+
+; -----------------------------------------------------------------------
diff --git a/tests/configs/validate_error_meta_wrong_type.ini b/tests/configs/validate_error_meta_wrong_type.ini
new file mode 100644
index 0000000..be28f67
--- /dev/null
+++ b/tests/configs/validate_error_meta_wrong_type.ini
@@ -0,0 +1,29 @@
+; =======================================================================
+;
+; APPMONITOR CLI CLIENT
+;
+; for the checks see its parameters
+; <https://os-docs.iml.unibe.ch/appmonitor/PHP_client/Plugins/Checks/index.html>
+;
+; =======================================================================
+
+
+[meta]
+host = "www.example.com"
+website = "Company website"
+
+; DETECT THIS
+; this ttl is not a number - and should be detected
+ttl = "123aa"
+
+tags='["monitoring"]'
+
+
+["hello plugin"]
+description="I sust wann say hello"
+function="hello"
+params='{
+"message": "Here I am"
+}'
+
+; -----------------------------------------------------------------------
diff --git a/tests/configs/validate_error_notify_wrong_email.ini b/tests/configs/validate_error_notify_wrong_email.ini
new file mode 100644
index 0000000..2e9e4fe
--- /dev/null
+++ b/tests/configs/validate_error_notify_wrong_email.ini
@@ -0,0 +1,32 @@
+; =======================================================================
+;
+; APPMONITOR CLI CLIENT
+;
+; for the checks see its parameters
+; <https://os-docs.iml.unibe.ch/appmonitor/PHP_client/Plugins/Checks/index.html>
+;
+; =======================================================================
+
+
+[meta]
+host = "www.example.com"
+website = "Company website"
+ttl = "300"
+; tags[]="monitoring"
+tags='["monitoring"]'
+
+
+[notifications]
+; DETECT THIS (not an array)
+email="support@example.com"
+slack["#support-channel2"]="https://hooks.slack.com/services/XXXXXX/YYYYYY/ZZZZZ"
+
+
+["hello plugin"]
+description="I sust wann say hello"
+function="hello"
+params='{
+"message": "Here I am"
+}'
+
+; -----------------------------------------------------------------------
-- 
GitLab