Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions lib/mactrack_functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ function build_InterfacesTable(&$device, &$ifIndexes, $getLinkPorts = false, $ge
}

// required only for interfaces table
$db_data = db_fetch_assoc("SELECT * FROM mac_track_interfaces WHERE device_id='" . $device['device_id'] . "' ORDER BY ifIndex");
$db_data = db_fetch_assoc_prepared('SELECT * FROM mac_track_interfaces WHERE device_id = ? ORDER BY ifIndex', [$device['device_id']]);

if (cacti_sizeof($db_data)) {
foreach ($db_data as $interface) {
Expand Down Expand Up @@ -3135,7 +3135,7 @@ function mactrack_rescan($web = false) {

// create the command script
$command_string = $config['base_path'] . '/plugins/mactrack/mactrack_scanner.php';
$extra_args = ' -id=' . $dbinfo['device_id'] . ($web ? ' --web' : '');
$extra_args = ' -id=' . cacti_escapeshellarg($dbinfo['device_id']) . ($web ? ' --web' : '');

// print out the type, and device_id
$data['device_id'] = $device_id;
Expand All @@ -3146,9 +3146,14 @@ function mactrack_rescan($web = false) {

// execute the command, and show the results
$command = cacti_escapeshellcmd(read_config_option('path_php_binary')) . ' -q ' . cacti_escapeshellarg($command_string) . $extra_args;
passthru($command);
passthru($command, $exit_code);

$data['content'] = ob_get_clean();

if ($exit_code !== 0) {
$data['error'] = 'rescan process exited with code ' . intval($exit_code);
$data['content'] .= '<p class="textError">' . html_escape('Subprocess error: exit code ' . intval($exit_code)) . '</p>';
}
Comment thread
somethingwithproof marked this conversation as resolved.
}
}

Expand All @@ -3158,7 +3163,9 @@ function mactrack_rescan($web = false) {
}

function mactrack_site_scan($web = false) {
global $config, $web;
global $config;

get_filter_request_var('site_id');

$site_id = get_filter_request_var('site_id');

Expand All @@ -3175,7 +3182,7 @@ function mactrack_site_scan($web = false) {

// create the command script
$command_string = $config['base_path'] . '/plugins/mactrack/poller_mactrack.php';
$extra_args = ' --web -sid=' . $dbinfo['site_id'];
$extra_args = ' -sid=' . cacti_escapeshellarg($dbinfo['site_id']) . ($web ? ' --web' : '');

// print out the type, and device_id
$data['site_id'] = $site_id;
Expand All @@ -3185,9 +3192,14 @@ function mactrack_site_scan($web = false) {

// execute the command, and show the results
$command = cacti_escapeshellcmd(read_config_option('path_php_binary')) . ' -q ' . cacti_escapeshellarg($command_string) . $extra_args;
passthru($command);
passthru($command, $exit_code);

$data['content'] = ob_get_clean();

if ($exit_code !== 0) {
$data['error'] = 'site_scan process exited with code ' . intval($exit_code);
$data['content'] .= '<p class="textError">' . html_escape('Subprocess error: exit code ' . intval($exit_code)) . '</p>';
}
}

header('Content-Type: application/json; charset=utf-8');
Expand Down
4 changes: 2 additions & 2 deletions mactrack_actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ function sync_mactrack_to_cacti($mt_device) {
$mt_device['snmp_engine_id'] ??= '';

// fetch current data for cacti device
$cacti_device = db_fetch_row('SELECT * FROM host WHERE id=' . $mt_device['host_id']);
$cacti_device = db_fetch_row_prepared('SELECT * FROM host WHERE id = ?', [$mt_device['host_id']]);

if (cacti_sizeof($cacti_device)) {
// update cacti device
Expand Down Expand Up @@ -176,7 +176,7 @@ function sync_cacti_to_mactrack($device) {
if ((read_config_option('mt_update_policy', true) == 2) && ($device['id'] > 0)) {
// $devices holds the whole row from host table
// now fetch the related device from mac_track_devices, if any
$mt_device = db_fetch_row('SELECT * from mac_track_devices WHERE host_id=' . $device['id']);
$mt_device = db_fetch_row_prepared('SELECT * FROM mac_track_devices WHERE host_id = ?', [$device['id']]);

if (is_array($mt_device) && $mt_device) {
$mt_device['snmp_engine_id'] ??= '';
Expand Down
4 changes: 2 additions & 2 deletions mactrack_devices.php
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ function form_mactrack_actions() {
FROM mac_track_devices
WHERE device_id = ?',
[$matches[1]]);
$device_list .= '<li>' . $device_info['device_name'] . ' (' . $device_info['hostname'] . ')</li>';
$device_list .= '<li>' . html_escape($device_info['device_name']) . ' (' . html_escape($device_info['hostname']) . ')</li>';
$device_array[$i] = $matches[1];
$i++;
}
Expand Down Expand Up @@ -886,7 +886,7 @@ function mactrack_device_edit() {
<table class='cactiTable'>
<tr>
<td class='textInfo' colspan='2'>
<?php print $device['device_name']; ?> (<?php print $device['hostname']; ?>)
<?php print html_escape($device['device_name']); ?> (<?php print html_escape($device['hostname']); ?>)
</td>
</tr>
<tr>
Expand Down
6 changes: 4 additions & 2 deletions mactrack_view_arp.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,14 @@ function mactrack_view_ips_validate_request_vars() {
'options' => ['options' => 'sanitize_search_string']
],
'ip_filter' => [
'filter' => FILTER_DEFAULT,
'filter' => FILTER_CALLBACK,
'default' => '',
'options' => ['options' => 'sanitize_search_string']
],
'mac_filter' => [
'filter' => FILTER_DEFAULT,
'filter' => FILTER_CALLBACK,
'default' => '',
'options' => ['options' => 'sanitize_search_string']
],
'sort_column' => [
'filter' => FILTER_CALLBACK,
Expand Down
9 changes: 6 additions & 3 deletions mactrack_view_dot1x.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,19 @@ function mactrack_view_dot1x_validate_request_vars() {
'options' => ['options' => 'sanitize_search_string']
],
'ip_filter' => [
'filter' => FILTER_DEFAULT,
'filter' => FILTER_CALLBACK,
'default' => '',
'options' => ['options' => 'sanitize_search_string']
],
'mac_filter' => [
'filter' => FILTER_DEFAULT,
'filter' => FILTER_CALLBACK,
'default' => '',
'options' => ['options' => 'sanitize_search_string']
],
'port_name_filter' => [
'filter' => FILTER_DEFAULT,
'filter' => FILTER_CALLBACK,
'default' => '',
'options' => ['options' => 'sanitize_search_string']
],
'scan_date' => [
'filter' => FILTER_CALLBACK,
Expand Down
9 changes: 6 additions & 3 deletions mactrack_view_macs.php
Original file line number Diff line number Diff line change
Expand Up @@ -400,16 +400,19 @@ function mactrack_view_macs_validate_request_vars() {
'options' => ['options' => 'sanitize_search_string']
],
'ip_filter' => [
'filter' => FILTER_DEFAULT,
'filter' => FILTER_CALLBACK,
'default' => '',
'options' => ['options' => 'sanitize_search_string']
],
'mac_filter' => [
'filter' => FILTER_DEFAULT,
'filter' => FILTER_CALLBACK,
'default' => '',
'options' => ['options' => 'sanitize_search_string']
],
'port_name_filter' => [
'filter' => FILTER_DEFAULT,
'filter' => FILTER_CALLBACK,
'default' => '',
'options' => ['options' => 'sanitize_search_string']
],
'scan_date' => [
'filter' => FILTER_CALLBACK,
Expand Down
6 changes: 6 additions & 0 deletions tests/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDtool-based Graphing Solution |
+-------------------------------------------------------------------------+
*/

// Pest configuration file.

require_once __DIR__ . '/bootstrap.php';

uses()->group('mactrack');
108 changes: 108 additions & 0 deletions tests/Security/Php74CompatibilityTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?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 mactrack', function () {
$files = [
'mactrack_devices.php',
'mactrack_device_types.php',
'mactrack_interfaces.php',
'mactrack_sites.php',
'mactrack_snmp.php',
'mactrack_utilities.php',
'mactrack_view_arp.php',
'mactrack_view_macs.php',
'mactrack_view_sites.php',
'setup.php',
];

it('does not use str_contains (PHP 8.0)', function () use ($files) {
foreach ($files as $relativeFile) {
$path = realpath(__DIR__ . '/../../' . $relativeFile);

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

$contents = file_get_contents($path);

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

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);

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

$contents = file_get_contents($path);

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

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);

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

$contents = file_get_contents($path);

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

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);

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

$contents = file_get_contents($path);

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

expect(preg_match('/\?->/', $contents))->toBe(0,
"{$relativeFile} uses nullsafe operator which requires PHP 8.0"
);
}
});
});
66 changes: 66 additions & 0 deletions tests/Security/PreparedStatementConsistencyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?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 mactrack', function () {
it('uses prepared DB helpers in all plugin files', function () {
$targetFiles = [
'mactrack_devices.php',
'mactrack_device_types.php',
'mactrack_interfaces.php',
'mactrack_sites.php',
'mactrack_snmp.php',
'mactrack_utilities.php',
'mactrack_view_arp.php',
'mactrack_view_macs.php',
'mactrack_view_sites.php',
'setup.php',
];

$rawPattern = '/\bdb_(?:execute|fetch_row|fetch_assoc|fetch_cell)\s*\(/';
$preparedPattern = '/\bdb_(?:execute|fetch_row|fetch_assoc|fetch_cell)_prepared\s*\(/';

foreach ($targetFiles as $relativeFile) {
$path = realpath(__DIR__ . '/../../' . $relativeFile);

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

$contents = file_get_contents($path);

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

$lines = explode("\n", $contents);
$rawCallsOutsideComments = 0;

foreach ($lines as $line) {
$trimmed = ltrim($line);

if (strpos($trimmed, '//') === 0 || strpos($trimmed, '*') === 0 || strpos($trimmed, '#') === 0) {
continue;
}

if (preg_match($rawPattern, $line) && !preg_match($preparedPattern, $line)) {
$rawCallsOutsideComments++;
}
}

expect($rawCallsOutsideComments)->toBe(0,
"File {$relativeFile} contains raw (unprepared) DB calls"
);
}
});
});
34 changes: 34 additions & 0 deletions tests/Security/SetupStructureTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?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('mactrack setup.php structure', function () {
$source = file_get_contents(realpath(__DIR__ . '/../../setup.php'));

it('defines plugin_mactrack_install function', function () use ($source) {
expect($source)->toContain('function plugin_mactrack_install');
});

it('defines plugin_mactrack_version function', function () use ($source) {
expect($source)->toContain('function plugin_mactrack_version');
});

it('defines plugin_mactrack_uninstall function', function () use ($source) {
expect($source)->toContain('function plugin_mactrack_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*=>/');
});
});
Loading
Loading