From 5e42efc3ec6bab951103669497a6a311739ff898 Mon Sep 17 00:00:00 2001 From: Molkobain Date: Mon, 16 May 2022 17:42:24 +0200 Subject: [PATCH 01/12] =?UTF-8?q?N=C2=B04985=20-=20PHP=208.0:=20Fix=20usor?= =?UTF-8?q?t=20callback=20return=20type=20in=20portal's=20lists=20initiali?= =?UTF-8?q?zation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SilexCompatBootstrap/PortalXmlConfiguration/Lists.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php index bc12832ca..e28deca52 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php @@ -91,7 +91,10 @@ class Lists extends AbstractConfiguration } // - Sorting list items by rank usort($aListItems, function ($a, $b) { - return $a['rank'] > $b['rank']; + if ($a['rank'] == $b['rank']) { + return 0; + } + return $a['rank'] > $b['rank'] ? 1 : -1; }); $aClassLists[$sListId] = $aListItems; } From e909eac98e3ede6324808cb9b715cf8cfec99e11 Mon Sep 17 00:00:00 2001 From: Molkobain Date: Mon, 16 May 2022 17:43:52 +0200 Subject: [PATCH 02/12] =?UTF-8?q?N=C2=B04985=20-=20PHP=208.0:=20Fix=20is?= =?UTF-8?q?=5Fcallable()=20first=20param=20syntax=20in=20ObjectFormManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php b/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php index 3b97ff593..4f7041d50 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php @@ -703,7 +703,7 @@ class ObjectFormManager extends FormManager /** @var Field $oField */ $oField = null; - if (is_callable(get_class($oAttDef).'::MakeFormField')) + if (is_callable([$oAttDef, 'MakeFormField'])) { $oField = $oAttDef->MakeFormField($this->oObject); } From cf745554fb84ef0dcef863c7b1422aa698e48de8 Mon Sep 17 00:00:00 2001 From: Molkobain Date: Mon, 16 May 2022 18:04:29 +0200 Subject: [PATCH 03/12] =?UTF-8?q?N=C2=B04985=20-=20PHP=208.0:=20Fix=20strl?= =?UTF-8?q?en()=20test=20condition=20that=20needs=20to=20be=20more=20stric?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/action.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/action.class.inc.php b/core/action.class.inc.php index 5bb64979b..16754df99 100644 --- a/core/action.class.inc.php +++ b/core/action.class.inc.php @@ -179,7 +179,7 @@ class ActionEmail extends ActionNotification protected function FindRecipients($sRecipAttCode, $aArgs) { $sOQL = $this->Get($sRecipAttCode); - if (strlen($sOQL) == '') return ''; + if (strlen($sOQL) === 0) return ''; try { From 562dd8fc21b15191b23d8e93e6e60c5b0075a0e1 Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass Date: Tue, 17 May 2022 08:17:21 +0200 Subject: [PATCH 04/12] =?UTF-8?q?N=C2=B04900=20-=20Stored=20XSS=20in=20das?= =?UTF-8?q?hlets=20failed=20OQL=20query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/dashlet.class.inc.php | 315 +++++++++++++++--------------- 1 file changed, 162 insertions(+), 153 deletions(-) diff --git a/application/dashlet.class.inc.php b/application/dashlet.class.inc.php index 42eeca069..8ae3c5464 100644 --- a/application/dashlet.class.inc.php +++ b/application/dashlet.class.inc.php @@ -919,24 +919,28 @@ class DashletObjectList extends Dashlet */ public function Render($oPage, $bEditMode = false, $aExtraParams = array()) { - $sTitle = $this->aProperties['title']; - $sShowMenu = $this->aProperties['menu'] ? '1' : '0'; + try { + $sTitle = $this->aProperties['title']; + $sShowMenu = $this->aProperties['menu'] ? '1' : '0'; - $oPage->add('
'); - $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block - if ($sHtmlTitle != '') - { - $oPage->add('

 '.$sHtmlTitle.'

'); + $oPage->add('
'); + $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block + if ($sHtmlTitle != '') { + $oPage->add('

 '.$sHtmlTitle.'

