From 741fb6843c00c2b6fce01e04e3adcd7bc9fda884 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Wed, 7 Oct 2026 16:13:49 -0400 Subject: [PATCH 1/2] security: move filter inline onChange handlers into ready() for CSP The graph-export filter's Rows and refresh-interval selects carried inline onChange='applyFilter()' attributes. Under Cacti's Content-Security-Policy these trip the script-src-attr directive (inline event handlers). Remove them and bind both selects via change in the existing ready block (the refresh select was previously bound via click, which does not reliably fire on a select, and the rows select was unbound). Regenerated locales/po/cacti.pot for the shifted source line references. gexport.php is already in the patch-coverage gate's allowlist. The cactiReturnTo() cancel buttons are left inline (shared Cacti-core pattern, tracked separately). --- CHANGELOG.md | 1 + gexport.php | 6 +++--- locales/po/cacti.pot | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d3a6b6..7f7579c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ --- 2.1 --- +- security: Move the filter selects' inline onChange handlers into the ready block (bound via change, not click) so the page no longer trips Cacti's Content-Security-Policy script-src-attr directive - refactor: Move the functions.php and gexport_security.php library files into includes/ and switch every file inclusion from include/include_once to require/require_once for fail-fast consistency (references updated across setup.php, gexport.php, poller_export.php, and the test suite) - refactor: Move all schema management into includes/database.php (the thold model) and manage the graph_exports/graph_exports_tasks schema through Cacti's plugin table API - create with api_plugin_db_table_create() and refresh existing tables with db_update_table() from a single shared definition, replacing the raw CREATE TABLE/version-gated ALTER TABLE migrations (the one column rename is kept as a pre-step since db_update_table() can not rename) - 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/gexport.php b/gexport.php index 30596f9..4bb50ca 100644 --- a/gexport.php +++ b/gexport.php @@ -697,7 +697,7 @@ function export_filter() { - - __('Never', 'gexport'), @@ -759,7 +759,7 @@ function clearFilter() { } $(function() { - $('#refresh').click(function() { + $('#rows, #refresh').change(function() { applyFilter(); }); diff --git a/locales/po/cacti.pot b/locales/po/cacti.pot index 17975e4..35c87d3 100644 --- a/locales/po/cacti.pot +++ b/locales/po/cacti.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Cacti \n" "Report-Msgid-Bugs-To: developers@cacti.net\n" -"POT-Creation-Date: 2026-09-29 21:21-0400\n" +"POT-Creation-Date: 2026-10-07 16:13-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Cacti Developers >\n" "Language-Team: Cacti Developers \n" From 8677bb9328ec383a3c4e0f118894f0de2ff834a3 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Wed, 7 Oct 2026 17:10:49 -0400 Subject: [PATCH 2/2] security: convert cactiReturnTo cancel buttons to the CSP-safe class The confirmation pages' Cancel buttons used an inline onClick='cactiReturnTo()' handler, which trips Cacti's Content-Security-Policy script-src-attr directive. Switch them to the CSP-safe cactiReturnTo CSS class (class='cactiReturnTo', plus an optional data-url when a target is passed). Cacti 1.2.31 and later bind this class automatically. The README documents a one-time include/layout.js applySkin() snippet for operators still on an earlier release - no shim is baked into the core or the plugin. --- CHANGELOG.md | 1 + README.md | 14 ++++++++++++++ gexport.php | 12 ++++++------ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f7579c..4646a93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ --- 2.1 --- +- security: Replace the confirmation pages' inline `onClick='cactiReturnTo()'` Cancel buttons with the CSP-safe `cactiReturnTo` class so the pages no longer trip Cacti's Content-Security-Policy script-src-attr directive - security: Move the filter selects' inline onChange handlers into the ready block (bound via change, not click) so the page no longer trips Cacti's Content-Security-Policy script-src-attr directive - refactor: Move the functions.php and gexport_security.php library files into includes/ and switch every file inclusion from include/include_once to require/require_once for fail-fast consistency (references updated across setup.php, gexport.php, poller_export.php, and the test suite) - refactor: Move all schema management into includes/database.php (the thold model) and manage the graph_exports/graph_exports_tasks schema through Cacti's plugin table API - create with api_plugin_db_table_create() and refresh existing tables with db_update_table() from a single shared definition, replacing the raw CREATE TABLE/version-gated ALTER TABLE migrations (the one column rename is kept as a pre-step since db_update_table() can not rename) diff --git a/README.md b/README.md index bab7f34..bebb6bd 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,20 @@ Exports can be performed each impersonating a different users permissions. Graph Export contents can be transferred to remote sites using FTP, SFTP, RSYNC, and SCP. +## 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')); + }); +``` + ## Installation Install just like any other plugin, just copy it to the plugins directory, diff --git a/gexport.php b/gexport.php index 4bb50ca..393a787 100644 --- a/gexport.php +++ b/gexport.php @@ -312,7 +312,7 @@ function export_form_actions() { "; - $save_html = " + $save_html = " '; } elseif ($bulk_action === '2') { // disable print " @@ -322,7 +322,7 @@ function export_form_actions() { "; - $save_html = " + $save_html = " '; } elseif ($bulk_action === '3') { // enable print " @@ -332,7 +332,7 @@ function export_form_actions() { "; - $save_html = " + $save_html = " " . __esc('Continue', 'gexport') . ''; } elseif ($bulk_action === '4') { // export now print " @@ -342,15 +342,15 @@ function export_form_actions() { "; - $save_html = " + $save_html = " '; } else { print "" . __('You must select a valid action.', 'gexport') . ''; - $save_html = "'; + $save_html = "'; } } else { print "" . __('You must select at least one Graph Export Definition.', 'gexport') . ''; - $save_html = "'; + $save_html = "'; } print "