Skip to content

Commit 04b74da

Browse files
committed
Dashboard widget settings and widgetkey validation. Issue #15844
* Adds validation of submitted widgetkey values before use when storing widget settings and other similar operations. * Adds validation of widget settings which were not already validated.
1 parent 92a55a0 commit 04b74da

17 files changed

Lines changed: 343 additions & 24 deletions

src/etc/inc/util.inc

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5166,3 +5166,63 @@ function unserialize_data(?string $path, mixed $default = null, ?array $options
51665166
return $data;
51675167
}
51685168

5169+
/* Get an array of active widgets and metadata from user settings */
5170+
function get_active_widgets($user_settings) {
5171+
$widgets = [];
5172+
5173+
/* Break up the sequence string into an array of widget definitions */
5174+
$widget_sep = ',';
5175+
$widget_seq_array = explode($widget_sep, rtrim($user_settings['widgets']['sequence'], $widget_sep));
5176+
5177+
foreach ($widget_seq_array as $widget_seq_data) {
5178+
/* Break each widget definition into its component values */
5179+
[$name, $column, $display, $instance] = explode(':', $widget_seq_data);
5180+
$widgets[] = [
5181+
'name' => $name,
5182+
'column' => $column,
5183+
'display' => $display,
5184+
'instance' => $instance
5185+
];
5186+
}
5187+
return $widgets;
5188+
}
5189+
5190+
/* Test the validity of a given widget key based on user settings. */
5191+
function is_valid_widgetkey($widgetkey, $user_settings, $widgetfile = null) {
5192+
/* Proper form of a widgetkey is <widget-name>-<instance-id>
5193+
* Where:
5194+
* widget-name : Name of an active widget, which should be found in
5195+
* the current sequence list.
5196+
* instance-id : An integer 0 or higher identifying a widget instance
5197+
*
5198+
* Additionally, for a widget to be valid in this context it must also
5199+
* be present on the current Dashboard layout.
5200+
*/
5201+
5202+
/* Break the given widgetkey into its component parts */
5203+
[$wname, $wid] = explode('-', $widgetkey, 2);
5204+
5205+
/* Test for basic validity conditions */
5206+
if (empty($wname) ||
5207+
!is_numericint($wid) ||
5208+
empty($user_settings)) {
5209+
return false;
5210+
}
5211+
5212+
/* Check if this widget also matches a specific widget name */
5213+
if (!empty($widgetfile) &&
5214+
($wname != basename($widgetfile, '.widget.php'))) {
5215+
return false;
5216+
}
5217+
5218+
/* Ensure the key is for a widget which is in the Dashboard
5219+
* configuration. */
5220+
$widgets = get_active_widgets($user_settings);
5221+
foreach ($widgets as $widget) {
5222+
if (($widget['name'] == $wname) &&
5223+
($widget['instance'] == $wid)) {
5224+
return true;
5225+
}
5226+
}
5227+
return false;
5228+
}

src/usr/local/www/guiconfig.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,9 @@ function gen_customwidgettitle_div($widgettitle) {
599599
}
600600

