Skip to content

fix: repoint include/ -> includes/ plugin_hooks on every upgrade check so broken installs self-heal - #55

Merged
TheWitness merged 2 commits into
mainfrom
fix/repoint-include-hooks-unconditionally
Oct 7, 2026
Merged

TheWitness merged 2 commits into
mainfrom
fix/repoint-include-hooks-unconditionally

Conversation

@TheWitness

Copy link
Copy Markdown
Member

Summary

Carries the same fix as Cacti/plugin_intropage#410 over to plugin_evidence, which has the identical pattern.

When the plugin's library directory was renamed include/ -> includes/, plugin_evidence_upgrade_database() gained a migration that repoints stale plugin_hooks rows from the old path to the new one:

db_execute("UPDATE plugin_hooks SET file = REPLACE(file, 'include/', 'includes/')
    WHERE name = 'evidence' AND file LIKE 'include/%'");

…but it sits inside the if (!cacti_version_compare($oldv, $current, '=')) version gate. On an install whose plugin_config.version was already bumped to the current version without its hooks having been repointed, the gate is permanently closed and the repoint never runs again. That leaves the hook rows pointing at include/<file>:

  • with the old include/ still on disk, loading both include/<lib> and includes/<lib> risks Cannot redeclare ... fatals;
  • after the old include/ is deleted, Cacti's plugin file-inclusion security check rejects every stale row with SECURITY ERROR: Attempted inclusion of invalid plugin file include/<lib> ....

Fix

Run the idempotent repoint unconditionally, before the version comparison, so a broken install self-heals on the next upgrade check (reached via plugin_evidence_check_config(), invoked by Cacti core directly rather than through the broken hook rows). The WHERE ... file LIKE 'include/%' filter makes it a no-op on healthy installs. While here, the over-indented evidence_prune_files(); call inside the gate is normalized to the surrounding indentation.

Scope / fleet review

I audited the other Cacti plugins that performed the same include/ -> includes/ move (an include/ tombstone in manifest.json):

  • plugin_intropage - same bug, fixed in #410.
  • plugin_evidence - same bug, this PR.
  • plugin_routerconfigs - migrated, but registers every hook against setup.php (never an include/<file> library path), so there are no stale hook rows to repoint and no failure. No change needed.

plugin_tags lists include/ under expected (it still ships that directory) rather than tombstones, so it never did this migration and is unaffected.

Validation

  • Only includes/database.php changes; the repoint moves out of the version gate, no other logic is touched.
  • Healthy installs: zero rows match file LIKE 'include/%', so the extra UPDATE is a no-op.
  • Broken installs (version already current, hooks still on include/...): the repoint now runs, clearing both the redeclaration fatal and the SECURITY ERROR floods on the next page load.

Note: I was unable to run php -l in this environment; the change is a straight hoist of an existing statement plus an indentation normalization, reviewed by inspection.

The hook file repoint lived inside the version-change gate in
plugin_evidence_upgrade_database(), so installs whose plugin_config.version
was already bumped to the current version (but whose plugin_hooks rows were
never repointed from the old include/ path) never got healed. With the old
include/ directory still present this risks "Cannot redeclare ..." fatals
from loading both include/<lib> and includes/<lib>; after deleting include/
it produces repeated "SECURITY ERROR: Attempted inclusion of invalid plugin
file include/<lib>" because the stale hook rows point at a now-missing file.

Run the idempotent repoint UPDATE unconditionally (before the version
comparison) so broken installs self-heal on the next upgrade check. The
WHERE ... file LIKE 'include/%' filter makes it a no-op on healthy installs.
Mirrors the intropage fix (Cacti/plugin_intropage#410).
@TheWitness
TheWitness requested review from xmacan and a balanced review from Copilot October 1, 2026 18:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The existing matching-version lifecycle test will fail until updated for the new unconditional database call.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Moves stale plugin hook repair outside the version gate so broken installations self-heal.

Changes:

  • Repoints legacy include/ hook paths on every upgrade check.
  • Normalizes pruning-call indentation.
File Description
includes/​database.php Makes hook-path repair unconditional.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread includes/database.php
…e installs

The repoint moved out of the version-change gate, so plugin_evidence_check_config()
now issues the idempotent plugin_hooks UPDATE even when the stored version already
matches. Update the lifecycle test to pin that contract (exactly one db call: the
repoint) instead of asserting no work is done.
@TheWitness
TheWitness merged commit f01979d into main Oct 7, 2026
3 checks passed
@TheWitness
TheWitness deleted the fix/repoint-include-hooks-unconditionally branch October 7, 2026 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants