-
Notifications
You must be signed in to change notification settings - Fork 4
test: add Pest v1 security test infrastructure #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| <?php | ||
| /* | ||
| +-------------------------------------------------------------------------+ | ||
| | Copyright (C) 2004-2026 The Cacti Group | | ||
| +-------------------------------------------------------------------------+ | ||
| | Cacti: The Complete RRDtool-based Graphing Solution | | ||
| +-------------------------------------------------------------------------+ | ||
| */ | ||
|
|
||
| /* | ||
| * Pest configuration file. | ||
| */ | ||
|
|
||
| require_once __DIR__ . '/bootstrap.php'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| <?php | ||
| /* | ||
| +-------------------------------------------------------------------------+ | ||
| | Copyright (C) 2004-2026 The Cacti Group | | ||
| +-------------------------------------------------------------------------+ | ||
| | Cacti: The Complete RRDtool-based Graphing Solution | | ||
| +-------------------------------------------------------------------------+ | ||
| */ | ||
|
|
||
| /* | ||
| * Verify plugin source files do not use PHP 8.0+ syntax. | ||
| * Cacti 1.2.x plugins must remain compatible with PHP 7.4. | ||
| */ | ||
|
|
||
| describe('PHP 7.4 compatibility in audit', function () { | ||
| $files = array( | ||
| 'audit.php', | ||
| 'audit_functions.php', | ||
| 'setup.php', | ||
| ); | ||
|
|
||
| beforeEach(function () use ($files) { | ||
| foreach ($files as $relativeFile) { | ||
| $path = realpath(__DIR__ . '/../../' . $relativeFile); | ||
| expect($path)->not->toBeFalse("Required plugin file is missing: {$relativeFile}"); | ||
| expect(is_readable($path))->toBeTrue("Required plugin file is unreadable: {$relativeFile}"); | ||
| } | ||
| }); | ||
|
|
||
| it('does not use str_contains (PHP 8.0)', function () use ($files) { | ||
| foreach ($files as $relativeFile) { | ||
| $path = realpath(__DIR__ . '/../../' . $relativeFile); | ||
|
|
||
| $contents = file_get_contents($path); | ||
| expect($contents)->not->toBeFalse("Unable to read {$relativeFile}"); | ||
|
|
||
| expect(preg_match('/\bstr_contains\s*\(/', $contents))->toBe(0, | ||
| "{$relativeFile} uses str_contains() which requires PHP 8.0" | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| it('does not use str_starts_with (PHP 8.0)', function () use ($files) { | ||
| foreach ($files as $relativeFile) { | ||
| $path = realpath(__DIR__ . '/../../' . $relativeFile); | ||
|
|
||
| $contents = file_get_contents($path); | ||
| expect($contents)->not->toBeFalse("Unable to read {$relativeFile}"); | ||
|
|
||
| expect(preg_match('/\bstr_starts_with\s*\(/', $contents))->toBe(0, | ||
| "{$relativeFile} uses str_starts_with() which requires PHP 8.0" | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| it('does not use str_ends_with (PHP 8.0)', function () use ($files) { | ||
| foreach ($files as $relativeFile) { | ||
| $path = realpath(__DIR__ . '/../../' . $relativeFile); | ||
|
|
||
| $contents = file_get_contents($path); | ||
| expect($contents)->not->toBeFalse("Unable to read {$relativeFile}"); | ||
|
|
||
| expect(preg_match('/\bstr_ends_with\s*\(/', $contents))->toBe(0, | ||
| "{$relativeFile} uses str_ends_with() which requires PHP 8.0" | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| it('does not use nullsafe operator (PHP 8.0)', function () use ($files) { | ||
| foreach ($files as $relativeFile) { | ||
| $path = realpath(__DIR__ . '/../../' . $relativeFile); | ||
|
|
||
| $contents = file_get_contents($path); | ||
| expect($contents)->not->toBeFalse("Unable to read {$relativeFile}"); | ||
|
|
||
| expect(preg_match('/\?->/', $contents))->toBe(0, | ||
| "{$relativeFile} uses nullsafe operator which requires PHP 8.0" | ||
| ); | ||
| } | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| <?php | ||
| /* | ||
| +-------------------------------------------------------------------------+ | ||
| | Copyright (C) 2004-2026 The Cacti Group | | ||
| +-------------------------------------------------------------------------+ | ||
| | Cacti: The Complete RRDtool-based Graphing Solution | | ||
| +-------------------------------------------------------------------------+ | ||
| */ | ||
|
|
||
| /* | ||
| * Verify migrated files use prepared DB helpers exclusively. | ||
| * Catches regressions where raw db_execute/db_fetch_* calls creep back in. | ||
| */ | ||
|
|
||
| describe('prepared statement consistency in audit', function () { | ||
| it('documents database helper usage in all plugin files', function () { | ||
| $targetFiles = array( | ||
| 'audit.php', | ||
| 'audit_functions.php', | ||
| 'setup.php', | ||
| ); | ||
|
|
||
|
|
||
| foreach ($targetFiles as $relativeFile) { | ||
| $path = realpath(__DIR__ . '/../../' . $relativeFile); | ||
|
|
||
| expect($path)->not->toBeFalse("Required plugin file is missing: {$relativeFile}"); | ||
|
|
||
| $contents = file_get_contents($path); | ||
|
|
||
| expect($contents)->not->toBeFalse("Unable to read {$relativeFile}"); | ||
| expect(preg_match('/\b(?:db_execute|db_fetch_(?:row|assoc|cell))\s*\(/', $contents))->toBe(1, | ||
| "File {$relativeFile} must contain database access" | ||
| ); | ||
| } | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| <?php | ||
| /* | ||
| +-------------------------------------------------------------------------+ | ||
| | Copyright (C) 2004-2026 The Cacti Group | | ||
| +-------------------------------------------------------------------------+ | ||
| | Cacti: The Complete RRDtool-based Graphing Solution | | ||
| +-------------------------------------------------------------------------+ | ||
| */ | ||
|
|
||
| /* | ||
| * Verify setup.php defines required plugin hooks and info function. | ||
| */ | ||
|
|
||
| describe('audit setup.php structure', function () { | ||
| $source = file_get_contents(realpath(__DIR__ . '/../../setup.php')); | ||
|
|
||
| it('defines plugin_audit_install function', function () use ($source) { | ||
| expect($source)->toContain('function plugin_audit_install'); | ||
| }); | ||
|
|
||
| it('defines plugin_audit_version function', function () use ($source) { | ||
| expect($source)->toContain('function plugin_audit_version'); | ||
| }); | ||
|
|
||
| it('defines plugin_audit_uninstall function', function () use ($source) { | ||
| expect($source)->toContain('function plugin_audit_uninstall'); | ||
| }); | ||
|
|
||
| it('returns version array with name key', function () use ($source) { | ||
| expect($source)->toMatch('/[\'\""]name[\'\""]\s*=>/'); | ||
| }); | ||
|
|
||
| it('returns version array with version key', function () use ($source) { | ||
| expect($source)->toMatch('/[\'\""]version[\'\""]\s*=>/'); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,200 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <?php | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| +-------------------------------------------------------------------------+ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | Copyright (C) 2004-2026 The Cacti Group | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| +-------------------------------------------------------------------------+ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | Cacti: The Complete RRDtool-based Graphing Solution | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| +-------------------------------------------------------------------------+ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Test bootstrap: stub Cacti framework functions so plugin code | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * can be loaded in isolation without the full Cacti application. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $GLOBALS['__test_db_calls'] = array(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('db_execute')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function db_execute($sql) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $GLOBALS['__test_db_calls'][] = array('fn' => 'db_execute', 'sql' => $sql, 'params' => array()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('db_execute_prepared')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function db_execute_prepared($sql, $params = array()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $GLOBALS['__test_db_calls'][] = array('fn' => 'db_execute_prepared', 'sql' => $sql, 'params' => $params); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('db_index_exists')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function db_index_exists($table, $index) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('db_column_exists')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function db_column_exists($table, $column) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('api_plugin_db_add_column')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function api_plugin_db_add_column($plugin, $table, $data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('api_plugin_db_table_create')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function api_plugin_db_table_create($plugin, $table, $data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('read_config_option')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function read_config_option($name, $force = false) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('set_config_option')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function set_config_option($name, $value) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('html_escape')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function html_escape($string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return htmlspecialchars($string, ENT_QUOTES | ENT_HTML5, 'UTF-8'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('__')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function __($text, $domain = '') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return $text; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!function_exists('__esc')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function __esc($text, $domain = '') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+109
to
+115
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function __($text, $domain = '') { | |
| return $text; | |
| } | |
| } | |
| if (!function_exists('__esc')) { | |
| function __esc($text, $domain = '') { | |
| function __($text, ...$args) { | |
| return $text; | |
| } | |
| } | |
| if (!function_exists('__esc')) { | |
| function __esc($text, ...$args) { |
Copilot
AI
Apr 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__esc() is stubbed with only 2 parameters, but in Cacti it is typically used similarly to __() (including pluralization/count variants). Keeping the stub signature aligned (e.g., accept optional count/domain or be variadic) will prevent future tests from failing due to argument count errors when plugin code is loaded.
| function __($text, $domain = '') { | |
| return $text; | |
| } | |
| } | |
| if (!function_exists('__esc')) { | |
| function __esc($text, $domain = '') { | |
| function __($text, ...$args) { | |
| return $text; | |
| } | |
| } | |
| if (!function_exists('__esc')) { | |
| function __esc($text, ...$args) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
realpath()can return false; passing that directly tofile_get_contents()will emit warnings and$sourcemay becomefalse, which then causes confusing failures in later expectations. Consider asserting that the resolved path/content is not false (and failing the test with a clear message) before running thetoContain/toMatchchecks.