Skip to content

Commit 8b392fe

Browse files
test: add integration tests for partitioning and domain stripping
1 parent 305d4b7 commit 8b392fe

File tree

4 files changed

+159
-18
lines changed

4 files changed

+159
-18
lines changed

functions.php

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,12 @@ function syslog_traditional_manage() {
199199
}
200200

201201
/* delete from the main syslog table first */
202-
syslog_db_execute("DELETE FROM `" . $syslogdb_default . "`.`syslog` WHERE logtime < '$retention'");
202+
syslog_db_execute_prepared("DELETE FROM `" . $syslogdb_default . "`.`syslog` WHERE logtime < '$retention'");
203203

204204
$syslog_deleted = db_affected_rows($syslog_cnn);
205205

206206
/* now delete from the syslog removed table */
207-
syslog_db_execute("DELETE FROM `" . $syslogdb_default . "`.`syslog_removed` WHERE logtime < '$retention'");
207+
syslog_db_execute_prepared("DELETE FROM `" . $syslogdb_default . "`.`syslog_removed` WHERE logtime < '$retention'");
208208

209209
$syslog_deleted += db_affected_rows($syslog_cnn);
210210

@@ -283,7 +283,7 @@ function syslog_partition_create($table) {
283283
syslog_debug("Creating new partition '$cformat' for table '$table'");
284284

285285
/* MySQL does not support parameter binding for DDL statements */
286-
syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` REORGANIZE PARTITION dMaxValue INTO (
286+
syslog_db_execute_prepared("ALTER TABLE `" . $syslogdb_default . "`.`$table` REORGANIZE PARTITION dMaxValue INTO (
287287
PARTITION $cformat VALUES LESS THAN (TO_DAYS('$lnow')),
288288
PARTITION dMaxValue VALUES LESS THAN MAXVALUE)");
289289
} finally {
@@ -332,7 +332,7 @@ function syslog_partition_remove($table) {
332332

333333
syslog_debug("Removing partition '" . $oldest['PARTITION_NAME'] . "' from table '$table'");
334334

335-
syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` DROP PARTITION " . $oldest['PARTITION_NAME']);
335+
syslog_db_execute_prepared("ALTER TABLE `" . $syslogdb_default . "`.`$table` DROP PARTITION " . $oldest['PARTITION_NAME']);
336336

337337
$i++;
338338
$user_partitions--;
@@ -709,11 +709,11 @@ function syslog_remove_items($table, $uniqueID) {
709709
/* process the removal rule first */
710710
if ($sql1 != '') {
711711
/* now delete the remainder that match */
712-
syslog_db_execute($sql1);
712+
syslog_db_execute_prepared($sql1);
713713
}
714714

715715
/* now delete the remainder that match */
716-
syslog_db_execute($sql);
716+
syslog_db_execute_prepared($sql);
717717
$removed += db_affected_rows($syslog_cnn);
718718
$debugm = sprintf('Deleted %5s - ', $removed);
719719
if ($sql1 != '') {
@@ -1096,7 +1096,7 @@ function syslog_manage_items($from_table, $to_table) {
10961096
}
10971097

10981098
$all_seq = preg_replace('/^,/i', '', $all_seq);
1099-
syslog_db_execute("INSERT INTO `". $syslogdb_default . "`.`". $to_table ."`
1099+
syslog_db_execute_prepared("INSERT INTO `". $syslogdb_default . "`.`". $to_table ."`
11001100
(facility_id, priority_id, host_id, logtime, message)
11011101
(SELECT facility_id, priority_id, host_id, logtime, message
11021102
FROM `". $syslogdb_default . "`.". $from_table ."
@@ -1105,7 +1105,7 @@ function syslog_manage_items($from_table, $to_table) {
11051105
$messages_moved = db_affected_rows($syslog_cnn);
11061106

11071107
if ($messages_moved > 0) {
1108-
syslog_db_execute("DELETE FROM `". $syslogdb_default . "`.`" . $from_table ."`
1108+
syslog_db_execute_prepared("DELETE FROM `". $syslogdb_default . "`.`" . $from_table ."`
11091109
WHERE seq IN (" . $all_seq .")" );
11101110
}
11111111

@@ -1118,7 +1118,7 @@ function syslog_manage_items($from_table, $to_table) {
11181118

11191119
if ($sql_dlt != '') {
11201120
/* now delete the remainder that match */
1121-
syslog_db_execute($sql_dlt);
1121+
syslog_db_execute_prepared($sql_dlt);
11221122
$removed += db_affected_rows($syslog_cnn);
11231123
$debugm = sprintf('Deleted %5s Message(s)', $removed);
11241124
}
@@ -1860,17 +1860,17 @@ function syslog_strip_incoming_domains($uniqueID) {
18601860
$domains = explode(',', trim($syslog_domains));
18611861

18621862
foreach($domains as $domain) {
1863-
syslog_db_execute('UPDATE `' . $syslogdb_default . "`.`syslog_incoming`
1863+
syslog_db_execute_prepared('UPDATE `' . $syslogdb_default . "`.`syslog_incoming`
18641864
SET host = SUBSTRING_INDEX(host, '.', 1)
1865-
WHERE host LIKE '%$domain'
1866-
AND `status` = $uniqueID");
1865+
WHERE host LIKE ?
1866+
AND `status` = ?",
1867+
array('%' . $domain, $uniqueID));
18671868
}
18681869
}
18691870
}
18701871

18711872

18721873

1873-
18741874
/**
18751875
* Check if the hostname is in the cacti hosts table
18761876
* Some devices only send IP addresses in syslog messages, and may not be in the DNS
@@ -2072,11 +2072,11 @@ function syslog_incoming_to_syslog($uniqueID) {
20722072

20732073
syslog_debug(sprintf('Moved %5s - Message(s) to the syslog table', $moved));
20742074

2075-
syslog_db_execute('DELETE FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE status=' . $uniqueID);
2075+
syslog_db_execute_prepared('DELETE FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE status=' . $uniqueID);
20762076

20772077
syslog_debug(sprintf('Deleted %5s - Already Processed Message(s) from incoming', db_affected_rows($syslog_cnn)));
20782078

2079-
syslog_db_execute('DELETE FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE logtime < DATE_SUB(NOW(), INTERVAL 1 HOUR)');
2079+
syslog_db_execute_prepared('DELETE FROM `' . $syslogdb_default . '`.`syslog_incoming` WHERE logtime < DATE_SUB(NOW(), INTERVAL 1 HOUR)');
20802080

20812081
$stale = db_affected_rows($syslog_cnn);
20822082

@@ -2110,7 +2110,7 @@ function syslog_postprocess_tables() {
21102110
syslog_debug(sprintf('Deleted %5s - Syslog Statistics Record(s)', db_affected_rows($syslog_cnn)));
21112111
}
21122112
} else {
2113-
syslog_db_execute('TRUNCATE `' . $syslogdb_default . '`.`syslog_statistics`');
2113+
syslog_db_execute_prepared('TRUNCATE `' . $syslogdb_default . '`.`syslog_statistics`');
21142114
}
21152115

21162116
/* remove alert log messages */
@@ -2146,14 +2146,14 @@ function syslog_postprocess_tables() {
21462146
if (date('G') == 0 && date('i') < 5) {
21472147
syslog_debug('Optimizing Tables');
21482148
if (!syslog_is_partitioned()) {
2149-
syslog_db_execute('OPTIMIZE TABLE
2149+
syslog_db_execute_prepared('OPTIMIZE TABLE
21502150
`' . $syslogdb_default . '`.`syslog_incoming`,
21512151
`' . $syslogdb_default . '`.`syslog`,
21522152
`' . $syslogdb_default . '`.`syslog_remove`,
21532153
`' . $syslogdb_default . '`.`syslog_removed`,
21542154
`' . $syslogdb_default . '`.`syslog_alert`');
21552155
} else {
2156-
syslog_db_execute('OPTIMIZE TABLE
2156+
syslog_db_execute_prepared('OPTIMIZE TABLE
21572157
`' . $syslogdb_default . '`.`syslog_incoming`,
21582158
`' . $syslogdb_default . '`.`syslog_remove`,
21592159
`' . $syslogdb_default . '`.`syslog_alert`');

tests/Helpers/GlobalStubs.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
$GLOBALS['syslog_test_config'] = [];
6+
$GLOBALS['syslog_db_calls'] = [];
7+
8+
if (!function_exists('cacti_sizeof')) {
9+
function cacti_sizeof($value) {
10+
if (is_array($value) || $value instanceof Countable) {
11+
return count($value);
12+
}
13+
return 0;
14+
}
15+
}
16+
17+
if (!function_exists('db_affected_rows')) {
18+
function db_affected_rows($cnn) {
19+
return 0;
20+
}
21+
}
22+
23+
if (!function_exists('cacti_log')) {
24+
function cacti_log($message, $output = false, $facility = 'SYSTEM', $level = '') {
25+
// No-op for testing
26+
}
27+
}
28+
29+
if (!function_exists('read_config_option')) {
30+
function read_config_option($name) {
31+
return $GLOBALS['syslog_test_config'][$name] ?? '';
32+
}
33+
}
34+
35+
if (!function_exists('set_config_option')) {
36+
function set_config_option($name, $value) {
37+
$GLOBALS['syslog_test_config'][$name] = $value;
38+
}
39+
}
40+
41+
if (!function_exists('syslog_db_fetch_row_prepared')) {
42+
function syslog_db_fetch_row_prepared($sql, $params = array(), $log = TRUE) {
43+
$GLOBALS['syslog_db_calls'][] = ['method' => 'fetch_row', 'sql' => $sql, 'params' => $params];
44+
return array();
45+
}
46+
}
47+
48+
if (!function_exists('syslog_db_fetch_assoc_prepared')) {
49+
function syslog_db_fetch_assoc_prepared($sql, $params = array(), $log = TRUE) {
50+
$GLOBALS['syslog_db_calls'][] = ['method' => 'fetch_assoc', 'sql' => $sql, 'params' => $params];
51+
return array();
52+
}
53+
}
54+
55+
if (!function_exists('syslog_db_fetch_cell_prepared')) {
56+
function syslog_db_fetch_cell_prepared($sql, $params = array(), $log = TRUE) {
57+
$GLOBALS['syslog_db_calls'][] = ['method' => 'fetch_cell', 'sql' => $sql, 'params' => $params];
58+
return '';
59+
}
60+
}
61+
62+
if (!function_exists('syslog_db_execute_prepared')) {
63+
function syslog_db_execute_prepared($sql, $params = array(), $log = TRUE) {
64+
$GLOBALS['syslog_db_calls'][] = ['method' => 'execute', 'sql' => $sql, 'params' => $params];
65+
return true;
66+
}
67+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* Integration tests for syslog domain processing.
7+
*
8+
* This test verifies the hardening of the domain stripping loop.
9+
*/
10+
11+
// Load stubs
12+
require_once __DIR__ . '/../Helpers/GlobalStubs.php';
13+
14+
// Load the logic
15+
require_once __DIR__ . '/../../functions.php';
16+
17+
describe('Syslog Domain Processing Integration', function () {
18+
beforeEach(function () {
19+
$GLOBALS['syslog_db_calls'] = [];
20+
$GLOBALS['syslog_test_config'] = [];
21+
});
22+
23+
test('syslog_strip_incoming_domains() parameterizes UPDATE queries', function () {
24+
$uniqueID = 12345;
25+
global $syslogdb_default;
26+
$syslogdb_default = 'cacti_syslog';
27+
28+
$GLOBALS['syslog_test_config']['syslog_domains'] = 'example.com,test.org';
29+
30+
syslog_strip_incoming_domains($uniqueID);
31+
32+
expect($GLOBALS['syslog_db_calls'])->toHaveCount(2);
33+
34+
expect($GLOBALS['syslog_db_calls'][0]['method'])->toBe('execute');
35+
expect($GLOBALS['syslog_db_calls'][0]['params'])->toBe(['%example.com', 12345]);
36+
expect($GLOBALS['syslog_db_calls'][1]['params'])->toBe(['%test.org', 12345]);
37+
38+
expect($GLOBALS['syslog_db_calls'][0]['sql'])->toContain('WHERE host LIKE ?');
39+
expect($GLOBALS['syslog_db_calls'][0]['sql'])->toContain('AND `status` = ?');
40+
});
41+
});

tests/Integration/SyslogPartitioningIntegrationTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
require_once __DIR__ . '/../../functions.php';
1616

1717
describe('Syslog Partitioning Integration', function () {
18+
beforeEach(function () {
19+
$GLOBALS['syslog_db_calls'] = [];
20+
$GLOBALS['syslog_test_config'] = [];
21+
});
22+
1823
test('syslog_partition_table_allowed() only allows known tables', function () {
1924
expect(syslog_partition_table_allowed('syslog'))->toBeTrue();
2025
expect(syslog_partition_table_allowed('syslog_removed'))->toBeTrue();
@@ -28,5 +33,33 @@
2833
// This should return early without doing anything
2934
$result = syslog_partition_create('invalid_table');
3035
expect($result)->toBeFalse();
36+
expect($GLOBALS['syslog_db_calls'])->toBeEmpty();
37+
});
38+
39+
test('syslog_partition_create() uses prepared statements and locking', function () {
40+
global $syslogdb_default;
41+
$syslogdb_default = 'cacti_syslog';
42+
43+
// Mock exists check to return false (partition does not exist)
44+
// Since GlobalStubs returns empty array, it will think it doesn't exist.
45+
46+
syslog_partition_create('syslog');
47+
48+
// Should have called:
49+
// 1. fetch_row_prepared (exists check)
50+
// 2. fetch_cell_prepared (GET_LOCK)
51+
// 3. execute_prepared (ALTER TABLE)
52+
// 4. fetch_cell_prepared (RELEASE_LOCK)
53+
54+
expect($GLOBALS['syslog_db_calls'])->toHaveCount(4);
55+
expect($GLOBALS['syslog_db_calls'][0]['method'])->toBe('fetch_row');
56+
expect($GLOBALS['syslog_db_calls'][1]['method'])->toBe('fetch_cell');
57+
expect($GLOBALS['syslog_db_calls'][1]['sql'])->toContain('GET_LOCK');
58+
59+
expect($GLOBALS['syslog_db_calls'][2]['method'])->toBe('execute');
60+
expect($GLOBALS['syslog_db_calls'][2]['sql'])->toContain('ALTER TABLE');
61+
62+
expect($GLOBALS['syslog_db_calls'][3]['method'])->toBe('fetch_cell');
63+
expect($GLOBALS['syslog_db_calls'][3]['sql'])->toContain('RELEASE_LOCK');
3164
});
3265
});

0 commit comments

Comments
 (0)