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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
* issue: Decode MAC/IP OIDs as hex in the Extreme scanner by correcting a misplaced xform_stripped_oid() argument
* issue: Fix an undefined $ifindex reference (should be $ifIndex) in the Nortel Bay scanner
* issue: Fix a misplaced parenthesis in the running-process stats parser
* issue: Fix "401 Permission Denied" on the Dashboard after upgrading: the Dashboard page was added to the Mactrack Viewer realm without a version bump, so upgraded installs never mapped it to a realm. The upgrade check now also compares the registered realm file lists against the code (`mactrack_realms_are_stale()`) and re-registers them, which carries existing Viewer grants over to the Dashboard. The repair only registers the realms (no schema upgrade) and never grants a newly created realm to the requesting user
* issue: Fix the Graphs tab fatal error "Call to undefined function graph_view_has_untemplated_graphs()" on Cacti 1.2.x releases whose graph preview filter calls a helper defined in graph_view.php; the plugin now supplies a guarded polyfill, which is a no-op where core already defines it
* issue: Make the MacTrack tab script repoint only the MacTrack main tab rather than whichever main tab is selected, so it can never retarget the Console tab at a MacTrack page
* security: Move every inline `onClick`/`onChange` handler (filter selects, Return/Cancel buttons, the device rescan icon, the utilities refresh controls) into `$(function() {})` handlers, using delegated handlers in `js/mactrack.js` for markup rendered by shared or AJAX-refreshed code, so pages work under a Content-Security-Policy that blocks inline event-handler attributes; all inline `<script>` tags already carry the version-safe nonce, and a regression test now enforces both
* security: Add a version-safe CSP nonce (`plugin_mactrack_csp_nonce()`) to every inline `<script>` tag, and emit external JavaScript includes through Cacti's `get_md5_include_js()` helper so they carry the request nonce automatically; pages stay compatible with Cacti's Content-Security-Policy nonce enforcement while falling back cleanly on older Cacti releases that lack the `CactiSecureHeaders` class
* issue#349: Keep punctuation in the port name filter
Expand Down
24 changes: 23 additions & 1 deletion lib/mactrack_functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ function plugin_mactrack_csp_nonce(): string {
return '';
}

if (!function_exists('graph_view_has_untemplated_graphs')) {
/**
* Polyfill for the helper Cacti 1.2.x defines in graph_view.php and calls
* from html_graph_preview_filter() (lib/html_graph.php). Plugin pages such
* as the MacTrack Graphs tab render that filter without loading
* graph_view.php, so the call would otherwise be a fatal "undefined
* function". Declared only when core has not already defined it.
*
* @param string $templates_where Device restriction for the current filter.
*
* @return bool True when an allowed graph without a Graph Template exists.
*/
function graph_view_has_untemplated_graphs(string $templates_where = ''): bool {
$sql_where = '(gl.graph_template_id = 0)' . ($templates_where != '' ? ' AND ' . $templates_where : '');
$total_rows = 0;

get_allowed_graphs($sql_where, '', '1', $total_rows);

return ($total_rows > 0);
}
}

// register these scanning functions
global $mactrack_scanning_functions;

Expand Down Expand Up @@ -4364,7 +4386,7 @@ function mactrack_tabs(): void {

print "</ul></nav><script type='text/javascript' " . plugin_mactrack_csp_nonce() . ">\n";

print "$(function() { if (pageName.indexOf('mactrack_view') >= 0) { $('.maintabs a.selected').attr('href', urlPath+'plugins/mactrack/'+pageName); } });";
print "$(function() { if (pageName.indexOf('mactrack_view') >= 0) { $('.maintabs a[href*=\"plugins/mactrack/mactrack_view_\"]').attr('href', urlPath+'plugins/mactrack/'+pageName); } });";
print '</script></div>';
}

