Skip to content

test: add Pest v1 security test infrastructure#305

Closed
somethingwithproof wants to merge 3 commits intoCacti:developfrom
somethingwithproof:test/add-security-test-infrastructure
Closed

test: add Pest v1 security test infrastructure#305
somethingwithproof wants to merge 3 commits intoCacti:developfrom
somethingwithproof:test/add-security-test-infrastructure

Conversation

@somethingwithproof
Copy link
Copy Markdown
Contributor

Summary

  • Add Pest v1 test scaffold with Cacti framework stubs
  • Source-scan tests for prepared statement consistency
  • PHP 7.4 compatibility verification tests
  • Plugin setup.php structure validation

Test plan

  • composer install && vendor/bin/pest passes
  • Tests verify security patterns match hardening PRs

Add source-scan tests verifying security patterns (prepared statements,
output escaping, auth guards, PHP 7.4 compatibility) remain in place
across refactors. Tests run with Pest v1 (PHP 7.3+) and stub the Cacti
framework so plugins can be tested in isolation.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Copilot AI review requested due to automatic review settings April 9, 2026 06:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a Pest v1 test scaffold and lightweight Cacti stubs to enable security/compatibility checks against the syslog plugin codebase.

Changes:

  • Introduces Pest bootstrap/config and several source-scanning security/compatibility tests.
  • Adds a composer.json with Pest as a dev dependency and test bootstrap autoloading.
  • Adds a CodeQL workflow configuration.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
tests/Security/SetupStructureTest.php Verifies setup.php defines expected plugin entrypoints and version keys.
tests/Security/PreparedStatementConsistencyTest.php Scans key plugin files for raw db_* calls to prevent regressions.
tests/Security/Php74CompatibilityTest.php Scans plugin files for PHP 8-only syntax/function usage.
tests/Pest.php Loads the test bootstrap for Pest runs.
tests/bootstrap.php Provides Cacti framework stubs for running tests without full Cacti.
composer.json Adds Pest v1 as a dev dependency and wires test bootstrap autoloading.
.github/workflows/codeql.yml Adds CodeQL workflow definition.