'); + } + $oFilter = $this->GetDBSearch($aExtraParams); + $oBlock = new DisplayBlock($oFilter, 'list'); + $aParams = array( + 'menu' => $sShowMenu, + 'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId, + ); + $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM) + $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); + $oPage->add('
'); + } + catch(Exception $e){ + $oPage->add(utils::HtmlEntities($e->getMessage())); } - $oFilter = $this->GetDBSearch($aExtraParams); - $oBlock = new DisplayBlock($oFilter, 'list'); - $aParams = array( - 'menu' => $sShowMenu, - 'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId, - ); - $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM) - $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); - $oPage->add('
'); } public function GetDBSearch($aExtraParams = array()) @@ -1198,97 +1202,96 @@ abstract class DashletGroupBy extends Dashlet */ public function Render($oPage, $bEditMode = false, $aExtraParams = array()) { - $sTitle = $this->aProperties['title']; - $sQuery = $this->aProperties['query']; - $sStyle = $this->aProperties['style']; + try { + $sTitle = $this->aProperties['title']; + $sQuery = $this->aProperties['query']; + $sStyle = $this->aProperties['style']; - // First perform the query - if the OQL is not ok, it will generate an exception : no need to go further - if (isset($aExtraParams['query_params'])) - { - $aQueryParams = $aExtraParams['query_params']; - } - elseif (isset($aExtraParams['this->class']) && isset($aExtraParams['this->id'])) - { - $oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']); - $aQueryParams = $oObj->ToArgsForQuery(); - } - else - { - $aQueryParams = array(); - } - $oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams); - $oFilter->SetShowObsoleteData(utils::ShowObsoleteData()); - - $sClass = $oFilter->GetClass(); - if (!$this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode)) - { - $oPage->add('

'.Dict::S('UI:DashletGroupBy:MissingGroupBy').'

'); - } - else - { - switch($sStyle) + // First perform the query - if the OQL is not ok, it will generate an exception : no need to go further + if (isset($aExtraParams['query_params'])) { - case 'bars': - $sType = 'chart'; - $aParams = array( - 'chart_type' => 'bars', - 'chart_title' => $sTitle, - 'group_by' => $this->sGroupByExpr, - 'group_by_label' => $this->sGroupByLabel, - 'aggregation_function' => $this->sAggregationFunction, - 'aggregation_attribute' => $this->sAggregationAttribute, - 'limit' => $this->sLimit, - 'order_direction' => $this->sOrderDirection, - 'order_by' => $this->sOrderBy, - ); - $sHtmlTitle = ''; // done in the itop block - break; - - case 'pie': - $sType = 'chart'; - $aParams = array( - 'chart_type' => 'pie', - 'chart_title' => $sTitle, - 'group_by' => $this->sGroupByExpr, - 'group_by_label' => $this->sGroupByLabel, - 'aggregation_function' => $this->sAggregationFunction, - 'aggregation_attribute' => $this->sAggregationAttribute, - 'limit' => $this->sLimit, - 'order_direction' => $this->sOrderDirection, - 'order_by' => $this->sOrderBy, - ); - $sHtmlTitle = ''; // done in the itop block - break; - - case 'table': - default: - $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block - $sType = 'count'; - $aParams = array( - 'group_by' => $this->sGroupByExpr, - 'group_by_label' => $this->sGroupByLabel, - 'aggregation_function' => $this->sAggregationFunction, - 'aggregation_attribute' => $this->sAggregationAttribute, - 'limit' => $this->sLimit, - 'order_direction' => $this->sOrderDirection, - 'order_by' => $this->sOrderBy, - ); - break; + $aQueryParams = $aExtraParams['query_params']; } - - $oPage->add('
'); - if ($sHtmlTitle != '') + elseif (isset($aExtraParams['this->class']) && isset($aExtraParams['this->id'])) { - $oPage->add('

 '.$sHtmlTitle.'

'); + $oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']); + $aQueryParams = $oObj->ToArgsForQuery(); } - $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM) - $oBlock = new DisplayBlock($oFilter, $sType); - $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); - if($bEditMode) + else { - $oPage->add('
'); + $aQueryParams = array(); } - $oPage->add('
'); + $oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams); + $oFilter->SetShowObsoleteData(utils::ShowObsoleteData()); + + $sClass = $oFilter->GetClass(); + if (!$this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode)) { + $oPage->add('

'.Dict::S('UI:DashletGroupBy:MissingGroupBy').'

'); + } else { + switch ($sStyle) { + case 'bars': + $sType = 'chart'; + $aParams = array( + 'chart_type' => 'bars', + 'chart_title' => $sTitle, + 'group_by' => $this->sGroupByExpr, + 'group_by_label' => $this->sGroupByLabel, + 'aggregation_function' => $this->sAggregationFunction, + 'aggregation_attribute' => $this->sAggregationAttribute, + 'limit' => $this->sLimit, + 'order_direction' => $this->sOrderDirection, + 'order_by' => $this->sOrderBy, + ); + $sHtmlTitle = ''; // done in the itop block + break; + + case 'pie': + $sType = 'chart'; + $aParams = array( + 'chart_type' => 'pie', + 'chart_title' => $sTitle, + 'group_by' => $this->sGroupByExpr, + 'group_by_label' => $this->sGroupByLabel, + 'aggregation_function' => $this->sAggregationFunction, + 'aggregation_attribute' => $this->sAggregationAttribute, + 'limit' => $this->sLimit, + 'order_direction' => $this->sOrderDirection, + 'order_by' => $this->sOrderBy, + ); + $sHtmlTitle = ''; // done in the itop block + break; + + case 'table': + default: + $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block + $sType = 'count'; + $aParams = array( + 'group_by' => $this->sGroupByExpr, + 'group_by_label' => $this->sGroupByLabel, + 'aggregation_function' => $this->sAggregationFunction, + 'aggregation_attribute' => $this->sAggregationAttribute, + 'limit' => $this->sLimit, + 'order_direction' => $this->sOrderDirection, + 'order_by' => $this->sOrderBy, + ); + break; + } + + $oPage->add('
'); + if ($sHtmlTitle != '') { + $oPage->add('

 '.$sHtmlTitle.'

'); + } + $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM) + $oBlock = new DisplayBlock($oFilter, $sType); + $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); + if ($bEditMode) { + $oPage->add('
'); + } + $oPage->add('
'); + } + } + catch(Exception $e){ + $oPage->add(utils::HtmlEntities($e->getMessage())); } } @@ -2050,63 +2053,69 @@ class DashletHeaderDynamic extends Dashlet */ public function Render($oPage, $bEditMode = false, $aExtraParams = array()) { - $sTitle = utils::HtmlEntities($this->aProperties['title']); - $sIcon = $this->aProperties['icon']; - $sSubtitle = utils::HtmlEntities($this->aProperties['subtitle']); - $sQuery = $this->aProperties['query']; - $sGroupBy = $this->aProperties['group_by']; + try{ + $sTitle = utils::HtmlEntities($this->aProperties['title']); + $sIcon = $this->aProperties['icon']; + $sSubtitle = utils::HtmlEntities($this->aProperties['subtitle']); + $sQuery = $this->aProperties['query']; + $sGroupBy = $this->aProperties['group_by']; - $oIconSelect = $this->oModelReflection->GetIconSelectionField('icon'); - $sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon)); + $oIconSelect = $this->oModelReflection->GetIconSelectionField('icon'); + $sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon)); - $aValues = $this->GetValues(); - if (count($aValues) > 0) - { - // Stats grouped by - $sCSV = implode(',', $aValues); - $aParams = array( - 'title[block]' => $sTitle, - 'label[block]' => $sSubtitle, - 'status[block]' => $sGroupBy, - 'status_codes[block]' => $sCSV, - 'context_filter' => 1, - ); + $aValues = $this->GetValues(); + if (count($aValues) > 0) + { + // Stats grouped by + $sCSV = implode(',', $aValues); + $aParams = array( + 'title[block]' => $sTitle, + 'label[block]' => $sSubtitle, + 'status[block]' => $sGroupBy, + 'status_codes[block]' => $sCSV, + 'context_filter' => 1, + ); + } + else + { + // Simple stats + $aParams = array( + 'title[block]' => $sTitle, + 'label[block]' => $sSubtitle, + 'context_filter' => 1, + ); + } + + $oPage->add('
'); + $oPage->add('
'); + + $oPage->add(''); + + if (isset($aExtraParams['query_params'])) + { + $aQueryParams = $aExtraParams['query_params']; + } + elseif (isset($aExtraParams['this->class'])) + { + $oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']); + $aQueryParams = $oObj->ToArgsForQuery(); + } + else + { + $aQueryParams = array(); + } + + $oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams); + $oBlock = new DisplayBlock($oFilter, 'summary'); + $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM) + $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); + + $oPage->add('
'); + $oPage->add('
'); } - else - { - // Simple stats - $aParams = array( - 'title[block]' => $sTitle, - 'label[block]' => $sSubtitle, - 'context_filter' => 1, - ); + catch(Exception $e){ + $oPage->add(utils::HtmlEntities($e->getMessage())); } - - $oPage->add('
'); - $oPage->add('
'); - - $oPage->add(''); - - if (isset($aExtraParams['query_params'])) - { - $aQueryParams = $aExtraParams['query_params']; - } - elseif (isset($aExtraParams['this->class'])) - { - $oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']); - $aQueryParams = $oObj->ToArgsForQuery(); - } - else - { - $aQueryParams = array(); - } - $oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams); - $oBlock = new DisplayBlock($oFilter, 'summary'); - $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM) - $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); - - $oPage->add('
'); - $oPage->add('
'); } /** From 59424c312660e272847d8a208dbfd7940a6b26bf Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass Date: Tue, 17 May 2022 09:02:06 +0200 Subject: [PATCH 05/12] =?UTF-8?q?N=C2=B04976=20-=20CSRF=20in=20import=20pa?= =?UTF-8?q?ge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/csvimport.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pages/csvimport.php b/pages/csvimport.php index 9f4b566a2..a8ce680f6 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -197,6 +197,11 @@ try { throw new CoreException(Dict::S('UI:ActionNotAllowed')); } + // CSRF transaction id verification + if(!$bSimulate && !utils::IsTransactionValid(utils::ReadPostedParam('transaction_id', '', 'raw_data'))){ + throw new CoreException(Dict::S('UI:Error:InvalidToken')); + } + $aResult = array(); $sCSVData = utils::ReadParam('csvdata', '', false, 'raw_data'); $sCSVDataTruncated = utils::ReadParam('csvdata_truncated', '', false, 'raw_data'); @@ -487,11 +492,12 @@ try { $sHtml .= "$sMessage"; $sHtml .= ''; } - + $iUnchanged = count($aRes) - $iErrors - $iModified - $iCreated; $sHtml .= ''; $oPage->add('
'); $oPage->add('
'); + $oPage->add(''); $oPage->add(''); $oPage->add(''); $oPage->add(''); From 8e972794013bdd991eff0735b21fcc1a915bc09f Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass Date: Tue, 17 May 2022 09:27:06 +0200 Subject: [PATCH 06/12] =?UTF-8?q?N=C2=B04899=20-=20Reflected=20XSS=20on=20?= =?UTF-8?q?revert=5Fdashboard=20operation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/utils.inc.php | 5 +++++ pages/ajax.render.php | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/application/utils.inc.php b/application/utils.inc.php index f64cf306d..4a8c0e5b5 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -358,6 +358,11 @@ class utils $retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value); break; + // For URL + case 'url': + $retValue = filter_var($value, FILTER_SANITIZE_URL); + break; + default: case 'raw_data': $retValue = $value; diff --git a/pages/ajax.render.php b/pages/ajax.render.php index fbe25a152..dead51401 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -1183,7 +1183,7 @@ try $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); - $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); + $sReloadURL = utils::ReadParam('reload_url', '', false, 'url'); $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); $aResult = array('error' => ''); if (!is_null($oDashboard)) @@ -1202,7 +1202,7 @@ try $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'raw_data'); $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); - $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); + $sReloadURL = utils::ReadParam('reload_url', '', false, 'url'); $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); $aResult = array('error' => ''); if (!is_null($oDashboard)) @@ -1219,7 +1219,7 @@ try case 'save_dashboard': $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'context_param'); $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); - $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); + $sReloadURL = utils::ReadParam('reload_url', '', false, 'url'); $sJSExtraParams = json_encode($aExtraParams); $aParams = array(); $aParams['layout_class'] = utils::ReadParam('layout_class', ''); @@ -1252,7 +1252,7 @@ JS case 'revert_dashboard': $sDashboardId = utils::ReadParam('dashboard_id', '', false, 'raw_data'); - $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); + $sReloadURL = utils::ReadParam('reload_url', '', false, 'url'); appUserPreferences::UnsetPref('display_original_dashboard_'.$sDashboardId); $oDashboard = new RuntimeDashboard($sDashboardId); $oDashboard->Revert(); @@ -1282,7 +1282,7 @@ EOF $aParams['cells'] = utils::ReadParam('cells', array(), false, 'raw_data'); $aParams['auto_reload'] = utils::ReadParam('auto_reload', false); $aParams['auto_reload_sec'] = utils::ReadParam('auto_reload_sec', 300); - $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); + $sReloadURL = utils::ReadParam('reload_url', '', false, 'url'); $oKPI = new ExecutionKPI(); $oDashboard = new RuntimeDashboard($sDashboardId); $oDashboard->FromParams($aParams); @@ -1296,7 +1296,7 @@ EOF $aExtraParams = utils::ReadParam('extra_params', array(), false, 'raw_data'); $aExtraParams['dashboard_div_id'] = utils::Sanitize($sId, '', 'element_identifier'); $sDashboardFile = utils::ReadParam('file', '', false, 'string'); - $sReloadURL = utils::ReadParam('reload_url', '', false, 'raw_data'); + $sReloadURL = utils::ReadParam('reload_url', '', false, 'url'); $oKPI = new ExecutionKPI(); $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sId); if (!is_null($oDashboard)) From 0ef4fee0b4df098cc81a511fb63b85a4bc9d3f42 Mon Sep 17 00:00:00 2001 From: Molkobain Date: Tue, 17 May 2022 15:26:57 +0200 Subject: [PATCH 07/12] =?UTF-8?q?N=C2=B04985=20-=20PHP=208.0:=20Fix=20usor?= =?UTF-8?q?t=20callback=20return=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../portal/src/Brick/BrickCollection.php | 12 ++++++++++-- .../portal/src/Brick/ManageBrick.php | 6 +++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php index 1fc733ff0..ed7f370d5 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php @@ -162,12 +162,20 @@ class BrickCollection // - Home $this->aHomeOrdering = $this->aAllowedBricks; usort($this->aHomeOrdering, function (PortalBrick $a, PortalBrick $b) { - return $a->GetRankHome() > $b->GetRankHome(); + if ($a->GetRankHome() === $b->GetRankHome()) { + return 0; + } + + return $a->GetRankHome() > $b->GetRankHome() ? 1 : -1; }); // - Navigation menu $this->aNavigationMenuOrdering = $this->aAllowedBricks; usort($this->aNavigationMenuOrdering, function (PortalBrick $a, PortalBrick $b) { - return $a->GetRankNavigationMenu() > $b->GetRankNavigationMenu(); + if ($a->GetRankNavigationMenu() === $b->GetRankNavigationMenu()) { + return 0; + } + + return $a->GetRankNavigationMenu() > $b->GetRankNavigationMenu() ? 1 : -1; }); } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php index 9416eda0d..8699f1849 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php @@ -481,7 +481,11 @@ class ManageBrick extends PortalBrick if (!$this->IsGroupingByDistinctValues($sName)) { usort($this->aGrouping[$sName]['groups'], function ($a, $b) { - return $a['rank'] > $b['rank']; + if ($a['rank'] === $b['rank']) { + return 0; + } + + return $a['rank'] > $b['rank'] ? 1 : -1; }); } From 424e2a5745c4774f179d461041141f54c0f64a94 Mon Sep 17 00:00:00 2001 From: Pierre Goiffon Date: Tue, 17 May 2022 15:52:43 +0200 Subject: [PATCH 08/12] :bulb: Fix PHPDoc for \DBObject::CheckConsistency --- core/dbobject.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/dbobject.class.php b/core/dbobject.class.php index c842fe6d2..a06816115 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -1977,9 +1977,9 @@ abstract class DBObject implements iDisplay /** * check attributes together * - * @overwritable-hook You can extend this method in order to provide your own logic. - * - * @return bool + * @overwritable-hook You can extend this method in order to provide your own logic. + * + * @return true|string true if successful, the error description otherwise */ public function CheckConsistency() { From eac6f07823b6621ff80b3723dd3de5181755a059 Mon Sep 17 00:00:00 2001 From: Molkobain Date: Tue, 17 May 2022 16:46:42 +0200 Subject: [PATCH 09/12] =?UTF-8?q?N=C2=B04985=20-=20PHP=208.0:=20Fix=20opti?= =?UTF-8?q?onal=20parameter=20before=20mandatory=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Method is always (once) called with the value defined in iTop * No Combodo extension call the method * No customization in the ITSM Designer (snippets / extensions) call the method * Calling method with only the first parameter would crash anyway --- core/displayablegraph.class.inc.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/displayablegraph.class.inc.php b/core/displayablegraph.class.inc.php index 66cd7e660..a39a1a0fc 100644 --- a/core/displayablegraph.class.inc.php +++ b/core/displayablegraph.class.inc.php @@ -1203,8 +1203,10 @@ class DisplayableGraph extends SimpleGraph * @param float $xMax Right coordinate of the bounding box to display the graph * @param float $yMin Top coordinate of the bounding box to display the graph * @param float $yMax Bottom coordinate of the bounding box to display the graph + * + * @since 2.7.7 3.0.2 3.1.0 N°4985 $sComments param is no longer optional */ - function RenderAsPDF(PDFPage $oPage, $sComments = '', $sContextKey, $xMin = -1, $xMax = -1, $yMin = -1, $yMax = -1) + function RenderAsPDF(PDFPage $oPage, $sComments, $sContextKey, $xMin = -1, $xMax = -1, $yMin = -1, $yMax = -1) { $aContextDefs = static::GetContextDefinitions($sContextKey, false); // No need to develop the parameters $oPdf = $oPage->get_tcpdf(); From 3d6bbe4029d6ac14e9daef094d33ccb5e8156b88 Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass Date: Wed, 18 May 2022 08:05:19 +0200 Subject: [PATCH 10/12] =?UTF-8?q?Revert=20"N=C2=B04900=20-=20Stored=20XSS?= =?UTF-8?q?=20in=20dashlets=20failed=20OQL=20query"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 562dd8fc21b15191b23d8e93e6e60c5b0075a0e1. --- application/dashlet.class.inc.php | 325 +++++++++++++++--------------- 1 file changed, 158 insertions(+), 167 deletions(-) diff --git a/application/dashlet.class.inc.php b/application/dashlet.class.inc.php index 8ae3c5464..42eeca069 100644 --- a/application/dashlet.class.inc.php +++ b/application/dashlet.class.inc.php @@ -919,28 +919,24 @@ class DashletObjectList extends Dashlet */ public function Render($oPage, $bEditMode = false, $aExtraParams = array()) { - try { - $sTitle = $this->aProperties['title']; - $sShowMenu = $this->aProperties['menu'] ? '1' : '0'; + $sTitle = $this->aProperties['title']; + $sShowMenu = $this->aProperties['menu'] ? '1' : '0'; - $oPage->add('
'); - $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block - if ($sHtmlTitle != '') { - $oPage->add('

 '.$sHtmlTitle.'

'); - } - $oFilter = $this->GetDBSearch($aExtraParams); - $oBlock = new DisplayBlock($oFilter, 'list'); - $aParams = array( - 'menu' => $sShowMenu, - 'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId, - ); - $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM) - $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); - $oPage->add('
'); - } - catch(Exception $e){ - $oPage->add(utils::HtmlEntities($e->getMessage())); + $oPage->add('
'); + $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block + if ($sHtmlTitle != '') + { + $oPage->add('

 '.$sHtmlTitle.'

'); } + $oFilter = $this->GetDBSearch($aExtraParams); + $oBlock = new DisplayBlock($oFilter, 'list'); + $aParams = array( + 'menu' => $sShowMenu, + 'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId, + ); + $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM) + $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); + $oPage->add('
'); } public function GetDBSearch($aExtraParams = array()) @@ -1202,96 +1198,97 @@ abstract class DashletGroupBy extends Dashlet */ public function Render($oPage, $bEditMode = false, $aExtraParams = array()) { - try { - $sTitle = $this->aProperties['title']; - $sQuery = $this->aProperties['query']; - $sStyle = $this->aProperties['style']; + $sTitle = $this->aProperties['title']; + $sQuery = $this->aProperties['query']; + $sStyle = $this->aProperties['style']; - // First perform the query - if the OQL is not ok, it will generate an exception : no need to go further - if (isset($aExtraParams['query_params'])) - { - $aQueryParams = $aExtraParams['query_params']; - } - elseif (isset($aExtraParams['this->class']) && isset($aExtraParams['this->id'])) - { - $oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']); - $aQueryParams = $oObj->ToArgsForQuery(); - } - else - { - $aQueryParams = array(); - } - $oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams); - $oFilter->SetShowObsoleteData(utils::ShowObsoleteData()); - - $sClass = $oFilter->GetClass(); - if (!$this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode)) { - $oPage->add('

'.Dict::S('UI:DashletGroupBy:MissingGroupBy').'

'); - } else { - switch ($sStyle) { - case 'bars': - $sType = 'chart'; - $aParams = array( - 'chart_type' => 'bars', - 'chart_title' => $sTitle, - 'group_by' => $this->sGroupByExpr, - 'group_by_label' => $this->sGroupByLabel, - 'aggregation_function' => $this->sAggregationFunction, - 'aggregation_attribute' => $this->sAggregationAttribute, - 'limit' => $this->sLimit, - 'order_direction' => $this->sOrderDirection, - 'order_by' => $this->sOrderBy, - ); - $sHtmlTitle = ''; // done in the itop block - break; - - case 'pie': - $sType = 'chart'; - $aParams = array( - 'chart_type' => 'pie', - 'chart_title' => $sTitle, - 'group_by' => $this->sGroupByExpr, - 'group_by_label' => $this->sGroupByLabel, - 'aggregation_function' => $this->sAggregationFunction, - 'aggregation_attribute' => $this->sAggregationAttribute, - 'limit' => $this->sLimit, - 'order_direction' => $this->sOrderDirection, - 'order_by' => $this->sOrderBy, - ); - $sHtmlTitle = ''; // done in the itop block - break; - - case 'table': - default: - $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block - $sType = 'count'; - $aParams = array( - 'group_by' => $this->sGroupByExpr, - 'group_by_label' => $this->sGroupByLabel, - 'aggregation_function' => $this->sAggregationFunction, - 'aggregation_attribute' => $this->sAggregationAttribute, - 'limit' => $this->sLimit, - 'order_direction' => $this->sOrderDirection, - 'order_by' => $this->sOrderBy, - ); - break; - } - - $oPage->add('
'); - if ($sHtmlTitle != '') { - $oPage->add('

 '.$sHtmlTitle.'

'); - } - $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM) - $oBlock = new DisplayBlock($oFilter, $sType); - $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); - if ($bEditMode) { - $oPage->add('
'); - } - $oPage->add('
'); - } + // First perform the query - if the OQL is not ok, it will generate an exception : no need to go further + if (isset($aExtraParams['query_params'])) + { + $aQueryParams = $aExtraParams['query_params']; } - catch(Exception $e){ - $oPage->add(utils::HtmlEntities($e->getMessage())); + elseif (isset($aExtraParams['this->class']) && isset($aExtraParams['this->id'])) + { + $oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']); + $aQueryParams = $oObj->ToArgsForQuery(); + } + else + { + $aQueryParams = array(); + } + $oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams); + $oFilter->SetShowObsoleteData(utils::ShowObsoleteData()); + + $sClass = $oFilter->GetClass(); + if (!$this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode)) + { + $oPage->add('

'.Dict::S('UI:DashletGroupBy:MissingGroupBy').'

'); + } + else + { + switch($sStyle) + { + case 'bars': + $sType = 'chart'; + $aParams = array( + 'chart_type' => 'bars', + 'chart_title' => $sTitle, + 'group_by' => $this->sGroupByExpr, + 'group_by_label' => $this->sGroupByLabel, + 'aggregation_function' => $this->sAggregationFunction, + 'aggregation_attribute' => $this->sAggregationAttribute, + 'limit' => $this->sLimit, + 'order_direction' => $this->sOrderDirection, + 'order_by' => $this->sOrderBy, + ); + $sHtmlTitle = ''; // done in the itop block + break; + + case 'pie': + $sType = 'chart'; + $aParams = array( + 'chart_type' => 'pie', + 'chart_title' => $sTitle, + 'group_by' => $this->sGroupByExpr, + 'group_by_label' => $this->sGroupByLabel, + 'aggregation_function' => $this->sAggregationFunction, + 'aggregation_attribute' => $this->sAggregationAttribute, + 'limit' => $this->sLimit, + 'order_direction' => $this->sOrderDirection, + 'order_by' => $this->sOrderBy, + ); + $sHtmlTitle = ''; // done in the itop block + break; + + case 'table': + default: + $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block + $sType = 'count'; + $aParams = array( + 'group_by' => $this->sGroupByExpr, + 'group_by_label' => $this->sGroupByLabel, + 'aggregation_function' => $this->sAggregationFunction, + 'aggregation_attribute' => $this->sAggregationAttribute, + 'limit' => $this->sLimit, + 'order_direction' => $this->sOrderDirection, + 'order_by' => $this->sOrderBy, + ); + break; + } + + $oPage->add('
'); + if ($sHtmlTitle != '') + { + $oPage->add('

 '.$sHtmlTitle.'

'); + } + $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM) + $oBlock = new DisplayBlock($oFilter, $sType); + $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); + if($bEditMode) + { + $oPage->add('
'); + } + $oPage->add('
'); } } @@ -2053,69 +2050,63 @@ class DashletHeaderDynamic extends Dashlet */ public function Render($oPage, $bEditMode = false, $aExtraParams = array()) { - try{ - $sTitle = utils::HtmlEntities($this->aProperties['title']); - $sIcon = $this->aProperties['icon']; - $sSubtitle = utils::HtmlEntities($this->aProperties['subtitle']); - $sQuery = $this->aProperties['query']; - $sGroupBy = $this->aProperties['group_by']; + $sTitle = utils::HtmlEntities($this->aProperties['title']); + $sIcon = $this->aProperties['icon']; + $sSubtitle = utils::HtmlEntities($this->aProperties['subtitle']); + $sQuery = $this->aProperties['query']; + $sGroupBy = $this->aProperties['group_by']; - $oIconSelect = $this->oModelReflection->GetIconSelectionField('icon'); - $sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon)); + $oIconSelect = $this->oModelReflection->GetIconSelectionField('icon'); + $sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon)); - $aValues = $this->GetValues(); - if (count($aValues) > 0) - { - // Stats grouped by - $sCSV = implode(',', $aValues); - $aParams = array( - 'title[block]' => $sTitle, - 'label[block]' => $sSubtitle, - 'status[block]' => $sGroupBy, - 'status_codes[block]' => $sCSV, - 'context_filter' => 1, - ); - } - else - { - // Simple stats - $aParams = array( - 'title[block]' => $sTitle, - 'label[block]' => $sSubtitle, - 'context_filter' => 1, - ); - } - - $oPage->add('
'); - $oPage->add('
'); - - $oPage->add(''); - - if (isset($aExtraParams['query_params'])) - { - $aQueryParams = $aExtraParams['query_params']; - } - elseif (isset($aExtraParams['this->class'])) - { - $oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']); - $aQueryParams = $oObj->ToArgsForQuery(); - } - else - { - $aQueryParams = array(); - } - - $oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams); - $oBlock = new DisplayBlock($oFilter, 'summary'); - $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM) - $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); - - $oPage->add('
'); - $oPage->add('
'); + $aValues = $this->GetValues(); + if (count($aValues) > 0) + { + // Stats grouped by + $sCSV = implode(',', $aValues); + $aParams = array( + 'title[block]' => $sTitle, + 'label[block]' => $sSubtitle, + 'status[block]' => $sGroupBy, + 'status_codes[block]' => $sCSV, + 'context_filter' => 1, + ); } - catch(Exception $e){ - $oPage->add(utils::HtmlEntities($e->getMessage())); + else + { + // Simple stats + $aParams = array( + 'title[block]' => $sTitle, + 'label[block]' => $sSubtitle, + 'context_filter' => 1, + ); } + + $oPage->add('
'); + $oPage->add('
'); + + $oPage->add(''); + + if (isset($aExtraParams['query_params'])) + { + $aQueryParams = $aExtraParams['query_params']; + } + elseif (isset($aExtraParams['this->class'])) + { + $oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']); + $aQueryParams = $oObj->ToArgsForQuery(); + } + else + { + $aQueryParams = array(); + } + $oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams); + $oBlock = new DisplayBlock($oFilter, 'summary'); + $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM) + $oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams)); + + $oPage->add('
'); + $oPage->add('
'); } /** From 61a2d200b44e6955be5e7ffb17aa56bac4345fc9 Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass Date: Wed, 18 May 2022 08:10:01 +0200 Subject: [PATCH 11/12] =?UTF-8?q?N=C2=B04900=20-=20Stored=20XSS=20in=20das?= =?UTF-8?q?hlets=20failed=20OQL=20query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/dashlet.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/dashlet.class.inc.php b/application/dashlet.class.inc.php index 42eeca069..0bc8979f4 100644 --- a/application/dashlet.class.inc.php +++ b/application/dashlet.class.inc.php @@ -255,7 +255,7 @@ abstract class Dashlet catch(OqlException $e) { $oPage->add('
'); - $oPage->p($e->GetUserFriendlyDescription()); + $oPage->p(utils::HtmlEntities($e->GetUserFriendlyDescription())); $oPage->add('
'); } catch(Exception $e) From 534e7cf59df726c2a44550f7395915a2d1bb1823 Mon Sep 17 00:00:00 2001 From: Pierre Goiffon Date: Wed, 18 May 2022 08:54:07 +0200 Subject: [PATCH 12/12] =?UTF-8?q?:white=5Fcheck=5Fmark:=20N=C2=B04655=20Ne?= =?UTF-8?q?w=20nightly=20PHPUnit=20file=20containing=20OQL=20tests=20Those?= =?UTF-8?q?=20tests=20were=20removed=20in=2072af2b7c=20as=20they=20took=20?= =?UTF-8?q?too=20much=20time=20to=20run.=20We=20are=20re-enabling=20them?= =?UTF-8?q?=20but=20only=20for=20nightly=20builds=20!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/phpunit.xml.dist | 4 ---- test/phpunit_nightly.xml.dist | 42 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 test/phpunit_nightly.xml.dist diff --git a/test/phpunit.xml.dist b/test/phpunit.xml.dist index 47ecd5e43..1dcf5848d 100644 --- a/test/phpunit.xml.dist +++ b/test/phpunit.xml.dist @@ -44,10 +44,6 @@ itop-tickets - - setup diff --git a/test/phpunit_nightly.xml.dist b/test/phpunit_nightly.xml.dist new file mode 100644 index 000000000..684f4b6ba --- /dev/null +++ b/test/phpunit_nightly.xml.dist @@ -0,0 +1,42 @@ + + + + + + + + + + + + + OQL + + + + + + + ../core/apc-emulation.php + ../core/ormlinkset.class.inc.php + ../datamodels/2.x/itop-tickets/main.itop-tickets.php + + + +