CVE-2022-3561: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting (XSS) - Generic in GitHub repository librenms/librenms prior to 22.10.0.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 6.1
Published: 2022-11-20T05:15Z
Repository: https://github.com/librenms/librenms
Language: PHP
Files changed: includes/html/print-alert-rules.php
Commit date: 2022-10-17 12:32:58 -0500
Commit message: Fix alert rules XSS (#14471)

Fixes html injection for map items
Diff: diff --git a/includes/html/print-alert-rules.php b/includes/html/print-alert-rules.php
index 2cb27d160e8..89ab9a7b6fa 100644
--- a/includes/html/print-alert-rules.php
+++ b/includes/html/print-alert-rules.php
@@ -281,29 +281,29 @@ foreach ($rule_list as $rule) {
     $locations = null;
     if ($location_count) {
         $location_query = 'SELECT locations.location, locations.id FROM alert_location_map, locations WHERE alert_location_map.rule_id=? and alert_location_map.location_id = locations.id ORDER BY location';
         $location_maps = dbFetchRows($location_query, [$rule['id']]);
         foreach ($location_maps as $location_map) {
-            $locations .= $except_device_or_group . '<a href="' . url('devices/location=' . $location_map['id']) . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . '" data-content="View Devices for Location" target="_blank">' . $location_map['location'] . '</a><br>';
+            $locations .= $except_device_or_group . '<a href="' . url('devices/location=' . $location_map['id']) . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . '" data-content="View Devices for Location" target="_blank">' . htmlentities($location_map['location']) . '</a><br>';
         }
     }
 
     $groups = null;
     if ($group_count) {
         $group_query = 'SELECT device_groups.name, device_groups.id FROM alert_group_map, device_groups WHERE alert_group_map.rule_id=? and alert_group_map.group_id = device_groups.id ORDER BY name';
         $group_maps = dbFetchRows($group_query, [$rule['id']]);
         foreach ($group_maps as $group_map) {
-            $groups .= $except_device_or_group . '<a href="' . url('device-groups/' . $group_map['id'] . '/edit') . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . ' data-content="' . $group_map['name'] . '" title="' . $groups_msg . '" target="_blank">' . $group_map['name'] . '</a><br>';
+            $groups .= $except_device_or_group . '<a href="' . url('device-groups/' . $group_map['id'] . '/edit') . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . ' data-content="' . htmlentities($group_map['name']) . '" title="' . $groups_msg . '" target="_blank">' . htmlentities($group_map['name']) . '</a><br>';
         }
     }
 
     $devices = null;
     if ($device_count) {
         $device_query = 'SELECT devices.device_id,devices.hostname FROM alert_device_map, devices WHERE alert_device_map.rule_id=? and alert_device_map.device_id = devices.device_id ORDER BY hostname';
         $device_maps = dbFetchRows($device_query, [$rule['id']]);
         foreach ($device_maps as $device_map) {
-            $devices .= $except_device_or_group . '<a href="' . url('device/device=' . $device_map['device_id'] . '/tab=edit/') . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . '" data-content="' . $device_map['hostname'] . '" title="' . $devices_msg . '" target="_blank">' . $device_map['hostname'] . '</a><br>';
+            $devices .= $except_device_or_group . '<a href="' . url('device/device=' . $device_map['device_id'] . '/tab=edit/') . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . '" data-content="' . htmlentities($device_map['hostname']) . '" title="' . $devices_msg . '" target="_blank">' . htmlentities($device_map['hostname']) . '</a><br>';
         }
     }
 
     echo "<td colspan='2'>";
     if ($locations) {

Vulnerable code:             $locations .= $except_device_or_group . '<a href="' . url('devices/location=' . $location_map['id']) . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . '" data-content="View Devices for Location" target="_blank">' . $location_map['location'] . '</a><br>';
            $groups .= $except_device_or_group . '<a href="' . url('device-groups/' . $group_map['id'] . '/edit') . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . ' data-content="' . $group_map['name'] . '" title="' . $groups_msg . '" target="_blank">' . $group_map['name'] . '</a><br>';
            $devices .= $except_device_or_group . '<a href="' . url('device/device=' . $device_map['device_id'] . '/tab=edit/') . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . '" data-content="' . $device_map['hostname'] . '" title="' . $devices_msg . '" target="_blank">' . $device_map['hostname'] . '</a><br>';
Fixed code:             $locations .= $except_device_or_group . '<a href="' . url('devices/location=' . $location_map['id']) . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . '" data-content="View Devices for Location" target="_blank">' . htmlentities($location_map['location']) . '</a><br>';
            $groups .= $except_device_or_group . '<a href="' . url('device-groups/' . $group_map['id'] . '/edit') . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . ' data-content="' . htmlentities($group_map['name']) . '" title="' . $groups_msg . '" target="_blank">' . htmlentities($group_map['name']) . '</a><br>';
            $devices .= $except_device_or_group . '<a href="' . url('device/device=' . $device_map['device_id'] . '/tab=edit/') . '" data-container="body" data-toggle="popover" data-placement="' . $popover_position . '" data-content="' . htmlentities($device_map['hostname']) . '" title="' . $devices_msg . '" target="_blank">' . htmlentities($device_map['hostname']) . '</a><br>';