Comment on lines +16 to +26
it('uses prepared DB helpers in all plugin files', function () {
$targetFiles = array(
'database.php',
'functions.php',
'setup.php',
'syslog.php',
'syslog_alerts.php',
'syslog_process.php',
'syslog_removal.php',
'syslog_reports.php',
);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

This test will flag legitimate db_* calls that must exist in this repo, so it’s likely to fail immediately (e.g., database.php contains return db_execute(...) inside the syslog_db_* wrappers, and setup.php has db_execute(...) calls for plugin metadata cleanup). Consider excluding database.php (and/or known-safe sections) from scanning, or tightening the check to only prohibit raw db_* usage outside the syslog DB wrapper layer (e.g., enforce syslog_db_* usage in non-wrapper files).

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +41
continue;
}

$contents = file_get_contents($path);

if ($contents === false) {
continue;
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

Silently continue-ing when a target file is missing or unreadable makes this test pass without actually validating anything. Prefer failing the test with a clear message when realpath() or file_get_contents() fails so CI can catch moved/renamed files or unexpected read errors.

Suggested change
continue;
}
$contents = file_get_contents($path);
if ($contents === false) {
continue;
throw new RuntimeException("Unable to resolve target file for prepared statement consistency test: {$relativeFile}");
}
$contents = file_get_contents($path);
if ($contents === false) {
throw new RuntimeException("Unable to read target file for prepared statement consistency test: {$path}");

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +19
function db_execute($sql) {
$GLOBALS['__test_db_calls'][] = array('fn' => 'db_execute', 'sql' => $sql, 'params' => array());
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The db_execute stub signature doesn’t match how the plugin calls it (the syslog DB wrappers call db_execute($sql, $log, $syslog_cnn)), which can cause ArgumentCountError if any test loads plugin code. Update the stub to accept the additional optional parameters (log flag / connection) to mirror Cacti’s function signature.

Suggested change
function db_execute($sql) {
$GLOBALS['__test_db_calls'][] = array('fn' => 'db_execute', 'sql' => $sql, 'params' => array());
function db_execute($sql, $log = true, $cnn = false) {
$GLOBALS['__test_db_calls'][] = array('fn' => 'db_execute', 'sql' => $sql, 'params' => array(), 'log' => $log, 'cnn' => $cnn);

Copilot uses AI. Check for mistakes.
}

if (!function_exists('db_execute_prepared')) {
function db_execute_prepared($sql, $params = array()) {
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

Similarly, db_execute_prepared is called by the plugin with additional parameters (e.g. $log, $syslog_cnn). The stub should accept those optional arguments to avoid runtime errors when plugin code is executed during tests.

Suggested change
function db_execute_prepared($sql, $params = array()) {
function db_execute_prepared($sql, $params = array(), $log = true, $cnn = null) {

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +65
if (!function_exists('db_fetch_assoc')) {
function db_fetch_assoc($sql) {
return array();
}
}

if (!function_exists('db_fetch_assoc_prepared')) {
function db_fetch_assoc_prepared($sql, $params = array()) {
return array();
}
}

if (!function_exists('db_fetch_row')) {
function db_fetch_row($sql) {
return array();
}
}

if (!function_exists('db_fetch_row_prepared')) {
function db_fetch_row_prepared($sql, $params = array()) {
return array();
}
}

if (!function_exists('db_fetch_cell')) {
function db_fetch_cell($sql) {
return '';
}
}

if (!function_exists('db_fetch_cell_prepared')) {
function db_fetch_cell_prepared($sql, $params = array()) {
return '';
}
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The db_fetch_* stubs accept fewer parameters than the plugin’s wrappers pass through (e.g. db_fetch_cell($sql, $col_name, $log, $cnn) and db_fetch_assoc($sql, $log, $cnn)). If any test includes code paths that invoke these wrappers, this will fatally error; consider updating these stubs to accept the optional $log/$cnn parameters (and $col_name where applicable).

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +16
$source = file_get_contents(realpath(__DIR__ . '/../../setup.php'));

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

If setup.php can’t be resolved/read (e.g., path changes), $source becomes false and subsequent string expectations will behave unpredictably and/or emit warnings. Consider asserting that realpath() and file_get_contents() succeeded (fail fast with a clear message) before running toContain / toMatch expectations.

Suggested change
$source = file_get_contents(realpath(__DIR__ . '/../../setup.php'));
$setupPath = realpath(__DIR__ . '/../../setup.php');
expect($setupPath)
->not->toBeFalse('Failed to resolve setup.php path from ' . __DIR__);
$source = file_get_contents($setupPath);
expect($source)
->not->toBeFalse('Failed to read setup.php at path: ' . $setupPath);

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +40
foreach ($files as $relativeFile) {
$path = realpath(__DIR__ . '/../../' . $relativeFile);

if ($path === false) {
continue;
}

$contents = file_get_contents($path);

if ($contents === false) {
continue;
}

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

These tests silently skip files when realpath() or file_get_contents() fails, which can lead to false-green results. Prefer failing with an explicit assertion/message when a target file is missing/unreadable so the compatibility gate remains meaningful over time.

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +13
push:
branches: [main, master, develop, regression-audit]
paths-ignore:
- "**/*.php"
- "**/*.md"
pull_request:
branches: [main, master, develop, regression-audit]
paths-ignore:
- "**/*.php"
- "**/*.md"
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

paths-ignore excludes all **/*.php, so CodeQL won’t run on the vast majority of changes in this PHP plugin. If the intent is to security-scan the plugin, remove the PHP ignore (and consider ignoring only vendor/ or generated paths instead).

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +35
strategy:
fail-fast: false
matrix:
language: ["javascript-typescript", "python", "ruby"]
steps:
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The CodeQL language matrix doesn’t include php, so even when the workflow runs it won’t analyze the primary language in this repository. Add PHP to the matrix (or switch to a single-language config) so CodeQL produces useful results for this codebase.

Copilot uses AI. Check for mistakes.
@TheWitness
Copy link
Copy Markdown
Member

Closing this one. We can discuss on Sunday.

@TheWitness TheWitness closed this Apr 10, 2026
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.

3 participants