From 495b1355767c6d7880c4c35e7d297159142de908 Mon Sep 17 00:00:00 2001 From: tmp Date: Tue, 6 Oct 2026 12:34:39 -0400 Subject: [PATCH 1/7] Theme the syslog severity legends as rounded, solid-colour chips Restyle the System Logs and Alert Logs legends to match the Thold status legends: rounded, evenly spaced, solid-colour severity chips. Move the two legend renderers into functions.php (so they are unit-testable) emitting .syslogLegend/.syslogLegendItem markup, and have syslog_include_js() load a per-theme css/.css legend palette with a css/syslog.css fallback. Ship light (classic, modern, paw) and dark (dark, midwinter, sunrise, paper-plane) palettes, plus the base fallback palette in syslog.css. --- CHANGELOG.md | 1 + css/classic.css | 32 +++++++++++ css/dark.css | 34 ++++++++++++ css/midwinter.css | 34 ++++++++++++ css/modern.css | 32 +++++++++++ css/paper-plane.css | 34 ++++++++++++ css/paw.css | 32 +++++++++++ css/sunrise.css | 34 ++++++++++++ css/syslog.css | 35 ++++++++++++ functions.php | 57 +++++++++++++++++++ syslog.php | 39 ------------- tests/Unit/SyslogLegendTest.php | 97 +++++++++++++++++++++++++++++++++ 12 files changed, 422 insertions(+), 39 deletions(-) create mode 100644 css/classic.css create mode 100644 css/dark.css create mode 100644 css/midwinter.css create mode 100644 css/modern.css create mode 100644 css/paper-plane.css create mode 100644 css/paw.css create mode 100644 css/sunrise.css create mode 100644 tests/Unit/SyslogLegendTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index be6d09ce..cea73797 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ --- develop --- +* feature: Restyle the System Logs and Alert Logs severity legends as rounded, evenly spaced, solid-colour chips that line up with the Thold status legends, with theme-appropriate backgrounds loaded from a per-theme css/.css file (falling back to css/syslog.css) * 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) * dev: Enforce patch coverage of changed lines in CI and remove the inert COMPOSER_ROOT_VERSION env from the Pest step * feature: Add Device Alert Rules for device-wide alert handling: administrators can pause a device's non-exempt alerts until a selected time or indefinitely, choose a priority threshold that always passes through device pauses and maintenance windows, and allow all device alerts during maintenance for critical devices diff --git a/css/classic.css b/css/classic.css new file mode 100644 index 00000000..7d7ff54e --- /dev/null +++ b/css/classic.css @@ -0,0 +1,32 @@ +/* + * Syslog status-legend palette for the classic theme (light surface). + * + * Rounded, evenly spaced, solid-colour severity chips that line up with the + * Thold status legends. Loaded by syslog_include_js() when this theme is + * active; themes without a matching file fall back to css/syslog.css. + */ +.syslogLegend { + display: flex; + flex-wrap: wrap; + gap: 4px; + width: 100%; +} + +.syslogLegend .syslogLegendItem { + flex: 1 1 0; + text-align: center; + white-space: nowrap; + padding: 4px 6px; + border-radius: 3px; + color: #ffffff; + font-size: 11px; +} + +.syslogLegend .logEmergency { background-color: #8e1f18; } +.syslogLegend .logCritical { background-color: #a5281f; } +.syslogLegend .logAlert { background-color: #b23a2f; } +.syslogLegend .logError { background-color: #c0483c; } +.syslogLegend .logWarning { background-color: #b5730e; } +.syslogLegend .logNotice { background-color: #2f6fa8; } +.syslogLegend .logInfo { background-color: #3f8f4a; } +.syslogLegend .logDebug { background-color: #6c7a72; } diff --git a/css/dark.css b/css/dark.css new file mode 100644 index 00000000..40f59f63 --- /dev/null +++ b/css/dark.css @@ -0,0 +1,34 @@ +/* + * Syslog status-legend palette for the dark theme (dark surface). + * + * Rounded, evenly spaced, solid-colour severity chips that line up with the + * Thold status legends. Backgrounds are muted to sit on the dark surface and + * the chip text is lightened for contrast. Loaded by syslog_include_js() when + * this theme is active; themes without a matching file fall back to + * css/syslog.css. + */ +.syslogLegend { + display: flex; + flex-wrap: wrap; + gap: 4px; + width: 100%; +} + +.syslogLegend .syslogLegendItem { + flex: 1 1 0; + text-align: center; + white-space: nowrap; + padding: 4px 6px; + border-radius: 3px; + color: #edf3ef; + font-size: 11px; +} + +.syslogLegend .logEmergency { background-color: #8a2f2f; } +.syslogLegend .logCritical { background-color: #863a3c; } +.syslogLegend .logAlert { background-color: #8f4339; } +.syslogLegend .logError { background-color: #8a5244; } +.syslogLegend .logWarning { background-color: #86682f; } +.syslogLegend .logNotice { background-color: #35607f; } +.syslogLegend .logInfo { background-color: #3a6e46; } +.syslogLegend .logDebug { background-color: #4a534e; } diff --git a/css/midwinter.css b/css/midwinter.css new file mode 100644 index 00000000..5d0db79c --- /dev/null +++ b/css/midwinter.css @@ -0,0 +1,34 @@ +/* + * Syslog status-legend palette for the midwinter theme (dark surface). + * + * Rounded, evenly spaced, solid-colour severity chips that line up with the + * Thold status legends. Backgrounds are muted to sit on the dark surface and + * the chip text is lightened for contrast. Loaded by syslog_include_js() when + * this theme is active; themes without a matching file fall back to + * css/syslog.css. + */ +.syslogLegend { + display: flex; + flex-wrap: wrap; + gap: 4px; + width: 100%; +} + +.syslogLegend .syslogLegendItem { + flex: 1 1 0; + text-align: center; + white-space: nowrap; + padding: 4px 6px; + border-radius: 3px; + color: #edf3ef; + font-size: 11px; +} + +.syslogLegend .logEmergency { background-color: #8a2f2f; } +.syslogLegend .logCritical { background-color: #863a3c; } +.syslogLegend .logAlert { background-color: #8f4339; } +.syslogLegend .logError { background-color: #8a5244; } +.syslogLegend .logWarning { background-color: #86682f; } +.syslogLegend .logNotice { background-color: #35607f; } +.syslogLegend .logInfo { background-color: #3a6e46; } +.syslogLegend .logDebug { background-color: #4a534e; } diff --git a/css/modern.css b/css/modern.css new file mode 100644 index 00000000..2bad866d --- /dev/null +++ b/css/modern.css @@ -0,0 +1,32 @@ +/* + * Syslog status-legend palette for the modern theme (light surface). + * + * Rounded, evenly spaced, solid-colour severity chips that line up with the + * Thold status legends. Loaded by syslog_include_js() when this theme is + * active; themes without a matching file fall back to css/syslog.css. + */ +.syslogLegend { + display: flex; + flex-wrap: wrap; + gap: 4px; + width: 100%; +} + +.syslogLegend .syslogLegendItem { + flex: 1 1 0; + text-align: center; + white-space: nowrap; + padding: 4px 6px; + border-radius: 3px; + color: #ffffff; + font-size: 11px; +} + +.syslogLegend .logEmergency { background-color: #8e1f18; } +.syslogLegend .logCritical { background-color: #a5281f; } +.syslogLegend .logAlert { background-color: #b23a2f; } +.syslogLegend .logError { background-color: #c0483c; } +.syslogLegend .logWarning { background-color: #b5730e; } +.syslogLegend .logNotice { background-color: #2f6fa8; } +.syslogLegend .logInfo { background-color: #3f8f4a; } +.syslogLegend .logDebug { background-color: #6c7a72; } diff --git a/css/paper-plane.css b/css/paper-plane.css new file mode 100644 index 00000000..9442ca9e --- /dev/null +++ b/css/paper-plane.css @@ -0,0 +1,34 @@ +/* + * Syslog status-legend palette for the paper-plane theme (dark surface). + * + * Rounded, evenly spaced, solid-colour severity chips that line up with the + * Thold status legends. Backgrounds are muted to sit on the dark surface and + * the chip text is lightened for contrast. Loaded by syslog_include_js() when + * this theme is active; themes without a matching file fall back to + * css/syslog.css. + */ +.syslogLegend { + display: flex; + flex-wrap: wrap; + gap: 4px; + width: 100%; +} + +.syslogLegend .syslogLegendItem { + flex: 1 1 0; + text-align: center; + white-space: nowrap; + padding: 4px 6px; + border-radius: 3px; + color: #edf3ef; + font-size: 11px; +} + +.syslogLegend .logEmergency { background-color: #8a2f2f; } +.syslogLegend .logCritical { background-color: #863a3c; } +.syslogLegend .logAlert { background-color: #8f4339; } +.syslogLegend .logError { background-color: #8a5244; } +.syslogLegend .logWarning { background-color: #86682f; } +.syslogLegend .logNotice { background-color: #35607f; } +.syslogLegend .logInfo { background-color: #3a6e46; } +.syslogLegend .logDebug { background-color: #4a534e; } diff --git a/css/paw.css b/css/paw.css new file mode 100644 index 00000000..80f10073 --- /dev/null +++ b/css/paw.css @@ -0,0 +1,32 @@ +/* + * Syslog status-legend palette for the paw theme (light surface). + * + * Rounded, evenly spaced, solid-colour severity chips that line up with the + * Thold status legends. Loaded by syslog_include_js() when this theme is + * active; themes without a matching file fall back to css/syslog.css. + */ +.syslogLegend { + display: flex; + flex-wrap: wrap; + gap: 4px; + width: 100%; +} + +.syslogLegend .syslogLegendItem { + flex: 1 1 0; + text-align: center; + white-space: nowrap; + padding: 4px 6px; + border-radius: 3px; + color: #ffffff; + font-size: 11px; +} + +.syslogLegend .logEmergency { background-color: #8e1f18; } +.syslogLegend .logCritical { background-color: #a5281f; } +.syslogLegend .logAlert { background-color: #b23a2f; } +.syslogLegend .logError { background-color: #c0483c; } +.syslogLegend .logWarning { background-color: #b5730e; } +.syslogLegend .logNotice { background-color: #2f6fa8; } +.syslogLegend .logInfo { background-color: #3f8f4a; } +.syslogLegend .logDebug { background-color: #6c7a72; } diff --git a/css/sunrise.css b/css/sunrise.css new file mode 100644 index 00000000..6f41d2d6 --- /dev/null +++ b/css/sunrise.css @@ -0,0 +1,34 @@ +/* + * Syslog status-legend palette for the sunrise theme (dark surface). + * + * Rounded, evenly spaced, solid-colour severity chips that line up with the + * Thold status legends. Backgrounds are muted to sit on the dark surface and + * the chip text is lightened for contrast. Loaded by syslog_include_js() when + * this theme is active; themes without a matching file fall back to + * css/syslog.css. + */ +.syslogLegend { + display: flex; + flex-wrap: wrap; + gap: 4px; + width: 100%; +} + +.syslogLegend .syslogLegendItem { + flex: 1 1 0; + text-align: center; + white-space: nowrap; + padding: 4px 6px; + border-radius: 3px; + color: #edf3ef; + font-size: 11px; +} + +.syslogLegend .logEmergency { background-color: #8a2f2f; } +.syslogLegend .logCritical { background-color: #863a3c; } +.syslogLegend .logAlert { background-color: #8f4339; } +.syslogLegend .logError { background-color: #8a5244; } +.syslogLegend .logWarning { background-color: #86682f; } +.syslogLegend .logNotice { background-color: #35607f; } +.syslogLegend .logInfo { background-color: #3a6e46; } +.syslogLegend .logDebug { background-color: #4a534e; } diff --git a/css/syslog.css b/css/syslog.css index 12adcbed..76935a31 100644 --- a/css/syslog.css +++ b/css/syslog.css @@ -118,3 +118,38 @@ p { /* Slightly highlight on hover but keep parent coloring visible */ background: rgba(0,0,0,0.02) !important; } + +/* + * Status legend. Laid out as an evenly sized, wrapping flex row (flex: 1 1 0 + * gives every chip the same width) with rounded corners and spacing between + * chips. A legend is a colour key, so the chips use their own solid, opaque + * colours with light text so they stay legible rather than inheriting the + * subtle row tints. This base palette is the fallback used by themes that do + * not ship a dedicated legend stylesheet; see css/.css for the + * per-theme variants. + */ +.syslogLegend { + display: flex; + flex-wrap: wrap; + gap: 4px; + width: 100%; +} + +.syslogLegend .syslogLegendItem { + flex: 1 1 0; + text-align: center; + white-space: nowrap; + padding: 4px 6px; + border-radius: 3px; + color: #ffffff; + font-size: 11px; +} + +.syslogLegend .logEmergency { background-color: #8e1f18; } +.syslogLegend .logCritical { background-color: #a5281f; } +.syslogLegend .logAlert { background-color: #b23a2f; } +.syslogLegend .logError { background-color: #c0483c; } +.syslogLegend .logWarning { background-color: #b5730e; } +.syslogLegend .logNotice { background-color: #2f6fa8; } +.syslogLegend .logInfo { background-color: #3f8f4a; } +.syslogLegend .logDebug { background-color: #6c7a72; } diff --git a/functions.php b/functions.php index 23599024..2423070c 100644 --- a/functions.php +++ b/functions.php @@ -719,15 +719,72 @@ function syslog_export_form_end(bool $export): void { */ function syslog_include_js(): void { global $config; + + // Load the legend palette for the active theme, falling back to the base + // syslog.css when the theme ships no dedicated file. The theme name is a + // user/DB setting, so it is sanitised before it reaches the filesystem. + $theme = preg_replace('/[^a-z0-9_-]/i', '', (string) get_selected_theme()); + $legend_css = ($theme !== '' && file_exists(__DIR__ . '/css/' . $theme . '.css')) ? $theme . '.css' : 'syslog.css'; ?> plugins/syslog/css/search.css?v='> plugins/syslog/css/dashboard.css?v='> + plugins/syslog/css/?v='> '; + print '
'; + print '
' . __('Emergency', 'syslog') . '
'; + print '
' . __('Critical', 'syslog') . '
'; + print '
' . __('Alert', 'syslog') . '
'; + print '
' . __('Error', 'syslog') . '
'; + print '
' . __('Warning', 'syslog') . '
'; + print '
' . __('Notice', 'syslog') . '
'; + print '
' . __('Info', 'syslog') . '
'; + print '
' . __('Debug', 'syslog') . '
'; + print '
'; + print ''; + + html_end_box(false); +} + +/** + * Display the severity legend for the Alert Logs tab. + * + * Mirrors syslog_syslog_legend() with the smaller set of severities carried by + * alert log rows. + * + * @return void + */ +function syslog_log_legend(): void { + html_start_box('', '100%', '', '3', 'center', ''); + + print ''; + print '
'; + print '
' . __('Alert', 'syslog') . '
'; + print '
' . __('Warning', 'syslog') . '
'; + print '
' . __('Informational', 'syslog') . '
'; + print '
'; + print ''; + + html_end_box(false); +} + /** * __esc() is not enough inside a - - + plugins/syslog/css/search.css?v='> + plugins/syslog/css/dashboard.css?v='> + plugins/syslog/css/?v='> + + + setup.php database.php - functions.php + includes/functions.php lib/syslog_dashboard.php - syslog.php - syslog_alerts.php - syslog_removal.php - syslog_reports.php - syslog_saved_searches.php diff --git a/setup.php b/setup.php index 47a5ea98..886ee4a1 100644 --- a/setup.php +++ b/setup.php @@ -22,6 +22,11 @@ +-------------------------------------------------------------------------+ */ +// The shared function library is required by every Syslog hook and page; load +// it once here so setup.php's hooks (including the poller bottom hook) always +// have it available without per-callsite include_once guards. +require_once __DIR__ . '/includes/functions.php'; + /** * Install the Syslog plugin, registering its hooks, realms and database tables. * @@ -228,7 +233,6 @@ function syslog_connect(): bool { include(SYSLOG_CONFIG); } - include_once(__DIR__ . '/functions.php'); include_once(__DIR__ . '/database.php'); $connect_remote = false; @@ -1581,7 +1585,6 @@ function syslog_poller_bottom(): void { global $config; if (syslog_config_safe()) { - include_once(__DIR__ . '/functions.php'); include_once(__DIR__ . '/database.php'); syslog_connect(); diff --git a/syslog.php b/syslog.php index 7ebcb77b..cd936de6 100644 --- a/syslog.php +++ b/syslog.php @@ -33,7 +33,7 @@ include('./include/auth.php'); include_once('./lib/html_tree.php'); include_once(__DIR__ . '/setup.php'); -include_once(__DIR__ . '/functions.php'); +include_once(__DIR__ . '/includes/functions.php'); include_once(__DIR__ . '/database.php'); include_once(__DIR__ . '/lib/syslog_dashboard.php'); diff --git a/syslog_alerts.php b/syslog_alerts.php index 963ee794..a4d2e848 100644 --- a/syslog_alerts.php +++ b/syslog_alerts.php @@ -26,7 +26,7 @@ include('./include/auth.php'); include_once('./lib/xml.php'); include_once(__DIR__ . '/setup.php'); -include_once(__DIR__ . '/functions.php'); +include_once(__DIR__ . '/includes/functions.php'); include_once(__DIR__ . '/database.php'); syslog_connect(); diff --git a/syslog_batch_transfer.php b/syslog_batch_transfer.php index 05810dc5..9f14a00b 100644 --- a/syslog_batch_transfer.php +++ b/syslog_batch_transfer.php @@ -26,7 +26,7 @@ include('./include/cli_check.php'); include_once('./lib/poller.php'); include_once(__DIR__ . '/setup.php'); -include_once(__DIR__ . '/functions.php'); +include_once(__DIR__ . '/includes/functions.php'); include_once(__DIR__ . '/database.php'); syslog_connect(); diff --git a/syslog_dashboards.php b/syslog_dashboards.php index 0793a07f..0ecbcc42 100644 --- a/syslog_dashboards.php +++ b/syslog_dashboards.php @@ -1,7 +1,7 @@ 'Host','message'=>'Message','program'=>'Program','logtime'=>'Date','priority'=>'Priority'])); $saved_tree_json = html_escape(json_encode(['AND',['predicate','host','=','10.0.0.5'],['predicate','logtime','last','86400']])); diff --git a/tests/Fixtures/saved_template_editor.php b/tests/Fixtures/saved_template_editor.php index 4c365db7..8032cde0 100644 --- a/tests/Fixtures/saved_template_editor.php +++ b/tests/Fixtures/saved_template_editor.php @@ -26,7 +26,7 @@ function purify($value) { return htmlspecialchars($value, ENT_QUOTES); } require $cacti . '/lib/html.php'; require $cacti . '/lib/html_form.php'; require $cacti . '/lib/html_utility.php'; -require dirname(__DIR__, 2) . '/functions.php'; +require dirname(__DIR__, 2) . '/includes/functions.php'; $source = file_get_contents(dirname(__DIR__, 2) . '/syslog_saved_searches.php'); eval(substr($source, strpos($source, 'function syslog_template_list('))); $row = ['id' => 7, 'name' => 'Router errors', 'user' => 'admin', 'search' => 'host = "router-1" AND (message contains "error" OR priority = "warning") AND logtime last "86400"']; diff --git a/tests/Security/AlertSqlPlaceholderTest.php b/tests/Security/AlertSqlPlaceholderTest.php index 7438a87f..03e355c8 100644 --- a/tests/Security/AlertSqlPlaceholderTest.php +++ b/tests/Security/AlertSqlPlaceholderTest.php @@ -21,7 +21,7 @@ 'textField' => 'message', ]; - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $hostAlert = ['type' => 'host', 'message' => 'router1']; $programAlert = ['type' => 'program', 'message' => 'sshd']; diff --git a/tests/Security/CommandExecutionRefactorTest.php b/tests/Security/CommandExecutionRefactorTest.php index d13df304..02cc368a 100644 --- a/tests/Security/CommandExecutionRefactorTest.php +++ b/tests/Security/CommandExecutionRefactorTest.php @@ -14,7 +14,7 @@ */ it('keeps alert and ticket command execution shell-safe', function () { - $functions = plugin_test_read_source('functions.php'); + $functions = plugin_test_read_source('includes/functions.php'); if (strpos($functions, 'function syslog_execute_ticket_command(') === false) { throw new RuntimeException('Ticket command execution helper is missing.'); diff --git a/tests/Security/CsrfPurgeTest.php b/tests/Security/CsrfPurgeTest.php index 6a667a81..306187e6 100644 --- a/tests/Security/CsrfPurgeTest.php +++ b/tests/Security/CsrfPurgeTest.php @@ -33,6 +33,14 @@ } register_shutdown_function(function () use ($sandbox) { + foreach (glob($sandbox . '/includes/*') as $file) { + unlink($file); + } + + if (is_dir($sandbox . '/includes')) { + rmdir($sandbox . '/includes'); + } + foreach (glob($sandbox . '/*') as $file) { unlink($file); } @@ -40,9 +48,20 @@ rmdir($sandbox); }); - foreach (['setup.php', 'functions.php'] as $file) { - if (!copy($root . '/' . $file, $sandbox . '/' . $file)) { - throw new RuntimeException("Unable to stage $file"); + if (!mkdir($sandbox . '/includes', 0700)) { + throw new RuntimeException('Unable to create the sandbox includes directory'); + } + + // functions.php lives under includes/; mirror that layout so the copied + // setup.php resolves its require_once __DIR__ . '/includes/functions.php'. + $staged = [ + $root . '/setup.php' => $sandbox . '/setup.php', + $root . '/includes/functions.php' => $sandbox . '/includes/functions.php', + ]; + + foreach ($staged as $src => $dst) { + if (!copy($src, $dst)) { + throw new RuntimeException("Unable to stage $src"); } } @@ -102,7 +121,7 @@ function csrf_check($fatal = true) { } } - require_once __DIR__ . '/functions.php'; + require_once __DIR__ . '/includes/functions.php'; require_once __DIR__ . '/setup.php'; switch ($scenario) { @@ -156,7 +175,7 @@ function csrf_check($fatal = true) { }; // The encoder, on its own. - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $encoded = syslog_json_safe($payload); diff --git a/tests/Security/CsvImportHardeningTest.php b/tests/Security/CsvImportHardeningTest.php index 81164c94..01c35c88 100644 --- a/tests/Security/CsvImportHardeningTest.php +++ b/tests/Security/CsvImportHardeningTest.php @@ -13,7 +13,7 @@ */ it('defuses CSV formula injection and enforces the import size limit', function () { - $functions = plugin_test_read_source('functions.php'); + $functions = plugin_test_read_source('includes/functions.php'); foreach ([ 'SYSLOG_IMPORT_MAX_BYTES', @@ -154,7 +154,7 @@ function raise_message(string $id, string $text = '', int $level = 0): void { syslog_get_import_xml_payload('/blocked'); print 'UNREACHABLE'; PHP, - var_export($root . '/functions.php', true) + var_export($root . '/includes/functions.php', true) ); $pipes = []; diff --git a/tests/Security/CsvSafeUnitTest.php b/tests/Security/CsvSafeUnitTest.php index 86483f02..10918203 100644 --- a/tests/Security/CsvSafeUnitTest.php +++ b/tests/Security/CsvSafeUnitTest.php @@ -15,7 +15,7 @@ */ it('prefixes only values a spreadsheet would treat as a formula', function () { - $functions = plugin_test_read_source('functions.php'); + $functions = plugin_test_read_source('includes/functions.php'); if (!preg_match('/function\s+syslog_csv_safe\s*\([^)]*\)\s*(?::\s*mixed\s*)?\{.*?\n\}/s', $functions, $m)) { throw new RuntimeException('Could not extract syslog_csv_safe from functions.php'); diff --git a/tests/Security/DashboardPermissionsTest.php b/tests/Security/DashboardPermissionsTest.php index 14a7866d..ccdb16f5 100644 --- a/tests/Security/DashboardPermissionsTest.php +++ b/tests/Security/DashboardPermissionsTest.php @@ -116,7 +116,7 @@ }); it('treats another user\'s shared dashboard as view-only', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); syslog_load_plugin_source('lib/syslog_dashboard.php'); $GLOBALS['syslogdb_default'] = 'syslog'; @@ -182,7 +182,7 @@ }); it('lets an administrator edit a shared dashboard', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); syslog_load_plugin_source('lib/syslog_dashboard.php'); $GLOBALS['syslogdb_default'] = 'syslog'; @@ -227,7 +227,7 @@ }); it('rejects panel definitions that fail validation or the search DSL', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); syslog_load_plugin_source('lib/syslog_dashboard.php'); $GLOBALS['syslogdb_default'] = 'syslog'; diff --git a/tests/Security/DashboardSharePermissionsTest.php b/tests/Security/DashboardSharePermissionsTest.php index 69c71cf1..d8f41c5a 100644 --- a/tests/Security/DashboardSharePermissionsTest.php +++ b/tests/Security/DashboardSharePermissionsTest.php @@ -18,7 +18,7 @@ // api_plugin_user_realm_auth(), which is overridden below; loading them // here keeps this test correct regardless of what other test files // already loaded. - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); syslog_load_plugin_source('lib/syslog_dashboard.php'); test_override('get_username', function ($id) { diff --git a/tests/Security/DashboardSqlTest.php b/tests/Security/DashboardSqlTest.php index 856364c7..1092971d 100644 --- a/tests/Security/DashboardSqlTest.php +++ b/tests/Security/DashboardSqlTest.php @@ -16,7 +16,7 @@ */ it('builds timeseries SQL with fixed aliases and integer literals', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); syslog_load_plugin_source('lib/syslog_dashboard.php'); $GLOBALS['syslogdb_default'] = 'syslogdb'; @@ -53,7 +53,7 @@ }); it('resolves breakdown dimensions through lookup subqueries', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); syslog_load_plugin_source('lib/syslog_dashboard.php'); $GLOBALS['syslogdb_default'] = 'syslogdb'; @@ -78,7 +78,7 @@ }); it('resolves dashboard loads by id and classifies ownership client-side', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); syslog_load_plugin_source('lib/syslog_dashboard.php'); $GLOBALS['syslogdb_default'] = 'syslogdb'; @@ -118,7 +118,7 @@ }); it('repositions panels within one dashboard only', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); syslog_load_plugin_source('lib/syslog_dashboard.php'); $GLOBALS['syslogdb_default'] = 'syslogdb'; @@ -157,7 +157,7 @@ }); it('repositions panels by absolute drag position', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); syslog_load_plugin_source('lib/syslog_dashboard.php'); $GLOBALS['syslogdb_default'] = 'syslogdb'; diff --git a/tests/Security/ImportPayloadLoaderTest.php b/tests/Security/ImportPayloadLoaderTest.php index d3c62b17..9d7a4564 100644 --- a/tests/Security/ImportPayloadLoaderTest.php +++ b/tests/Security/ImportPayloadLoaderTest.php @@ -14,7 +14,7 @@ */ it('validates uploads before reading and rejects zero-byte imports', function () { - $functions = plugin_test_read_source('functions.php'); + $functions = plugin_test_read_source('includes/functions.php'); if (substr_count($functions, 'function syslog_get_import_xml_payload(') !== 1 || preg_match('/^function syslog_get_import_xml_payload\([^)]*\)\s*\{.*?^\}/ms', $functions, $matches) !== 1) { @@ -52,7 +52,7 @@ throw new RuntimeException('Shared import payload loader must validate an upload before opening it.'); } - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $emptyFixture = tempnam(sys_get_temp_dir(), 'syslog-empty-import-'); $payloadFixture = tempnam(sys_get_temp_dir(), 'syslog-import-'); diff --git a/tests/Security/IncludePathNormalizationTest.php b/tests/Security/IncludePathNormalizationTest.php index fddb402e..7550ff6d 100644 --- a/tests/Security/IncludePathNormalizationTest.php +++ b/tests/Security/IncludePathNormalizationTest.php @@ -29,7 +29,7 @@ $plugin_includes = [ 'setup.php', - 'functions.php', + 'includes/functions.php', 'database.php', ]; @@ -42,7 +42,7 @@ // setup.php is not part of the standard per-entrypoint include chain; it is // pulled in on demand via $config['base_path'] where a runtime setup step is // actually needed, so only functions.php and database.php are required here. - $required_includes = ['functions.php', 'database.php']; + $required_includes = ['includes/functions.php', 'database.php']; foreach ($entrypoints as $file) { $path = $root . '/' . $file; @@ -95,7 +95,7 @@ } } - $functions = file_get_contents($root . '/functions.php'); + $functions = file_get_contents($root . '/includes/functions.php'); $database = file_get_contents($root . '/database.php'); if ($functions === false || $database === false) { @@ -110,8 +110,8 @@ throw new RuntimeException('functions.php missing syslog_apply_selected_items_action'); } - if (!preg_match('/include_once\s*\(\s*__DIR__\s*\.\s*[\'"]\/functions\.php[\'"]\s*\)/', $setup)) { - throw new RuntimeException('setup.php must use __DIR__ for functions.php include'); + if (!preg_match('/require_once\s+__DIR__\s*\.\s*[\'"]\/includes\/functions\.php[\'"]/', $setup)) { + throw new RuntimeException('setup.php must require includes/functions.php via __DIR__'); } if (!preg_match('/include_once\s*\(\s*__DIR__\s*\.\s*[\'"]\/database\.php[\'"]\s*\)/', $setup)) { diff --git a/tests/Security/ItemShareVisibilityTest.php b/tests/Security/ItemShareVisibilityTest.php index 34018e76..c2faaaee 100644 --- a/tests/Security/ItemShareVisibilityTest.php +++ b/tests/Security/ItemShareVisibilityTest.php @@ -15,7 +15,7 @@ */ it('resolves shared ids from user and group grants', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); test_override('db_fetch_assoc_prepared', function ($sql, $params) { return [['group_id' => '3'], ['group_id' => '5']]; @@ -53,7 +53,7 @@ }); it('keeps unknown kinds, anonymous sessions, and ungranted ids out', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $queried = false; @@ -91,7 +91,7 @@ }); it('replaces share rows and validates posted selections', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $statements = []; @@ -139,7 +139,7 @@ expect($shares)->toBe(['users' => [['id' => 4]], 'groups' => [['id' => 7]]], 'Grants read back shaped for the multiselects'); }); it('grants everyone through the all sentinel', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $captured = []; diff --git a/tests/Security/LogicalMessageSearchTest.php b/tests/Security/LogicalMessageSearchTest.php index a58c13d7..209d3d57 100644 --- a/tests/Security/LogicalMessageSearchTest.php +++ b/tests/Security/LogicalMessageSearchTest.php @@ -16,7 +16,7 @@ */ it('parses the logical search grammar and rejects malformed or oversized input', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); test_override('db_qstr', function ($value) { return "'" . str_replace(['\\', "'"], ['\\\\', "''"], $value) . "'"; @@ -116,7 +116,7 @@ }); it('applies the logical search predicate consistently in the real query builder', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); test_override('db_qstr', function ($value) { return "'" . str_replace(['\\', "'"], ['\\\\', "''"], $value) . "'"; @@ -232,7 +232,7 @@ echo 'CODE:' . http_response_code() . "\n"; echo 'BODY:' . $error; PHP, - var_export($root . '/functions.php', true) + var_export($root . '/includes/functions.php', true) ); $process = proc_open([PHP_BINARY, '-r', $code], [ diff --git a/tests/Security/MessageDetailsEscapeTest.php b/tests/Security/MessageDetailsEscapeTest.php index f787ede8..bf79723b 100644 --- a/tests/Security/MessageDetailsEscapeTest.php +++ b/tests/Security/MessageDetailsEscapeTest.php @@ -15,7 +15,7 @@ */ it('escapes hostile log and device text in the message-details button', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $message = ' & "quoted"'; $html = syslog_message_button($message, 'router" onmouseover="bad', 'kernel', 'kern', 'warning', '2026-09-13 00:00:00'); diff --git a/tests/Security/PartitionTableLockingTest.php b/tests/Security/PartitionTableLockingTest.php index afbbeebb..86fd6cfa 100644 --- a/tests/Security/PartitionTableLockingTest.php +++ b/tests/Security/PartitionTableLockingTest.php @@ -18,7 +18,7 @@ */ it('keeps syslog partition table locking and DDL identifiers safe', function () { - $functions = plugin_test_read_source('functions.php'); + $functions = plugin_test_read_source('includes/functions.php'); // All five information_schema queries must be prepared statements // scoped to the requested table via a placeholder. Match only calls diff --git a/tests/Security/RulePreviewTest.php b/tests/Security/RulePreviewTest.php index 43e0b5d9..3b42de4f 100644 --- a/tests/Security/RulePreviewTest.php +++ b/tests/Security/RulePreviewTest.php @@ -41,7 +41,7 @@ function rule_preview_capture_db(array &$calls, array $overrides = []): void { } it('previews a filter rule through the QueryBuilder with bound parameters', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslogdb'; $GLOBALS['syslog_incoming_config'] = [ @@ -87,7 +87,7 @@ function rule_preview_capture_db(array &$calls, array $overrides = []): void { }); it('previews structured filters without the poller-only processing boundary', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslogdb'; $GLOBALS['syslog_incoming_config'] = [ @@ -120,7 +120,7 @@ function rule_preview_capture_db(array &$calls, array $overrides = []): void { }); it('rejects unknown rule types without running any query', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $calls = []; rule_preview_capture_db($calls); @@ -132,7 +132,7 @@ function rule_preview_capture_db(array &$calls, array $overrides = []): void { }); it('clamps the preview row count to the hard maximum', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslogdb'; $GLOBALS['syslog_incoming_config'] = ['timeField' => 'logtime', 'textField' => 'message']; @@ -154,7 +154,7 @@ function rule_preview_capture_db(array &$calls, array $overrides = []): void { }); it('compiles legacy match types with bound placeholders', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslogdb'; $GLOBALS['syslog_incoming_config'] = [ @@ -184,7 +184,7 @@ function rule_preview_capture_db(array &$calls, array $overrides = []): void { }); it('rejects unsafe configured column mappings', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslogdb'; $GLOBALS['syslog_incoming_config'] = [ @@ -202,7 +202,7 @@ function rule_preview_capture_db(array &$calls, array $overrides = []): void { }); it('blocks the test action for users without the editor realm', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['request'] = []; $GLOBALS['__test_db_calls'] = []; @@ -224,7 +224,7 @@ function rule_preview_capture_db(array &$calls, array $overrides = []): void { }); it('blocks the test action when CSRF validation fails', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['request'] = []; $GLOBALS['__test_db_calls'] = []; @@ -246,7 +246,7 @@ function rule_preview_capture_db(array &$calls, array $overrides = []): void { }); it('blocks the test action for non-POST requests', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['request'] = []; $GLOBALS['__test_db_calls'] = []; diff --git a/tests/Security/SavedSearchDeletePermissionsTest.php b/tests/Security/SavedSearchDeletePermissionsTest.php index 576f7c69..3fecb0e9 100644 --- a/tests/Security/SavedSearchDeletePermissionsTest.php +++ b/tests/Security/SavedSearchDeletePermissionsTest.php @@ -18,7 +18,7 @@ // api_plugin_user_realm_auth(), which is overridden below; loading it // here (rather than faking syslog_saved_search_admin directly) keeps // this test correct regardless of what other test files already loaded. - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $source = plugin_test_read_source('syslog.php'); $start = strpos($source, 'function saved_search_delete()'); diff --git a/tests/Security/TraditionalTableDeprecationTest.php b/tests/Security/TraditionalTableDeprecationTest.php index abe47453..4739ee20 100644 --- a/tests/Security/TraditionalTableDeprecationTest.php +++ b/tests/Security/TraditionalTableDeprecationTest.php @@ -25,7 +25,7 @@ */ it('restricts the storage engine choice to InnoDB and Aria', function () { - $functions = plugin_test_read_source('functions.php'); + $functions = plugin_test_read_source('includes/functions.php'); if (!preg_match('/function\s+syslog_validate_storage_engine\s*\([^)]*\)\s*\{.*?\n\}/s', $functions, $m)) { throw new RuntimeException('Could not extract syslog_validate_storage_engine from functions.php'); @@ -94,7 +94,7 @@ it('warns about traditional tables but keeps them working', function () { $root = dirname(__DIR__, 2); - $functions = file_get_contents($root . '/functions.php'); + $functions = file_get_contents($root . '/includes/functions.php'); $setup = file_get_contents($root . '/setup.php'); $process = file_get_contents($root . '/syslog_process.php'); @@ -177,7 +177,7 @@ return null; }); - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); // Table exists and is not partitioned: warn, and raise when asked. expect(syslog_notice_traditional_tables(true))->toBeTrue('A traditional table must be reported'); @@ -232,7 +232,7 @@ return null; }); - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); expect(syslog_notice_traditional_tables(true))->toBeFalse('A partitioned table must not raise the notice'); expect($calls['logs'])->toHaveCount(0, 'No log line is expected for a partitioned table'); diff --git a/tests/Unit/AlertSuppressionScheduleTest.php b/tests/Unit/AlertSuppressionScheduleTest.php index 7085c3ba..5d1c5480 100644 --- a/tests/Unit/AlertSuppressionScheduleTest.php +++ b/tests/Unit/AlertSuppressionScheduleTest.php @@ -6,7 +6,7 @@ */ it('recognizes same-day and overnight maintenance windows', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $mondayLate = strtotime('2024-01-01 23:00:00'); $tuesdayEarly = strtotime('2024-01-02 01:00:00'); @@ -18,7 +18,7 @@ }); it('accepts wildcard and weekday ranges in maintenance windows', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $wednesday = strtotime('2024-01-03 12:30:00'); @@ -28,7 +28,7 @@ }); it('builds maintenance schedules from day and time controls', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $wednesdayEarly = strtotime('2024-01-03 02:00:00'); $schedule = syslog_alert_maintenance_window('1,2,3,4,5', '00:00', '06:00'); @@ -39,7 +39,7 @@ }); it('recognizes one-time date and time maintenance windows', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); expect(syslog_alert_datetime_window_is_active('2024-02-14 09:00', '2024-02-14 12:00', strtotime('2024-02-14 10:00')))->toBeTrue(); expect(syslog_alert_datetime_window_is_active('2024-02-14 09:00', '2024-02-14 12:00', strtotime('2024-02-14 12:30')))->toBeFalse(); diff --git a/tests/Unit/BulkActionDispatchHelperTest.php b/tests/Unit/BulkActionDispatchHelperTest.php index c60d088b..7b6138c0 100644 --- a/tests/Unit/BulkActionDispatchHelperTest.php +++ b/tests/Unit/BulkActionDispatchHelperTest.php @@ -15,7 +15,7 @@ it('routes bulk selected-item actions through the shared dispatch helper', function () { $root = dirname(__DIR__, 2); - $functions = file_get_contents($root . '/functions.php'); + $functions = file_get_contents($root . '/includes/functions.php'); if ($functions === false) { throw new RuntimeException('Failed to load functions.php'); diff --git a/tests/Unit/CollectorHealthTest.php b/tests/Unit/CollectorHealthTest.php index 5ea5611d..344f3b0c 100644 --- a/tests/Unit/CollectorHealthTest.php +++ b/tests/Unit/CollectorHealthTest.php @@ -116,7 +116,7 @@ function collector_health_capture_db(array &$calls, array $overrides = []): void } it('formats healthy collector metrics from the configured incoming table', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); collector_health_load_functions(); collector_health_setup_config(); @@ -159,7 +159,7 @@ function collector_health_capture_db(array &$calls, array $overrides = []): void }); it('marks collector metrics unavailable when the database cannot answer', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); collector_health_load_functions(); collector_health_setup_config(); @@ -181,7 +181,7 @@ function collector_health_capture_db(array &$calls, array $overrides = []): void }); it('warns when the incoming backlog exceeds the configured threshold', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); collector_health_load_functions(); collector_health_setup_config(); @@ -201,7 +201,7 @@ function collector_health_capture_db(array &$calls, array $overrides = []): void }); it('uses the default thresholds when the settings are unset', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); collector_health_load_functions(); collector_health_setup_config(); @@ -222,7 +222,7 @@ function collector_health_capture_db(array &$calls, array $overrides = []): void }); it('does not warn when metrics are within thresholds', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); collector_health_load_functions(); collector_health_setup_config(); @@ -242,7 +242,7 @@ function collector_health_capture_db(array &$calls, array $overrides = []): void }); it('rejects unsafe incoming table column mappings as unavailable', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); collector_health_load_functions(); $GLOBALS['syslogdb_default'] = 'syslogdb'; @@ -260,7 +260,7 @@ function collector_health_capture_db(array &$calls, array $overrides = []): void }); it('formats compact human readable ages', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); collector_health_load_functions(); expect(syslog_status_format_age(45))->toContain('second'); diff --git a/tests/Unit/DashboardPanelSettingsTest.php b/tests/Unit/DashboardPanelSettingsTest.php index 2cc2ac96..678b6d5b 100644 --- a/tests/Unit/DashboardPanelSettingsTest.php +++ b/tests/Unit/DashboardPanelSettingsTest.php @@ -93,7 +93,7 @@ }); it('folds breakdown rows beyond the top-n into an Other slice', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); syslog_load_plugin_source('lib/syslog_dashboard.php'); $GLOBALS['syslogdb_default'] = 'syslog'; diff --git a/tests/Unit/DefaultDateReappliedTest.php b/tests/Unit/DefaultDateReappliedTest.php index 503585cf..207b81a4 100644 --- a/tests/Unit/DefaultDateReappliedTest.php +++ b/tests/Unit/DefaultDateReappliedTest.php @@ -14,7 +14,7 @@ */ it('reapplies the default last-day limit whenever a search has no date condition', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $source = plugin_test_read_source('syslog.php'); $start = strpos($source, "set_shift_span(\$shift_span, 'sess_sl_' . \$current_tab);"); diff --git a/tests/Unit/DeviceRulePolicyTest.php b/tests/Unit/DeviceRulePolicyTest.php index 262b051a..3163a5d7 100644 --- a/tests/Unit/DeviceRulePolicyTest.php +++ b/tests/Unit/DeviceRulePolicyTest.php @@ -23,7 +23,7 @@ */ it('keeps global maintenance closed unless a device rule explicitly permits a priority', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $GLOBALS['syslog_incoming_config'] = ['hostField' => 'host', 'priorityField' => 'priority_id']; diff --git a/tests/Unit/ImportTextTrimCheckTest.php b/tests/Unit/ImportTextTrimCheckTest.php index e167c781..aae983c4 100644 --- a/tests/Unit/ImportTextTrimCheckTest.php +++ b/tests/Unit/ImportTextTrimCheckTest.php @@ -16,7 +16,7 @@ it('preserves import text trim semantics in the shared payload helper', function () { $root = dirname(__DIR__, 2); - $helper = file_get_contents($root . '/functions.php'); + $helper = file_get_contents($root . '/includes/functions.php'); $targets = [ $root . '/syslog_alerts.php', $root . '/syslog_reports.php', diff --git a/tests/Unit/MessageRuleLinksTest.php b/tests/Unit/MessageRuleLinksTest.php index 656729ee..8909005a 100644 --- a/tests/Unit/MessageRuleLinksTest.php +++ b/tests/Unit/MessageRuleLinksTest.php @@ -13,7 +13,7 @@ */ it('builds rule-editor links only for permitted, valid main-table records', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $allowed = []; diff --git a/tests/Unit/PartitionAheadTest.php b/tests/Unit/PartitionAheadTest.php index f6132011..35968920 100644 --- a/tests/Unit/PartitionAheadTest.php +++ b/tests/Unit/PartitionAheadTest.php @@ -8,7 +8,7 @@ */ it('defaults partition pre-create window to three days and clamps invalid values', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); test_override('read_config_option', function ($name) { return $name === 'syslog_partition_ahead_days' ? '' : ''; @@ -32,7 +32,7 @@ }); it('ensures partitions sequentially through the configured future horizon', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $created = []; $last_partition = '20260914'; @@ -95,7 +95,7 @@ }); it('keeps retention plus future partitions when pruning', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $dropped = []; diff --git a/tests/Unit/PartitionFailSafeTest.php b/tests/Unit/PartitionFailSafeTest.php index 9f34eef4..8522fa55 100644 --- a/tests/Unit/PartitionFailSafeTest.php +++ b/tests/Unit/PartitionFailSafeTest.php @@ -53,7 +53,7 @@ function partition_failsafe_healthy_state(): void { } it('stops all partition maintenance when partition metadata is invalid', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $values = partition_failsafe_install_status_capture(); @@ -86,7 +86,7 @@ function partition_failsafe_healthy_state(): void { }); it('defers retention pruning and reports the gap when partition creation fails', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $values = partition_failsafe_install_status_capture(); @@ -149,7 +149,7 @@ function partition_failsafe_healthy_state(): void { }); it('creates only the bounded number of missing partitions per run', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $values = partition_failsafe_install_status_capture(); @@ -211,7 +211,7 @@ function partition_failsafe_healthy_state(): void { }); it('runs retention pruning and clears the block when recovery completes', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $values = partition_failsafe_install_status_capture(); @@ -270,7 +270,7 @@ function partition_failsafe_healthy_state(): void { }); it('never removes the dMaxValue safety partition', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $dropped = []; diff --git a/tests/Unit/PartitionPrecreateBoundaryTest.php b/tests/Unit/PartitionPrecreateBoundaryTest.php index e92ab874..05944126 100644 --- a/tests/Unit/PartitionPrecreateBoundaryTest.php +++ b/tests/Unit/PartitionPrecreateBoundaryTest.php @@ -20,7 +20,7 @@ * generated DDL so the partition list can be asserted without a database. */ function partition_precreate_extract_function() { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $setup = plugin_test_read_source('setup.php'); diff --git a/tests/Unit/PartitionStateReportTest.php b/tests/Unit/PartitionStateReportTest.php index 2010d24b..caf6d959 100644 --- a/tests/Unit/PartitionStateReportTest.php +++ b/tests/Unit/PartitionStateReportTest.php @@ -8,7 +8,7 @@ */ it('reports suspicious partition metadata without changing partition state', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $GLOBALS['partition_logs'] = []; diff --git a/tests/Unit/PhaseTelemetryTest.php b/tests/Unit/PhaseTelemetryTest.php index 848374cd..b9824897 100644 --- a/tests/Unit/PhaseTelemetryTest.php +++ b/tests/Unit/PhaseTelemetryTest.php @@ -27,7 +27,7 @@ function phase_telemetry_status_store(array &$values): void { } it('records phase telemetry through the syslog status mechanism', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $values = []; @@ -48,7 +48,7 @@ function phase_telemetry_status_store(array &$values): void { }); it('rejects unknown phase names and inverted time ranges', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $values = []; phase_telemetry_status_store($values); @@ -59,7 +59,7 @@ function phase_telemetry_status_store(array &$values): void { }); it('rounds the phase duration to three decimals', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $values = []; phase_telemetry_status_store($values); @@ -72,7 +72,7 @@ function phase_telemetry_status_store(array &$values): void { }); it('exposes the full phase list including all six processing phases', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); expect(syslog_status_phase_names())->toBe([ 'partition', @@ -85,7 +85,7 @@ function phase_telemetry_status_store(array &$values): void { }); it('reads back recorded telemetry and leaves unrecorded phases empty', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; @@ -117,7 +117,7 @@ function phase_telemetry_status_store(array &$values): void { }); it('ignores malformed stored phase documents', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; @@ -141,7 +141,7 @@ function phase_telemetry_status_store(array &$values): void { }); it('renders phase timings with the slowest phase highlighted', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; diff --git a/tests/Unit/RemovalRuleTransactionTest.php b/tests/Unit/RemovalRuleTransactionTest.php index fe18cdf1..3ea1690d 100644 --- a/tests/Unit/RemovalRuleTransactionTest.php +++ b/tests/Unit/RemovalRuleTransactionTest.php @@ -13,7 +13,7 @@ */ it('wraps syslog_remove_items archive and delete operations in a transaction', function () { - $functions = plugin_test_read_source('functions.php'); + $functions = plugin_test_read_source('includes/functions.php'); if (!preg_match('/function\s+syslog_remove_items\s*\(\s*\$table\s*,\s*\$max_seq\s*\)\s*\{(.+?)\nfunction\s+syslog_log_row_color/s', $functions, $match)) { throw new RuntimeException('syslog_remove_items function not found.'); @@ -29,7 +29,7 @@ }); it('wraps syslog_manage_items move and delete operations in a transaction', function () { - $functions = plugin_test_read_source('functions.php'); + $functions = plugin_test_read_source('includes/functions.php'); if (!preg_match('/function\s+syslog_manage_items\s*\(\s*\$from_table\s*,\s*\$to_table\s*\)\s*\{(.+?)\nfunction\s+get_hash_syslog/s', $functions, $match)) { throw new RuntimeException('syslog_manage_items function not found.'); diff --git a/tests/Unit/RetentionCutoffUtcTest.php b/tests/Unit/RetentionCutoffUtcTest.php index 9c63be78..a5e4469e 100644 --- a/tests/Unit/RetentionCutoffUtcTest.php +++ b/tests/Unit/RetentionCutoffUtcTest.php @@ -32,7 +32,7 @@ function syslog_extract_function(string $file, string $function): string { it('computes the traditional retention cutoff in UTC', function () { $root = dirname(__DIR__, 2); - $function = syslog_extract_function($root . '/functions.php', 'syslog_traditional_manage'); + $function = syslog_extract_function($root . '/includes/functions.php', 'syslog_traditional_manage'); expect(str_contains($function, "gmdate('Y-m-d'"))->toBeTrue(); @@ -45,7 +45,7 @@ function syslog_extract_function(string $file, string $function): string { it('computes the reference table retention cutoff in UTC', function () { $root = dirname(__DIR__, 2); - $function = syslog_extract_function($root . '/functions.php', 'syslog_postprocess_tables'); + $function = syslog_extract_function($root . '/includes/functions.php', 'syslog_postprocess_tables'); expect(str_contains($function, "gmdate('Y-m-d H:i:s'"))->toBeTrue(); @@ -54,7 +54,7 @@ function syslog_extract_function(string $file, string $function): string { it('keeps the daily optimize window in local time on purpose', function () { $root = dirname(__DIR__, 2); - $function = syslog_extract_function($root . '/functions.php', 'syslog_postprocess_tables'); + $function = syslog_extract_function($root . '/includes/functions.php', 'syslog_postprocess_tables'); // The optimize gate is a scheduling concern tied to the server's local // calendar; converting it to UTC would move the window. It must remain diff --git a/tests/Unit/RulePreviewShapeTest.php b/tests/Unit/RulePreviewShapeTest.php index 79db7469..a3a4babb 100644 --- a/tests/Unit/RulePreviewShapeTest.php +++ b/tests/Unit/RulePreviewShapeTest.php @@ -47,7 +47,7 @@ function preview_setup(): void { } it('projects preview rows onto the configured incoming fields', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); preview_setup(); $calls = []; @@ -80,7 +80,7 @@ function preview_setup(): void { }); it('returns empty rows for a rule that matches nothing', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); preview_setup(); $calls = []; @@ -94,7 +94,7 @@ function preview_setup(): void { }); it('reports an error when the filter document is invalid', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); preview_setup(); $calls = []; @@ -109,7 +109,7 @@ function preview_setup(): void { }); it('reports an error when the count query fails', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); preview_setup(); $calls = []; @@ -122,7 +122,7 @@ function preview_setup(): void { }); it('previews removal rules against the incoming table', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); preview_setup(); $calls = []; @@ -152,7 +152,7 @@ function preview_setup(): void { }); it('previews a structured removal filter through the shared compiler', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); preview_setup(); $calls = []; @@ -181,7 +181,7 @@ function preview_setup(): void { }); it('resolves legacy facility rules through the reference table', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); preview_setup(); $calls = []; @@ -204,7 +204,7 @@ function preview_setup(): void { }); it('previews the sql type under the trusted-admin model', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); preview_setup(); $calls = []; diff --git a/tests/Unit/SavedSearchStripDatesTest.php b/tests/Unit/SavedSearchStripDatesTest.php index b1074207..35c56b74 100644 --- a/tests/Unit/SavedSearchStripDatesTest.php +++ b/tests/Unit/SavedSearchStripDatesTest.php @@ -15,7 +15,7 @@ */ it('strips only the auto-appended date clause from a saved search expression', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); test_override('syslog_db_fetch_assoc', function ($sql) { return []; diff --git a/tests/Unit/SearchValuesTest.php b/tests/Unit/SearchValuesTest.php index 6c7a3de4..38589676 100644 --- a/tests/Unit/SearchValuesTest.php +++ b/tests/Unit/SearchValuesTest.php @@ -15,7 +15,7 @@ */ it('builds search choices and suggestions safely, rejecting unknown fields', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); test_override('syslog_db_fetch_assoc', function ($sql) { return [['id' => 4, 'name' => 'warning']]; diff --git a/tests/Unit/StatisticsDeprecationTest.php b/tests/Unit/StatisticsDeprecationTest.php index fd448dd7..aa4b6eca 100644 --- a/tests/Unit/StatisticsDeprecationTest.php +++ b/tests/Unit/StatisticsDeprecationTest.php @@ -18,7 +18,7 @@ $viewer = file_get_contents($root . '/syslog.php'); $setup = file_get_contents($root . '/setup.php'); - $functions = file_get_contents($root . '/functions.php'); + $functions = file_get_contents($root . '/includes/functions.php'); $process = file_get_contents($root . '/syslog_process.php'); $javascript = file_get_contents($root . '/js/functions.js'); diff --git a/tests/Unit/StatusTelemetryTest.php b/tests/Unit/StatusTelemetryTest.php index a8c7eed8..d98f3db9 100644 --- a/tests/Unit/StatusTelemetryTest.php +++ b/tests/Unit/StatusTelemetryTest.php @@ -8,7 +8,7 @@ */ it('stores syslog status values in the telemetry table', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $executed = []; @@ -30,7 +30,7 @@ }); it('rejects invalid syslog status field names', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $executed = false; @@ -45,7 +45,7 @@ }); it('records polling runtime min average and max values', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $values = []; @@ -77,7 +77,7 @@ }); it('increments rule processing totals from existing status values', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $values = ['total_alert_rules_processed' => '3']; @@ -106,7 +106,7 @@ }); it('serializes last run rule activity with names and counts', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $json = syslog_status_rule_activity_json([ ['name' => 'Disk Full', 'count' => 2], @@ -120,7 +120,7 @@ }); it('keeps a bounded partition maintenance history and records recovery progress', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); $GLOBALS['syslogdb_default'] = 'syslog'; $values = []; diff --git a/tests/Unit/SyslogLegendTest.php b/tests/Unit/SyslogLegendTest.php index 6fc346eb..c2aa4b2f 100644 --- a/tests/Unit/SyslogLegendTest.php +++ b/tests/Unit/SyslogLegendTest.php @@ -19,7 +19,7 @@ */ it('renders one chip per severity for the system log legend', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); ob_start(); syslog_syslog_legend(); @@ -37,7 +37,7 @@ }); it('renders the smaller alert log legend', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); ob_start(); syslog_log_legend(); @@ -52,7 +52,7 @@ }); it('links the theme legend stylesheet when the theme ships one', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); test_override('get_selected_theme', function () { return 'modern'; @@ -67,7 +67,7 @@ }); it('falls back to syslog.css when the theme ships no legend stylesheet', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); test_override('get_selected_theme', function () { return 'carrot'; @@ -82,7 +82,7 @@ }); it('sanitises the theme name before building the stylesheet path', function () { - syslog_load_plugin_source('functions.php'); + syslog_load_plugin_source('includes/functions.php'); test_override('get_selected_theme', function () { return '../../etc/passwd'; diff --git a/tests/Unit/SyslogWorkerAggregationTest.php b/tests/Unit/SyslogWorkerAggregationTest.php index 3529614a..079a154c 100644 --- a/tests/Unit/SyslogWorkerAggregationTest.php +++ b/tests/Unit/SyslogWorkerAggregationTest.php @@ -20,7 +20,7 @@ * wiring inside syslog_process.php is pinned by the last test. */ -syslog_load_plugin_source('functions.php'); +syslog_load_plugin_source('includes/functions.php'); it('sums moved and resolved counts from a fresh settings table read', function () { $GLOBALS['syslogdb_default'] = 'syslog'; @@ -98,7 +98,7 @@ it('does not read worker stats through the cached config option helper', function () { $root = dirname(__DIR__, 2); - $functions = file_get_contents($root . '/functions.php'); + $functions = file_get_contents($root . '/includes/functions.php'); // The regression: read_config_option() caches per process, so the // master re-read its references phase numbers instead of the fresh diff --git a/tests/Unit/SyslogWorkerArgsTest.php b/tests/Unit/SyslogWorkerArgsTest.php index d31ce7a6..afc972dd 100644 --- a/tests/Unit/SyslogWorkerArgsTest.php +++ b/tests/Unit/SyslogWorkerArgsTest.php @@ -15,7 +15,7 @@ * bounds. */ -syslog_load_plugin_source('functions.php'); +syslog_load_plugin_source('includes/functions.php'); it('accepts a fully valid worker argument set', function () { expect(syslog_validate_worker_args(1, str_repeat('a', 32), 'references', 1, 100))->toBeTrue(); diff --git a/tests/Unit/SyslogWorkerSlicesTest.php b/tests/Unit/SyslogWorkerSlicesTest.php index a17dd1e6..53bc322a 100644 --- a/tests/Unit/SyslogWorkerSlicesTest.php +++ b/tests/Unit/SyslogWorkerSlicesTest.php @@ -27,7 +27,7 @@ function syslog_slice_coverage(array $slices): array { return array_keys($covered); } -syslog_load_plugin_source('functions.php'); +syslog_load_plugin_source('includes/functions.php'); it('splits a seq range into disjoint contiguous slices', function (int $start, int $end, int $workers) { $slices = syslog_compute_slices($start, $end, $workers); diff --git a/tests/Unit/SyslogWorkerStatsTest.php b/tests/Unit/SyslogWorkerStatsTest.php index 49ab338b..a202d0c1 100644 --- a/tests/Unit/SyslogWorkerStatsTest.php +++ b/tests/Unit/SyslogWorkerStatsTest.php @@ -14,7 +14,7 @@ * parallel run. Malformed statistics rows must be ignored, not fatal. */ -syslog_load_plugin_source('functions.php'); +syslog_load_plugin_source('includes/functions.php'); it('reports running workers, configured workers, and per child records', function () { test_override('read_config_option', function ($name, $force = false) { diff --git a/tests/Unit/SyslogWorkerWaitTest.php b/tests/Unit/SyslogWorkerWaitTest.php index 71fa5f35..79e539d8 100644 --- a/tests/Unit/SyslogWorkerWaitTest.php +++ b/tests/Unit/SyslogWorkerWaitTest.php @@ -31,7 +31,7 @@ * scenario's snapshot list accounts for. */ -syslog_load_plugin_source('functions.php'); +syslog_load_plugin_source('includes/functions.php'); /** * Install fakes that replay a fixed snapshot of the process table on diff --git a/tests/bin/patch-coverage.php b/tests/bin/patch-coverage.php index e65b53db..dd0ca9f2 100644 --- a/tests/bin/patch-coverage.php +++ b/tests/bin/patch-coverage.php @@ -160,6 +160,20 @@ function changed_lines($base_ref) { * Empty by default; add entries per repository as the need arises. */ $unmeasured_allowlist = [ + // Web and CLI entry points: each chdir()s and includes auth.php (or runs + // at the top level) before defining anything, so they cannot be loaded + // into the isolated unit process. Their only coverage-relevant content is + // the CWD-independent include chain, which tests/Security/ + // IncludePathNormalizationTest.php verifies by static inspection instead. + 'syslog.php', + 'syslog_alerts.php', + 'syslog_removal.php', + 'syslog_reports.php', + 'syslog_saved_searches.php', + 'syslog_dashboards.php', + 'syslog_device_rules.php', + 'syslog_batch_transfer.php', + 'syslog_process.php', ]; $unmeasured = array_values(array_diff(array_keys($changed), array_keys($measured))); $unexpected_unmeasured = array_values(array_diff($unmeasured, $unmeasured_allowlist)); diff --git a/tests/regression/alarm_filter_builder_test.php b/tests/regression/alarm_filter_builder_test.php index c186b0cc..6627d9ca 100644 --- a/tests/regression/alarm_filter_builder_test.php +++ b/tests/regression/alarm_filter_builder_test.php @@ -9,7 +9,7 @@ 'textField' => 'message' ]; -require_once dirname(__DIR__, 2) . '/functions.php'; +require_once dirname(__DIR__, 2) . '/includes/functions.php'; function alarm_filter_assert($condition, $message) { if (!$condition) { diff --git a/tests/regression/alarm_filter_upgrade_test.php b/tests/regression/alarm_filter_upgrade_test.php index a90f1d70..bc7ea5d4 100644 --- a/tests/regression/alarm_filter_upgrade_test.php +++ b/tests/regression/alarm_filter_upgrade_test.php @@ -1,7 +1,7 @@ 'message' ]; -require_once dirname(__DIR__, 2) . '/functions.php'; +require_once dirname(__DIR__, 2) . '/includes/functions.php'; /** Retroactive translation runs against reference tables; stub them here. */ $GLOBALS['syslog_reference_hosts'] = ['router01' => 12, 'core-sw' => 15]; $GLOBALS['syslog_reference_programs'] = ['snmpd' => 3, 'sshd' => 9]; diff --git a/tests/regression/rule_save_table_name_test.php b/tests/regression/rule_save_table_name_test.php index 25590df5..1050e82c 100644 --- a/tests/regression/rule_save_table_name_test.php +++ b/tests/regression/rule_save_table_name_test.php @@ -17,7 +17,7 @@ function syslog_sql_save($data, $table, $primary = '') { function raise_message($message) { } -require_once dirname(__DIR__, 2) . '/functions.php'; +require_once dirname(__DIR__, 2) . '/includes/functions.php'; syslog_sync_save(['id' => '', 'name' => 'test'], 'syslog_alert', 'id'); From fb353cd81fe5664b40d6288e0b4731306539ad23 Mon Sep 17 00:00:00 2001 From: Cacti Group Date: Tue, 6 Oct 2026 13:48:34 -0400 Subject: [PATCH 3/7] Keep functions.php out of setup.php's global scope Revert the top-level require_once added to setup.php (it pulled functions.php into every page that loads the plugin's setup.php for hook registration). Restore the original on-demand include_once of the library inside syslog_connect() and syslog_poller_bottom() at the new includes/ path. Those two lines run only in a live Cacti/poller process and cannot be unit-covered, so setup.php moves out of phpunit.xml into the patch-coverage allowlist; SetupSmallHelpersTest still exercises its pure helpers. --- CHANGELOG.md | 2 +- phpunit.xml | 1 - setup.php | 7 ++----- tests/Security/IncludePathNormalizationTest.php | 4 ++-- tests/bin/patch-coverage.php | 6 ++++++ 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d3ab13c..ea5271f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ --- develop --- -* dev: Move the shared function library to includes/functions.php, load it once from setup.php, and point the coverage source, entry-point includes, and tests at the new path +* dev: Move the shared function library to includes/functions.php and point the entry-point includes, coverage source, and tests at the new path * feature: Restyle the System Logs and Alert Logs severity legends as rounded, evenly spaced, solid-colour chips that line up with the Thold status legends, with theme-appropriate backgrounds loaded from a per-theme css/.css file (falling back to css/syslog.css) * 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) * dev: Enforce patch coverage of changed lines in CI and remove the inert COMPOSER_ROOT_VERSION env from the Pest step diff --git a/phpunit.xml b/phpunit.xml index d5a4bdbc..33e31eeb 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -27,7 +27,6 @@ --> - setup.php database.php includes/functions.php lib/syslog_dashboard.php diff --git a/setup.php b/setup.php index 886ee4a1..765da717 100644 --- a/setup.php +++ b/setup.php @@ -22,11 +22,6 @@ +-------------------------------------------------------------------------+ */ -// The shared function library is required by every Syslog hook and page; load -// it once here so setup.php's hooks (including the poller bottom hook) always -// have it available without per-callsite include_once guards. -require_once __DIR__ . '/includes/functions.php'; - /** * Install the Syslog plugin, registering its hooks, realms and database tables. * @@ -233,6 +228,7 @@ function syslog_connect(): bool { include(SYSLOG_CONFIG); } + include_once(__DIR__ . '/includes/functions.php'); include_once(__DIR__ . '/database.php'); $connect_remote = false; @@ -1585,6 +1581,7 @@ function syslog_poller_bottom(): void { global $config; if (syslog_config_safe()) { + include_once(__DIR__ . '/includes/functions.php'); include_once(__DIR__ . '/database.php'); syslog_connect(); diff --git a/tests/Security/IncludePathNormalizationTest.php b/tests/Security/IncludePathNormalizationTest.php index 7550ff6d..fe735e0d 100644 --- a/tests/Security/IncludePathNormalizationTest.php +++ b/tests/Security/IncludePathNormalizationTest.php @@ -110,8 +110,8 @@ throw new RuntimeException('functions.php missing syslog_apply_selected_items_action'); } - if (!preg_match('/require_once\s+__DIR__\s*\.\s*[\'"]\/includes\/functions\.php[\'"]/', $setup)) { - throw new RuntimeException('setup.php must require includes/functions.php via __DIR__'); + if (!preg_match('/include_once\s*\(\s*__DIR__\s*\.\s*[\'"]\/includes\/functions\.php[\'"]\s*\)/', $setup)) { + throw new RuntimeException('setup.php must use __DIR__ for the includes/functions.php include'); } if (!preg_match('/include_once\s*\(\s*__DIR__\s*\.\s*[\'"]\/database\.php[\'"]\s*\)/', $setup)) { diff --git a/tests/bin/patch-coverage.php b/tests/bin/patch-coverage.php index dd0ca9f2..c934d5e0 100644 --- a/tests/bin/patch-coverage.php +++ b/tests/bin/patch-coverage.php @@ -174,6 +174,12 @@ function changed_lines($base_ref) { 'syslog_device_rules.php', 'syslog_batch_transfer.php', 'syslog_process.php', + // setup.php is unit-loadable (SetupSmallHelpersTest exercises its pure + // helpers), but the only lines this change touches are the include_once + // chain inside syslog_connect()/syslog_poller_bottom(), which run solely in a + // live Cacti or poller process; it is kept out of so those lines are + // not gate-measured. + 'setup.php', ]; $unmeasured = array_values(array_diff(array_keys($changed), array_keys($measured))); $unexpected_unmeasured = array_values(array_diff($unmeasured, $unmeasured_allowlist)); From b0efa2793f3970f70613ef1cbbac202ced425236 Mon Sep 17 00:00:00 2001 From: Cacti Group Date: Tue, 6 Oct 2026 14:01:00 -0400 Subject: [PATCH 4/7] Address legend review: contrast, scoped fallback, GPL headers Darken the light-theme warning (#b5730e -> #9c5d0a) and info (#3f8f4a -> #2f7d3a) chips so white label text meets WCAG AA 4.5:1. Replace the email-oriented syslog.css web fallback with a new legend-only css/legend.css so themes without a dedicated palette (carrot, cacti) no longer inherit syslog.css's global body/table/th rules; syslog.css returns to report/email styling only. Add the full GPL v2 header to every new css file. --- CHANGELOG.md | 2 +- css/classic.css | 34 ++++++++++++++++------ css/dark.css | 32 +++++++++++++++------ css/legend.css | 50 +++++++++++++++++++++++++++++++++ css/midwinter.css | 32 +++++++++++++++------ css/modern.css | 34 ++++++++++++++++------ css/paper-plane.css | 32 +++++++++++++++------ css/paw.css | 34 ++++++++++++++++------ css/sunrise.css | 32 +++++++++++++++------ css/syslog.css | 35 ----------------------- includes/functions.php | 8 +++--- tests/Unit/SyslogLegendTest.php | 8 +++--- 12 files changed, 233 insertions(+), 100 deletions(-) create mode 100644 css/legend.css diff --git a/CHANGELOG.md b/CHANGELOG.md index ea5271f2..bdc74bb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ --- develop --- * dev: Move the shared function library to includes/functions.php and point the entry-point includes, coverage source, and tests at the new path -* feature: Restyle the System Logs and Alert Logs severity legends as rounded, evenly spaced, solid-colour chips that line up with the Thold status legends, with theme-appropriate backgrounds loaded from a per-theme css/.css file (falling back to css/syslog.css) +* feature: Restyle the System Logs and Alert Logs severity legends as rounded, evenly spaced, solid-colour chips that line up with the Thold status legends, with theme-appropriate backgrounds loaded from a per-theme css/.css file (falling back to a theme-neutral css/legend.css) * 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) * dev: Enforce patch coverage of changed lines in CI and remove the inert COMPOSER_ROOT_VERSION env from the Pest step * feature: Add Device Alert Rules for device-wide alert handling: administrators can pause a device's non-exempt alerts until a selected time or indefinitely, choose a priority threshold that always passes through device pauses and maintenance windows, and allow all device alerts during maintenance for critical devices diff --git a/css/classic.css b/css/classic.css index 7d7ff54e..7eee6b3c 100644 --- a/css/classic.css +++ b/css/classic.css @@ -1,10 +1,28 @@ /* - * Syslog status-legend palette for the classic theme (light surface). - * - * Rounded, evenly spaced, solid-colour severity chips that line up with the - * Thold status legends. Loaded by syslog_include_js() when this theme is - * active; themes without a matching file fall back to css/syslog.css. - */ + +-------------------------------------------------------------------------+ + | Copyright (C) 2004-2026 The Cacti Group | + | | + | This program is free software; you can redistribute it and/or | + | modify it under the terms of the GNU General Public License | + | as published by the Free Software Foundation; either version 2 | + | of the License, or (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + +-------------------------------------------------------------------------+ + | Cacti: The Complete RRDtool-based Graphing Solution | + +-------------------------------------------------------------------------+ + | This code is designed, written, and maintained by the Cacti Group. See | + | about.php and/or the AUTHORS file for specific developer information. | + +-------------------------------------------------------------------------+ + | http://www.cacti.net/ | + +-------------------------------------------------------------------------+ +*/ + +/* Syslog status-legend palette for the classic (light surface) theme. */ + .syslogLegend { display: flex; flex-wrap: wrap; @@ -26,7 +44,7 @@ .syslogLegend .logCritical { background-color: #a5281f; } .syslogLegend .logAlert { background-color: #b23a2f; } .syslogLegend .logError { background-color: #c0483c; } -.syslogLegend .logWarning { background-color: #b5730e; } +.syslogLegend .logWarning { background-color: #9c5d0a; } .syslogLegend .logNotice { background-color: #2f6fa8; } -.syslogLegend .logInfo { background-color: #3f8f4a; } +.syslogLegend .logInfo { background-color: #2f7d3a; } .syslogLegend .logDebug { background-color: #6c7a72; } diff --git a/css/dark.css b/css/dark.css index 40f59f63..e8692ed9 100644 --- a/css/dark.css +++ b/css/dark.css @@ -1,12 +1,28 @@ /* - * Syslog status-legend palette for the dark theme (dark surface). - * - * Rounded, evenly spaced, solid-colour severity chips that line up with the - * Thold status legends. Backgrounds are muted to sit on the dark surface and - * the chip text is lightened for contrast. Loaded by syslog_include_js() when - * this theme is active; themes without a matching file fall back to - * css/syslog.css. - */ + +-------------------------------------------------------------------------+ + | Copyright (C) 2004-2026 The Cacti Group | + | | + | This program is free software; you can redistribute it and/or | + | modify it under the terms of the GNU General Public License | + | as published by the Free Software Foundation; either version 2 | + | of the License, or (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + +-------------------------------------------------------------------------+ + | Cacti: The Complete RRDtool-based Graphing Solution | + +-------------------------------------------------------------------------+ + | This code is designed, written, and maintained by the Cacti Group. See | + | about.php and/or the AUTHORS file for specific developer information. | + +-------------------------------------------------------------------------+ + | http://www.cacti.net/ | + +-------------------------------------------------------------------------+ +*/ + +/* Syslog status-legend palette for the dark (dark surface) theme. */ + .syslogLegend { display: flex; flex-wrap: wrap; diff --git a/css/legend.css b/css/legend.css new file mode 100644 index 00000000..a627d08f --- /dev/null +++ b/css/legend.css @@ -0,0 +1,50 @@ +/* + +-------------------------------------------------------------------------+ + | Copyright (C) 2004-2026 The Cacti Group | + | | + | This program is free software; you can redistribute it and/or | + | modify it under the terms of the GNU General Public License | + | as published by the Free Software Foundation; either version 2 | + | of the License, or (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + +-------------------------------------------------------------------------+ + | Cacti: The Complete RRDtool-based Graphing Solution | + +-------------------------------------------------------------------------+ + | This code is designed, written, and maintained by the Cacti Group. See | + | about.php and/or the AUTHORS file for specific developer information. | + +-------------------------------------------------------------------------+ + | http://www.cacti.net/ | + +-------------------------------------------------------------------------+ +*/ + +/* Theme-neutral Syslog status-legend palette; the fallback loaded by syslog_include_js() when the active theme ships no dedicated css/.css file. */ + +.syslogLegend { + display: flex; + flex-wrap: wrap; + gap: 4px; + width: 100%; +} + +.syslogLegend .syslogLegendItem { + flex: 1 1 0; + text-align: center; + white-space: nowrap; + padding: 4px 6px; + border-radius: 3px; + color: #ffffff; + font-size: 11px; +} + +.syslogLegend .logEmergency { background-color: #8e1f18; } +.syslogLegend .logCritical { background-color: #a5281f; } +.syslogLegend .logAlert { background-color: #b23a2f; } +.syslogLegend .logError { background-color: #c0483c; } +.syslogLegend .logWarning { background-color: #9c5d0a; } +.syslogLegend .logNotice { background-color: #2f6fa8; } +.syslogLegend .logInfo { background-color: #2f7d3a; } +.syslogLegend .logDebug { background-color: #6c7a72; } diff --git a/css/midwinter.css b/css/midwinter.css index 5d0db79c..1febac31 100644 --- a/css/midwinter.css +++ b/css/midwinter.css @@ -1,12 +1,28 @@ /* - * Syslog status-legend palette for the midwinter theme (dark surface). - * - * Rounded, evenly spaced, solid-colour severity chips that line up with the - * Thold status legends. Backgrounds are muted to sit on the dark surface and - * the chip text is lightened for contrast. Loaded by syslog_include_js() when - * this theme is active; themes without a matching file fall back to - * css/syslog.css. - */ + +-------------------------------------------------------------------------+ + | Copyright (C) 2004-2026 The Cacti Group | + | | + | This program is free software; you can redistribute it and/or | + | modify it under the terms of the GNU General Public License | + | as published by the Free Software Foundation; either version 2 | + | of the License, or (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + +-------------------------------------------------------------------------+ + | Cacti: The Complete RRDtool-based Graphing Solution | + +-------------------------------------------------------------------------+ + | This code is designed, written, and maintained by the Cacti Group. See | + | about.php and/or the AUTHORS file for specific developer information. | + +-------------------------------------------------------------------------+ + | http://www.cacti.net/ | + +-------------------------------------------------------------------------+ +*/ + +/* Syslog status-legend palette for the midwinter (dark surface) theme. */ + .syslogLegend { display: flex; flex-wrap: wrap; diff --git a/css/modern.css b/css/modern.css index 2bad866d..3ded6964 100644 --- a/css/modern.css +++ b/css/modern.css @@ -1,10 +1,28 @@ /* - * Syslog status-legend palette for the modern theme (light surface). - * - * Rounded, evenly spaced, solid-colour severity chips that line up with the - * Thold status legends. Loaded by syslog_include_js() when this theme is - * active; themes without a matching file fall back to css/syslog.css. - */ + +-------------------------------------------------------------------------+ + | Copyright (C) 2004-2026 The Cacti Group | + | | + | This program is free software; you can redistribute it and/or | + | modify it under the terms of the GNU General Public License | + | as published by the Free Software Foundation; either version 2 | + | of the License, or (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + +-------------------------------------------------------------------------+ + | Cacti: The Complete RRDtool-based Graphing Solution | + +-------------------------------------------------------------------------+ + | This code is designed, written, and maintained by the Cacti Group. See | + | about.php and/or the AUTHORS file for specific developer information. | + +-------------------------------------------------------------------------+ + | http://www.cacti.net/ | + +-------------------------------------------------------------------------+ +*/ + +/* Syslog status-legend palette for the modern (light surface) theme. */ + .syslogLegend { display: flex; flex-wrap: wrap; @@ -26,7 +44,7 @@ .syslogLegend .logCritical { background-color: #a5281f; } .syslogLegend .logAlert { background-color: #b23a2f; } .syslogLegend .logError { background-color: #c0483c; } -.syslogLegend .logWarning { background-color: #b5730e; } +.syslogLegend .logWarning { background-color: #9c5d0a; } .syslogLegend .logNotice { background-color: #2f6fa8; } -.syslogLegend .logInfo { background-color: #3f8f4a; } +.syslogLegend .logInfo { background-color: #2f7d3a; } .syslogLegend .logDebug { background-color: #6c7a72; } diff --git a/css/paper-plane.css b/css/paper-plane.css index 9442ca9e..089d85ad 100644 --- a/css/paper-plane.css +++ b/css/paper-plane.css @@ -1,12 +1,28 @@ /* - * Syslog status-legend palette for the paper-plane theme (dark surface). - * - * Rounded, evenly spaced, solid-colour severity chips that line up with the - * Thold status legends. Backgrounds are muted to sit on the dark surface and - * the chip text is lightened for contrast. Loaded by syslog_include_js() when - * this theme is active; themes without a matching file fall back to - * css/syslog.css. - */ + +-------------------------------------------------------------------------+ + | Copyright (C) 2004-2026 The Cacti Group | + | | + | This program is free software; you can redistribute it and/or | + | modify it under the terms of the GNU General Public License | + | as published by the Free Software Foundation; either version 2 | + | of the License, or (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + +-------------------------------------------------------------------------+ + | Cacti: The Complete RRDtool-based Graphing Solution | + +-------------------------------------------------------------------------+ + | This code is designed, written, and maintained by the Cacti Group. See | + | about.php and/or the AUTHORS file for specific developer information. | + +-------------------------------------------------------------------------+ + | http://www.cacti.net/ | + +-------------------------------------------------------------------------+ +*/ + +/* Syslog status-legend palette for the paper-plane (dark surface) theme. */ + .syslogLegend { display: flex; flex-wrap: wrap; diff --git a/css/paw.css b/css/paw.css index 80f10073..07d1b4c3 100644 --- a/css/paw.css +++ b/css/paw.css @@ -1,10 +1,28 @@ /* - * Syslog status-legend palette for the paw theme (light surface). - * - * Rounded, evenly spaced, solid-colour severity chips that line up with the - * Thold status legends. Loaded by syslog_include_js() when this theme is - * active; themes without a matching file fall back to css/syslog.css. - */ + +-------------------------------------------------------------------------+ + | Copyright (C) 2004-2026 The Cacti Group | + | | + | This program is free software; you can redistribute it and/or | + | modify it under the terms of the GNU General Public License | + | as published by the Free Software Foundation; either version 2 | + | of the License, or (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + +-------------------------------------------------------------------------+ + | Cacti: The Complete RRDtool-based Graphing Solution | + +-------------------------------------------------------------------------+ + | This code is designed, written, and maintained by the Cacti Group. See | + | about.php and/or the AUTHORS file for specific developer information. | + +-------------------------------------------------------------------------+ + | http://www.cacti.net/ | + +-------------------------------------------------------------------------+ +*/ + +/* Syslog status-legend palette for the paw (light surface) theme. */ + .syslogLegend { display: flex; flex-wrap: wrap; @@ -26,7 +44,7 @@ .syslogLegend .logCritical { background-color: #a5281f; } .syslogLegend .logAlert { background-color: #b23a2f; } .syslogLegend .logError { background-color: #c0483c; } -.syslogLegend .logWarning { background-color: #b5730e; } +.syslogLegend .logWarning { background-color: #9c5d0a; } .syslogLegend .logNotice { background-color: #2f6fa8; } -.syslogLegend .logInfo { background-color: #3f8f4a; } +.syslogLegend .logInfo { background-color: #2f7d3a; } .syslogLegend .logDebug { background-color: #6c7a72; } diff --git a/css/sunrise.css b/css/sunrise.css index 6f41d2d6..00432e34 100644 --- a/css/sunrise.css +++ b/css/sunrise.css @@ -1,12 +1,28 @@ /* - * Syslog status-legend palette for the sunrise theme (dark surface). - * - * Rounded, evenly spaced, solid-colour severity chips that line up with the - * Thold status legends. Backgrounds are muted to sit on the dark surface and - * the chip text is lightened for contrast. Loaded by syslog_include_js() when - * this theme is active; themes without a matching file fall back to - * css/syslog.css. - */ + +-------------------------------------------------------------------------+ + | Copyright (C) 2004-2026 The Cacti Group | + | | + | This program is free software; you can redistribute it and/or | + | modify it under the terms of the GNU General Public License | + | as published by the Free Software Foundation; either version 2 | + | of the License, or (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + +-------------------------------------------------------------------------+ + | Cacti: The Complete RRDtool-based Graphing Solution | + +-------------------------------------------------------------------------+ + | This code is designed, written, and maintained by the Cacti Group. See | + | about.php and/or the AUTHORS file for specific developer information. | + +-------------------------------------------------------------------------+ + | http://www.cacti.net/ | + +-------------------------------------------------------------------------+ +*/ + +/* Syslog status-legend palette for the sunrise (dark surface) theme. */ + .syslogLegend { display: flex; flex-wrap: wrap; diff --git a/css/syslog.css b/css/syslog.css index 76935a31..12adcbed 100644 --- a/css/syslog.css +++ b/css/syslog.css @@ -118,38 +118,3 @@ p { /* Slightly highlight on hover but keep parent coloring visible */ background: rgba(0,0,0,0.02) !important; } - -/* - * Status legend. Laid out as an evenly sized, wrapping flex row (flex: 1 1 0 - * gives every chip the same width) with rounded corners and spacing between - * chips. A legend is a colour key, so the chips use their own solid, opaque - * colours with light text so they stay legible rather than inheriting the - * subtle row tints. This base palette is the fallback used by themes that do - * not ship a dedicated legend stylesheet; see css/.css for the - * per-theme variants. - */ -.syslogLegend { - display: flex; - flex-wrap: wrap; - gap: 4px; - width: 100%; -} - -.syslogLegend .syslogLegendItem { - flex: 1 1 0; - text-align: center; - white-space: nowrap; - padding: 4px 6px; - border-radius: 3px; - color: #ffffff; - font-size: 11px; -} - -.syslogLegend .logEmergency { background-color: #8e1f18; } -.syslogLegend .logCritical { background-color: #a5281f; } -.syslogLegend .logAlert { background-color: #b23a2f; } -.syslogLegend .logError { background-color: #c0483c; } -.syslogLegend .logWarning { background-color: #b5730e; } -.syslogLegend .logNotice { background-color: #2f6fa8; } -.syslogLegend .logInfo { background-color: #3f8f4a; } -.syslogLegend .logDebug { background-color: #6c7a72; } diff --git a/includes/functions.php b/includes/functions.php index c7b7e2ad..6e3722d2 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -720,11 +720,11 @@ function syslog_export_form_end(bool $export): void { function syslog_include_js(): void { global $config; - // Load the legend palette for the active theme, falling back to the base - // syslog.css when the theme ships no dedicated file. The theme name is a - // user/DB setting, so it is sanitised before it reaches the filesystem. + // Load the legend palette for the active theme, falling back to the + // theme-neutral legend.css when the theme ships no dedicated file. The theme + // name is a user/DB setting, so it is sanitised before it reaches the filesystem. $theme = preg_replace('/[^a-z0-9_-]/i', '', (string) get_selected_theme()); - $legend_css = ($theme !== '' && file_exists(dirname(__DIR__) . '/css/' . $theme . '.css')) ? $theme . '.css' : 'syslog.css'; + $legend_css = ($theme !== '' && file_exists(dirname(__DIR__) . '/css/' . $theme . '.css')) ? $theme . '.css' : 'legend.css'; ?> plugins/syslog/css/search.css?v='> plugins/syslog/css/dashboard.css?v='> diff --git a/tests/Unit/SyslogLegendTest.php b/tests/Unit/SyslogLegendTest.php index c2aa4b2f..a24f63ec 100644 --- a/tests/Unit/SyslogLegendTest.php +++ b/tests/Unit/SyslogLegendTest.php @@ -63,10 +63,10 @@ $output = ob_get_clean(); expect($output)->toContain('plugins/syslog/css/modern.css?v='); - expect($output)->not->toContain('plugins/syslog/css/syslog.css?v='); + expect($output)->not->toContain('plugins/syslog/css/legend.css?v='); }); -it('falls back to syslog.css when the theme ships no legend stylesheet', function () { +it('falls back to legend.css when the theme ships no legend stylesheet', function () { syslog_load_plugin_source('includes/functions.php'); test_override('get_selected_theme', function () { @@ -77,7 +77,7 @@ syslog_include_js(); $output = ob_get_clean(); - expect($output)->toContain('plugins/syslog/css/syslog.css?v='); + expect($output)->toContain('plugins/syslog/css/legend.css?v='); expect($output)->not->toContain('plugins/syslog/css/carrot.css?v='); }); @@ -92,6 +92,6 @@ syslog_include_js(); $output = ob_get_clean(); - expect($output)->toContain('plugins/syslog/css/syslog.css?v='); + expect($output)->toContain('plugins/syslog/css/legend.css?v='); expect($output)->not->toContain('..'); }); From fa353adab3eacacae2aea4fdfa851a1ba1de4ff2 Mon Sep 17 00:00:00 2001 From: Cacti Group Date: Tue, 6 Oct 2026 14:06:31 -0400 Subject: [PATCH 5/7] Use the full GPL v2 header in SyslogLegendTest --- tests/Unit/SyslogLegendTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Unit/SyslogLegendTest.php b/tests/Unit/SyslogLegendTest.php index a24f63ec..91fdef98 100644 --- a/tests/Unit/SyslogLegendTest.php +++ b/tests/Unit/SyslogLegendTest.php @@ -2,9 +2,24 @@ /* +-------------------------------------------------------------------------+ | Copyright (C) 2004-2026 The Cacti Group | + | | + | This program is free software; you can redistribute it and/or | + | modify it under the terms of the GNU General Public License | + | as published by the Free Software Foundation; either version 2 | + | of the License, or (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | +-------------------------------------------------------------------------+ | Cacti: The Complete RRDTool-based Graphing Solution | +-------------------------------------------------------------------------+ + | This code is designed, written, and maintained by the Cacti Group. See | + | about.php and/or the AUTHORS file for specific developer information. | + +-------------------------------------------------------------------------+ + | http://www.cacti.net/ | + +-------------------------------------------------------------------------+ */ /* From 6b5b787e1126452efcc31de89fbb5373af29d751 Mon Sep 17 00:00:00 2001 From: Cacti Group Date: Tue, 6 Oct 2026 14:11:36 -0400 Subject: [PATCH 6/7] Drop Ruby from the CodeQL language matrix The plugin ships no Ruby source, so 'codeql database finalize' for Ruby failed with 'no source code seen'. Analyze only the languages actually present (javascript-typescript, python). --- .github/workflows/codeql.yml | 2 +- CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d0791720..2ba4f8b6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -34,7 +34,7 @@ jobs: strategy: fail-fast: false matrix: - language: ["javascript-typescript", "python", "ruby"] + language: ["javascript-typescript", "python"] steps: - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index bdc74bb3..9237296d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ --- develop --- +* ci: Drop Ruby from the CodeQL language matrix; the plugin ships no Ruby source, so the Ruby database build failed with "no source code seen" * dev: Move the shared function library to includes/functions.php and point the entry-point includes, coverage source, and tests at the new path * feature: Restyle the System Logs and Alert Logs severity legends as rounded, evenly spaced, solid-colour chips that line up with the Thold status legends, with theme-appropriate backgrounds loaded from a per-theme css/.css file (falling back to a theme-neutral css/legend.css) * 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) From 2459db76ac74021eeca9e772524f64ae3386ac86 Mon Sep 17 00:00:00 2001 From: Cacti Group Date: Tue, 6 Oct 2026 14:17:43 -0400 Subject: [PATCH 7/7] Move database.php into includes/ database.php is a pure library (function definitions only, no top-level execution), so it joins includes/functions.php. Update every entry-point include, phpunit.xml coverage source, and IncludePathNormalizationTest at the new path. The move is a 100%-identical rename (gate-excluded); the touched entry points and setup.php are already in the patch-coverage allowlist. --- CHANGELOG.md | 2 +- database.php => includes/database.php | 0 phpunit.xml | 2 +- setup.php | 4 ++-- syslog.php | 2 +- syslog_alerts.php | 2 +- syslog_batch_transfer.php | 2 +- syslog_dashboards.php | 2 +- syslog_device_rules.php | 2 +- syslog_process.php | 2 +- syslog_removal.php | 2 +- syslog_reports.php | 2 +- syslog_saved_searches.php | 2 +- tests/Security/IncludePathNormalizationTest.php | 10 +++++----- 14 files changed, 18 insertions(+), 18 deletions(-) rename database.php => includes/database.php (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9237296d..2ebe7617 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ --- develop --- * ci: Drop Ruby from the CodeQL language matrix; the plugin ships no Ruby source, so the Ruby database build failed with "no source code seen" -* dev: Move the shared function library to includes/functions.php and point the entry-point includes, coverage source, and tests at the new path +* dev: Move the shared function and database libraries to includes/ and point the entry-point includes, coverage source, and tests at the new path * feature: Restyle the System Logs and Alert Logs severity legends as rounded, evenly spaced, solid-colour chips that line up with the Thold status legends, with theme-appropriate backgrounds loaded from a per-theme css/.css file (falling back to a theme-neutral css/legend.css) * 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) * dev: Enforce patch coverage of changed lines in CI and remove the inert COMPOSER_ROOT_VERSION env from the Pest step diff --git a/database.php b/includes/database.php similarity index 100% rename from database.php rename to includes/database.php diff --git a/phpunit.xml b/phpunit.xml index 33e31eeb..126ab566 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -27,7 +27,7 @@ --> - database.php + includes/database.php includes/functions.php lib/syslog_dashboard.php diff --git a/setup.php b/setup.php index 765da717..59e3d325 100644 --- a/setup.php +++ b/setup.php @@ -229,7 +229,7 @@ function syslog_connect(): bool { } include_once(__DIR__ . '/includes/functions.php'); - include_once(__DIR__ . '/database.php'); + include_once(__DIR__ . '/includes/database.php'); $connect_remote = false; $connected = true; @@ -1582,7 +1582,7 @@ function syslog_poller_bottom(): void { if (syslog_config_safe()) { include_once(__DIR__ . '/includes/functions.php'); - include_once(__DIR__ . '/database.php'); + include_once(__DIR__ . '/includes/database.php'); syslog_connect(); syslog_status_set('last_polling_time', time()); diff --git a/syslog.php b/syslog.php index cd936de6..5ada5289 100644 --- a/syslog.php +++ b/syslog.php @@ -34,7 +34,7 @@ include_once('./lib/html_tree.php'); include_once(__DIR__ . '/setup.php'); include_once(__DIR__ . '/includes/functions.php'); -include_once(__DIR__ . '/database.php'); +include_once(__DIR__ . '/includes/database.php'); include_once(__DIR__ . '/lib/syslog_dashboard.php'); global $config; diff --git a/syslog_alerts.php b/syslog_alerts.php index a4d2e848..9a728964 100644 --- a/syslog_alerts.php +++ b/syslog_alerts.php @@ -27,7 +27,7 @@ include_once('./lib/xml.php'); include_once(__DIR__ . '/setup.php'); include_once(__DIR__ . '/includes/functions.php'); -include_once(__DIR__ . '/database.php'); +include_once(__DIR__ . '/includes/database.php'); syslog_connect(); diff --git a/syslog_batch_transfer.php b/syslog_batch_transfer.php index 9f14a00b..f3ffec92 100644 --- a/syslog_batch_transfer.php +++ b/syslog_batch_transfer.php @@ -27,7 +27,7 @@ include_once('./lib/poller.php'); include_once(__DIR__ . '/setup.php'); include_once(__DIR__ . '/includes/functions.php'); -include_once(__DIR__ . '/database.php'); +include_once(__DIR__ . '/includes/database.php'); syslog_connect(); diff --git a/syslog_dashboards.php b/syslog_dashboards.php index 0ecbcc42..c3a475ac 100644 --- a/syslog_dashboards.php +++ b/syslog_dashboards.php @@ -2,7 +2,7 @@ chdir('../../'); include('./include/auth.php'); include_once('./plugins/syslog/includes/functions.php'); -include_once('./plugins/syslog/database.php'); +include_once('./plugins/syslog/includes/database.php'); include_once(__DIR__ . '/lib/syslog_dashboard.php'); // The page is part of the Syslog Administration realm. Accept the explicit diff --git a/syslog_device_rules.php b/syslog_device_rules.php index e8fb3bbd..53af0423 100644 --- a/syslog_device_rules.php +++ b/syslog_device_rules.php @@ -5,7 +5,7 @@ include('./include/auth.php'); include_once(__DIR__ . '/setup.php'); include_once(__DIR__ . '/includes/functions.php'); -include_once(__DIR__ . '/database.php'); +include_once(__DIR__ . '/includes/database.php'); syslog_connect(); diff --git a/syslog_process.php b/syslog_process.php index 19ce1125..8e0994c5 100644 --- a/syslog_process.php +++ b/syslog_process.php @@ -31,7 +31,7 @@ include(__DIR__ . '/../../include/cli_check.php'); include_once(__DIR__ . '/setup.php'); include_once(__DIR__ . '/includes/functions.php'); -include_once(__DIR__ . '/database.php'); +include_once(__DIR__ . '/includes/database.php'); syslog_connect(); diff --git a/syslog_removal.php b/syslog_removal.php index 6bef7b39..ab44abf4 100644 --- a/syslog_removal.php +++ b/syslog_removal.php @@ -27,7 +27,7 @@ include_once('./lib/xml.php'); include_once(__DIR__ . '/setup.php'); include_once(__DIR__ . '/includes/functions.php'); -include_once(__DIR__ . '/database.php'); +include_once(__DIR__ . '/includes/database.php'); syslog_connect(); diff --git a/syslog_reports.php b/syslog_reports.php index 4b4c8b0b..dedde3c8 100644 --- a/syslog_reports.php +++ b/syslog_reports.php @@ -27,7 +27,7 @@ include_once('./lib/xml.php'); include_once(__DIR__ . '/setup.php'); include_once(__DIR__ . '/includes/functions.php'); -include_once(__DIR__ . '/database.php'); +include_once(__DIR__ . '/includes/database.php'); syslog_connect(); diff --git a/syslog_saved_searches.php b/syslog_saved_searches.php index eba2ed89..fe721535 100644 --- a/syslog_saved_searches.php +++ b/syslog_saved_searches.php @@ -2,7 +2,7 @@ chdir('../../'); include('./include/auth.php'); include_once('./plugins/syslog/includes/functions.php'); -include_once('./plugins/syslog/database.php'); +include_once('./plugins/syslog/includes/database.php'); // The page was originally registered in its own realm and is now part of // Syslog Administration. Accept both mappings so existing installations do diff --git a/tests/Security/IncludePathNormalizationTest.php b/tests/Security/IncludePathNormalizationTest.php index fe735e0d..c621fa25 100644 --- a/tests/Security/IncludePathNormalizationTest.php +++ b/tests/Security/IncludePathNormalizationTest.php @@ -30,7 +30,7 @@ $plugin_includes = [ 'setup.php', 'includes/functions.php', - 'database.php', + 'includes/database.php', ]; foreach ($plugin_includes as $inc) { @@ -42,7 +42,7 @@ // setup.php is not part of the standard per-entrypoint include chain; it is // pulled in on demand via $config['base_path'] where a runtime setup step is // actually needed, so only functions.php and database.php are required here. - $required_includes = ['includes/functions.php', 'database.php']; + $required_includes = ['includes/functions.php', 'includes/database.php']; foreach ($entrypoints as $file) { $path = $root . '/' . $file; @@ -96,7 +96,7 @@ } $functions = file_get_contents($root . '/includes/functions.php'); - $database = file_get_contents($root . '/database.php'); + $database = file_get_contents($root . '/includes/database.php'); if ($functions === false || $database === false) { throw new RuntimeException('Failed to read functions.php or database.php'); @@ -114,8 +114,8 @@ throw new RuntimeException('setup.php must use __DIR__ for the includes/functions.php include'); } - if (!preg_match('/include_once\s*\(\s*__DIR__\s*\.\s*[\'"]\/database\.php[\'"]\s*\)/', $setup)) { - throw new RuntimeException('setup.php must use __DIR__ for database.php include'); + if (!preg_match('/include_once\s*\(\s*__DIR__\s*\.\s*[\'"]\/includes\/database\.php[\'"]\s*\)/', $setup)) { + throw new RuntimeException('setup.php must use __DIR__ for the includes/database.php include'); } expect(true)->toBeTrue();