From ca76a663471b97c8d96746ddf31ecc1453b28657 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Wed, 7 Oct 2026 18:24:41 -0400 Subject: [PATCH 1/4] security: move inline event handlers to CSP-safe bindings Cacti's Content-Security-Policy script-src-attr directive blocks inline event-handler attributes. This converts the plugin's remaining inline handlers: - Confirmation Cancel buttons (flowview_databases.php, flowview_filters.php, flowview_schedules.php) switch from onClick='cactiReturnTo()' to the cactiReturnTo class; flowview_devices.php's Cancel button drops its onClick='document.location=...' for the cactiReturnTo class + data-url. - The database page's source/rows/verified/version selects drop inline onChange='applyFilter()' and are bound via change() in each ready block. - The main filter's report/sort/cutoff selects, the predefined-timespan select and the time-shift icons (flowview_display_filter in includes/functions.php) drop their inline onChange/onclick and are bound in the function's ready block via delegated change()/click() calls to the same applyFilter/ applyTimespan/timeshift functions they already used. includes/functions.php is measured; a new FlowviewFilterControlsTest exercises flowview_display_filter() so the changed time-shift lines stay covered. The web UI entry points are already in the patch-coverage allowlist. No i18n calls changed, so cacti.pot is untouched. Cacti 1.2.31+ binds the cactiReturnTo class automatically; the README documents a one-time applySkin() snippet for earlier releases. --- CHANGELOG.md | 1 + README.md | 14 ++++++++ flowview_databases.php | 30 ++++++++++++----- flowview_devices.php | 2 +- flowview_filters.php | 2 +- flowview_schedules.php | 2 +- includes/functions.php | 28 +++++++++++----- tests/Unit/FlowviewFilterControlsTest.php | 41 +++++++++++++++++++++++ 8 files changed, 100 insertions(+), 20 deletions(-) create mode 100644 tests/Unit/FlowviewFilterControlsTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index ea0c594..50a4ee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ --- develop --- +* security: Move the plugin's remaining inline event handlers to CSP-safe bindings so the pages no longer trip Cacti's Content-Security-Policy script-src-attr directive: confirmation Cancel buttons use the `cactiReturnTo` class, the database/filter selects are bound via change() in their ready blocks, and the main filter's sort/report selects and time-shift icons are bound in flowview_display_filter's ready block * dev: Replace the translation-template CI check that regenerated locales/po/cacti.pot and compared it with a diff-based gate (tests/bin/check-i18n-pot.php) requiring cacti.pot to be updated only when a pull request adds, removes, or modifies an i18n function call * issue: Prevent flow-capture from fatally erroring (TypeError in spl_object_hash) when flowview_db_check_reconnect() held a null shared-database handle; normalize a non-object connection to false so db_check_reconnect() checks and reconnects the default connection instead * dev: Measure CI coverage with xdebug instead of pcov so the plugin's own sources are instrumented (pcov auto-scopes to the Composer root and skipped cacti/plugins/, leaving the patch-coverage gate with nothing to measure) diff --git a/README.md b/README.md index 8984f12..8de558c 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,20 @@ transfer into the Cacti database. Check the README.md in the service folder to describe this process. Any time you add a new listener, you must restart this service. +## Cacti compatibility + +If you are running a version of Cacti below 1.2.31, please add the function +below to the `applySkin()` function in `include/layout.js` to enable the Cancel +buttons on forms to work: + +```js +$(document).off('click.cactiReturnTo', '.cactiReturnTo') + .on('click.cactiReturnTo', '.cactiReturnTo', function(event) { + event.preventDefault(); + cactiReturnTo($(this).attr('data-url')); + }); +``` + ## Upgrading After upgrading the plugin (when new plugin files have been copied into place), diff --git a/flowview_databases.php b/flowview_databases.php index d97a39d..2ef2043 100644 --- a/flowview_databases.php +++ b/flowview_databases.php @@ -946,7 +946,7 @@ function view_db_table($tab, &$tabs): void { - - 0) { @@ -1063,6 +1063,10 @@ function() { } $(function() { + $('#source, #rows, #verified, #version').change(function() { + applyFilter(); + }); + $('#refresh').on('click', function() { applyFilter(); }); @@ -1217,7 +1221,7 @@ function form_actions(): void { \n"; - $save_html = " + $save_html = " '; } } else { @@ -1313,7 +1317,7 @@ function view_dns_cache(): void { - @@ -1323,7 +1327,7 @@ function view_dns_cache(): void { - - 0) { @@ -1389,6 +1393,10 @@ function purgeFilter() { } $(function() { + $('#source, #rows, #verified, #version').change(function() { + applyFilter(); + }); + $('#refresh').on('click', function() { applyFilter(); }); @@ -1614,7 +1622,7 @@ function view_routes($tab): void { - @@ -1624,7 +1632,7 @@ function view_routes($tab): void { - - 0) { @@ -1742,6 +1750,10 @@ function() { } $(function() { + $('#source, #rows, #verified, #version').change(function() { + applyFilter(); + }); + $('#refresh').on('click', function() { applyFilter(); }); diff --git a/flowview_devices.php b/flowview_devices.php index 5318e9c..bf52069 100644 --- a/flowview_devices.php +++ b/flowview_devices.php @@ -296,7 +296,7 @@ function actions_devices(): void { - + $save_html \n"; diff --git a/flowview_filters.php b/flowview_filters.php index f51c15c..ca758a5 100644 --- a/flowview_filters.php +++ b/flowview_filters.php @@ -178,7 +178,7 @@ function actions_filters(): void { - + $save_html "; diff --git a/flowview_schedules.php b/flowview_schedules.php index 4cf116b..77b3ea7 100644 --- a/flowview_schedules.php +++ b/flowview_schedules.php @@ -343,7 +343,7 @@ function actions_schedules(): void { - + $save_html "; diff --git a/includes/functions.php b/includes/functions.php index 1320f9c..9c41102 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -798,7 +798,7 @@ function flowview_display_filter(): void { - - - - $value) { @@ -946,7 +946,7 @@ function flowview_display_filter(): void { - $value) { @@ -976,7 +976,7 @@ function flowview_display_filter(): void { ' class='calendar fa fa-calendar-alt' id='endDate'> - ' onclick='timeshiftFilterLeft()' class='shiftArrow fa fa-backward'> + ' class='shiftArrow fa fa-backward timeshiftBackward'> - ' onclick='timeshiftFilterRight()' class='shiftArrow fa fa-forward'> + ' class='shiftArrow fa fa-forward timeshiftForward'> @@ -1157,10 +1157,22 @@ function flowview_display_filter(): void { changeQuery(true); }); - $('#domains, #usenat, #exclude, #graph_type, #graph_height, #device_id, #ex_addr').off('change').on('change', function() { + $('#domains, #usenat, #exclude, #graph_type, #graph_height, #device_id, #ex_addr, #report, #sortfield, #cutofflines, #cutoffoctets').off('change').on('change', function() { applyFilter(false); }); + $('#predefined_timespan').off('change').on('change', function() { + applyTimespan(); + }); + + $('.timeshiftBackward').off('click').on('click', function() { + timeshiftFilterLeft(); + }); + + $('.timeshiftForward').off('click').on('click', function() { + timeshiftFilterRight(); + }); + $('#flowview_filter').on('submit', function(event) { event.preventDefault(); applyFilter(false); diff --git a/tests/Unit/FlowviewFilterControlsTest.php b/tests/Unit/FlowviewFilterControlsTest.php new file mode 100644 index 0000000..7d5db95 --- /dev/null +++ b/tests/Unit/FlowviewFilterControlsTest.php @@ -0,0 +1,41 @@ +toContain('timeshiftBackward'); + expect($output)->toContain('timeshiftForward'); +}); From b1e0bf47548689897eda3bdeacca9d9488cba196 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Wed, 7 Oct 2026 18:54:15 -0400 Subject: [PATCH 2/4] Add functions.php inclusion to FlowviewFilterControlsTest Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/Unit/FlowviewFilterControlsTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Unit/FlowviewFilterControlsTest.php b/tests/Unit/FlowviewFilterControlsTest.php index 7d5db95..37af65a 100644 --- a/tests/Unit/FlowviewFilterControlsTest.php +++ b/tests/Unit/FlowviewFilterControlsTest.php @@ -13,7 +13,8 @@ */ beforeAll(function () { - require_once __DIR__ . '/../../setup.php'; +require_once __DIR__ . '/../../setup.php'; +require_once __DIR__ . '/../../includes/functions.php'; // The full filter render calls a handful of Cacti UI helpers the unit // harness does not otherwise need; stub the ones it does not already define. From 4c08bc75b1f6200f9b8cbb3dbb9045beab721fa5 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Wed, 7 Oct 2026 18:54:27 -0400 Subject: [PATCH 3/4] Update FlowviewFilterControlsTest expectations Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/Unit/FlowviewFilterControlsTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Unit/FlowviewFilterControlsTest.php b/tests/Unit/FlowviewFilterControlsTest.php index 37af65a..b1d9b8a 100644 --- a/tests/Unit/FlowviewFilterControlsTest.php +++ b/tests/Unit/FlowviewFilterControlsTest.php @@ -37,6 +37,7 @@ $output = ob_get_clean(); - expect($output)->toContain('timeshiftBackward'); - expect($output)->toContain('timeshiftForward'); +expect($output)->toContain('timeshiftBackward'); +expect($output)->toContain('timeshiftForward'); +expect($output)->not->toMatch('/\son(?:click|change)\s*=/i'); }); From ccb95215866f7f0a53915e51fbd02c90fc18a012 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Wed, 7 Oct 2026 19:03:46 -0400 Subject: [PATCH 4/4] test: stub array_rekey so FlowviewFilterControls render reaches time-shift controls flowview_display_filter() requires includes/arrays.php, which re-keys its device/template lookups with Cacti core's array_rekey(). The stub test bootstrap does not define it, so the require threw 'Call to undefined function array_rekey()' before any markup was emitted and the timeshiftBackward/Forward assertions saw empty output. Add a function_exists-guarded array_rekey stub mirroring core behaviour. --- tests/Unit/FlowviewFilterControlsTest.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/Unit/FlowviewFilterControlsTest.php b/tests/Unit/FlowviewFilterControlsTest.php index b1d9b8a..7513659 100644 --- a/tests/Unit/FlowviewFilterControlsTest.php +++ b/tests/Unit/FlowviewFilterControlsTest.php @@ -22,6 +22,26 @@ if (!function_exists('html_end_box')) { function html_end_box(...$args) {} } if (!function_exists('title_trim')) { function title_trim($text, $length = 0) { return (string) $text; } } if (!function_exists('html_escape_request_var')) { function html_escape_request_var($name) { return ''; } } + + // includes/arrays.php (required inside flowview_display_filter) re-keys its + // device/template lookups with Cacti core's array_rekey(), which the stub + // harness does not provide; mirror core's behaviour so the require succeeds. + if (!function_exists('array_rekey')) { + function array_rekey($array, $key, $value) { + $ret = array(); + if (is_array($array)) { + foreach ($array as $item) { + $k = $item[$key]; + if (is_array($value)) { + foreach ($value as $v) { $ret[$k][$v] = $item[$v]; } + } else { + $ret[$k] = $item[$value]; + } + } + } + return $ret; + } + } }); it('renders the time-shift controls with CSP-safe classes and no inline handlers', function () {