diff --git a/application/dashboard.class.inc.php b/application/dashboard.class.inc.php index d473c2234..b3fb736cf 100644 --- a/application/dashboard.class.inc.php +++ b/application/dashboard.class.inc.php @@ -1549,6 +1549,29 @@ JS return $this->sDefinitionFile; } + /** + * @param string $sDashboardFileRelative can also be an absolute path (compatibility with old URL) + * + * @return string full path to the Dashboard file + * @throws \SecurityException if path isn't under approot + * @uses utils::RealPath() + * @since 2.7.8 3.0.3 3.1.0 N°4449 remove FPD + */ + public static function GetDashboardFileFromRelativePath($sDashboardFileRelative) + { + if (utils::RealPath($sDashboardFileRelative, APPROOT)) { + // compatibility with old URL containing absolute path ! + return $sDashboardFileRelative; + } + + $sDashboardFile = APPROOT.$sDashboardFileRelative; + if (false === utils::RealPath($sDashboardFile, APPROOT)) { + throw new SecurityException('Invalid dashboard file !'); + } + + return $sDashboardFile; + } + /** * @param string $sDefinitionFile */ diff --git a/application/utils.inc.php b/application/utils.inc.php index 614a7f8e1..3afd936ed 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -1467,19 +1467,19 @@ class utils $oDashboard = $param; $sDashboardId = $oDashboard->GetId(); $sDashboardFile = $oDashboard->GetDefinitionFile(); + $sDashboardFileRelative = utils::LocalPath($sDashboardFile); $sDlgTitle = addslashes(Dict::S('UI:ImportDashboardTitle')); $sDlgText = addslashes(Dict::S('UI:ImportDashboardText')); $sCloseBtn = addslashes(Dict::S('UI:Button:Cancel')); - $sDashboardFileJS = addslashes($sDashboardFile); - $sDashboardFileURL = urlencode($sDashboardFile); + $sDashboardFileJS = addslashes($sDashboardFileRelative); + $sDashboardFileURL = urlencode($sDashboardFileRelative); $sUploadDashboardTransactId = utils::GetNewTransactionId(); $aResult = array( new SeparatorPopupMenuItem(), new URLPopupMenuItem('UI:ExportDashboard', Dict::S('UI:ExportDashBoard'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=export_dashboard&id='.$sDashboardId.'&file='.$sDashboardFileURL), new JSPopupMenuItem('UI:ImportDashboard', Dict::S('UI:ImportDashBoard'), "UploadDashboard({dashboard_id: '$sDashboardId', file: '$sDashboardFileJS', title: '$sDlgTitle', text: '$sDlgText', close_btn: '$sCloseBtn', transaction: '$sUploadDashboardTransactId' })"), ); - if ($oDashboard->GetReloadURL()) - { + if ($oDashboard->GetReloadURL()) { $aResult[] = new SeparatorPopupMenuItem(); $aResult[] = new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $oDashboard->GetReloadURL().'&printable=1', '_blank'); } diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 57bb8e6d1..3b871f467 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -876,7 +876,10 @@ try case 'export_dashboard': $oPage = new DownloadPage(''); $sDashboardId = utils::ReadParam('id', '', false, 'raw_data'); - $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); + $sDashboardFileRelative = utils::ReadParam('file', '', false, 'raw_data'); + + $sDashboardFile = RuntimeDashboard::GetDashboardFileFromRelativePath($sDashboardFileRelative); + $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); if (!is_null($oDashboard)) { $oPage->TrashUnexpectedOutput(); @@ -891,18 +894,18 @@ try $oPage->SetOutputDataOnly(true); $sTransactionId = utils::ReadParam('transaction_id', '', false, 'transaction_id'); - if (!utils::IsTransactionValid($sTransactionId, true)) - { + if (!utils::IsTransactionValid($sTransactionId, true)) { throw new SecurityException('ajax.render.php import_dashboard : invalid transaction_id'); } $sDashboardId = utils::ReadParam('id', '', false, 'raw_data'); - $sDashboardFile = utils::ReadParam('file', '', false, 'raw_data'); + $sDashboardFileRelative = utils::ReadParam('file', '', false, 'raw_data'); + + $sDashboardFile = RuntimeDashboard::GetDashboardFileFromRelativePath($sDashboardFileRelative); + $oDashboard = RuntimeDashboard::GetDashboard($sDashboardFile, $sDashboardId); $aResult = array('error' => ''); - if (!is_null($oDashboard)) - { - try - { + if (!is_null($oDashboard)) { + try { $oDoc = utils::ReadPostedDocument('dashboard_upload_file'); $oDashboard->FromXml($oDoc->GetData()); $oDashboard->Save(); diff --git a/test/application/RuntimeDashboardTest.php b/test/application/RuntimeDashboardTest.php new file mode 100644 index 000000000..b8dc86b8d --- /dev/null +++ b/test/application/RuntimeDashboardTest.php @@ -0,0 +1,46 @@ +assertNotNull($oDashboard); + + $this->expectException(SecurityException::class); + $sDashboardFileSuspect = APPROOT.self::SYSTEM_FILE_PATH;; + RuntimeDashboard::GetDashboard($sDashboardFileSuspect, $sDashboardId); + } + + /** @noinspection PhpUnhandledExceptionInspection */ + public function testGetDefinitionFileRelative() + { + $sFullDashboardPath = RuntimeDashboard::GetDashboardFileFromRelativePath(self::DEFAULT_WELCOME_DASHBOARD_PATH); + $this->assertSame(APPROOT.self::DEFAULT_WELCOME_DASHBOARD_PATH, $sFullDashboardPath); + + $this->expectException(SecurityException::class); + RuntimeDashboard::GetDashboardFileFromRelativePath(self::SYSTEM_FILE_PATH); + } +}