hardening: migrate uninstall drop statements to prepared + align test framework with Thold - #30
Merged
TheWitness merged 17 commits intoSep 12, 2026
Conversation
Add targeted tests for prepared statement migration, output escaping, auth guard presence, CSRF token validation, redirect safety, and PHP 7.4 compatibility. Tests use source-scan patterns that verify security invariants without requiring the Cacti database. Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Supersedes Cacti#25 ("hardening: migrate uninstall drop statements to prepared" by @somethingwithproof) — this branch carries forward that PR's prepared-statement hardening and test coverage, then updates the test/CI plumbing to match the model used by Cacti/plugin_thold, mirroring the same work already applied in PR Cacti#29: - Remove composer.json (no composer.lock was present): Pest/PHPUnit no longer come from a vendor tree local to this plugin. - Add tests/.cacti-version, tests/TestCase.php, and tests/bootstrap-unit.php (adapted from thold). bootstrap-unit.php verifies the Cacti checkout in CI matches tests/.cacti-version, requires Cacti's own Composer vendor autoloader, and stubs the Cacti global functions plugin source expects. - Replace tests/bootstrap.php with tests/bootstrap-unit.php and update tests/Pest.php's comment to match. - Add phpunit.xml (none existed before) bootstrapping from tests/bootstrap-unit.php and covering tests/Security, tests/Unit, tests/Integration, and tests/E2E. - Add .github/copilot-instructions.md adapted from thold's for the Evidence plugin. - Add .github/workflows/php-unit-tests.yml modeled on thold's workflow: checks out this plugin plus a pinned Cacti runtime and test toolchain, builds a Docker test image, lints, and runs Pest with coverage.
Adding phpunit.xml means these tests actually execute in CI for the first time; two of them had latent bugs that were never caught before: - AuthGuardTest.php checked tests/test_prepared_statements.php (a test helper script) instead of the plugin's real UI entry points (evidence.php, evidence_tab.php), causing a false-positive failure and a risky (zero-assertion) test once no matching line existed. - SetupStructureTest.php regexed setup.php's raw source for `'name' =>` / `'version' =>`, but those keys live in the INFO ini file that setup.php parses at runtime, not in its literal source text. Assert against the parsed INFO file instead, matching PR Cacti#29's approach.
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Several security checks are failing or vacuous, and the prepared-statement regression test is not executed by CI.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Hardens uninstall SQL and output escaping while aligning Evidence tests and CI with Cacti’s toolchain.
Changes:
- Uses prepared uninstall statements and escapes rendered values.
- Adds security, unit, integration, and E2E tests.
- Adds Cacti bootstrap, PHPUnit configuration, pinned runtime, CI workflow, and project guidance.
File summaries
| File | Summary |
|---|---|
tests/Unit/FilterOutputEscapingTest.php |
Tests output escaping. |
tests/TestCase.php |
Adds shared test fixture. |
tests/test_prepared_statements.php |
Prepared-statement assertions; moderate issue (3 votes): not discovered by the configured CI suite. |
tests/Security/SetupStructureTest.php |
Setup structure checks; critical issue (3 votes): assertions do not match the current INFO contract. |
tests/Security/RedirectSafetyTest.php |
Redirect checks; moderate issue (2 votes): scans files without redirects and is vacuous. |
tests/Security/PreparedStatementConsistencyTest.php |
Prepared-statement consistency checks; moderate issue (2 votes): scans an incomplete file set. |
tests/Security/Php74CompatibilityTest.php |
PHP 7.4 compatibility checks. |
tests/Security/OutputEscapingTest.php |
Output escaping security checks. |
tests/Security/AuthGuardTest.php |
Authentication guard checks; critical issue (3 votes): scans a helper that contains none of the expected includes. |
tests/Pest.php |
Documents the test bootstrap. |
tests/Integration/EvidenceTabEscapingTest.php |
Tests escaped tab output. |
tests/E2E/EvidenceFilterXssRegressionTest.php |
Tests filter XSS regression wiring. |
tests/bootstrap-unit.php |
Loads Cacti tooling and stubs globals. |
tests/.cacti-version |
Pins the expected Cacti version. |
setup.php |
Uses prepared uninstall statements. |
phpunit.xml |
Configures test suites and coverage. |
evidence_tab.php |
Escapes rendered values. |
.github/workflows/php-unit-tests.yml |
Adds Cacti-based Docker CI. |
.github/copilot-instructions.md |
Documents project conventions. |
Review details
- Files reviewed: 19/19 changed files
- Comments generated: 5
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
.github/copilot-instructions.md, .github/workflows/php-unit-tests.yml, tests/.cacti-version, tests/TestCase.php, and tests/bootstrap-unit.php were committed with CRLF line endings. Normalize them to LF to match the rest of the repo, and add .gitattributes (`* text=auto eol=lf`) so this doesn't regress.
- PreparedStatementConsistencyTest.php: drop the non-existent
tests/test_prepared_statements.php target and narrow the description/
scope to the setup.php uninstall/drop path it actually verifies,
instead of implying (and failing to enforce) coverage of every plugin
file's DB calls.
- RedirectSafetyTest.php: scan the real redirect entry points
(index.php, images/index.php, data/index.php) instead of setup.php,
which has no header(Location) call at all.
- index.php, images/index.php, data/index.php: follow the
header("Location: ...") redirect with exit, so the test above passes
for a real reason and the scripts can't fall through if code is later
added after the redirect.
- Php74CompatibilityTest.php: drop the dangling reference to the removed
tests/test_prepared_statements.php.
- Convert tests/test_prepared_statements.php (a standalone script never
discovered by phpunit.xml or Pest's *Test.php convention, so its
assertions never ran in CI) into
tests/Security/UninstallPreparedStatementsTest.php, a proper Pest test
with the same assertions.
TheWitness
requested review from
bmfmancini,
browniebraun,
cigamit,
netniV and
somethingwithproof
September 12, 2026 13:49
…s: raise PHP floor to 8.2
… breaking Pest cache writes
bmfmancini
approved these changes
Sep 12, 2026
bmfmancini
approved these changes
Sep 12, 2026
bmfmancini
approved these changes
Sep 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This carries forward #25 ("hardening: migrate uninstall drop statements to
prepared" by @somethingwithproof) and, in addition to that PR's
prepared-statement hardening, re-designs the Evidence plugin's test/CI
infrastructure identical to pull request #29:
composer.json(nocomposer.lockwas present) — Pest/PHPUnitno longer come from a vendor tree local to this plugin.
tests/.cacti-version,tests/TestCase.php, andtests/bootstrap-unit.php(adapted from thold).bootstrap-unit.phpverifies the Cacti checkout in CI matches
tests/.cacti-version,requires Cacti's own Composer vendor autoloader, and stubs the Cacti
global functions plugin source expects.
tests/bootstrap.phpwithtests/bootstrap-unit.phpand updatetests/Pest.php's comment to match.phpunit.xml(none existed before), bootstrapping fromtests/bootstrap-unit.phpand coveringtests/Security,tests/Unit,tests/Integration, andtests/E2E..github/copilot-instructions.mdadapted from thold's for theEvidence plugin.
Supersedes #25.
Test plan
Pest Tests) builds the Cacti test image and runs Pestagainst
tests/Security,tests/Unit,tests/Integration, andtests/E2Esuccessfully.