From 631a374a8eac09ad51e2972aa036ea6e194c8da5 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Sun, 17 May 2026 02:23:24 -0700 Subject: [PATCH 1/5] fix(weathermap): fix 12 logic bugs in rendering, config, and editor Signed-off-by: Thomas Vincent --- lib/WeatherMap.class.php | 14 +++++--------- lib/WeatherMap.functions.php | 6 +++--- lib/WeatherMapLink.class.php | 8 ++++---- lib/WeatherMapNode.class.php | 8 ++++---- lib/editor.inc.php | 2 +- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/lib/WeatherMap.class.php b/lib/WeatherMap.class.php index 91894b3..65f7bc0 100644 --- a/lib/WeatherMap.class.php +++ b/lib/WeatherMap.class.php @@ -1610,16 +1610,16 @@ function DrawLegend_Vertical($image, $scalename = 'DEFAULT', $height = 400, $inv if (!is_none($this->colours['DEFAULT']['KEYBG'])) { wimagefilledrectangle($scale_im, $box_left, $box_top, $box_right, $box_bottom, - $this->colours['DEFAULT']['KEYBG']['gdref1']); + $this->colours['DEFAULT']['KEYBG'][$scale_ref]); } if (!is_none($this->colours['DEFAULT']['KEYOUTLINE'])) { wimagerectangle($scale_im, $box_left, $box_top, $box_right, $box_bottom, - $this->colours['DEFAULT']['KEYOUTLINE']['gdref1']); + $this->colours['DEFAULT']['KEYOUTLINE'][$scale_ref]); } $this->myimagestring($scale_im, $font, $scale_left - $scalefactor, $scale_top - $tileheight , $title, - $this->colours['DEFAULT']['KEYTEXT']['gdref1'] + $this->colours['DEFAULT']['KEYTEXT'][$scale_ref] ); $updown = 1; @@ -1749,8 +1749,6 @@ function DrawLegend_Classic($image, $scalename = 'DEFAULT', $use_tags = false) { $boxx = $x; $boxy = $y; - $boxx = 0; - $boxy = 0; // allow for X11-style negative positioning if ($boxx < 0) { @@ -2143,7 +2141,7 @@ function ReadConfig($input, $is_include = false) { // if it isn't, it's the filename $lines = []; - if (strchr($input, "\n") != false || strchr($input, "\r") != false) { + if (strchr($input, "\n") !== false || strchr($input, "\r") !== false) { wm_debug('ReadConfig Detected that this is a config fragment.'); // strip out any Windows line-endings that have gotten in here @@ -3536,7 +3534,7 @@ function DrawMap($filename = '', $thumbnailfile = '', $thumbnailmax = 250, $with if (file_exists($this->configfile)) { $this->cachefile_version = crc32(file_get_contents($this->configfile)); } else { - wm_warn('Failed to find configuration file: ' . $this->configFile); + wm_warn('Failed to find configuration file: ' . $this->configfile); } } @@ -4492,8 +4490,6 @@ function SeedCoverage() { } function LoadCoverage($file) { - // ToDo - Why the return? - return 0; $i = 0; $fd = fopen($file, 'r'); diff --git a/lib/WeatherMap.functions.php b/lib/WeatherMap.functions.php index f167361..7d4ca55 100644 --- a/lib/WeatherMap.functions.php +++ b/lib/WeatherMap.functions.php @@ -242,14 +242,14 @@ function mysprintf($format, $value, $kilo = 1000) { wm_debug("KMGT formatting $value with $spec."); $result = nice_scalar($value, $kilo, $places); - $output = preg_replace('/%' . $spec . 'k/', $format, $result); + $output = preg_replace('/%' . $spec . 'k/', $result, $format); } elseif (preg_match('/%(-*)(\d*)([Tt])/', $format, $matches)) { $spec = $matches[3]; $precision = ($matches[2] == '' ? 10 : intval($matches[2])); $joinchar = ' '; if ($matches[1] == '-') { - $joinchar = ' '; + $joinchar = '-'; } // special formatting for time_t (t) and SNMP TimeTicks (T) @@ -878,7 +878,7 @@ function calc_curve(&$in_xarray, &$in_yarray, $pointsperspan = 32) { $yarray[$i], $xarray[$i + 1], $yarray[$i + 1], $xarray[$i + 2], $yarray[$i + 2], $xarray[$i + 3], $yarray[$i + 3]); - $curvepoints = $curvepoints + $newpoints; + $curvepoints = array_merge($curvepoints, $newpoints); } return $curvepoints; diff --git a/lib/WeatherMapLink.class.php b/lib/WeatherMapLink.class.php index af3a1c2..d25f1d4 100644 --- a/lib/WeatherMapLink.class.php +++ b/lib/WeatherMapLink.class.php @@ -860,8 +860,8 @@ function asJS() { $js .= 'bw_out:' . js_escape($this->max_bandwidth_out_cfg) . ', '; $js .= 'name:' . js_escape($this->name) . ', '; - $js .= 'overlibwidth:"' . $this->overlibheight . '", '; - $js .= 'overlibheight:"' . $this->overlibwidth . '", '; + $js .= 'overlibwidth:"' . $this->overlibwidth . '", '; + $js .= 'overlibheight:"' . $this->overlibheight . '", '; $js .= 'overlibcaption:' . js_escape($this->overlibcaption[IN]) . ', '; $js .= 'commentin:' . js_escape($this->comments[IN]) . ', '; @@ -910,8 +910,8 @@ function asJSON($complete = true) { $js .= '"bw_out":' . js_escape($this->max_bandwidth_out_cfg) . ', '; $js .= '"name":' . js_escape($this->name) . ', '; - $js .= '"overlibwidth":"' . $this->overlibheight . '", '; - $js .= '"overlibheight":"' . $this->overlibwidth . '", '; + $js .= '"overlibwidth":"' . $this->overlibwidth . '", '; + $js .= '"overlibheight":"' . $this->overlibheight . '", '; $js .= '"overlibcaption":' . js_escape($this->overlibcaption) . ', '; } diff --git a/lib/WeatherMapNode.class.php b/lib/WeatherMapNode.class.php index ab0614a..f0c2b60 100644 --- a/lib/WeatherMapNode.class.php +++ b/lib/WeatherMapNode.class.php @@ -977,8 +977,8 @@ function asJS() { $js .= 'infourl:' . js_escape($this->infourl[IN]) . ', '; $js .= 'overlibcaption:' . js_escape($this->overlibcaption[IN]) . ', '; $js .= 'overliburl:' . js_escape(join(' ',$this->overliburl[IN])) . ', '; - $js .= 'overlibwidth:' . $this->overlibheight . ', '; - $js .= 'overlibheight:' . $this->overlibwidth . ', '; + $js .= 'overlibwidth:' . $this->overlibwidth . ', '; + $js .= 'overlibheight:' . $this->overlibheight . ', '; if (preg_match('/^(none|nink|inpie|outpie|box|rbox|gauge|round)$/', $this->iconfile)) { $js .= 'iconfile:' . js_escape('::' . $this->iconfile); @@ -1011,8 +1011,8 @@ function asJSON($complete = true) { $js .= '"overliburl":' . js_escape($this->overliburl) . ', '; $js .= '"overlibcaption":' . js_escape($this->overlibcaption) . ', '; - $js .= '"overlibwidth":' . $this->overlibheight . ', '; - $js .= '"overlibheight":' . $this->overlibwidth . ', '; + $js .= '"overlibwidth":' . $this->overlibwidth . ', '; + $js .= '"overlibheight":' . $this->overlibheight . ', '; $js .= '"iconfile":' . js_escape($this->iconfile) . ', '; } diff --git a/lib/editor.inc.php b/lib/editor.inc.php index f8f1df6..b0faf36 100644 --- a/lib/editor.inc.php +++ b/lib/editor.inc.php @@ -543,7 +543,7 @@ function handle_inheritance(&$map, &$inheritables) { if ($inheritable[0] == 'node') { $map->nodes['DEFAULT']->$fieldname = $new; - foreach ($map->nodes as $link_name => $node) { + foreach ($map->nodes as $node_name => $node) { if ($node->name != ':: DEFAULT ::' && $old == $node->$fieldname) { $map->nodes[$node->name]->$fieldname = $new; } From 9584e4b51a4735ed62ea73dc3090c866684cfcba Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Sun, 17 May 2026 02:38:02 -0700 Subject: [PATCH 2/5] fix(weathermap): path guard, explode validation, preg_replace_callback, scale_ref isset Signed-off-by: Thomas Vincent --- lib/WeatherMap.class.php | 31 +++++++++++++++++++++++-------- lib/WeatherMap.functions.php | 2 +- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/lib/WeatherMap.class.php b/lib/WeatherMap.class.php index 65f7bc0..b4890ad 100644 --- a/lib/WeatherMap.class.php +++ b/lib/WeatherMap.class.php @@ -1608,19 +1608,21 @@ function DrawLegend_Vertical($image, $scalename = 'DEFAULT', $height = 400, $inv $this->AllocateScaleColours($scale_im,$scale_ref); - if (!is_none($this->colours['DEFAULT']['KEYBG'])) { + if (!is_none($this->colours['DEFAULT']['KEYBG']) && isset($this->colours['DEFAULT']['KEYBG'][$scale_ref])) { wimagefilledrectangle($scale_im, $box_left, $box_top, $box_right, $box_bottom, $this->colours['DEFAULT']['KEYBG'][$scale_ref]); } - if (!is_none($this->colours['DEFAULT']['KEYOUTLINE'])) { + if (!is_none($this->colours['DEFAULT']['KEYOUTLINE']) && isset($this->colours['DEFAULT']['KEYOUTLINE'][$scale_ref])) { wimagerectangle($scale_im, $box_left, $box_top, $box_right, $box_bottom, $this->colours['DEFAULT']['KEYOUTLINE'][$scale_ref]); } - $this->myimagestring($scale_im, $font, $scale_left - $scalefactor, $scale_top - $tileheight , $title, - $this->colours['DEFAULT']['KEYTEXT'][$scale_ref] - ); + if (isset($this->colours['DEFAULT']['KEYTEXT'][$scale_ref])) { + $this->myimagestring($scale_im, $font, $scale_left - $scalefactor, $scale_top - $tileheight , $title, + $this->colours['DEFAULT']['KEYTEXT'][$scale_ref] + ); + } $updown = 1; @@ -4492,14 +4494,27 @@ function SeedCoverage() { function LoadCoverage($file) { $i = 0; - $fd = fopen($file, 'r'); + $real = realpath($file); + $base = defined('CACTI_PATH_BASE') ? realpath(CACTI_PATH_BASE) : realpath(dirname(dirname(__FILE__))); + + if ($real === false || $base === false || strpos($real, $base . DIRECTORY_SEPARATOR) !== 0) { + return; + } + + $fd = fopen($real, 'r'); if (is_resource($fd)) { while (!feof($fd)) { - $line = fgets($fd,1024); + $line = fgets($fd, 1024); $line = trim($line); - [$val,$key] = explode("\t",$line); + $parts = explode("\t", $line); + + if (count($parts) < 2) { + continue; + } + + [$val, $key] = $parts; if ($key != '') { $this->coverage[$key] = $val; diff --git a/lib/WeatherMap.functions.php b/lib/WeatherMap.functions.php index 7d4ca55..1fa74e4 100644 --- a/lib/WeatherMap.functions.php +++ b/lib/WeatherMap.functions.php @@ -242,7 +242,7 @@ function mysprintf($format, $value, $kilo = 1000) { wm_debug("KMGT formatting $value with $spec."); $result = nice_scalar($value, $kilo, $places); - $output = preg_replace('/%' . $spec . 'k/', $result, $format); + $output = preg_replace_callback('/%' . $spec . 'k/', function() use ($result) { return $result; }, $format); } elseif (preg_match('/%(-*)(\d*)([Tt])/', $format, $matches)) { $spec = $matches[3]; $precision = ($matches[2] == '' ? 10 : intval($matches[2])); From 98a62e64dafad952a69db6c1e7ab42e995ecb2b4 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Sun, 17 May 2026 03:13:18 -0700 Subject: [PATCH 3/5] fix(weathermap): render_colour col[2] sentinel, ReadData 3-element return, chdir not dir Signed-off-by: Thomas Vincent --- lib/WeatherMap.class.php | 2 +- lib/WeatherMap.functions.php | 6 +++--- weathermap-cacti-plugin.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/WeatherMap.class.php b/lib/WeatherMap.class.php index b4890ad..de97bc5 100644 --- a/lib/WeatherMap.class.php +++ b/lib/WeatherMap.class.php @@ -120,7 +120,7 @@ function Recognise($targetstring) { // itemtype and itemname may be used as part of the target (e.g. for TSV source line) // function ReadData($targetstring, $configline, $itemtype, $itemname, $map) { return (array(-1,-1)); } function ReadData($targetstring, &$map, &$item) { - return ([-1, -1]); + return ([-1, -1, 0]); } // pre-register a target + context, to allow a plugin to batch up queries to a slow database, or snmp for example diff --git a/lib/WeatherMap.functions.php b/lib/WeatherMap.functions.php index 1fa74e4..7a757d4 100644 --- a/lib/WeatherMap.functions.php +++ b/lib/WeatherMap.functions.php @@ -414,15 +414,15 @@ function is_none($arr) { } function render_colour($col) { - if (($col[0] == -1) && ($col[1] == -1) && ($col[1] == -1)) { + if (($col[0] == -1) && ($col[1] == -1) && ($col[2] == -1)) { return 'none'; } - if (($col[0] == -2) && ($col[1] == -2) && ($col[1] == -2)) { + if (($col[0] == -2) && ($col[1] == -2) && ($col[2] == -2)) { return 'copy'; } - if (($col[0] == -3) && ($col[1] == -3) && ($col[1] == -3)) { + if (($col[0] == -3) && ($col[1] == -3) && ($col[2] == -3)) { return 'contrast'; } else { return sprintf('%d %d %d', $col[0], $col[1], $col[2]); diff --git a/weathermap-cacti-plugin.php b/weathermap-cacti-plugin.php index f3f4faf..78111db 100644 --- a/weathermap-cacti-plugin.php +++ b/weathermap-cacti-plugin.php @@ -86,7 +86,7 @@ // readfile_chunked($imagefile); readfile($imagefile); - dir($orig_cwd); + chdir($orig_cwd); } else { // no permission to view this map } @@ -128,7 +128,7 @@ $map->ReadData(); $map->DrawMap('', '', 250, true, false); - dir($orig_cwd); + chdir($orig_cwd); } } } From dfb619881842267c3bce7fc3c29abc2371c1b692 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Sun, 17 May 2026 03:36:31 -0700 Subject: [PATCH 4/5] fix(weathermap): correct legend local coords, curve point ordering, regex spec Use (0,0) as origin for all drawing inside scale_im temp image; map position applies only at the imagecopy destination. Drop catmull-rom span initializer via array_slice to prevent out-of-order control point. Escape preg_replace_callback spec with preg_quote so a decimal in the format string does not match any character in the pattern. Signed-off-by: Thomas Vincent --- lib/WeatherMap.class.php | 10 +++++----- lib/WeatherMap.functions.php | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/WeatherMap.class.php b/lib/WeatherMap.class.php index de97bc5..789aadb 100644 --- a/lib/WeatherMap.class.php +++ b/lib/WeatherMap.class.php @@ -1774,18 +1774,18 @@ function DrawLegend_Classic($image, $scalename = 'DEFAULT', $use_tags = false) { $this->AllocateScaleColours($scale_im,$scale_ref); if (!is_none($this->colours['DEFAULT']['KEYBG'])) { - wimagefilledrectangle($scale_im, $boxx, $boxy, $boxx + $boxwidth, $boxy + $boxheight, + wimagefilledrectangle($scale_im, 0, 0, $boxwidth, $boxheight, $this->colours['DEFAULT']['KEYBG'][$scale_ref] ); } if (!is_none($this->colours['DEFAULT']['KEYOUTLINE'])) { - wimagerectangle($scale_im, $boxx, $boxy, $boxx + $boxwidth, $boxy + $boxheight, + wimagerectangle($scale_im, 0, 0, $boxwidth, $boxheight, $this->colours['DEFAULT']['KEYOUTLINE'][$scale_ref] ); } - $this->myimagestring($scale_im, $font, $boxx + 4, $boxy + 4 + $tileheight, $title, + $this->myimagestring($scale_im, $font, 4, 4 + $tileheight, $title, $this->colours['DEFAULT']['KEYTEXT'][$scale_ref] ); @@ -1800,8 +1800,8 @@ function DrawLegend_Classic($image, $scalename = 'DEFAULT', $use_tags = false) { // debug("$i: drawing\n"); if (($hide_zero == 0) || $colour['key'] != '0_0') { - $y = $boxy + $tilespacing * $i + 8; - $x = $boxx + 6; + $y = $tilespacing * $i + 8; + $x = 6; $fudgefactor = 0; diff --git a/lib/WeatherMap.functions.php b/lib/WeatherMap.functions.php index 7a757d4..73110bc 100644 --- a/lib/WeatherMap.functions.php +++ b/lib/WeatherMap.functions.php @@ -242,7 +242,7 @@ function mysprintf($format, $value, $kilo = 1000) { wm_debug("KMGT formatting $value with $spec."); $result = nice_scalar($value, $kilo, $places); - $output = preg_replace_callback('/%' . $spec . 'k/', function() use ($result) { return $result; }, $format); + $output = preg_replace_callback('/%' . preg_quote($spec, '/') . 'k/', function() use ($result) { return $result; }, $format); } elseif (preg_match('/%(-*)(\d*)([Tt])/', $format, $matches)) { $spec = $matches[3]; $precision = ($matches[2] == '' ? 10 : intval($matches[2])); @@ -878,7 +878,7 @@ function calc_curve(&$in_xarray, &$in_yarray, $pointsperspan = 32) { $yarray[$i], $xarray[$i + 1], $yarray[$i + 1], $xarray[$i + 2], $yarray[$i + 2], $xarray[$i + 3], $yarray[$i + 3]); - $curvepoints = array_merge($curvepoints, $newpoints); + $curvepoints = array_merge($curvepoints, array_slice($newpoints, 1)); } return $curvepoints; From 9739e983f8272c19469aa1da2d9adc56aca99ab8 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Sun, 17 May 2026 04:00:49 -0700 Subject: [PATCH 5/5] fix(weathermap): SaveCoverage path guard, KEYTEXT isset in legend draw Add realpath-based directory containment guard to SaveCoverage matching the existing LoadCoverage pattern. Wrap legend title myimagestring call in isset check for KEYTEXT scale_ref key to prevent undefined-index notice when AllocateScaleColours omits a key for non-default scales. Signed-off-by: Thomas Vincent --- lib/WeatherMap.class.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/WeatherMap.class.php b/lib/WeatherMap.class.php index 789aadb..d5fb539 100644 --- a/lib/WeatherMap.class.php +++ b/lib/WeatherMap.class.php @@ -1785,9 +1785,11 @@ function DrawLegend_Classic($image, $scalename = 'DEFAULT', $use_tags = false) { ); } - $this->myimagestring($scale_im, $font, 4, 4 + $tileheight, $title, - $this->colours['DEFAULT']['KEYTEXT'][$scale_ref] - ); + if (isset($this->colours['DEFAULT']['KEYTEXT'][$scale_ref])) { + $this->myimagestring($scale_im, $font, 4, 4 + $tileheight, $title, + $this->colours['DEFAULT']['KEYTEXT'][$scale_ref] + ); + } $i = 1; @@ -4532,7 +4534,15 @@ function LoadCoverage($file) { function SaveCoverage($file) { $i = 0; - $fd = fopen($file, 'w+'); + $dir = realpath(dirname($file)); + $base = defined('CACTI_PATH_BASE') ? realpath(CACTI_PATH_BASE) : realpath(dirname(dirname(__FILE__))); + + if ($dir === false || $base === false || strpos($dir . DIRECTORY_SEPARATOR, $base . DIRECTORY_SEPARATOR) !== 0) { + return; + } + + $safe = $dir . DIRECTORY_SEPARATOR . basename($file); + $fd = fopen($safe, 'w+'); foreach ($this->coverage as $key=>$val) { fputs($fd, "$val\t$key\n");