Expand Down
82 changes: 78 additions & 4 deletions setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ function plugin_mactrack_install($operator_initiated = true): bool {
// device hook: Device Management Action dropdown selected: execute list of device
api_plugin_register_hook('mactrack', 'device_action_execute', 'mactrack_device_action_execute', 'mactrack_actions.php');

// Register our realms
api_plugin_register_realm('mactrack', 'mactrack_view_dashboard.php,mactrack_view_ips.php,mactrack_view_arp.php,mactrack_view_macs.php,mactrack_view_dot1x.php,mactrack_view_sites.php,mactrack_view_devices.php,mactrack_view_interfaces.php,mactrack_view_graphs.php,mactrack_ajax.php', 'Mactrack Viewer', 1);
api_plugin_register_realm('mactrack', 'mactrack_ajax_admin.php,mactrack_devices.php,mactrack_snmp.php,mactrack_sites.php,mactrack_device_types.php,mactrack_utilities.php,mactrack_macwatch.php,mactrack_macauth.php,mactrack_vendormacs.php', 'Mactrack Administrator', 1);
mactrack_install_realms(true);

$site_ready = mactrack_setup_table_new($operator_initiated);

Expand Down Expand Up @@ -131,6 +129,73 @@ function plugin_mactrack_upgrade(): bool {
return false;
}

/**
* Permission realms this plugin registers, keyed by realm display name,
* each with its comma-delimited list of protected files. Shared by the
* installer and by mactrack_realms_are_stale() so a page added to a realm
* (for example the Dashboard) reaches upgraded installs.
*
* @return array<string, string> Realm display name => comma-delimited files.
*/
function mactrack_realm_definitions(): array {
return [
'Mactrack Viewer' => 'mactrack_view_dashboard.php,mactrack_view_ips.php,mactrack_view_arp.php,mactrack_view_macs.php,mactrack_view_dot1x.php,mactrack_view_sites.php,mactrack_view_devices.php,mactrack_view_interfaces.php,mactrack_view_graphs.php,mactrack_ajax.php',
'Mactrack Administrator' => 'mactrack_ajax_admin.php,mactrack_devices.php,mactrack_snmp.php,mactrack_sites.php,mactrack_device_types.php,mactrack_utilities.php,mactrack_macwatch.php,mactrack_macauth.php,mactrack_vendormacs.php',
];
}

/**
* Registers this plugin's permission realms with Cacti.
*
* Cacti's api_plugin_register_realm() maps a realm that has to be created to
* the configured admin and to the user making the request when its fourth
* argument is true. Repairs that run from an arbitrary Console page pass
* false so that a missing realm is never granted to whoever triggered them.
*
* The name must keep "install": Cacti only honors api_plugin_register_realm()
* when the calling function's name contains install, upgrade or setup.
*
* @param bool $grant_access Whether newly created realms are granted to the
* admin and current session user.
*
* @return void
*/
function mactrack_install_realms(bool $grant_access): void {
foreach (mactrack_realm_definitions() as $display => $files) {
api_plugin_register_realm('mactrack', $files, $display, $grant_access);
}
}

/**
* Reports whether any file this plugin protects is missing from the realms
* stored in plugin_realms. The realm file lists are only written when the
* plugin is installed or its version changes, so a file added to a realm
* without a version bump stays unmapped on upgraded installs and Cacti
* denies it with "401 Permission Denied".
*
* @return bool True when at least one expected file is not registered.
*/
function mactrack_realms_are_stale(): bool {
$rows = db_fetch_assoc_prepared('SELECT file FROM plugin_realms WHERE plugin = ?', ['mactrack']);
$registered = [];

foreach ((array) $rows as $row) {
foreach (explode(',', (string) $row['file']) as $file) {
$registered[trim($file)] = true;
}
}

foreach (mactrack_realm_definitions() as $files) {
foreach (explode(',', $files) as $file) {
if (!isset($registered[$file])) {
return true;
}
}
}

return false;
}

/**
* Detects a version mismatch between the running plugin code and the
* version recorded in plugin_config, and when found (and only on a
Expand Down Expand Up @@ -163,7 +228,16 @@ function mactrack_check_upgrade(): void {

$old = (array) db_fetch_row("SELECT * FROM plugin_config WHERE directory='mactrack'");

if (!cacti_sizeof($old) || $current != $old['version']) {
$version_changed = !cacti_sizeof($old) || $current != $old['version'];

// A realm file list can drift from the code without a version bump (the
// Dashboard was added that way). Repair only the realms in that case; the
// full upgrade below rebuilds indexes and rewrites large tables.
if (!$version_changed && in_array((int) $old['status'], [1, 4], true) && mactrack_realms_are_stale()) {
mactrack_install_realms(false);
}

if ($version_changed) {
// if the plugin is installed and/or active
if (!cacti_sizeof($old) || $old['status'] == 1 || $old['status'] == 4) {
// re-register the hooks
Expand Down
2 changes: 2 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ protected function setUp(): void {
$GLOBALS['__test_config_options'] = array();
$GLOBALS['__test_next_returns'] = array();
$GLOBALS['__test_matched_returns'] = array();
$GLOBALS['__test_graph_calls'] = array();
$GLOBALS['__test_graph_total'] = 0;
}

/**
Expand Down
Loading
Loading