Files
cacti/include/themes/raspberry/main.js
b2bf39b8dc fix(sqli): sanitize sort_column and enforce sort_direction in ORDER BY clauses (#7162)
* fix: correct grammar and possessive errors in English source strings (batch 2) (#1)

* fix: correct grammar and possessive errors in English source strings (batch 2)

Seven errors found via cacti.pot audit:

- automation_networks.php: "senders name" → "sender\'s name" (line 839)
- automation_networks.php: "senders Email" → "sender\'s Email" (line 848)
- automation_templates.php: "devices sysDescr" → "device\'s sysDescr" (line 1060)
- automation_templates.php: "devices sysName" → "device\'s sysName" (line 1067)
- automation_templates.php: "devices sysOid" → "device\'s sysOid" (line 1074)
- data_templates.php: garbled "it either it value...critical data Data Query" →
  "its value...Data Query" (line 903)
- package_repos.php: "Package Repositorites" → "Package Repositories" (line 436)

All changes are to UI description strings inside __() / __esc() calls.
cacti.pot should be regenerated via build_gettext.sh after merge.

* fix: correct grammar and possessive errors in English source strings (batch 2)

Seven errors found via cacti.pot audit:

- automation_networks.php: "senders name" → "sender\'s name" (line 839)
- automation_networks.php: "senders Email" → "sender\'s Email" (line 848)
- automation_templates.php: "devices sysDescr" → "device\'s sysDescr" (line 1060)
- automation_templates.php: "devices sysName" → "device\'s sysName" (line 1067)
- automation_templates.php: "devices sysOid" → "device\'s sysOid" (line 1074)
- data_templates.php: garbled string → "its value...Data Query" (line 903)
- package_repos.php: "Package Repositorites" → "Package Repositories" (line 436)

All changes are to UI description strings inside __() / __esc() calls.
cacti.pot should be regenerated via build_gettext.sh after merge.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* ci: add dependabot config with npm and github-actions

* ci: add CodeQL (JS/TS, excludes PHP) with workflow_dispatch

* ci: update CodeQL for javascript-typescript, python

* fix(ci): use specific runner labels, fix codeql concurrency

* Improve IPv6 support in RRDtool proxy and ping utilities (#2)

* Improve IPv6 support in RRDtool proxy and ping utilities

- Fix RRDtool proxy to dynamically detect IPv6 addresses and use
  AF_INET6 sockets instead of hardcoded AF_INET (IPv4-only), enabling
  connections to IPv6 RRDtool proxy servers including backup servers
- Strip brackets from IPv6 addresses before passing to socket_connect
- Properly close failed sockets before creating new ones for failover
- Replace fragile str_contains(':') IPv6 detection in ping with
  filter_var(FILTER_FLAG_IPV6) for more robust address validation

https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo

* Fix JS code quality: implicit globals, deprecated APIs, loose equality

- Add var declarations to prevent implicit globals in install.js
  (element, enabled, button, buttonCheck)
- Remove console.log debug output left in production (install.js)
- Replace deprecated jQuery .unbind() with .off() (layout.js)
- Fix "depreciated" typo to "deprecated" in deprecation warnings
- Convert == / != to === / !== for null, boolean, string, typeof,
  and numeric comparisons across install.js and realtime.js

https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo

* Replace deprecated jQuery shorthand event methods in layout.js

Replace .click(), .keyup(), .keydown(), .mousedown(), .mouseenter(),
.mouseleave(), .submit(), .resize() with .on() equivalents. Replace
.focus(), .change() trigger calls with .trigger(). These shorthands
were deprecated in jQuery 3.5.

https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo

* Replace deprecated jQuery methods in realtime.js

Replace .bind() with .on() and .change() trigger calls with
.trigger('change'). .bind() was deprecated in jQuery 3.0 and
shorthand triggers in jQuery 3.5.

https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo

* Replace deprecated jQuery methods in install.js and fix ===/!== errors

Replace .click(), .change(), .focus() with .on()/.trigger() equivalents.
Also fix !=== and ==== operators that were incorrectly introduced by a
prior replace-all of == to === within existing !== and === expressions.

https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo

* Restore == null where needed to catch both null and undefined

In JavaScript, == null matches both null and undefined, which is an
intentional idiom. The prior === null conversion broke cases where
values come from jQuery .val(), .data(), $.urlParam(), or object
property access that may return undefined rather than null. Revert
those specific cases while keeping === null where variables are
explicitly initialized to null.

https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo

* Replace deprecated jQuery methods in all theme main.js files

Replace .unbind().click() with .off('click').on('click'), convert
.hover() to .on('mouseenter').on('mouseleave'), replace .change(),
.scroll(), .click() shorthands with .on() equivalents, and .blur()
with .trigger('blur') across all 10 theme files.

https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo

* Convert string concatenation to template literals

Replace 'str' + var + 'str' patterns with ES6 template literals
in realtime.js and install.js. Improves readability especially for
URL construction and HTML building. Also replace $.parseJSON() with
native JSON.parse() in realtime.js.

https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo

* Convert var to const for single-assignment variables

Replace var with const where the variable is assigned once and never
reassigned within its scope, in install.js and realtime.js. Keeps var
for variables that are conditionally reassigned (e.g. size, url).

https://claude.ai/code/session_01SbuDigvAkYvPKvdcougsdo

---------

Co-authored-by: Claude <noreply@anthropic.com>

* fix(sqli): sanitize sort_column and enforce sort_direction in ORDER BY clauses

Closes GHSA-q9xg-p762-9jm3 and GHSA-72vr-jr4v-55vf.

- Add sanitize_sql_column() to lib/functions.php stripping all chars
  outside [a-zA-Z0-9_().] from user-supplied column names
- Apply sanitize_sql_column() at every ORDER BY concatenation in
  api_automation.php and html_reports.php
- Enforce direction to literal ASC/DESC in html_reports.php,
  api_automation.php, and html_utility.php (update_order_string,
  get_order_string)
- Add tests/Unit/SafeSortTest.php with 22 stub-based regression tests

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(docs): move sanitize_sql_column above sanitize_search_string docblock

The sanitize_search_string docblock was orphaned after sanitize_sql_column
was inserted between it and the function body. Reorder so each function
follows its own docblock directly.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(sqli): escape apostrophes in automation_templates and harden sort_direction in display_matching_hosts

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(sqli): restore allowlist guard, sanitize session keys, add sanitize_sql_column default

Restore the preg_match allowlist guard in get_order_string() that was
removed by the PR, re-opening the ORDER BY injection surface. When the
request var is empty after sanitization, return '' rather than building
a malformed ORDER BY clause.

Sanitize sort_column before storing as SESSION array key in
update_order_string() and add read-time guard in the inplace loop that
rebuilds ORDER BY from stored keys.

Add $default parameter to sanitize_sql_column() so callers get a safe
fallback instead of an empty string that produces 'ORDER BY  ASC'.

Fix inconsistent INET_ATON substitution in display_matching_hosts().

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* Improve sanitize_sql_column function

Fix regex to sanitize SQL column names properly.

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 16:54:42 -04:00

184 lines
7.9 KiB
JavaScript

/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License |
| as published by the Free Software Foundation; either version 2 |
| of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDTool-based Graphing Solution |
+-------------------------------------------------------------------------+
| This code is designed, written, and maintained by the Cacti Group. See |
| about.php and/or the AUTHORS file for specific developer information. |
+-------------------------------------------------------------------------+
| http://www.cacti.net/ |
+-------------------------------------------------------------------------+
*/
function themeReady() {
if ($('#cactiPageBottom').length == 0) {
$('<div id="cactiPageBottom" class="cactiPageBottom"></a></div>').insertAfter('#cactiContent');
}
// Setup the navigation menu
setMenuVisibility();
// Add nice search filter to filters
if ($('input[id="filter"]').length > 0 && $('input[id="filter"]').next('i[class="ti ti-search filter"]').length < 1) {
$('input[id="filter"]').after("<i class='ti ti-search filter'/>").attr('autocomplete', 'off').attr('placeholder', searchFilter).parent('td').css('white-space', 'nowrap');
}
if ($('input[id="filterd"]').length > 0 && $('input[id="filterd"]').next('i[class="ti ti-search filter"]').length < 1) {
$('input[id="filterd"]').after("<i class='ti ti-search filter'/>").attr('autocomplete', 'off').attr('placeholder', searchFilter).parent('td').css('white-space', 'nowrap');
}
if ($('input[id="rfilter"]').length > 0 && $('input[id="rfilter"]').next('i[class="ti ti-search filter"]').length < 1) {
$('input[id="rfilter"]').after("<i class='ti ti-search filter'/>").attr('autocomplete', 'off').attr('placeholder', searchRFilter).parent('td').css('white-space', 'nowrap');
}
$('input#filter, input#rfilter').addClass('ui-state-default ui-corner-all');
$('input[type="text"], input[type="password"], input[type="checkbox"], textarea').not('image').addClass('ui-state-default ui-corner-all');
/* Start clean up */
//login page
$('.cactiLoginLogo').html("<i class='ti ti-paw'/>").css('font-size: 20px');
/* clean up the navigation menu */
$('.cactiConsoleNavigationArea').find('#menu').appendTo($('.cactiConsoleNavigationArea').find('#navigation'));
$('.cactiConsoleNavigationArea').find('#navigation > table').remove();
$('.maintabs nav ul li a.lefttab').each( function() {
id = $(this).attr('id');
if (id == 'tab-graphs' && $(this).parent().hasClass('maintabs-has-submenu') == 0 ) {
$(this).parent().addClass('maintabs-has-submenu');
$('<div class="dropdownMenu">'
+'<ul id="submenu-tab-graphs" class="submenuoptions" style="display:none;">'
+'<li><a href="'+urlPath+'graph_view.php?action=tree">'+treeView+'</a></li>'
+'<li><a href="'+urlPath+'graph_view.php?action=list">'+listView+'</a></li>'
+'<li><a href="'+urlPath+'graph_view.php?action=preview">'+previewView+'</a></li>'
+'</ul>'
+'</div>').appendTo('body');
}
});
/* user menu on the right ... */
if ($('.usertabs').length == 0) {
$('#userDocumentation').remove();
$('#userCommunity').remove();
$('.menuHr').remove();
$('<div class="maintabs usertabs">'
+'<nav><ul>'
+'<li><a id="menu-user-help" class="usertabs-submenu" href="#"><i class="ti ti-question-mark"></i></a></li>'
+'<li class="action-icon-user"><a class="pic" href="#"><i class="ti ti-user"></i></a></li>'
+'</ul></nav>'
+'</div>').insertAfter('.maintabs');
$('<div class="dropdownMenu">'
+'<ul id="submenu-user-help" class="submenuoptions right" style="display:none;">'
+'<li><a href="https://www.cacti.net" target="_blank"><span>'+cactiHome+'</span></a></li>'
+'<li><a href="https://github.com/cacti" target="_blank"><span>'+cactiProjectPage+'</span></a></li>'
+'<li><hr class="menu"></li>'
+'<li><a href="https://forums.cacti.net/" target="_blank"><span>'+cactiCommunityForum+'</span></a></li>'
+'<li><a href="https://github.com/Cacti/documentation/blob/develop/README.md" target="_blank"><span>'+cactiDocumentation+'</span></a></li>'
+'<li><hr class="menu"></li>'
+'<li><a href="https://github.com/Cacti/cacti/issues/new" target="_blank"><span>'+reportABug+'</span></a></li>'
+'<li><a href="'+urlPath+'about.php"><span>'+aboutCacti+'</span></a></li>'
+'</ul>'
+'</div>').appendTo('body');
}
ajaxAnchors();
/* User Menu */
$('.menuoptions').parent().appendTo('body');
$(window).trigger('resize');
$('.action-icon-user').off('click').on('click', function(event) {
event.preventDefault();
if ($('.menuoptions').is(':visible') === false) {
$('.submenuoptions').slideUp(120);
$('.menuoptions').slideDown(120);
} else {
$('.menuoptions').slideUp(120);
}
return false;
});
/* Highlight sortable table columns */
$('.tableHeader th').has('i.fa-sort').removeClass('tableHeaderColumnHover tableHeaderColumnSelected');
$('.tableHeader th').has('i.fa-sort-up').addClass('tableHeaderColumnSelected');
$('.tableHeader th').has('i.fa-sort-down').addClass('tableHeaderColumnSelected');
$('.tableHeader th').has('i.fa-sort').on('mouseenter', function() {
$(this).addClass('tableHeaderColumnHover');
}).on('mouseleave', function() {
$(this).removeClass('tableHeaderColumnHover');
});
$('input#filter, input#rfilter').addClass('ui-state-default ui-corner-all');
$('input[type="text"], input[type="password"], input[type="checkbox"], textarea').not('image').addClass('ui-state-default ui-corner-all');
setNavigationScroll();
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
// Initialize the navigation settings
$('#navigation').hide();
$('li.menuitem').each(function() {
active = storage.get($(this).attr('id'));
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
$(this).next('a').show();
} else {
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false').hide();
$(this).next('a').hide();
}
});
$('#navigation').show();
// Functon to give life to the Navigation pane
$('#nav li:has(ul) a.active').off('click').on('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')){
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).next().slideUp( { duration: 200, easing: 'swing' } );
storage.set($(this).closest('.menuitem').attr('id'), 'collapsed');
} else {
$(this).next('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true');
$(this).next().slideToggle( { duration: 200, easing: 'swing' } );
if ($(this).next().is(':visible')) {
storage.set($(this).closest('.menuitem').attr('id'), 'active');
} else {
storage.set($(this).closest('.menuitem').attr('id'), 'collapsed');
}
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
storage.set($(this).attr('id'), 'collapsed');
});
});
}