Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request from GHSA-5qpf-32w7-c56p
* Protect ITIL document deletion against CSRF

* Protect AJAX POST queries against CSRF

* Keep CSRF token when used from AJAX query

* Ensure marketplace writing actions are using POST

* Ensure planning writing actions are using POST

* Ensure datacenter writing actions are using POST

* Ensure timeline writing actions are using POST

* Ensure objectlock writing actions are using POST

* Ensure dashboard writing actions are using POST

* Ensure file uploads are using POST

* Ensure kanban writing actions are using POST

* Ensure cache reset operations are using POST
  • Loading branch information
cedric-anne committed Sep 15, 2021
1 parent 703c4c1 commit 93750ea
Show file tree
Hide file tree
Showing 24 changed files with 336 additions and 254 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,12 @@ The present file will list all changes made to the project; according to the

### API changes

#### Changed

- All POST request made to `/ajax/` scripts are now requiring a valid CSRF token in their `X-Glpi-Csrf-Token` header.
Requests done using jQuery are automatically including this header, from the moment that the page header is built using
`Html::includeHeader()` method and the `js/common.js` script is loaded.

#### Deprecated

- Usage of "followups" option in `CommonITILObject::showShort()`
Expand Down
18 changes: 9 additions & 9 deletions ajax/dashboard.php
Expand Up @@ -51,30 +51,30 @@

$dashboard = new Glpi\Dashboard\Dashboard($_REQUEST['dashboard'] ?? "");

