Skip to content

Commit 694fea1

Browse files
test: add integration tests for partitioning and domain stripping
1 parent 78433ef commit 694fea1

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
@@ -174,12 +174,12 @@ function syslog_traditional_manage() {
174174
}
175175

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

179179
$syslog_deleted = db_affected_rows($syslog_cnn);
180180

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

184184
$syslog_deleted += db_affected_rows($syslog_cnn);
185185

@@ -258,7 +258,7 @@ function syslog_partition_create($table) {
258258
syslog_debug("Creating new partition '$cformat' for table '$table'");
259259

260260
/* MySQL does not support parameter binding for DDL statements */
261-
syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` REORGANIZE PARTITION dMaxValue INTO (
261+
syslog_db_execute_prepared("ALTER TABLE `" . $syslogdb_default . "`.`$table` REORGANIZE PARTITION dMaxValue INTO (
262262
PARTITION $cformat VALUES LESS THAN (TO_DAYS('$lnow')),
263263
PARTITION dMaxValue VALUES LESS THAN MAXVALUE)");
264264
} finally {
@@ -307,7 +307,7 @@ function syslog_partition_remove($table) {
307307

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

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

312312
$i++;
313313
$user_partitions--;
@@ -684,11 +684,11 @@ function syslog_remove_items($table, $uniqueID) {
684684
/* process the removal rule first */
685685
if ($sql1 != '') {
686686
/* now delete the remainder that match */
687-
syslog_db_execute($sql1);
687+
syslog_db_execute_prepared($sql1);
688688
}
689689

690690
/* now delete the remainder that match */
691-
syslog_db_execute($sql);
691+
syslog_db_execute_prepared($sql);
692692
$removed += db_affected_rows($syslog_cnn);
693693
$debugm = sprintf('Deleted %5s - ', $removed);
694694
if ($sql1 != '') {
@@ -1071,7 +1071,7 @@ function syslog_manage_items($from_table, $to_table) {
10711071
}
10721072

10731073
$all_seq = preg_replace('/^,/i', '', $all_seq);
1074-
syslog_db_execute("INSERT INTO `". $syslogdb_default . "`.`". $to_table ."`
1074+
syslog_db_execute_prepared("INSERT INTO `". $syslogdb_default . "`.`". $to_table ."`
10751075
(facility_id, priority_id, host_id, logtime, message)
10761076
(SELECT facility_id, priority_id, host_id, logtime, message
10771077
FROM `". $syslogdb_default . "`.". $from_table ."
@@ -1080,7 +1080,7 @@ function syslog_manage_items($from_table, $to_table) {
10801080
$messages_moved = db_affected_rows($syslog_cnn);
10811081

10821082
if ($messages_moved > 0) {
1083-
syslog_db_execute("DELETE FROM `". $syslogdb_default . "`.`" . $from_table ."`
1083+
syslog_db_execute_prepared("DELETE FROM `". $syslogdb_default . "`.`" . $from_table ."`
10841084
WHERE seq IN (" . $all_seq .")" );
10851085
}
10861086

@@ -1093,7 +1093,7 @@ function syslog_manage_items($from_table, $to_table) {
10931093

10941094
if ($sql_dlt != '') {
10951095
/* now delete the remainder that match */
1096-
syslog_db_execute($sql_dlt);
1096+
syslog_db_execute_prepared($sql_dlt);
10971097
$removed += db_affected_rows($syslog_cnn);
10981098
$debugm = sprintf('Deleted %5s Message(s)', $removed);
10991099
}
@@ -1826,17 +1826,17 @@ function syslog_strip_incoming_domains($uniqueID) {
18261826
$domains = explode(',', trim($syslog_domains));
18271827

18281828
foreach($domains as $domain) {
1829-
syslog_db_execute('UPDATE `' . $syslogdb_default . "`.`syslog_incoming`
1829+
syslog_db_execute_prepared('UPDATE `' . $syslogdb_default . "`.`syslog_incoming`
18301830
SET host = SUBSTRING_INDEX(host, '.', 1)
1831-
WHERE host LIKE '%$domain'
1832-
AND `status` = $uniqueID");
1831+
WHERE host LIKE ?
1832+
AND `status` = ?",
1833+
array('%' . $domain, $uniqueID));
18331834
}
18341835
}
18351836
}
18361837

18371838

18381839

1839-
18401840
/**
18411841
* Check if the hostname is in the cacti hosts table
18421842
* Some devices only send IP addresses in syslog messages, and may not be in the DNS
@@ -2038,11 +2038,11 @@ function syslog_incoming_to_syslog($uniqueID) {
20382038

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

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

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

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

20472047
$stale = db_affected_rows($syslog_cnn);
20482048

@@ -2076,7 +2076,7 @@ function syslog_postprocess_tables() {
20762076
syslog_debug(sprintf('Deleted %5s - Syslog Statistics Record(s)', db_affected_rows($syslog_cnn)));
20772077
}
20782078
} else {
2079-
syslog_db_execute('TRUNCATE `' . $syslogdb_default . '`.`syslog_statistics`');
2079+
syslog_db_execute_prepared('TRUNCATE `' . $syslogdb_default . '`.`syslog_statistics`');
20802080
}
20812081

20822082
/* remove alert log messages */
@@ -2112,14 +2112,14 @@ function syslog_postprocess_tables() {
21122112
if (date('G') == 0 && date('i') < 5) {
21132113
syslog_debug('Optimizing Tables');
21142114
if (!syslog_is_partitioned()) {
2115-
syslog_db_execute('OPTIMIZE TABLE
2115+
syslog_db_execute_prepared('OPTIMIZE TABLE
21162116
`' . $syslogdb_default . '`.`syslog_incoming`,
21172117
`' . $syslogdb_default . '`.`syslog`,
21182118
`' . $syslogdb_default . '`.`syslog_remove`,
21192119
`' . $syslogdb_default . '`.`syslog_removed`,
21202120
`' . $syslogdb_default . '`.`syslog_alert`');
21212121
} else {
2122-
syslog_db_execute('OPTIMIZE TABLE
2122+
syslog_db_execute_prepared('OPTIMIZE TABLE
21232123
`' . $syslogdb_default . '`.`syslog_incoming`,
21242124
`' . $syslogdb_default . '`.`syslog_remove`,
21252125
`' . $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)