601601
function set_customwidgettitle(& $user_settings) {
602+
if (!is_valid_widgetkey($_POST['widgetkey'], $user_settings)) {
603+
return false;
604+
}
602605
if ($_POST['descr']) {
603606
$user_settings['widgets'][$_POST['widgetkey']]['descr'] = trim($_POST['descr']);
604607
} else {

src/usr/local/www/widgets/widgets/disks.widget.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,26 @@
2525
// pfSense includes
2626
require_once('guiconfig.inc');
2727

28+
/*
29+
* Validate the "widgetkey" value.
30+
* When this widget is present on the Dashboard, $widgetkey is defined before
31+
* the Dashboard includes the widget. During other types of requests, such as
32+
* saving settings or AJAX, the value may be set via $_POST or similar.
33+
*/
34+
if ($_REQUEST['widgetkey']) {
35+
if (is_valid_widgetkey($_REQUEST['widgetkey'], $user_settings, __FILE__)) {
36+
$widgetkey = $_REQUEST['widgetkey'];
37+
} else {
38+
print gettext("Invalid Widget Key");
39+
exit;
40+
}
41+
}
42+
2843
// Widget includes
2944
require_once('/usr/local/www/widgets/include/disks.inc');
3045

3146
global $disks_widget_defaults;
3247

33-
$widgetkey = (isset($_POST['widgetkey'])) ? $_POST['widgetkey'] : $widgetkey;
34-
3548
// Now override any defaults with user settings
3649
$widget_config = array_replace($disks_widget_defaults, (array) $user_settings['widgets'][$widgetkey]);
3750

src/usr/local/www/widgets/widgets/dyn_dns_status.widget.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@
2929
require_once("functions.inc");
3030
require_once("/usr/local/www/widgets/include/dyn_dns_status.inc");
3131

32+
/*
33+
* Validate the "widgetkey" value.
34+
* When this widget is present on the Dashboard, $widgetkey is defined before
35+
* the Dashboard includes the widget. During other types of requests, such as
36+
* saving settings or AJAX, the value may be set via $_POST or similar.
37+
*/
38+
if ($_REQUEST['widgetkey']) {
39+
if (is_valid_widgetkey($_REQUEST['widgetkey'], $user_settings, __FILE__)) {
40+
$widgetkey = $_REQUEST['widgetkey'];
41+
} else {
42+
print gettext("Invalid Widget Key");
43+
exit;
44+
}
45+
}
46+
3247
// Constructs a unique key that will identify a Dynamic DNS entry in the filter list.
3348
if (!function_exists('get_dyndnsent_key')) {
3449
function get_dyndnsent_key($dyndns) {

src/usr/local/www/widgets/widgets/gateways.widget.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,40 @@
3131
require_once("functions.inc");
3232
require_once("/usr/local/www/widgets/include/gateways.inc");
3333

34+
/*
35+
* Validate the "widgetkey" value.
36+
* When this widget is present on the Dashboard, $widgetkey is defined before
37+
* the Dashboard includes the widget. During other types of requests, such as
38+
* saving settings or AJAX, the value may be set via $_POST or similar.
39+
*/
40+
if ($_REQUEST['widgetkey']) {
41+
if (is_valid_widgetkey($_REQUEST['widgetkey'], $user_settings, __FILE__)) {
42+
$widgetkey = $_REQUEST['widgetkey'];
43+
} else {
44+
print gettext("Invalid Widget Key");
45+
exit;
46+
}
47+
}
48+
49+
global $display_types;
50+
$display_types = array(
51+
'gw_ip' => gettext('Gateway IP Address'),
52+
'monitor_ip' => gettext('Monitor IP Address'),
53+
'both_ip' => gettext('Both')
54+
);
55+
3456
if (!function_exists('compose_table_body_contents')) {
3557
function compose_table_body_contents($widgetkey) {
36-
global $user_settings;
58+
global $user_settings, $display_types;
3759

3860
$rtnstr = '';
3961

4062
$a_gateways = get_gateways();
4163
$gateways_status = array();
4264
$gateways_status = return_gateways_status(true);
4365

44-
if (isset($user_settings["widgets"][$widgetkey]["display_type"])) {
66+
if (isset($user_settings["widgets"][$widgetkey]["display_type"]) &&
67+
array_key_exists($user_settings["widgets"][$widgetkey]["display_type"], $display_types)) {
4568
$display_type = $user_settings["widgets"][$widgetkey]["display_type"];
4669
} else {
4770
$display_type = "gw_ip";
@@ -211,7 +234,8 @@ function compose_table_body_contents($widgetkey) {
211234
$user_settings["widgets"][$_POST['widgetkey']] = array();
212235
}
213236

214-
if (isset($_POST["display_type"])) {
237+
if (isset($_POST["display_type"]) &&
238+
array_key_exists($_POST["display_type"], $display_types)) {
215239
$user_settings["widgets"][$_POST['widgetkey']]["display_type"] = $_POST["display_type"];
216240
}
217241

src/usr/local/www/widgets/widgets/interface_statistics.widget.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,26 @@
3333
require_once("functions.inc");
3434
require_once("/usr/local/www/widgets/include/interface_statistics.inc");
3535

36+
/*
37+
* Validate the "widgetkey" value.
38+
* When this widget is present on the Dashboard, $widgetkey is defined before
39+
* the Dashboard includes the widget. During other types of requests, such as
40+
* saving settings or AJAX, the value may be set via $_POST or similar.
41+
*/
42+
if ($_REQUEST['widgetkey']) {
43+
if (is_valid_widgetkey($_REQUEST['widgetkey'], $user_settings, __FILE__)) {
44+
$widgetkey = $_REQUEST['widgetkey'];
45+
} else {
46+
print gettext("Invalid Widget Key");
47+
exit;
48+
}
49+
}
50+
51+
$orientations = array(
52+
'if_columns' => gettext('Each interface in a column'),
53+
'if_rows' => gettext('Each interface in a row')
54+
);
55+
3656
$ifdescrs = get_configured_interface_with_descr();
3757
$ifstats = array(
3858
'inpkts' => gettext('Packets In'),
@@ -53,7 +73,8 @@
5373
$an_interface_is_displayed = false; // decide if at least 1 interface is displayed (i.e. not down)
5474
$an_ifstat_is_displayed = false;
5575

56-
if (isset($user_settings["widgets"][$_REQUEST['widgetkey']]["orientation_type"])) {
76+
if (isset($user_settings["widgets"][$_REQUEST['widgetkey']]["orientation_type"]) &&
77+
array_key_exists($user_settings["widgets"][$_REQUEST['widgetkey']]["orientation_type"], $orientations)) {
5778
$orientation_type = $user_settings["widgets"][$_REQUEST['widgetkey']]["orientation_type"];
5879
} else {
5980
$orientation_type = "if_columns";
@@ -160,7 +181,8 @@
160181
} else if ($_POST['widgetkey']) {
161182
set_customwidgettitle($user_settings);
162183

163-
if (isset($_POST['orientation_type'])) {
184+
if (isset($_POST['orientation_type']) &&
185+
array_key_exists($_POST['orientation_type'], $orientations)) {
164186
$user_settings['widgets'][$_POST['widgetkey']]['orientation_type'] = $_POST['orientation_type'];
165187
}
166188

src/usr/local/www/widgets/widgets/interfaces.widget.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@
2727
require_once("functions.inc");
2828
require_once("/usr/local/www/widgets/include/interfaces.inc");
2929

30+
/*
31+
* Validate the "widgetkey" value.
32+
* When this widget is present on the Dashboard, $widgetkey is defined before
33+
* the Dashboard includes the widget. During other types of requests, such as
34+
* saving settings or AJAX, the value may be set via $_POST or similar.
35+
*/
36+
if ($_REQUEST['widgetkey']) {
37+
if (is_valid_widgetkey($_REQUEST['widgetkey'], $user_settings, __FILE__)) {
38+
$widgetkey = $_REQUEST['widgetkey'];
39+
} else {
40+
print gettext("Invalid Widget Key");
41+
exit;
42+
}
43+
}
44+
3045
$ifdescrs = get_configured_interface_with_descr();
3146
// Update once per minute by default, instead of every 10 seconds
3247
$widgetperiod = config_get_path('widgets/period', 10) * 1000 * 6;
@@ -50,12 +65,6 @@
5065
header("Location: /index.php");
5166
}
5267

53-
// When this widget is included in the dashboard, $widgetkey is already defined before the widget is included.
54-
// When the ajax call is made to refresh the interfaces table, 'widgetkey' comes in $_REQUEST.
55-
if ($_REQUEST['widgetkey']) {
56-
$widgetkey = $_REQUEST['widgetkey'];
57-
}
58-
5968
?>
6069

6170
<div class="table-responsive" id="ifaces_status_<?=htmlspecialchars($widgetkey)?>">

src/usr/local/www/widgets/widgets/ipsec.widget.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@
3333
require_once("service-utils.inc");
3434
require_once("ipsec.inc");
3535

36+
/*
37+
* Validate the "widgetkey" value.
38+
* When this widget is present on the Dashboard, $widgetkey is defined before
39+
* the Dashboard includes the widget. During other types of requests, such as
40+
* saving settings or AJAX, the value may be set via $_POST or similar.
41+
*/
42+
if ($_REQUEST['widgetkey']) {
43+
if (is_valid_widgetkey($_REQUEST['widgetkey'], $user_settings, __FILE__)) {
44+
$widgetkey = $_REQUEST['widgetkey'];
45+
} else {
46+
print gettext("Invalid Widget Key");
47+
exit;
48+
}
49+
}
50+
3651
// Should always be initialized
3752
$ipsec_widget_tabs = array(
3853
'overview' => gettext('Overview'),

src/usr/local/www/widgets/widgets/log.widget.php

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@
4848
/* In an effort to reduce duplicate code, many shared functions have been moved here. */
4949
require_once("syslog.inc");
5050

51+
/*
52+
* Validate the "widgetkey" value.
53+
* When this widget is present on the Dashboard, $widgetkey is defined before
54+
* the Dashboard includes the widget. During other types of requests, such as
55+
* saving settings or AJAX, the value may be set via $_POST or similar.
56+
*/
57+
if ($_REQUEST['widgetkey']) {
58+
if (is_valid_widgetkey($_REQUEST['widgetkey'], $user_settings, __FILE__)) {
59+
$widgetkey = $_REQUEST['widgetkey'];
60+
} else {
61+
print gettext("Invalid Widget Key");
62+
exit;
63+
}
64+
}
65+
5166
/* Enable or disable debugging (detail level depending on removed ^//DEBUG^statements */
5267
$DebugOn = false;
5368
/* Debugging options */
@@ -84,7 +99,9 @@
8499
}
85100
unset($acts);
86101

87-
if (($_POST['filterlogentriesinterfaces']) and ($_POST['filterlogentriesinterfaces'] != "All")) {
102+
if ($_POST['filterlogentriesinterfaces'] &&
103+
($_POST['filterlogentriesinterfaces'] != "All") &&
104+
array_key_exists($_POST['filterlogentriesinterfaces'], get_configured_interface_with_descr())) {
88105
$user_settings['widgets'][$_POST['widgetkey']]['filterlogentriesinterfaces'] = trim($_POST['filterlogentriesinterfaces']);
89106
} else {
90107
unset($user_settings['widgets'][$_POST['widgetkey']]['filterlogentriesinterfaces']);
@@ -106,11 +123,6 @@
106123

107124
if ($DebugOn) { $logContent .= date($dateFormat)."_^START^".PHP_EOL; }
108125

109-
// When this widget is included in the dashboard, $widgetkey is already defined before the widget is included.
110-
// When the ajax call is made to refresh the firewall log table, 'widgetkey' comes in $_REQUEST.
111-
if ($_REQUEST['widgetkey']) {
112-
$widgetkey = $_REQUEST['widgetkey'];
113-
}
114126
//DEBUG: $logContent .= date($dateFormat)."_After request widgetkey".PHP_EOL;
115127

116128
$iface_descr_arr = get_configured_interface_with_descr();
@@ -130,7 +142,7 @@
130142
);
131143
//DEBUG: $logContent .= date($dateFormat)."_After filling_filter array".PHP_EOL;
132144

133-
$nentriesinterval = isset($user_settings['widgets'][$widgetkey]['filterlogentriesinterval']) ? $user_settings['widgets'][$widgetkey]['filterlogentriesinterval'] : 60;
145+
$nentriesinterval = is_numeric($user_settings['widgets'][$widgetkey]['filterlogentriesinterval']) ? $user_settings['widgets'][$widgetkey]['filterlogentriesinterval'] : 60;
134146
//DEBUG: $logContent .= date($dateFormat)."_After entries_interval".PHP_EOL;
135147

136148
$filter_logfile = "{$g['varlog_path']}/filter.log";

src/usr/local/www/widgets/widgets/openvpn.widget.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@
2424
require_once("guiconfig.inc");
2525
require_once("openvpn.inc");
2626

27+
/*
28+
* Validate the "widgetkey" value.
29+
* When this widget is present on the Dashboard, $widgetkey is defined before
30+
* the Dashboard includes the widget. During other types of requests, such as
31+
* saving settings or AJAX, the value may be set via $_POST or similar.
32+
*/
33+
if ($_REQUEST['widgetkey']) {
34+
if (is_valid_widgetkey($_REQUEST['widgetkey'], $user_settings, __FILE__)) {
35+
$widgetkey = $_REQUEST['widgetkey'];
36+
} else {
37+
print gettext("Invalid Widget Key");
38+
exit;
39+
}
40+
}
41+
2742
// Output the widget panel from this function so that it can be called from the AJAX handler as well as
2843
// when first rendering the page
2944
if (!function_exists('printPanel')) {

0 commit comments

Comments
 (0)