From 1fc24b1a2465fdf17e24e51532f716973491332d Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Fri, 6 Mar 2026 15:28:15 -0800 Subject: [PATCH 1/4] hardening: remove eval from autocomplete callback execution Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Thomas Vincent --- .github/workflows/plugin-ci-workflow.yml | 10 ++++++ CHANGELOG.md | 1 + js/functions.js | 17 ++++++++- .../issue260_remove_eval_callback_test.php | 36 +++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/regression/issue260_remove_eval_callback_test.php diff --git a/.github/workflows/plugin-ci-workflow.yml b/.github/workflows/plugin-ci-workflow.yml index 7580ee1..76612ac 100644 --- a/.github/workflows/plugin-ci-workflow.yml +++ b/.github/workflows/plugin-ci-workflow.yml @@ -187,6 +187,16 @@ jobs: echo "Syntax errors found!" exit 1 fi + + - name: Run Plugin Regression Tests + run: | + cd ${{ github.workspace }}/cacti/plugins/syslog + if [ -d tests/regression ]; then + for test in tests/regression/*.php; do + [ -f "$test" ] || continue + php "$test" + done + fi - name: Run Cacti Poller diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f1e7f2..fb93014 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ --- develop --- +* issue#260: Replace eval-based callback execution in autocomplete handling * issue: Making changes to support Cacti 1.3 * issue: Don't use MyISAM for non-analytical tables * issue: The install advisor for Syslog was broken in current Cacti releases diff --git a/js/functions.js b/js/functions.js index a96d47d..78d4ff2 100644 --- a/js/functions.js +++ b/js/functions.js @@ -573,6 +573,21 @@ function initSyslogReports() { * @param {string} callback - The AJAX callback action * @param {string} onChange - The onChange callback to execute when selection changes */ +function runSyslogAutocompleteOnChange(onChange) { + if (typeof onChange !== 'string') { + return; + } + + var callbackName = onChange.trim().replace(/\(\)\s*$/, ''); + if (!callbackName.match(/^[A-Za-z_$][A-Za-z0-9_$]*$/)) { + return; + } + + if (typeof window[callbackName] === 'function') { + window[callbackName](); + } +} + function initSyslogAutocomplete(formName, callback, onChange) { var formNameTimer; var formNameClickTimer; @@ -591,7 +606,7 @@ function initSyslogAutocomplete(formName, callback, onChange) { $('#' + formName).val(ui.item.value); } if (onChange) { - eval(onChange); + runSyslogAutocompleteOnChange(onChange); } } }).css('border', 'none').css('background-color', 'transparent'); diff --git a/tests/regression/issue260_remove_eval_callback_test.php b/tests/regression/issue260_remove_eval_callback_test.php new file mode 100644 index 0000000..a3cf13f --- /dev/null +++ b/tests/regression/issue260_remove_eval_callback_test.php @@ -0,0 +1,36 @@ + Date: Fri, 6 Mar 2026 21:58:52 -0800 Subject: [PATCH 2/4] fix: move JSDoc block to sit directly above initSyslogAutocomplete Signed-off-by: Thomas Vincent --- js/functions.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/js/functions.js b/js/functions.js index 78d4ff2..95017d0 100644 --- a/js/functions.js +++ b/js/functions.js @@ -567,12 +567,6 @@ function initSyslogReports() { * Autocomplete Form Callback Functions * ======================================================================== */ -/** - * Initialize autocomplete for form dropdown fields - * @param {string} formName - The name of the form field - * @param {string} callback - The AJAX callback action - * @param {string} onChange - The onChange callback to execute when selection changes - */ function runSyslogAutocompleteOnChange(onChange) { if (typeof onChange !== 'string') { return; @@ -588,6 +582,12 @@ function runSyslogAutocompleteOnChange(onChange) { } } +/** + * Initialize autocomplete for form dropdown fields + * @param {string} formName - The name of the form field + * @param {string} callback - The AJAX callback action + * @param {string} onChange - The onChange callback to execute when selection changes + */ function initSyslogAutocomplete(formName, callback, onChange) { var formNameTimer; var formNameClickTimer; From 4517461f64f7dbaa878654bd1e57dd48b1569d13 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Fri, 6 Mar 2026 22:17:31 -0800 Subject: [PATCH 3/4] fix: add JSDoc to runSyslogAutocompleteOnChange, use preg_match in test Add JSDoc block to the runSyslogAutocompleteOnChange helper so both autocomplete functions are documented. Replace exact-substring strpos check in the regression test with preg_match to tolerate minor whitespace variations in the signature without weakening the security assertion. Signed-off-by: Thomas Vincent --- js/functions.js | 4 ++++ tests/regression/issue260_remove_eval_callback_test.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/js/functions.js b/js/functions.js index 95017d0..f242349 100644 --- a/js/functions.js +++ b/js/functions.js @@ -567,6 +567,10 @@ function initSyslogReports() { * Autocomplete Form Callback Functions * ======================================================================== */ +/** + * Validate and invoke a named callback function specified as a string + * @param {string} onChange - Name of the global function to call (e.g. 'myCallback') + */ function runSyslogAutocompleteOnChange(onChange) { if (typeof onChange !== 'string') { return; diff --git a/tests/regression/issue260_remove_eval_callback_test.php b/tests/regression/issue260_remove_eval_callback_test.php index a3cf13f..06e1058 100644 --- a/tests/regression/issue260_remove_eval_callback_test.php +++ b/tests/regression/issue260_remove_eval_callback_test.php @@ -12,7 +12,7 @@ exit(1); } -if (strpos($javascript, 'function runSyslogAutocompleteOnChange(onChange)') === false) { +if (!preg_match('/function\s+runSyslogAutocompleteOnChange\s*\(\s*onChange\s*\)/', $javascript)) { fwrite(STDERR, "Safe autocomplete callback helper is missing.\n"); exit(1); } From fef4ca736b0e1e0072570f1f255603daa56c7174 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Sat, 7 Mar 2026 05:19:30 -0800 Subject: [PATCH 4/4] tests: use preg_match for callback assertions in issue260 regression test strpos() exact matches are brittle to whitespace/formatting changes. preg_match() with flexible patterns assert the same semantics without coupling to exact source formatting. Signed-off-by: Thomas Vincent Signed-off-by: Thomas Vincent --- .../issue260_remove_eval_callback_test.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/regression/issue260_remove_eval_callback_test.php b/tests/regression/issue260_remove_eval_callback_test.php index 06e1058..9e3b3fb 100644 --- a/tests/regression/issue260_remove_eval_callback_test.php +++ b/tests/regression/issue260_remove_eval_callback_test.php @@ -22,8 +22,17 @@ exit(1); } -if (strpos($javascript, "if (typeof window[callbackName] === 'function')") === false || - strpos($javascript, 'window[callbackName]();') === false) { +$hasCallbackTypeCheck = preg_match( + '/if\s*\(\s*typeof\s+window\s*\[\s*callbackName\s*\]\s*===\s*[\'"]{1}function[\'"]{1}\s*\)/', + $javascript +) === 1; + +$hasCallbackInvocation = preg_match( + '/window\s*\[\s*callbackName\s*\]\s*\(/', + $javascript +) === 1; + +if (!$hasCallbackTypeCheck || !$hasCallbackInvocation) { fwrite(STDERR, "Expected function-reference callback execution is missing.\n"); exit(1); }