diff --git a/includes/database.php b/includes/database.php index 04e580b..efa7a3e 100644 --- a/includes/database.php +++ b/includes/database.php @@ -299,6 +299,15 @@ function plugin_evidence_upgrade_database() { $current = $info['version']; $oldv = db_fetch_cell('SELECT version FROM plugin_config WHERE directory = "evidence"'); + // The plugin's library directory moved from include/ to includes/. Repoint + // any plugin_hooks row still bound to the old path on every check - not only + // during a version-change upgrade - because an install whose version was + // already bumped without its hooks being repointed would otherwise keep + // loading the stale include/ path (or, once include/ is deleted, fail + // Cacti's plugin file-inclusion security check for it) on every page. The + // LIKE filter makes this a no-op on healthy installs. + db_execute("UPDATE plugin_hooks SET file = REPLACE(file, 'include/', 'includes/') WHERE name = 'evidence' AND file LIKE 'include/%'"); + if (!cacti_version_compare($oldv, $current, '=')) { if (cacti_version_compare($oldv, '0.3', '<')) { $data = []; @@ -313,12 +322,9 @@ function plugin_evidence_upgrade_database() { api_plugin_db_table_create('evidence', 'plugin_evidence_snmp_info', $data); } - // The plugin's library directory moved from include/ to includes/; repoint - // any hook still registered against the old path so existing installs load - // them from the new location after upgrade. - db_execute("UPDATE plugin_hooks SET file = REPLACE(file, 'include/', 'includes/') WHERE name = 'evidence' AND file LIKE 'include/%'"); - // Remove files tombstoned in manifest.json (the old include/ tree). - evidence_prune_files(); + // Remove files tombstoned in manifest.json (the old include/ tree). + evidence_prune_files(); + // Set the new version db_execute_prepared("UPDATE plugin_config SET version = ?, author = ?, webpage = ? diff --git a/tests/Unit/EvidenceLifecycleTest.php b/tests/Unit/EvidenceLifecycleTest.php index 488af1d..7425f61 100644 --- a/tests/Unit/EvidenceLifecycleTest.php +++ b/tests/Unit/EvidenceLifecycleTest.php @@ -41,13 +41,23 @@ expect($drops)->toHaveCount(7); }); -it('does nothing when the stored version already matches the plugin version', function () { +it('repoints stale include/ hooks even when the stored version already matches', function () { $info = plugin_evidence_version(); evidence_test_mock_db('db_fetch_cell', 'plugin_config', $info['version']); expect(plugin_evidence_check_config())->toBeTrue(); - expect($GLOBALS['__test_db_calls'])->toBeEmpty(); + + // On an up-to-date install the only work is the idempotent include/ -> + // includes/ plugin_hooks repoint; no schema migration or version write runs. + expect($GLOBALS['__test_db_calls'])->toHaveCount(1); + + $call = $GLOBALS['__test_db_calls'][0]; + + expect($call['fn'])->toBe('db_execute') + ->and($call['sql'])->toContain('UPDATE plugin_hooks') + ->and($call['sql'])->toContain("REPLACE(file, 'include/', 'includes/')") + ->and($call['sql'])->toContain("file LIKE 'include/%'"); }); it('updates the stored plugin_config version when it drifts', function () {