From 2d6b8f237d4dfc494aca38e6e6e566d71cce3d5e Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Wed, 8 Apr 2026 22:37:06 -0700 Subject: [PATCH 1/7] fix(security): defense-in-depth hardening for plugin_thold Automated fixes: - XSS: escape request variables in HTML output - SQLi: convert string-concat queries to prepared statements - Deserialization: add allowed_classes=>false - Temp files: replace rand() with tempnam() Signed-off-by: Thomas Vincent --- notify_lists.php | 12 ++++++------ setup.php | 12 ++++++------ thold_graph.php | 4 ++-- thold_process.php | 2 +- thold_webapi.php | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/notify_lists.php b/notify_lists.php index 537563b..9921be9 100644 --- a/notify_lists.php +++ b/notify_lists.php @@ -1138,7 +1138,7 @@ function hosts($header_label) { @@ -1567,11 +1567,11 @@ function thold_device_template_top() { $('#continue').click(function(data) { $.post('host_templates.php?action=item_remove_tt', { __csrf_magic: csrfMagicToken, - host_template_id: , - id: + host_template_id: , + id: }).done(function(data) { $('#cdialog').dialog('close'); - loadPageNoHeader('host_templates.php?action=edit&header=false&id='); + loadPageNoHeader('host_templates.php?action=edit&header=false&id='); }); }); diff --git a/thold_graph.php b/thold_graph.php index 97a61af..ee63262 100644 --- a/thold_graph.php +++ b/thold_graph.php @@ -251,7 +251,7 @@ function form_thold_filter() { - '> + '> From 652419ff8796d2041c3d8d39622e542fd128486c Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Sat, 11 Apr 2026 01:28:33 -0700 Subject: [PATCH 5/7] fix: pass unserialize options to cacti_unserialize --- thold_webapi.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thold_webapi.php b/thold_webapi.php index 932fe20..d443db8 100644 --- a/thold_webapi.php +++ b/thold_webapi.php @@ -861,7 +861,7 @@ function applyTholdFilter() { function thold_new_graphs_save($host_id) { $return_array = false; - $selected_graphs_array = cacti_unserialize(stripslashes(get_nfilter_request_var('selected_graphs_array', array('allowed_classes' => false)))); + $selected_graphs_array = cacti_unserialize(stripslashes(get_nfilter_request_var('selected_graphs_array')), array('allowed_classes' => false)); $values = []; From 7191dbce1123b6ac30c7da2489844e564673a379 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Sat, 11 Apr 2026 01:30:51 -0700 Subject: [PATCH 6/7] chore: drop local .omc ignore noise --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 32791f0..eb71606 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,3 @@ # +-------------------------------------------------------------------------+ locales/po/*.mo -.omc/ From e591b102e69d6dccfd29811d9bee9472fcfe80d6 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Sat, 11 Apr 2026 13:37:48 -0700 Subject: [PATCH 7/7] fix(security): harden notify list form and filter handling --- notify_lists.php | 24 ++++++++--------- tests/Integration/test_notify_list_wiring.php | 24 +++++++++++++++++ .../Unit/test_notify_list_security_guards.php | 26 +++++++++++++++++++ ...t_notify_list_no_raw_sql_or_form_reuse.php | 26 +++++++++++++++++++ 4 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 tests/Integration/test_notify_list_wiring.php create mode 100644 tests/Unit/test_notify_list_security_guards.php create mode 100644 tests/e2e/test_notify_list_no_raw_sql_or_form_reuse.php diff --git a/notify_lists.php b/notify_lists.php index 4124be5..48291bf 100644 --- a/notify_lists.php +++ b/notify_lists.php @@ -590,7 +590,7 @@ function form_actions() { - + $save_html "; @@ -665,10 +665,10 @@ function form_actions() { print " - + - + $save_html "; @@ -743,10 +743,10 @@ function form_actions() { print " - + - + $save_html "; @@ -828,10 +828,10 @@ function form_actions() { print " - + - + $save_html "; @@ -1241,7 +1241,7 @@ function clearFilter() { $hosts = db_fetch_assoc_prepared($sql_query, $sql_params); - $nav = html_nav_bar('notify_lists.php?action=edit&id=' . get_request_var('id'), MAX_DISPLAY_PAGES, get_request_var('page'), $rows, $total_rows, 10, __('Devices', 'thold'), 'page', 'main'); + $nav = html_nav_bar('notify_lists.php?action=edit&id=' . (int)get_request_var('id'), MAX_DISPLAY_PAGES, get_request_var('page'), $rows, $total_rows, 10, __('Devices', 'thold'), 'page', 'main'); form_start('notify_lists.php', 'chk'); @@ -1387,7 +1387,7 @@ function tholds($header_label) { $limit = ($rows * (intval(get_request_var('page')) - 1)) . ", $rows"; if (!isempty_request_var('template') && get_request_var('template') != '-1') { - $sql_where .= ($sql_where == '' ? '' : ' AND ') . 'td.data_template_id = ' . get_request_var('template'); + $sql_where .= ($sql_where == '' ? '' : ' AND ') . 'td.data_template_id = ' . (int)get_request_var('template'); } if (get_request_var('site_id') == '-1') { @@ -1395,11 +1395,11 @@ function tholds($header_label) { } elseif (get_request_var('site_id') == '0') { $sql_where .= ($sql_where == '' ? '' : ' AND ') . ' h.site_id=0'; } elseif (!isempty_request_var('site_id')) { - $sql_where .= ($sql_where == '' ? '' : ' AND ') . ' h.site_id=' . get_request_var('site_id'); + $sql_where .= ($sql_where == '' ? '' : ' AND ') . ' h.site_id=' . (int)get_request_var('site_id'); } if (strlen(get_request_var('rfilter'))) { - $sql_where .= (!strlen($sql_where) ? '' : ' AND ') . "td.name_cache RLIKE '" . get_request_var('rfilter') . "'"; + $sql_where .= (!strlen($sql_where) ? '' : ' AND ') . 'td.name_cache RLIKE ' . db_qstr(get_request_var('rfilter')); } if ($statefilter != '') { @@ -1407,7 +1407,7 @@ function tholds($header_label) { } if (get_request_var('associated') == 'true') { - $sql_where .= (!strlen($sql_where) ? '' : ' AND ') . '(td.notify_warning=' . get_request_var('id') . ' OR td.notify_alert=' . get_request_var('id') . ')'; + $sql_where .= (!strlen($sql_where) ? '' : ' AND ') . '(td.notify_warning=' . (int)get_request_var('id') . ' OR td.notify_alert=' . (int)get_request_var('id') . ')'; } $result = get_allowed_thresholds($sql_where, $sort, $limit, $total_rows); diff --git a/tests/Integration/test_notify_list_wiring.php b/tests/Integration/test_notify_list_wiring.php new file mode 100644 index 0000000..5d492fe --- /dev/null +++ b/tests/Integration/test_notify_list_wiring.php @@ -0,0 +1,24 @@ +", + "'notify_lists.php?action=edit&id=' . (int)get_request_var('id')", + "", + "", +); + +foreach ($checks as $needle) { + if (strpos($source, $needle) === false) { + fwrite(STDERR, "Missing expected notify list wiring\n"); + exit(1); + } +} + +echo "OK\n"; diff --git a/tests/Unit/test_notify_list_security_guards.php b/tests/Unit/test_notify_list_security_guards.php new file mode 100644 index 0000000..1b7d86f --- /dev/null +++ b/tests/Unit/test_notify_list_security_guards.php @@ -0,0 +1,26 @@ +", + "", + "td.name_cache RLIKE '\" . get_request_var('rfilter') . \"'", + "'td.data_template_id = ' . get_request_var('template')", + "' h.site_id=' . get_request_var('site_id')", + "'(td.notify_warning=' . get_request_var('id') . ' OR td.notify_alert=' . get_request_var('id') . ')'", +); + +foreach ($legacy as $needle) { + if (strpos($source, $needle) !== false) { + fwrite(STDERR, "Found legacy insecure notify list pattern\n"); + exit(1); + } +} + +echo "OK\n";