Skip to content

Commit b82b78f

Browse files
test: add Pest v1 security test infrastructure
1 parent ef8811a commit b82b78f

5 files changed

Lines changed: 410 additions & 0 deletions

File tree

tests/Pest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
/*
3+
+-------------------------------------------------------------------------+
4+
| Copyright (C) 2004-2026 The Cacti Group |
5+
+-------------------------------------------------------------------------+
6+
| Cacti: The Complete RRDtool-based Graphing Solution |
7+
+-------------------------------------------------------------------------+
8+
*/
9+
10+
/*
11+
* Pest configuration file.
12+
*/
13+
14+
require_once __DIR__ . '/bootstrap.php';
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
/*
3+
+-------------------------------------------------------------------------+
4+
| Copyright (C) 2004-2026 The Cacti Group |
5+
+-------------------------------------------------------------------------+
6+
| Cacti: The Complete RRDtool-based Graphing Solution |
7+
+-------------------------------------------------------------------------+
8+
*/
9+
10+
/*
11+
* Verify plugin source files do not use PHP 8.0+ syntax.
12+
* Cacti 1.2.x plugins must remain compatible with PHP 7.4.
13+
*/
14+
15+
describe('PHP 7.4 compatibility in audit', function () {
16+
$files = array(
17+
'audit.php',
18+
'audit_functions.php',
19+
'setup.php',
20+
);
21+
22+
it('does not use str_contains (PHP 8.0)', function () use ($files) {
23+
foreach ($files as $relativeFile) {
24+
$path = realpath(__DIR__ . '/../../' . $relativeFile);
25+
26+
if ($path === false) {
27+
continue;
28+
}
29+
30+
$contents = file_get_contents($path);
31+
32+
if ($contents === false) {
33+
continue;
34+
}
35+
36+
expect(preg_match('/\bstr_contains\s*\(/', $contents))->toBe(0,
37+
"{$relativeFile} uses str_contains() which requires PHP 8.0"
38+
);
39+
}
40+
});
41+
42+
it('does not use str_starts_with (PHP 8.0)', function () use ($files) {
43+
foreach ($files as $relativeFile) {
44+
$path = realpath(__DIR__ . '/../../' . $relativeFile);
45+
46+
if ($path === false) {
47+
continue;
48+
}
49+
50+
$contents = file_get_contents($path);
51+
52+
if ($contents === false) {
53+
continue;
54+
}
55+
56+
expect(preg_match('/\bstr_starts_with\s*\(/', $contents))->toBe(0,
57+
"{$relativeFile} uses str_starts_with() which requires PHP 8.0"
58+
);
59+
}
60+
});
61+
62+
it('does not use str_ends_with (PHP 8.0)', function () use ($files) {
63+
foreach ($files as $relativeFile) {
64+
$path = realpath(__DIR__ . '/../../' . $relativeFile);
65+
66+
if ($path === false) {
67+
continue;
68+
}
69+
70+
$contents = file_get_contents($path);
71+
72+
if ($contents === false) {
73+
continue;
74+
}
75+
76+
expect(preg_match('/\bstr_ends_with\s*\(/', $contents))->toBe(0,
77+
"{$relativeFile} uses str_ends_with() which requires PHP 8.0"
78+
);
79+
}
80+
});
81+
82+
it('does not use nullsafe operator (PHP 8.0)', function () use ($files) {
83+
foreach ($files as $relativeFile) {
84+
$path = realpath(__DIR__ . '/../../' . $relativeFile);
85+
86+
if ($path === false) {
87+
continue;
88+
}
89+
90+
$contents = file_get_contents($path);
91+
92+
if ($contents === false) {
93+
continue;
94+
}
95+
96+
expect(preg_match('/\?->/', $contents))->toBe(0,
97+
"{$relativeFile} uses nullsafe operator which requires PHP 8.0"
98+
);
99+
}
100+
});
101+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/*
3+
+-------------------------------------------------------------------------+
4+
| Copyright (C) 2004-2026 The Cacti Group |
5+
+-------------------------------------------------------------------------+
6+
| Cacti: The Complete RRDtool-based Graphing Solution |
7+
+-------------------------------------------------------------------------+
8+
*/
9+
10+
/*
11+
* Verify migrated files use prepared DB helpers exclusively.
12+
* Catches regressions where raw db_execute/db_fetch_* calls creep back in.
13+
*/
14+
15+
describe('prepared statement consistency in audit', function () {
16+
it('uses prepared DB helpers in all plugin files', function () {
17+
$targetFiles = array(
18+
'audit.php',
19+
'audit_functions.php',
20+
'setup.php',
21+
);
22+
23+
$rawPattern = '/\bdb_(?:execute|fetch_row|fetch_assoc|fetch_cell)\s*\(/';
24+
$preparedPattern = '/\bdb_(?:execute|fetch_row|fetch_assoc|fetch_cell)_prepared\s*\(/';
25+
26+
foreach ($targetFiles as $relativeFile) {
27+
$path = realpath(__DIR__ . '/../../' . $relativeFile);
28+
29+
if ($path === false) {
30+
continue;
31+
}
32+
33+
$contents = file_get_contents($path);
34+
35+
if ($contents === false) {
36+
continue;
37+
}
38+
39+
$lines = explode("\n", $contents);
40+
$rawCallsOutsideComments = 0;
41+
42+
foreach ($lines as $line) {
43+
$trimmed = ltrim($line);
44+
45+
if (strpos($trimmed, '//') === 0 || strpos($trimmed, '*') === 0 || strpos($trimmed, '#') === 0) {
46+
continue;
47+
}
48+
49+
if (preg_match($rawPattern, $line) && !preg_match($preparedPattern, $line)) {
50+
$rawCallsOutsideComments++;
51+
}
52+
}
53+
54+
expect($rawCallsOutsideComments)->toBe(0,
55+
"File {$relativeFile} contains raw (unprepared) DB calls"
56+
);
57+
}
58+
});
59+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
/*
3+
+-------------------------------------------------------------------------+
4+
| Copyright (C) 2004-2026 The Cacti Group |
5+
+-------------------------------------------------------------------------+
6+
| Cacti: The Complete RRDtool-based Graphing Solution |
7+
+-------------------------------------------------------------------------+
8+
*/
9+
10+
/*
11+
* Verify setup.php defines required plugin hooks and info function.
12+
*/
13+
14+
describe('audit setup.php structure', function () {
15+
$source = file_get_contents(realpath(__DIR__ . '/../../setup.php'));
16+
17+
it('defines plugin_audit_install function', function () use ($source) {
18+
expect($source)->toContain('function plugin_audit_install');
19+
});
20+
21+
it('defines plugin_audit_version function', function () use ($source) {
22+
expect($source)->toContain('function plugin_audit_version');
23+
});
24+
25+
it('defines plugin_audit_uninstall function', function () use ($source) {
26+
expect($source)->toContain('function plugin_audit_uninstall');
27+
});
28+
29+
it('returns version array with name key', function () use ($source) {
30+
expect($source)->toMatch('/[\'\""]name[\'\""]\s*=>/');
31+
});
32+
33+
it('returns version array with version key', function () use ($source) {
34+
expect($source)->toMatch('/[\'\""]version[\'\""]\s*=>/');
35+
});
36+
});

0 commit comments

Comments
 (0)