switch ($_REQUEST['action']) {
switch ($_POST['action'] ?? null) {
case 'save_new_dashboard':
echo $dashboard->saveNew(
$_REQUEST['title'] ?? "",
$_REQUEST['context'] ?? ""
$_POST['title'] ?? "",
$_POST['context'] ?? ""
);
exit;

case 'save_items':
$dashboard->saveitems($_REQUEST['items'] ?? []);
$dashboard->saveTitle($_REQUEST['title'] ?? "");
$dashboard->saveitems($_POST['items'] ?? []);
$dashboard->saveTitle($_POST['title'] ?? "");
exit;

case 'save_rights':
echo $dashboard->saveRights($_REQUEST['rights'] ?? []);
echo $dashboard->saveRights($_POST['rights'] ?? []);
exit;

case 'delete_dashboard':
echo $dashboard->delete(['key' => $_REQUEST['dashboard']]);
echo $dashboard->delete(['key' => $_POST['dashboard']]);
exit;

case 'set_last_dashboard':
$grid = new Grid($_REQUEST['dashboard'] ?? "");
echo $grid->setLastDashboard($_REQUEST['page'], $_REQUEST['dashboard']);
$grid = new Grid($_POST['dashboard'] ?? "");
echo $grid->setLastDashboard($_POST['page'], $_POST['dashboard']);
exit;

case 'clone_dashboard':
Expand Down
2 changes: 1 addition & 1 deletion ajax/fileupload.php
Expand Up @@ -37,4 +37,4 @@
include ('../inc/includes.php');

Session::checkLoginUser();
GLPIUploadHandler::uploadFiles($_REQUEST);
GLPIUploadHandler::uploadFiles($_POST);
60 changes: 30 additions & 30 deletions ajax/kanban.php
Expand Up @@ -101,25 +101,25 @@
};

// Action Processing
if ($_REQUEST['action'] == 'update') {
if (($_POST['action'] ?? null) == 'update') {
$checkParams(['column_field', 'column_value']);
// Update project or task based on changes made in the Kanban
$item->update([
'id' => $_REQUEST['items_id'],
$_REQUEST['column_field'] => $_REQUEST['column_value']
'id' => $_POST['items_id'],
$_POST['column_field'] => $_POST['column_value']
]);
} else if ($_REQUEST['action'] == 'add_item') {
} else if (($_POST['action'] ?? null) == 'add_item') {
$checkParams(['inputs']);
$item = new $itemtype();
$inputs = [];
parse_str($_REQUEST['inputs'], $inputs);
parse_str($_POST['inputs'], $inputs);

$item->add(Toolbox::clean_cross_side_scripting_deep($inputs));
} else if ($_REQUEST['action'] == 'bulk_add_item') {
} else if (($_POST['action'] ?? null) == 'bulk_add_item') {
$checkParams(['inputs']);
$item = new $itemtype();
$inputs = [];
parse_str($_REQUEST['inputs'], $inputs);
parse_str($_POST['inputs'], $inputs);

$bulk_item_list = preg_split('/\r\n|[\r\n]/', $inputs['bulk_item_list']);
if (!empty($bulk_item_list)) {
Expand All @@ -131,31 +131,31 @@
}
}
}
} else if ($_REQUEST['action'] == 'move_item') {
} else if (($_POST['action'] ?? null) == 'move_item') {
$checkParams(['card', 'column', 'position', 'kanban']);
/** @var Kanban|CommonDBTM $kanban */
$kanban = new $_REQUEST['kanban']['itemtype'];
$can_move = $kanban->canOrderKanbanCard($_REQUEST['kanban']['items_id']);
$kanban = new $_POST['kanban']['itemtype'];
$can_move = $kanban->canOrderKanbanCard($_POST['kanban']['items_id']);
if ($can_move) {
Item_Kanban::moveCard($_REQUEST['kanban']['itemtype'], $_REQUEST['kanban']['items_id'],
$_REQUEST['card'], $_REQUEST['column'], $_REQUEST['position']);
Item_Kanban::moveCard($_POST['kanban']['itemtype'], $_POST['kanban']['items_id'],
$_POST['card'], $_POST['column'], $_POST['position']);
}
} else if ($_REQUEST['action'] == 'show_column') {
} else if (($_POST['action'] ?? null) == 'show_column') {
$checkParams(['column', 'kanban']);
Item_Kanban::showColumn($_REQUEST['kanban']['itemtype'], $_REQUEST['kanban']['items_id'], $_REQUEST['column']);
} else if ($_REQUEST['action'] == 'hide_column') {
Item_Kanban::showColumn($_POST['kanban']['itemtype'], $_POST['kanban']['items_id'], $_POST['column']);
} else if (($_POST['action'] ?? null) == 'hide_column') {
$checkParams(['column', 'kanban']);
Item_Kanban::hideColumn($_REQUEST['kanban']['itemtype'], $_REQUEST['kanban']['items_id'], $_REQUEST['column']);
} else if ($_REQUEST['action'] == 'collapse_column') {
Item_Kanban::hideColumn($_POST['kanban']['itemtype'], $_POST['kanban']['items_id'], $_POST['column']);
} else if (($_POST['action'] ?? null) == 'collapse_column') {
$checkParams(['column', 'kanban']);
Item_Kanban::collapseColumn($_REQUEST['kanban']['itemtype'], $_REQUEST['kanban']['items_id'], $_REQUEST['column']);
} else if ($_REQUEST['action'] == 'expand_column') {
Item_Kanban::collapseColumn($_POST['kanban']['itemtype'], $_POST['kanban']['items_id'], $_POST['column']);
} else if (($_POST['action'] ?? null) == 'expand_column') {
$checkParams(['column', 'kanban']);
Item_Kanban::expandColumn($_REQUEST['kanban']['itemtype'], $_REQUEST['kanban']['items_id'], $_REQUEST['column']);
} else if ($_REQUEST['action'] == 'move_column') {
Item_Kanban::expandColumn($_POST['kanban']['itemtype'], $_POST['kanban']['items_id'], $_POST['column']);
} else if (($_POST['action'] ?? null) == 'move_column') {
$checkParams(['column', 'kanban', 'position']);
Item_Kanban::moveColumn($_REQUEST['kanban']['itemtype'], $_REQUEST['kanban']['items_id'],
$_REQUEST['column'], $_REQUEST['position']);
Item_Kanban::moveColumn($_POST['kanban']['itemtype'], $_POST['kanban']['items_id'],
$_POST['column'], $_POST['position']);
} else if ($_REQUEST['action'] == 'refresh') {
$checkParams(['column_field']);
// Get all columns to refresh the kanban
Expand Down Expand Up @@ -183,26 +183,26 @@
return;
}
echo $itemtype::getFormURLWithID($_REQUEST['items_id'], true)."&forcetab={$tab_id}";
} else if ($_REQUEST['action'] == 'create_column') {
} else if (($_POST['action'] ?? null) == 'create_column') {
$checkParams(['column_field', 'items_id', 'column_name']);
$column_field = $_REQUEST['column_field'];
$column_field = $_POST['column_field'];
$column_itemtype = getItemtypeForForeignKeyField($column_field);
if (!$column_itemtype::canCreate() || !$column_itemtype::canView()) {
// Missing rights
http_response_code(403);
return;
}
$params = $_REQUEST['params'] ?? [];
$params = $_POST['params'] ?? [];
$column_item = new $column_itemtype();
$column_id = $column_item->add([
'name' => $_REQUEST['column_name']
'name' => $_POST['column_name']
] + $params);
header("Content-Type: application/json; charset=UTF-8", true);
$column = $itemtype::getKanbanColumns($_REQUEST['items_id'], $column_field, [$column_id]);
$column = $itemtype::getKanbanColumns($_POST['items_id'], $column_field, [$column_id]);
echo json_encode($column);
} else if ($_REQUEST['action'] == 'save_column_state') {
} else if (($_POST['action'] ?? null) == 'save_column_state') {
$checkParams(['items_id', 'state']);
Item_Kanban::saveStateForItem($_REQUEST['itemtype'], $_REQUEST['items_id'], $_REQUEST['state']);
Item_Kanban::saveStateForItem($_POST['itemtype'], $_POST['items_id'], $_POST['state']);
} else if ($_REQUEST['action'] == 'load_column_state') {
$checkParams(['items_id', 'last_load']);
header("Content-Type: application/json; charset=UTF-8", true);
Expand Down
54 changes: 25 additions & 29 deletions ajax/marketplace.php
Expand Up @@ -34,7 +34,7 @@

// follow download progress of a plugin with a minimal loading of files
// So we get a ajax answer in 5ms instead 100ms
if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "get_dl_progress") {
if (($_GET["action"] ?? null) == "get_dl_progress") {
if (!defined('GLPI_ROOT')) {
define('GLPI_ROOT', dirname(__DIR__));
}
Expand All @@ -43,14 +43,14 @@
Session::setPath();
Session::start();

echo $_SESSION['marketplace_dl_progress'][$_REQUEST['key']] ?? 0;
echo $_SESSION['marketplace_dl_progress'][$_GET['key']] ?? 0;
exit;
}

if ($_REQUEST["action"] == "download_plugin" || $_REQUEST["action"] == "update_plugin") {
if (in_array($_POST["action"] ?? null, ['download_plugin', 'update_plugin'])) {
// Do not load plugin that will be updated, to be able to load its new informations
// by redefining its plugin_version_ function after files replacement.
$PLUGINS_EXCLUDED = [$_REQUEST['key']];
$PLUGINS_EXCLUDED = [$_POST['key']];
}


Expand All @@ -62,56 +62,52 @@
use Glpi\Marketplace\Controller as MarketplaceController;
use Glpi\Marketplace\View as MarketplaceView;

if (isset($_REQUEST['key'])) {
$marketplace_ctrl = new MarketplaceController($_REQUEST['key']);
if ($_REQUEST["action"] == "download_plugin"
|| $_REQUEST["action"] == "update_plugin") {
if (isset($_POST['key']) && isset($_POST["action"])) {
$marketplace_ctrl = new MarketplaceController($_POST['key']);
if ($_POST["action"] == "download_plugin"
|| $_POST["action"] == "update_plugin") {
$marketplace_ctrl->downloadPlugin();
}
if ($_REQUEST["action"] == "clean_plugin") {
if ($_POST["action"] == "clean_plugin") {
if ($marketplace_ctrl->cleanPlugin()) {
echo "cleaned";
}
}
if ($_REQUEST["action"] == "install_plugin") {
if ($_POST["action"] == "install_plugin") {
$marketplace_ctrl->installPlugin();
}
if ($_REQUEST["action"] == "uninstall_plugin") {
if ($_POST["action"] == "uninstall_plugin") {
$marketplace_ctrl->uninstallPlugin();
}
if ($_REQUEST["action"] == "enable_plugin") {
if ($_POST["action"] == "enable_plugin") {
$marketplace_ctrl->enablePlugin();
}
if ($_REQUEST["action"] == "disable_plugin") {
if ($_POST["action"] == "disable_plugin") {
$marketplace_ctrl->disablePlugin();
}

echo MarketplaceView::getButtons($_REQUEST['key']);
}

if ($_REQUEST["action"] == "refresh_plugin_list") {
switch ($_REQUEST['tab']) {
echo MarketplaceView::getButtons($_POST['key']);
} else if (($_GET["action"] ?? null) == "refresh_plugin_list") {
switch ($_GET['tab']) {
default:
case 'discover':
echo MarketplaceView::discover(
$_REQUEST['force'] ?? false,
$_GET['force'] ?? false,
true,
$_REQUEST['tag'] ?? "",
$_REQUEST['filter'] ?? "",
$_REQUEST['page'] ?? 1,
$_REQUEST['sort'] ?? "sort-alpha-asc"
$_GET['tag'] ?? "",
$_GET['filter'] ?? "",
$_GET['page'] ?? 1,
$_GET['sort'] ?? "sort-alpha-asc"
);
break;
case 'installed':
echo MarketplaceView::installed(true, true, $_REQUEST['filter'] ?? "");
echo MarketplaceView::installed(true, true, $_GET['filter'] ?? "");
break;
}
}

if ($_REQUEST["action"] == "getPagination") {
} else if (($_GET["action"] ?? null) == "getPagination") {
echo MarketplaceView::getPaginationHtml(
$_REQUEST['page'] ?? 1,
$_REQUEST['total'] ?? 1,
$_GET['page'] ?? 1,
$_GET['total'] ?? 1,
true
);
}
28 changes: 14 additions & 14 deletions ajax/planning.php
Expand Up @@ -44,23 +44,23 @@
exit;
}

if ($_REQUEST["action"] == "update_event_times") {
echo Planning::updateEventTimes($_REQUEST);
if (($_POST["action"] ?? null) == "update_event_times") {
echo Planning::updateEventTimes($_POST);
exit;
}

if ($_REQUEST["action"] == "view_changed") {
echo Planning::viewChanged($_REQUEST['view']);
if (($_POST["action"] ?? null) == "view_changed") {
echo Planning::viewChanged($_POST['view']);
exit;
}

if ($_REQUEST["action"] == "clone_event") {
echo Planning::cloneEvent($_REQUEST['event']);
if (($_POST["action"] ?? null) == "clone_event") {
echo Planning::cloneEvent($_POST['event']);
exit;
}

if ($_REQUEST["action"] == "delete_event") {
echo Planning::deleteEvent($_REQUEST['event']);
if (($_POST["action"] ?? null) == "delete_event") {
echo Planning::deleteEvent($_POST['event']);
exit;
}

Expand Down Expand Up @@ -122,16 +122,16 @@
Planning::showPlanningFilter();
}

if ($_REQUEST["action"] == "toggle_filter") {
Planning::toggleFilter($_REQUEST);
if (($_POST["action"] ?? null) == "toggle_filter") {
Planning::toggleFilter($_POST);
}

if ($_REQUEST["action"] == "color_filter") {
Planning::colorFilter($_REQUEST);
if (($_POST["action"] ?? null) == "color_filter") {
Planning::colorFilter($_POST);
}

if ($_REQUEST["action"] == "delete_filter") {
Planning::deleteFilter($_REQUEST);
if (($_POST["action"] ?? null) == "delete_filter") {
Planning::deleteFilter($_POST);
}

Html::ajaxFooter();
16 changes: 9 additions & 7 deletions ajax/rack.php
Expand Up @@ -41,10 +41,16 @@
http_response_code(403);
die;
}
if (isset($_REQUEST['action'])) {
$answer = [];
if (!isset($_REQUEST['action'])) {
exit();
}

switch ($_REQUEST['action']) {
$answer = [];
if (($_GET['action'] ?? null) === 'show_pdu_form') {
PDU_Rack::showFirstForm((int) $_GET['racks_id']);
} else if (isset($_POST['action'])) {
header("Content-Type: application/json; charset=UTF-8", true);
switch ($_POST['action']) {
case 'move_item':
$item_rack = new Item_Rack;
$item_rack->getFromDB((int) $_POST['id']);
Expand Down Expand Up @@ -73,10 +79,6 @@
'position' => (int) $_POST['x'].",".(int) $_POST['y'],
]);
break;

case 'show_pdu_form':
PDU_Rack::showFirstForm((int) $_REQUEST['racks_id']);
exit;
}

echo json_encode($answer);
Expand Down

0 comments on commit 93750ea

Please sign in to comment.