diff --git a/public/main/admin/add_courses_to_usergroup.php b/public/main/admin/add_courses_to_usergroup.php index 1d91c909328..c31df8bb649 100644 --- a/public/main/admin/add_courses_to_usergroup.php +++ b/public/main/admin/add_courses_to_usergroup.php @@ -48,8 +48,8 @@ function remove_item(origin) { '; if (isset($_POST['form_sent']) && $_POST['form_sent']) { - $form_sent = $_POST['form_sent']; - $elements_posted = $_POST['elements_in_name']; + $form_sent = (int) $_POST['form_sent']; + $elements_posted = Security::remove_XSS($_POST['elements_in_name']); if (!is_array($elements_posted)) { $elements_posted = []; } @@ -192,7 +192,7 @@ function search($needle, $type) $searchForm->display(); echo ''; ?> -
- -'; if (isset($_POST['form_sent']) && $_POST['form_sent']) { - $form_sent = $_POST['form_sent']; - $elements_posted = $_POST['elements_in_name']; + $form_sent = (int) $_POST['form_sent']; + $elements_posted = Security::remove_XSS($_POST['elements_in_name']); if (!is_array($elements_posted)) { $elements_posted = []; } diff --git a/public/main/admin/course_list.php b/public/main/admin/course_list.php index 3c98e9a760a..0cb5043009c 100644 --- a/public/main/admin/course_list.php +++ b/public/main/admin/course_list.php @@ -245,7 +245,11 @@ function get_course_data( ICON_SIZE_SMALL, get_lang('Delete') ), - $path.'admin/course_list.php?delete_course='.$course['col0'], + $path.'admin/course_list.php?' + .http_build_query([ + 'delete_course' => $course['col0'], + 'sec_token' => Security::getTokenFromSession(), + ]), [ 'onclick' => "javascript: if (!confirm('" .addslashes(api_htmlentities(get_lang('Please confirm your choice'), \ENT_QUOTES)) @@ -320,7 +324,7 @@ function get_course_visibility_icon(int $visibility): string }; } -if (isset($_POST['action'])) { +if (isset($_POST['action']) && Security::check_token('get')) { // Delete selected courses if ('delete_courses' == $_POST['action']) { if (!empty($_POST['course'])) { @@ -393,7 +397,7 @@ function get_course_visibility_icon(int $visibility): string $content .= $form->returnForm(); } else { $tool_name = get_lang('Course list'); - if (isset($_GET['delete_course'])) { + if (isset($_GET['delete_course']) && Security::check_token('get')) { $result = CourseManager::delete_course($_GET['delete_course']); if ($result) { Display::addFlash(Display::return_message(get_lang('Deleted'))); @@ -504,6 +508,7 @@ function get_course_visibility_icon(int $visibility): string ); $parameters = []; + $parameters['sec_token'] = Security::get_token(); if (isset($_GET['keyword'])) { $parameters = ['keyword' => Security::remove_XSS($_GET['keyword'])]; } elseif (isset($_GET['keyword_code'])) { diff --git a/public/main/admin/course_list_admin.php b/public/main/admin/course_list_admin.php index c2d6cc142d8..bf2b1f4f4d9 100644 --- a/public/main/admin/course_list_admin.php +++ b/public/main/admin/course_list_admin.php @@ -194,7 +194,10 @@ function get_course_data($from, $number_of_items, $column, $direction, $dataFunc ); $actions[] = Display::url( Display::getMdiIcon('delete', 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')), - $path.'admin/course_list_admin.php?delete_course='.$courseCode, + $path.'admin/course_list_admin.php?'.http_build_query([ + 'delete_course' => $courseCode, + 'sec_token' => Security::getTokenFromSession(), + ]), [ 'onclick' => "javascript: if (!confirm('" .addslashes(api_htmlentities(get_lang('Please confirm your choice'), ENT_QUOTES))."')) return false;", @@ -266,7 +269,7 @@ function get_course_visibility_icon($visibility) } } -if (isset($_POST['action'])) { +if (isset($_POST['action']) && Security::check_token('get')) { switch ($_POST['action']) { // Delete selected courses case 'delete_courses': @@ -358,7 +361,7 @@ function get_course_visibility_icon($visibility) 'name' => get_lang('PlatformAdmin'), ]; $tool_name = get_lang('CourseList'); - if (isset($_GET['delete_course'])) { + if (isset($_GET['delete_course']) && Security::check_token('get')) { $result = CourseManager::delete_course($_GET['delete_course']); if ($result) { Display::addFlash(Display::return_message(get_lang('Deleted'))); @@ -425,6 +428,7 @@ function get_course_visibility_icon($visibility) ); $parameters = []; + $parameters['sec_token'] = Security::get_token(); if (isset($_GET['keyword'])) { $parameters = ['keyword' => Security::remove_XSS($_GET['keyword'])]; } elseif (isset($_GET['keyword_code'])) { diff --git a/public/main/admin/dashboard_add_sessions_to_user.php b/public/main/admin/dashboard_add_sessions_to_user.php index f3c47eeaf92..beb9c95a10d 100644 --- a/public/main/admin/dashboard_add_sessions_to_user.php +++ b/public/main/admin/dashboard_add_sessions_to_user.php @@ -154,12 +154,12 @@ function remove_item(origin) { '; $formSent = 0; -$firstLetterSession = isset($_POST['firstLetterSession']) ? $_POST['firstLetterSession'] : null; +$firstLetterSession = isset($_POST['firstLetterSession']) ? Security::remove_XSS($_POST['firstLetterSession']) : null; $errorMsg = ''; $UserList = []; if (isset($_POST['formSent']) && 1 == (int) ($_POST['formSent'])) { - $sessions_list = $_POST['SessionsList']; + $sessions_list = Security::remove_XSS($_POST['SessionsList']); $userInfo = api_get_user_info($user_id); $affected_rows = SessionManager::subscribeSessionsToDrh( $userInfo, diff --git a/public/main/admin/dashboard_add_users_to_user.php b/public/main/admin/dashboard_add_users_to_user.php index 472ba4aa62c..f382335c68b 100644 --- a/public/main/admin/dashboard_add_users_to_user.php +++ b/public/main/admin/dashboard_add_users_to_user.php @@ -40,7 +40,7 @@ $user = api_get_user_entity($user_id); $isSessionAdmin = api_is_session_admin($user); -$firstLetterUser = $_POST['firstLetterUser'] ?? null; +$firstLetterUser = isset($_POST['firstLetterUser']) ? Security::remove_XSS($_POST['firstLetterUser']) : null; // setting the name of the tool $isAdmin = UserManager::is_admin($user_id); @@ -291,7 +291,7 @@ function remove_item(origin) { } if (isset($_POST['formSent']) && 1 == (int) ($_POST['formSent'])) { - $user_list = isset($_POST['UsersList']) ? $_POST['UsersList'] : null; + $user_list = isset($_POST['UsersList']) ? Security::remove_XSS($_POST['UsersList']) : null; switch ($userStatus) { case DRH: case PLATFORM_ADMIN: diff --git a/public/main/admin/index.php b/public/main/admin/index.php index fdb2e2f4679..6a8c7f03585 100644 --- a/public/main/admin/index.php +++ b/public/main/admin/index.php @@ -575,22 +575,6 @@ $admin_ajax_url = api_get_path(WEB_AJAX_PATH).'admin.ajax.php'; $tpl = new Template(); - -// Display the Site Use Cookie Warning Validation -$useCookieValidation = api_get_setting('cookie_warning'); -if ('true' === $useCookieValidation) { - if (isset($_POST['acceptCookies'])) { - api_set_site_use_cookie_warning_cookie(); - } elseif (!api_site_use_cookie_warning_cookie_exist()) { - if (Template::isToolBarDisplayedForUser()) { - $tpl->assign('toolBarDisplayed', true); - } else { - $tpl->assign('toolBarDisplayed', false); - } - $tpl->assign('displayCookieUsageWarning', true); - } -} - $tpl->assign('web_admin_ajax_url', $admin_ajax_url); $tpl->assign('blocks_admin', $blocks); diff --git a/public/main/admin/languages.php b/public/main/admin/languages.php index e719be2165b..e825cbf8728 100644 --- a/public/main/admin/languages.php +++ b/public/main/admin/languages.php @@ -180,13 +180,15 @@ if (isset($_POST['Submit']) && $_POST['Submit']) { // changing the name - $name = Database::escape_string($_POST['txt_name']); + $name = html_filter($_POST['txt_name']); $postId = (int) $_POST['edit_id']; - $sql = "UPDATE $tbl_admin_languages SET original_name='$name' - WHERE id='$postId'"; - $result = Database::query($sql); + Database::update( + $tbl_admin_languages, + ['original_name' => $name], + ['id = ?' => $postId] + ); // changing the Platform language - if ($_POST['platformlanguage'] && '' != $_POST['platformlanguage']) { + if (isset($_POST['platformlanguage']) && '' != $_POST['platformlanguage']) { api_set_setting('platformLanguage', $_POST['platformlanguage'], null, null, api_get_current_access_url_id()); header("Location: $url"); exit; diff --git a/public/main/admin/resource_sequence.php b/public/main/admin/resource_sequence.php index 149c7752885..203735c6449 100644 --- a/public/main/admin/resource_sequence.php +++ b/public/main/admin/resource_sequence.php @@ -5,6 +5,7 @@ use Chamilo\CoreBundle\Entity\Sequence; use Chamilo\CoreBundle\Entity\SequenceResource; use ChamiloSession as Session; +use Symfony\Component\HttpFoundation\Request as HttpRequest; $cidReset = true; @@ -14,10 +15,14 @@ Session::erase('sr_vertex'); +$httpRequest = HttpRequest::createFromGlobals(); + // setting breadcrumbs $interbreadcrumb[] = ['url' => 'index.php', 'name' => get_lang('PlatformAdmin')]; -$type = isset($_REQUEST['type']) ? (int) $_REQUEST['type'] : SequenceResource::SESSION_TYPE; +$type = $httpRequest->query->has('type') + ? $httpRequest->query->getInt('type', SequenceResource::SESSION_TYPE) + : $httpRequest->request->getInt('type', SequenceResource::SESSION_TYPE); $tpl = new Template(get_lang('ResourcesSequencing')); $em = Database::getManager(); @@ -27,6 +32,7 @@ $formSequence = new FormValidator('sequence_form', 'post', $currentUrl, null, null, FormValidator::LAYOUT_INLINE); $formSequence->addText('name', get_lang('Sequence'), true, ['cols-size' => [3, 8, 1]]); +$formSequence->applyFilter('name', 'html_filter'); $formSequence->addButtonCreate(get_lang('AddSequence'), 'submit_sequence', false, ['cols-size' => [3, 8, 1]]); $em = Database::getManager(); diff --git a/public/main/admin/system_announcements.php b/public/main/admin/system_announcements.php index f5bca44eeab..6e37a75ffae 100644 --- a/public/main/admin/system_announcements.php +++ b/public/main/admin/system_announcements.php @@ -179,6 +179,7 @@ function showCareer() { $form = new FormValidator('system_announcement', 'post', $url); $form->addHeader($form_title); $form->addText('title', get_lang('Title'), true); + $form->applyFilter('title', 'html_filter'); $extraOption = []; $extraOption['all'] = get_lang('All'); diff --git a/public/main/admin/user_add.php b/public/main/admin/user_add.php index 55b14323824..3a48c1fa68d 100644 --- a/public/main/admin/user_add.php +++ b/public/main/admin/user_add.php @@ -153,7 +153,7 @@ function updateStatus(){ } // Phone -$form->addElement('text', 'phone', get_lang('Phone number'), ['autocomplete' => 'off', 'id' => 'phone']); +$form->addText('phone', get_lang('Phone number'), false, ['autocomplete' => 'off', 'id' => 'phone']); // Picture $form->addFile( 'picture', diff --git a/public/main/admin/user_anonymize_import.php b/public/main/admin/user_anonymize_import.php index b820c4c8561..82c7ac1ab92 100644 --- a/public/main/admin/user_anonymize_import.php +++ b/public/main/admin/user_anonymize_import.php @@ -5,6 +5,7 @@ * This tool allows platform admins to anonymize users by uploading a text file, with one username per line. */ +use Chamilo\CoreBundle\Entity\User; use Doctrine\Common\Collections\Criteria; $cidReset = true; @@ -38,7 +39,9 @@ $step2Form->addButtonUpdate(get_lang('Anonymize')); if ($step1Form->validate() && $usernameListFile->isUploadedFile()) { - $filePath = $usernameListFile->getValue()['tmp_name']; + $usernameListFileUploaded = $usernameListFile->getValue(); + $usernameListFileUploaded['name'] = api_htmlentities($usernameListFileUploaded['name']); + $filePath = $usernameListFileUploaded['tmp_name']; if (!file_exists($filePath)) { throw new Exception(get_lang('CouldNotReadFile').' '.$filePath); } @@ -46,15 +49,19 @@ if (false === $submittedUsernames) { throw new Exception(get_lang('CouldNotReadFileLines').' '.$filePath); } + + $submittedUsernames = array_map('api_htmlentities', $submittedUsernames); + $submittedUsernames = array_filter($submittedUsernames); + if (empty($submittedUsernames)) { printf( '

'.get_lang('FileXHasNoData').'

', - ''.$usernameListFile->getValue()['name'].'' + ''.$usernameListFileUploaded['name'].'' ); } else { printf( '

'.get_lang('FileXHasYNonEmptyLines').'

', - ''.$usernameListFile->getValue()['name'].'', + ''.$usernameListFileUploaded['name'].'', count($submittedUsernames) ); $uniqueSubmittedUsernames = array_values(array_unique($submittedUsernames)); @@ -78,6 +85,7 @@ echo '

'.get_lang('NoLineMatchedAnyActualUserName').'

'; } else { $foundUsernames = []; + /** @var User $user */ foreach ($users as $user) { $foundUsernames[] = $user->getUsername(); } @@ -112,6 +120,7 @@ $anonymized = []; $errors = []; $tableSession = Database::get_main_table(TABLE_MAIN_SESSION); + /** @var User $user */ foreach ($users as $user) { $username = $user->getUsername(); $userId = $user->getId(); diff --git a/public/main/admin/user_edit.php b/public/main/admin/user_edit.php index 497f3865e3e..e20ddfae49a 100644 --- a/public/main/admin/user_edit.php +++ b/public/main/admin/user_edit.php @@ -434,7 +434,11 @@ function confirmation(name) { $phone = $user['phone']; $username = $user['username'] ?? $userInfo['username']; $status = (int) $user['status']; - $platform_admin = (int) $user['platform_admin']; + $platform_admin = 0; + // Only platform admin can change user status to admin. + if (api_is_platform_admin()) { + $platform_admin = (int) $user['platform_admin']; + } $send_mail = (int) $user['send_mail']; $reset_password = (int) $user['reset_password']; $hr_dept_id = isset($user['hr_dept_id']) ? intval($user['hr_dept_id']) : null; diff --git a/public/main/admin/user_list.php b/public/main/admin/user_list.php index f8550bcfe79..1828c4014b9 100644 --- a/public/main/admin/user_list.php +++ b/public/main/admin/user_list.php @@ -225,7 +225,7 @@ function prepare_user_sql_query(bool $getCount, bool $showDeletedUsers = false): foreach ($keywordList as $keyword) { $keywordListValues[$keyword] = null; if (isset($_GET[$keyword]) && !empty($_GET[$keyword])) { - $keywordListValues[$keyword] = $_GET[$keyword]; + $keywordListValues[$keyword] = Security::remove_XSS($_GET[$keyword]); $atLeastOne = true; } } diff --git a/public/main/admin/user_list_consent.php b/public/main/admin/user_list_consent.php index 01f7b87f28e..807a552aafd 100644 --- a/public/main/admin/user_list_consent.php +++ b/public/main/admin/user_list_consent.php @@ -98,7 +98,7 @@ function prepare_user_sql_query($getCount) foreach ($keywordList as $keyword) { $keywordListValues[$keyword] = null; if (isset($_GET[$keyword]) && !empty($_GET[$keyword])) { - $keywordListValues[$keyword] = $_GET[$keyword]; + $keywordListValues[$keyword] = Security::remove_XSS($_GET[$keyword]); $atLeastOne = true; } } diff --git a/public/main/auth/profile.php b/public/main/auth/profile.php index acd82bb7c3b..f7966a1a80e 100644 --- a/public/main/auth/profile.php +++ b/public/main/auth/profile.php @@ -368,6 +368,7 @@ function show_image(image,width,height) { if ($form->validate()) { $wrong_current_password = false; $user_data = $form->getSubmitValues(1); + $user_data['item_id'] = api_get_user_id(); $user = api_get_user_entity(api_get_user_id()); // set password if a new one was provided @@ -661,7 +662,7 @@ function show_image(image,width,height) { $actions .= ''. Display::getMdiIcon(ToolIcon::MESSAGE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Messages')).''; } - $show = isset($_GET['show']) ? '&show='.Security::remove_XSS($_GET['show']) : ''; + $show = isset($_GET['show']) ? '&show='.(int) $_GET['show'] : ''; if (isset($_GET['type']) && 'extended' === $_GET['type']) { $actions .= ''. diff --git a/public/main/auth/reset.php b/public/main/auth/reset.php index 315d5c5f1f6..f0f14deb3d9 100644 --- a/public/main/auth/reset.php +++ b/public/main/auth/reset.php @@ -3,7 +3,7 @@ require_once __DIR__.'/../inc/global.inc.php'; -$token = isset($_GET['token']) ? $_GET['token'] : ''; +$token = $_GET['token'] ?? ''; if (!ctype_alnum($token)) { $token = ''; @@ -61,6 +61,14 @@ Database::getManager()->persist($user); Database::getManager()->flush(); + if ('true' === api_get_setting('platform.force_renew_password_at_first_login')) { + $extraFieldValue = new ExtraFieldValue('user'); + $value = $extraFieldValue->get_values_by_handler_and_field_variable($user->getId(), 'ask_new_password'); + if (!empty($value) && isset($value['value']) && 1 === (int) $value['value']) { + $extraFieldValue->delete($value['id']); + } + } + Display::addFlash(Display::return_message(get_lang('Update successful'))); header('Location: '.api_get_path(WEB_PATH)); exit; diff --git a/public/main/auth/user_mail_confirmation.php b/public/main/auth/user_mail_confirmation.php index 5243f344fa6..3ab9db8c94b 100644 --- a/public/main/auth/user_mail_confirmation.php +++ b/public/main/auth/user_mail_confirmation.php @@ -3,7 +3,7 @@ require_once __DIR__.'/../inc/global.inc.php'; -$token = isset($_GET['token']) ? $_GET['token'] : ''; +$token = $_GET['token'] ?? ''; if (!ctype_alnum($token)) { $token = ''; diff --git a/public/main/calendar/agenda_js.php b/public/main/calendar/agenda_js.php index 9783a3cb2df..e8396bbc9bd 100644 --- a/public/main/calendar/agenda_js.php +++ b/public/main/calendar/agenda_js.php @@ -275,7 +275,11 @@ ]; } $tpl->assign('on_hover_info', $options); - +if (api_is_https()) { + $tpl->assign('is_https', 1); +} else { + $tpl->assign('is_https', 0); +} $templateName = $tpl->get_template('agenda/month.tpl'); $content = $tpl->fetch($templateName); $tpl->assign('content', $content); diff --git a/public/main/exercise/MultipleAnswerTrueFalseDegreeCertainty.php b/public/main/exercise/MultipleAnswerTrueFalseDegreeCertainty.php index 02f73fc658b..d7bc1b55cd4 100644 --- a/public/main/exercise/MultipleAnswerTrueFalseDegreeCertainty.php +++ b/public/main/exercise/MultipleAnswerTrueFalseDegreeCertainty.php @@ -166,6 +166,7 @@ public function createAnswersForm($form) ['style' => 'vertical-align:middle;'], ); $form->addRule('answer['.$i.']', get_lang('Required field'), 'required'); + $form->applyFilter("answer[$i]", 'attr_on_filter'); if (isset($_POST['answer']) && isset($_POST['answer'][$i])) { $form->getElement("answer[$i]")->setValue(Security::remove_XSS($_POST['answer'][$i])); @@ -183,6 +184,7 @@ public function createAnswersForm($form) if (isset($_POST['comment']) && isset($_POST['comment'][$i])) { $form->getElement("comment[$i]")->setValue(Security::remove_XSS($_POST['comment'][$i])); } + $form->applyFilter("comment[$i]", 'attr_on_filter'); } $form->addElement('html', ''); } diff --git a/public/main/exercise/calculated_answer.class.php b/public/main/exercise/calculated_answer.class.php index 26211dcb2af..5e37ebe1d11 100644 --- a/public/main/exercise/calculated_answer.class.php +++ b/public/main/exercise/calculated_answer.class.php @@ -135,6 +135,7 @@ function updateBlanks(e) { 'regex', '/\[.*\]/' ); + $form->applyFilter('answer', 'attr_on_filter'); $form->addElement( 'label', diff --git a/public/main/exercise/exercise_reminder.php b/public/main/exercise/exercise_reminder.php index 56d08281fc2..da5db80a4cd 100644 --- a/public/main/exercise/exercise_reminder.php +++ b/public/main/exercise/exercise_reminder.php @@ -76,7 +76,7 @@ $question_list = explode(',', $exercise_stat_info['data_tracking']); } -if (empty($exercise_stat_info) || empty($question_list)) { +if (empty($exercise_stat_info) || empty($question_list) || $exercise_stat_info['exe_user_id'] != api_get_user_id()) { api_not_allowed(); } diff --git a/public/main/exercise/exercise_show.php b/public/main/exercise/exercise_show.php index 9837fc4bf4e..b39754d96d1 100644 --- a/public/main/exercise/exercise_show.php +++ b/public/main/exercise/exercise_show.php @@ -659,6 +659,7 @@ function getFCK(vals, marksid) { ); } else { $feedback_form->addElement('textarea', $textareaId, ['id' => $textareaId]); + $feedback_form->applyFilter($textareaId, 'attr_on_filter'); } $feedback_form->setDefaults($default); $feedback_form->display(); diff --git a/public/main/exercise/multiple_answer_true_false.class.php b/public/main/exercise/multiple_answer_true_false.class.php index 56f2f0ee7e1..d0c5229cab6 100644 --- a/public/main/exercise/multiple_answer_true_false.class.php +++ b/public/main/exercise/multiple_answer_true_false.class.php @@ -162,6 +162,7 @@ public function createAnswersForm($form) 'Height' => '100', ] ); + $form->applyFilter("comment[$i]", 'attr_on_filter'); if (isset($_POST['comment']) && isset($_POST['comment'][$i])) { $form->getElement("comment[$i]")->setValue(Security::remove_XSS($_POST['comment'][$i])); } diff --git a/public/main/exercise/unique_answer_no_option.class.php b/public/main/exercise/unique_answer_no_option.class.php index c8b31370b02..726f57b8414 100644 --- a/public/main/exercise/unique_answer_no_option.class.php +++ b/public/main/exercise/unique_answer_no_option.class.php @@ -243,6 +243,7 @@ public function createAnswersForm($form) $form->addHtmlEditor('answer['.$i.']', null, true, [], $editor_config); $form->addRule('answer['.$i.']', get_lang('Required field'), 'required'); + $form->applyFilter("answer[$i]", 'attr_on_filter'); $form->addHtmlEditor('comment['.$i.']', null, true, [], $editor_config); $form->addElement('text', "weighting[$i]", null)->freeze(); diff --git a/public/main/forum/forumfunction.inc.php b/public/main/forum/forumfunction.inc.php index eeff2b23877..16d25872411 100644 --- a/public/main/forum/forumfunction.inc.php +++ b/public/main/forum/forumfunction.inc.php @@ -216,6 +216,7 @@ function getForumCategoryAddForm(int $lp_id = null): string // Setting the form elements. $form->addHeader(get_lang('Add forum category')); $form->addText('forum_category_title', get_lang('Title'), true, ['autofocus']); + $form->applyFilter('forum_category_title', 'html_filter'); $form->addHtmlEditor( 'forum_category_comment', get_lang('Description'), @@ -293,6 +294,7 @@ function forumForm(CForum $forum = null, int $lp_id = null): string // The title of the forum $form->addText('forum_title', get_lang('Title'), true, ['autofocus']); + $form->applyFilter('forum_title', 'html_filter'); // The comment of the forum. $form->addHtmlEditor( @@ -521,6 +523,7 @@ function editForumCategoryForm(CForumCategory $category): string $form->addElement('hidden', 'action', 'edit_category'); $form->addElement('hidden', 'forum_category_id'); $form->addElement('text', 'forum_category_title', get_lang('Title')); + $form->applyFilter('forum_category_title', 'html_filter'); $form->addElement( 'html_editor', @@ -1934,6 +1937,7 @@ function show_add_post_form(CForum $forum, CForumThread $thread, CForumPost $pos } $form->addElement('text', 'post_title', get_lang('Title')); + $form->applyFilter('post_title', 'html_filter'); $form->addHtmlEditor( 'post_text', get_lang('Text'), @@ -2197,6 +2201,7 @@ function newThread(CForum $forum, $form_values = '', $showPreview = true) } $form->addElement('text', 'post_title', get_lang('Title')); + $form->applyFilter('post_title', 'html_filter'); $form->addHtmlEditor( 'post_text', get_lang('Text'), diff --git a/public/main/gradebook/lib/fe/evalform.class.php b/public/main/gradebook/lib/fe/evalform.class.php index daf7c119c95..4bfbeb32e8a 100644 --- a/public/main/gradebook/lib/fe/evalform.class.php +++ b/public/main/gradebook/lib/fe/evalform.class.php @@ -77,6 +77,7 @@ public function __construct( $this->build_add_user_to_eval(); break; } + $this->protect(); $this->setDefaults(); } diff --git a/public/main/inc/ajax/announcement.ajax.php b/public/main/inc/ajax/announcement.ajax.php index 17c102817f7..f7d65c9e22f 100644 --- a/public/main/inc/ajax/announcement.ajax.php +++ b/public/main/inc/ajax/announcement.ajax.php @@ -16,6 +16,7 @@ $courseCode = api_get_course_id(); $groupId = api_get_group_id(); $sessionId = api_get_session_id(); +$currentUserId = api_get_user_id(); $isTutor = false; if (!empty($groupId)) { @@ -29,9 +30,13 @@ switch ($action) { case 'preview': + $userInCourse = false; + if ($courseId != 0 && CourseManager::is_user_subscribed_in_course($currentUserId, CourseManager::get_course_code_from_course_id($courseId), true, $sessionId)) { + $userInCourse = true; + } $allowToEdit = ( api_is_allowed_to_edit(false, true) || - (1 === (int) api_get_course_setting('allow_user_edit_announcement') && !api_is_anonymous()) + (1 === (int) api_get_course_setting('allow_user_edit_announcement') && !api_is_anonymous() && $userInCourse) ); $drhHasAccessToSessionContent = api_drh_can_access_all_session_content(); @@ -49,7 +54,11 @@ // Last chance ... students can send announcements. if (GroupManager::TOOL_PRIVATE_BETWEEN_USERS == $groupEntity->getAnnouncementsState()) { - $allowToEdit = true; + // check if user is a group member to give access + $groupInfo = GroupManager::get_group_properties($groupId); + if (array_key_exists($currentUserId,GroupManager::get_subscribed_users($groupEntity))) { + $allowToEdit = true; + } } } diff --git a/public/main/inc/ajax/plugin.ajax.php b/public/main/inc/ajax/plugin.ajax.php index 2f25794fc4e..8c9b6ba2d4e 100644 --- a/public/main/inc/ajax/plugin.ajax.php +++ b/public/main/inc/ajax/plugin.ajax.php @@ -13,8 +13,15 @@ switch ($action) { case 'md_to_html': - $plugin = isset($_GET['plugin']) ? $_GET['plugin'] : ''; + $plugin = $_GET['plugin'] ?? ''; $appPlugin = new AppPlugin(); + + $pluginPaths = $appPlugin->read_plugins_from_path(); + if (!in_array($plugin, $pluginPaths)) { + echo Display::return_message(get_lang('NotAllowed'), 'error', false); + exit; + } + $pluginInfo = $appPlugin->getPluginInfo($plugin); $html = ''; diff --git a/public/main/inc/ajax/user_manager.ajax.php b/public/main/inc/ajax/user_manager.ajax.php index 6ff89d8c5c4..4f5d78d8dfd 100644 --- a/public/main/inc/ajax/user_manager.ajax.php +++ b/public/main/inc/ajax/user_manager.ajax.php @@ -14,6 +14,8 @@ switch ($action) { case 'get_user_like': + api_block_anonymous_users(false); + if (api_is_platform_admin() || api_is_drh()) { $query = $_REQUEST['q']; $conditions = [ @@ -73,7 +75,11 @@ // Only allow anonymous users to see user popup if the popup user // is a teacher (which might be necessary to illustrate a course) if (COURSEMANAGER === (int) $user_info['status']) { - echo $userData; + if ($user_info['status'] === COURSEMANAGER) { + echo $userData; + } else { + echo '

-

'; + } } } else { echo Display::url( @@ -245,7 +251,9 @@ } break; case 'user_by_role': - api_block_anonymous_users(false); + if (!api_is_platform_admin()) { + api_not_allowed(false, null, 403); + } $status = isset($_REQUEST['status']) ? (int) $_REQUEST['status'] : DRH; $role = User::getRoleFromStatus($status); diff --git a/public/main/inc/lib/CoursesAndSessionsCatalog.class.php b/public/main/inc/lib/CoursesAndSessionsCatalog.class.php index ade533f6832..a7ff5dcef7b 100644 --- a/public/main/inc/lib/CoursesAndSessionsCatalog.class.php +++ b/public/main/inc/lib/CoursesAndSessionsCatalog.class.php @@ -241,11 +241,11 @@ public static function getLimitFilterFromArray($limit) * @param string $categoryCode * @param int $randomValue * @param array $limit will be used if $randomValue is not set. - * This array should contains 'start' and 'length' keys + * This array should contain 'start' and 'length' keys * * @return array */ - public static function getCoursesInCategory($categoryCode, $randomValue = null, $limit = []) + public static function getCoursesInCategory(string $categoryCode, $randomValue = null, $limit = []) { $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE); $avoidCoursesCondition = self::getAvoidCourseCondition(); @@ -1506,7 +1506,7 @@ public static function getTabList($active = 1) */ public static function sessionsListByCoursesTag(array $limit) { - $searchTag = isset($_REQUEST['search_tag']) ? $_REQUEST['search_tag'] : ''; + $searchTag = $_REQUEST['search_tag'] ? Security::remove_XSS($_REQUEST['search_tag']) : ''; $searchDate = isset($_REQUEST['date']) ? $_REQUEST['date'] : date('Y-m-d'); $courseUrl = self::getCatalogUrl( 1, @@ -1854,7 +1854,7 @@ public static function getCatalogUrl( $action = isset($action) ? Security::remove_XSS($action) : $requestAction; $searchTerm = isset($_REQUEST['search_term']) ? Security::remove_XSS($_REQUEST['search_term']) : ''; $keyword = isset($_REQUEST['keyword']) ? Security::remove_XSS($_REQUEST['keyword']) : ''; - $searchTag = isset($_REQUEST['search_tag']) ? $_REQUEST['search_tag'] : ''; + $searchTag = $_REQUEST['search_tag'] ? Security::remove_XSS($_REQUEST['search_tag']) : ''; if ('subscribe_user_with_password' === $action) { $action = 'subscribe'; diff --git a/public/main/inc/lib/SkillProfileModel.php b/public/main/inc/lib/SkillProfileModel.php index 7bedd30afb5..901b293ae8b 100644 --- a/public/main/inc/lib/SkillProfileModel.php +++ b/public/main/inc/lib/SkillProfileModel.php @@ -45,11 +45,14 @@ public function updateProfileInfo($profileId, $name, $description) $name = Database::escape_string($name); $description = Database::escape_string($description); - $sql = "UPDATE $this->table SET - name = '$name', - description = '$description' - WHERE id = $profileId "; - Database::query($sql); + Database::update( + $this->table, + [ + 'name' => html_filter($name), + 'description' => html_filter($description), + ], + ['id = ?' => $profileId] + ); return true; } @@ -65,6 +68,8 @@ public function updateProfileInfo($profileId, $name, $description) public function save($params, $showQuery = false) { if (!empty($params)) { + $params['name'] = html_filter($params['name']); + $params['description'] = html_filter($params['description']); $profile_id = parent::save($params, $showQuery); if ($profile_id) { $skill_rel_profile = new SkillRelProfileModel(); diff --git a/public/main/inc/lib/api.lib.php b/public/main/inc/lib/api.lib.php index 971bfd81a8b..4adef766425 100644 --- a/public/main/inc/lib/api.lib.php +++ b/public/main/inc/lib/api.lib.php @@ -7540,6 +7540,22 @@ function api_protect_webservices() } } +function api_filename_has_blacklisted_stream_wrapper(string $filename) { + if (strpos($filename, '://') > 0) { + $wrappers = stream_get_wrappers(); + $allowedWrappers = ['http', 'https', 'file']; + foreach ($wrappers as $wrapper) { + if (in_array($wrapper, $allowedWrappers)) { + continue; + } + if (stripos($filename, $wrapper . '://') === 0) { + return true; + } + } + } + return false; +} + /** * Checks if a set of roles have a specific permission. * diff --git a/public/main/inc/lib/career.lib.php b/public/main/inc/lib/career.lib.php index 43565012efa..2c00cbeef6e 100644 --- a/public/main/inc/lib/career.lib.php +++ b/public/main/inc/lib/career.lib.php @@ -121,7 +121,7 @@ public function return_form($url, $action) $id = isset($_GET['id']) ? (int) $_GET['id'] : ''; $form->addHeader($header); $form->addHidden('id', $id); - $form->addElement('text', 'name', get_lang('Name'), ['size' => '70']); + $form->addText('name', get_lang('Name'), true, ['size' => '70']); $form->addHtmlEditor( 'description', get_lang('Description'), diff --git a/public/main/inc/lib/extra_field.lib.php b/public/main/inc/lib/extra_field.lib.php index f2680d9147e..4a9b9b7a5db 100644 --- a/public/main/inc/lib/extra_field.lib.php +++ b/public/main/inc/lib/extra_field.lib.php @@ -1150,6 +1150,8 @@ public function set_extra_fields_in_form( 'extra_'.$variable, 'trim' ); + $form->applyFilter('extra_'.$variable, 'html_filter'); + if ($freezeElement) { $form->freeze('extra_'.$variable); } @@ -1169,6 +1171,7 @@ public function set_extra_fields_in_form( ); $form->applyFilter('extra_'.$variable, 'stripslashes'); $form->applyFilter('extra_'.$variable, 'trim'); + $form->applyFilter('extra_'.$variable, 'html_filter'); if ($freezeElement) { $form->freeze('extra_'.$variable); } @@ -1631,6 +1634,7 @@ public function set_extra_fields_in_form( $form->applyFilter('extra_'.$variable, 'stripslashes'); $form->applyFilter('extra_'.$variable, 'trim'); $form->applyFilter('extra_'.$variable, 'mobile_phone_number_filter'); + $form->applyFilter('extra_'.$variable, 'html_filter'); $form->addRule( 'extra_'.$variable, get_lang('Mobile phone number is incomplete or contains invalid characters'), @@ -2312,9 +2316,9 @@ public function return_form($url, $action) { $form = new FormValidator($this->type.'_field', 'post', $url); - $form->addElement('hidden', 'type', $this->type); + $form->addHidden('type', $this->type); $id = isset($_GET['id']) ? (int) $_GET['id'] : null; - $form->addElement('hidden', 'id', $id); + $form->addHidden('id', $id); // Setting the form elements $header = get_lang('Add'); @@ -2326,7 +2330,7 @@ public function return_form($url, $action) $defaults = $this->get($id); } - $form->addElement('header', $header); + $form->addHeader($header); if ('edit' === $action) { $translateUrl = Container::getRouter()->generate( @@ -2342,7 +2346,7 @@ public function return_form($url, $action) $form->addElement('text', 'display_text', [get_lang('Name'), $translateButton]); } else { - $form->addElement('text', 'display_text', get_lang('Name')); + $form->addText('display_text', get_lang('Name')); } $form->addHtmlEditor('description', get_lang('Description'), false); @@ -2356,8 +2360,8 @@ public function return_form($url, $action) $types, ['id' => 'field_type'] ); - $form->addElement('label', get_lang('Example'), '
-
'); - $form->addElement('text', 'variable', get_lang('Field label'), ['class' => 'span5']); + $form->addLabel(get_lang('Example'), '
-
'); + $form->addText('variable', get_lang('Field label'), false); $form->addElement( 'text', 'field_options', @@ -2382,7 +2386,7 @@ public function return_form($url, $action) 'extra_field_options.php?type='.$this->type.'&field_id='.$id, ['class' => 'btn'] ); - $form->addElement('label', null, $url); + $form->addLabel(null, $url); if (self::FIELD_TYPE_SELECT == $defaults['value_type']) { $urlWorkFlow = Display::url( @@ -2390,16 +2394,16 @@ public function return_form($url, $action) 'extra_field_workflow.php?type='.$this->type.'&field_id='.$id, ['class' => 'btn'] ); - $form->addElement('label', null, $urlWorkFlow); + $form->addLabel(null, $urlWorkFlow); } $form->freeze('field_options'); } } - $form->addElement( - 'text', + $form->addText( 'default_value', get_lang('Default value'), + false, ['id' => 'default_value'] ); @@ -2430,7 +2434,7 @@ public function return_form($url, $action) $form->addGroup($group, '', get_lang('Field changes should be logged'), '', false); */ - $form->addElement('text', 'field_order', get_lang('Order')); + $form->addNumeric('field_order', get_lang('Order'), ['step' => 1, 'min' => 0]); if ($this->type == 'user') { $form->addElement( diff --git a/public/main/inc/lib/extra_field_value.lib.php b/public/main/inc/lib/extra_field_value.lib.php index 633f20e7cfc..776d1dfa6e9 100644 --- a/public/main/inc/lib/extra_field_value.lib.php +++ b/public/main/inc/lib/extra_field_value.lib.php @@ -297,6 +297,7 @@ public function saveFieldValues( case ExtraField::FIELD_TYPE_FILE: if (isset($value['name']) && !empty($value['tmp_name']) && isset($value['error']) && 0 == $value['error']) { $cleanedName = api_replace_dangerous_char($value['name']); + $cleanedName = disable_dangerous_file($cleanedName); $fileName = ExtraField::FIELD_TYPE_FILE."_{$params['item_id']}_$cleanedName"; $mimeType = mime_content_type($value['tmp_name']); diff --git a/public/main/inc/lib/fileUpload.lib.php b/public/main/inc/lib/fileUpload.lib.php index a04873065dd..9fe2942980d 100644 --- a/public/main/inc/lib/fileUpload.lib.php +++ b/public/main/inc/lib/fileUpload.lib.php @@ -21,7 +21,7 @@ function php2phps($file_name) } /** - * Renames .htaccess & .HTACCESS to htaccess.txt. + * Renames .htaccess & .HTACCESS & .htAccess to htaccess.txt. * * @param string $filename * @@ -29,7 +29,9 @@ function php2phps($file_name) */ function htaccess2txt($filename) { - return str_replace(['.htaccess', '.HTACCESS'], ['htaccess.txt', 'htaccess.txt'], $filename); + $filename = strtolower($filename); + + return str_replace('.htaccess', 'htaccess.txt', $filename); } /** @@ -490,7 +492,7 @@ function filter_extension(&$filename) if ('true' == $skip) { return 0; } else { - $new_ext = api_get_setting('upload_extensions_replace_by'); + $new_ext = getReplacedByExtension(); $filename = str_replace('.'.$ext, '.'.$new_ext, $filename); return 1; @@ -510,7 +512,7 @@ function filter_extension(&$filename) if ('true' == $skip) { return 0; } else { - $new_ext = api_get_setting('upload_extensions_replace_by'); + $new_ext = getReplacedByExtension(); $filename = str_replace('.'.$ext, '.'.$new_ext, $filename); return 1; @@ -521,6 +523,12 @@ function filter_extension(&$filename) } } +function getReplacedByExtension(): string +{ + $extension = api_get_setting('document.upload_extensions_replace_by'); + return 'REPLACED_'.api_replace_dangerous_char(str_replace('.', '', $extension)); +} + /** * Creates a new directory trying to find a directory name * that doesn't already exist. diff --git a/public/main/inc/lib/formvalidator/FormValidator.class.php b/public/main/inc/lib/formvalidator/FormValidator.class.php index 053e0a8fcc2..cefa1fdbf27 100644 --- a/public/main/inc/lib/formvalidator/FormValidator.class.php +++ b/public/main/inc/lib/formvalidator/FormValidator.class.php @@ -215,6 +215,8 @@ public function addText($name, $label, $required = true, $attributes = [], $crea } $this->applyFilter($name, 'trim'); + $this->applyFilter($name, 'html_filter'); + if ($required) { $this->addRule($name, get_lang('Required field'), 'required'); } @@ -1127,6 +1129,7 @@ public function addHtmlEditor( $this->addElement('html_editor', $name, $label, $attributes, $config); $this->applyFilter($name, 'trim'); + $this->applyFilter($name, 'attr_on_filter'); if ($required) { $this->addRule($name, get_lang('Required field'), 'required'); } @@ -1145,6 +1148,17 @@ public function addHtmlEditor( } } + /** + * Prevent execution of event handlers in HTML elements. + * + * @param string $html + * @return string + */ + function attr_on_filter($html) { + $prefix = uniqid('data-cke-').'-'; + return preg_replace('/(\s)(on)/i', '$1'.$prefix.'$2', $html); + } + /** * Adds a Google Maps Geolocalization field to the form. * diff --git a/public/main/inc/lib/message.lib.php b/public/main/inc/lib/message.lib.php index 7c73fb33a4c..136afac90f7 100644 --- a/public/main/inc/lib/message.lib.php +++ b/public/main/inc/lib/message.lib.php @@ -1017,7 +1017,9 @@ public static function display_message_for_group($groupId, $topic_id) $main_content .= '
'; $main_content .= '
'.$user_link.'
'; $main_content .= $date; - $main_content .= '
'.$main_message['content'].$attachment.'
'; + $main_content .= '
' + .Security::remove_XSS($main_message['content'], STUDENT, true) + .$attachment.'
'; $main_content .= ''; $main_content .= ''; @@ -1779,4 +1781,20 @@ public static function getLikesButton($messageId, $userId, $groupId = 0) return $btnLike.PHP_EOL.$btnDislike; } + + /** + * Reports whether the given user is sender or receiver of the given message + */ + public static function isUserOwner(int $userId, int $messageId): bool + { + $table = Database::get_main_table(TABLE_MESSAGE); + $sql = "SELECT id FROM $table + WHERE id = $messageId + AND (user_receiver_id = $userId OR user_sender_id = $userId)"; + $res = Database::query($sql); + if (Database::num_rows($res) === 1) { + return true; + } + return false; + } } diff --git a/public/main/inc/lib/online.inc.php b/public/main/inc/lib/online.inc.php index 77a90280209..b5bbc6f99db 100644 --- a/public/main/inc/lib/online.inc.php +++ b/public/main/inc/lib/online.inc.php @@ -76,10 +76,17 @@ function LoginCheck($uid) function preventMultipleLogin($userId) { $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE); - $userId = intval($userId); + $userId = (int) $userId; if ('true' === api_get_setting('prevent_multiple_simultaneous_login')) { if (!empty($userId) && !api_is_anonymous()) { $isFirstLogin = Session::read('first_user_login'); + $currentIp = Session::read('current_ip'); + $differentIp = false; + if (!empty($currentIp) && api_get_real_ip() !== $currentIp) { + $isFirstLogin = null; + $differentIp = true; + } + if (empty($isFirstLogin)) { $sql = "SELECT login_id FROM $table WHERE login_user_id = $userId @@ -94,7 +101,7 @@ function preventMultipleLogin($userId) $userIsReallyOnline = user_is_online($userId); // Trying double login. - if (!empty($loginData) && true == $userIsReallyOnline) { + if (!empty($loginData) && true == $userIsReallyOnline || $differentIp) { session_regenerate_id(); Session::destroy(); header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=multiple_connection_not_allowed'); @@ -102,6 +109,7 @@ function preventMultipleLogin($userId) } else { // First time Session::write('first_user_login', 1); + Session::write('current_ip', api_get_real_ip()); } } } diff --git a/public/main/inc/lib/pdf.lib.php b/public/main/inc/lib/pdf.lib.php index 9c10155b7e2..68f70d906ee 100644 --- a/public/main/inc/lib/pdf.lib.php +++ b/public/main/inc/lib/pdf.lib.php @@ -7,7 +7,10 @@ use Masterminds\HTML5; use Mpdf\HTMLParserMode; use Mpdf\Mpdf; +use Mpdf\MpdfException; use Mpdf\Output\Destination; +use Mpdf\Utils\UtfString; +use Symfony\Component\DomCrawler\Crawler; /** * Class PDF. @@ -29,6 +32,8 @@ class PDF * @param string $orientation orientation "P" = Portrait "L" = Landscape * @param array $params * @param Template $template + * + * @throws MpdfException */ public function __construct( $pageFormat = 'A4', @@ -37,31 +42,31 @@ public function __construct( $template = null ) { $this->template = $template; - /* More info @ http://mpdf1.com/manual/index.php?tid=184&searchstring=mPDF */ + /* More info @ http://mpdf1.com/manual/index.php?tid=184&searchstring=Mpdf */ if (!in_array($orientation, ['P', 'L'])) { $orientation = 'P'; } //left, right, top, bottom, margin_header, margin footer - $params['left'] = isset($params['left']) ? $params['left'] : 15; - $params['right'] = isset($params['right']) ? $params['right'] : 15; - $params['top'] = isset($params['top']) ? $params['top'] : 30; - $params['bottom'] = isset($params['bottom']) ? $params['bottom'] : 30; - $params['margin_footer'] = isset($params['margin_footer']) ? $params['margin_footer'] : 8; - - $this->params['filename'] = isset($params['filename']) ? $params['filename'] : api_get_local_time(); - $this->params['pdf_title'] = isset($params['pdf_title']) ? $params['pdf_title'] : ''; - $this->params['course_info'] = isset($params['course_info']) ? $params['course_info'] : api_get_course_info(); - $this->params['session_info'] = isset($params['session_info']) ? $params['session_info'] : api_get_session_info(api_get_session_id()); - $this->params['course_code'] = isset($params['course_code']) ? $params['course_code'] : api_get_course_id(); - $this->params['add_signatures'] = isset($params['add_signatures']) ? $params['add_signatures'] : []; - $this->params['show_real_course_teachers'] = isset($params['show_real_course_teachers']) ? $params['show_real_course_teachers'] : false; - $this->params['student_info'] = isset($params['student_info']) ? $params['student_info'] : false; - $this->params['show_grade_generated_date'] = isset($params['show_grade_generated_date']) ? $params['show_grade_generated_date'] : false; - $this->params['show_teacher_as_myself'] = isset($params['show_teacher_as_myself']) ? $params['show_teacher_as_myself'] : true; + $params['left'] = $params['left'] ?? 15; + $params['right'] = $params['right'] ?? 15; + $params['top'] = $params['top'] ?? 30; + $params['bottom'] = $params['bottom'] ?? 30; + $params['margin_footer'] = $params['margin_footer'] ?? 8; + + $this->params['filename'] = $params['filename'] ?? api_get_local_time(); + $this->params['pdf_title'] = $params['pdf_title'] ?? ''; + $this->params['course_info'] = $params['course_info'] ?? api_get_course_info(); + $this->params['session_info'] = $params['session_info'] ?? api_get_session_info(api_get_session_id()); + $this->params['course_code'] = $params['course_code'] ?? api_get_course_id(); + $this->params['add_signatures'] = $params['add_signatures'] ?? []; + $this->params['show_real_course_teachers'] = $params['show_real_course_teachers'] ?? false; + $this->params['student_info'] = $params['student_info'] ?? false; + $this->params['show_grade_generated_date'] = $params['show_grade_generated_date'] ?? false; + $this->params['show_teacher_as_myself'] = $params['show_teacher_as_myself'] ?? true; $localTime = api_get_local_time(); - $this->params['pdf_date'] = isset($params['pdf_date']) ? $params['pdf_date'] : api_format_date($localTime, DATE_TIME_FORMAT_LONG); - $this->params['pdf_date_only'] = isset($params['pdf_date']) ? $params['pdf_date'] : api_format_date($localTime, DATE_FORMAT_LONG); + $this->params['pdf_date'] = $params['pdf_date'] ?? api_format_date($localTime, DATE_TIME_FORMAT_LONG); + $this->params['pdf_date_only'] = $params['pdf_date'] ?? api_format_date($localTime, DATE_FORMAT_LONG); $params = [ 'tempDir' => Container::getParameter('kernel.cache_dir').'/mpdf', @@ -306,9 +311,23 @@ public function html_to_pdf( $filename = basename($filename, '.htm'); } + $webPath = api_get_path(WEB_PATH); + $documentHtml = @file_get_contents($file); $documentHtml = preg_replace($clean_search, '', $documentHtml); + $crawler = new Crawler($documentHtml); + $crawler + ->filter('link[rel="stylesheet"]') + ->each(function (Crawler $node) use ($webPath) { + $linkUrl = $node->link()->getUri(); + if (!str_starts_with($linkUrl, $webPath)) { + $node->getNode(0)->parentNode->removeChild($node->getNode(0)); + } + }) + ; + $documentHtml = $crawler->outerHtml(); + //absolute path for frames.css //TODO: necessary? $absolute_css_path = api_get_path(WEB_CODE_PATH).'css/'.api_get_setting('stylesheets.stylesheets').'/frames.css'; $documentHtml = str_replace('href="./css/frames.css"', $absolute_css_path, $documentHtml); @@ -773,7 +792,7 @@ public function format_pdf($courseInfo, $complete = true, $disablePagination = f } if (!empty($watermark_text)) { $this->pdf->SetWatermarkText( - strcode2utf($watermark_text), + UtfString::strcode2utf($watermark_text), 0.1 ); $this->pdf->showWatermarkText = true; @@ -921,11 +940,25 @@ private static function fixImagesPaths($documentHtml, array $courseInfo = null, $documentPath = $courseInfo ? $sysCoursePath.$courseInfo['path'].'/document/' : ''; + $notFoundImagePath = Display::return_icon( + 'closed-circle.png', + get_lang('FileNotFound'), + [], + ICON_SIZE_TINY, + false, + true + ); + /** @var \DOMElement $element */ foreach ($elements as $element) { $src = $element->getAttribute('src'); $src = trim($src); + if (api_filename_has_blacklisted_stream_wrapper($src)) { + $element->setAttribute('src', $notFoundImagePath); + continue; + } + if (false !== strpos($src, $protocol)) { continue; } diff --git a/public/main/inc/lib/pear/HTML/QuickForm.php b/public/main/inc/lib/pear/HTML/QuickForm.php index dcca0c17975..81f1993cb3e 100644 --- a/public/main/inc/lib/pear/HTML/QuickForm.php +++ b/public/main/inc/lib/pear/HTML/QuickForm.php @@ -257,11 +257,9 @@ public function __construct( public function protect() { - $token = $this->getSubmitValue('protect_token'); + $token = Security::get_existing_token(); if (null === $token) { $token = Security::get_token(); - } else { - $token = Security::get_existing_token(); } $this->addHidden('protect_token', $token); $this->setToken($token); @@ -1409,22 +1407,12 @@ function getRequiredNote() */ public function validate() { - if (count($this->_rules) == 0 && count($this->_formRules) == 0 && $this->isSubmitted()) { - return 0 === count($this->_errors); - } elseif (!$this->isSubmitted()) { - + if (!$this->isSubmitted()) { return false; } - if (null !== $this->getToken()) { - $check = Security::check_token('form', $this); - Security::clear_token(); - if (false === $check) { - return false; - } - } - - $registry =& HTML_QuickForm_RuleRegistry::singleton(); + if (count($this->_rules) > 0 || count($this->_formRules) > 0) { + $registry =& HTML_QuickForm_RuleRegistry::singleton(); foreach ($this->_rules as $target => $rules) { $submitValue = $this->getSubmitValue($target); @@ -1516,7 +1504,25 @@ public function validate() } } - return (0 == count($this->_errors)); + if (count($this->_errors) > 0) { + return false; + } + } + + if (null !== $this->getToken()) { + $check = Security::check_token('form', $this); + Security::clear_token(); + if (false === $check) { + // Redirect to the same URL + show token not validated message. + $url = $this->getAttribute('action'); + Display::addFlash(Display::return_message(get_lang('NotValidated'), 'warning')); + api_location($url); + + return false; + } + } + + return true; } /** diff --git a/public/main/inc/lib/promotion.lib.php b/public/main/inc/lib/promotion.lib.php index 1b81c3efbbc..54e58dc28ab 100644 --- a/public/main/inc/lib/promotion.lib.php +++ b/public/main/inc/lib/promotion.lib.php @@ -213,14 +213,9 @@ public function return_form($url, $action = 'add') $id = isset($_GET['id']) ? (int) $_GET['id'] : ''; - $form->addElement('header', '', $header); - $form->addElement('hidden', 'id', $id); - $form->addElement( - 'text', - 'name', - get_lang('Name'), - ['size' => '70', 'id' => 'name'] - ); + $form->addHeader($header); + $form->addHidden('id', $id); + $form->addText('name', get_lang('Name'), true, ['size' => '70', 'id' => 'name']); $form->addHtmlEditor( 'description', get_lang('Description'), diff --git a/public/main/inc/lib/security.lib.php b/public/main/inc/lib/security.lib.php index e7c3410858b..aa1acb1a5ff 100644 --- a/public/main/inc/lib/security.lib.php +++ b/public/main/inc/lib/security.lib.php @@ -13,13 +13,13 @@ * http://www.phpsec.org/ * The principles here are that all data is tainted (most scripts of Chamilo are * open to the public or at least to a certain public that could be malicious - * under specific circumstances). We use the white list approach, where as we + * under specific circumstances). We use the white list approach, whereas we * consider that data can only be used in the database or in a file if it has * been filtered. * * For session fixation, use ... * For session hijacking, use get_ua() and check_ua() - * For Cross-Site Request Forgeries, use get_token() and check_tocken() + * For Cross-Site Request Forgeries, use get_token() and check_token() * For basic filtering, use filter() * For files inclusions (using dynamic paths) use check_rel_path() and check_abs_path() * @@ -45,13 +45,13 @@ class Security * Checks if the absolute path (directory) given is really under the * checker path (directory). * - * @param string Absolute path to be checked (with trailing slash) - * @param string Checker path under which the path - * should be (absolute path, with trailing slash, get it from api_get_path(SYS_COURSE_PATH)) + * @param string $abs_path Absolute path to be checked (with trailing slash) + * @param string $checker_path Checker path under which the path + * should be (absolute path, with trailing slash, get it from api_get_path(SYS_COURSE_PATH)) * * @return bool True if the path is under the checker, false otherwise */ - public static function check_abs_path($abs_path, $checker_path) + public static function check_abs_path(string $abs_path, string $checker_path): bool { // The checker path must be set. if (empty($checker_path)) { @@ -59,8 +59,7 @@ public static function check_abs_path($abs_path, $checker_path) } // Clean $abs_path. - $abs_path = str_replace(['//', '../'], ['/', ''], $abs_path); - $true_path = str_replace("\\", '/', realpath($abs_path)); + $true_path = self::cleanPath($abs_path); $checker_path = str_replace("\\", '/', realpath($checker_path)); if (empty($checker_path)) { @@ -84,17 +83,24 @@ public static function check_abs_path($abs_path, $checker_path) return false; } + public static function cleanPath(string $absPath): string + { + $absPath = str_replace(['//', '../'], ['/', ''], $absPath); + + return str_replace("\\", '/', realpath($absPath)); + } + /** * Checks if the relative path (directory) given is really under the * checker path (directory). * - * @param string Relative path to be checked (relative to the current directory) (with trailing slash) - * @param string Checker path under which the path - * should be (absolute path, with trailing slash, get it from api_get_path(SYS_COURSE_PATH)) + * @param string $rel_path Relative path to be checked (relative to the current directory) (with trailing slash) + * @param string $checker_path Checker path under which the path + * should be (absolute path, with trailing slash, get it from api_get_path(SYS_COURSE_PATH)) * * @return bool True if the path is under the checker, false otherwise */ - public static function check_rel_path($rel_path, $checker_path) + public static function check_rel_path(string $rel_path, string $checker_path): bool { // The checker path must be set. if (empty($checker_path)) { @@ -120,48 +126,47 @@ public static function check_rel_path($rel_path, $checker_path) * other languages' files extensions). * * @param string $filename Unfiltered filename - * - * @return string */ - public static function filter_filename($filename) + public static function filter_filename(string $filename): string { return disable_dangerous_file($filename); } - /** - * @return string - */ - public static function getTokenFromSession() + public static function getTokenFromSession(string $prefix = ''): string { - return Session::read('sec_token'); + $secTokenVariable = self::generateSecTokenVariable($prefix); + + return Session::read($secTokenVariable); } /** * This function checks that the token generated in get_token() has been kept (prevents * Cross-Site Request Forgeries attacks). * - * @param string The array in which to get the token ('get' or 'post') + * @param string $requestType The array in which to get the token ('get' or 'post') + * @param ?FormValidator $form * * @return bool True if it's the right token, false otherwise */ - public static function check_token($request_type = 'post', FormValidator $form = null) + public static function check_token(string $requestType = 'post', FormValidator $form = null, string $prefix = ''): bool { - $sessionToken = Session::read('sec_token'); - switch ($request_type) { + $secTokenVariable = self::generateSecTokenVariable($prefix); + $sessionToken = Session::read($secTokenVariable); + switch ($requestType) { case 'request': - if (!empty($sessionToken) && isset($_REQUEST['sec_token']) && $sessionToken === $_REQUEST['sec_token']) { + if (!empty($sessionToken) && isset($_REQUEST[$secTokenVariable]) && $sessionToken === $_REQUEST[$secTokenVariable]) { return true; } return false; case 'get': - if (!empty($sessionToken) && isset($_GET['sec_token']) && $sessionToken === $_GET['sec_token']) { + if (!empty($sessionToken) && isset($_GET[$secTokenVariable]) && $sessionToken === $_GET[$secTokenVariable]) { return true; } return false; case 'post': - if (!empty($sessionToken) && isset($_POST['sec_token']) && $sessionToken === $_POST['sec_token']) { + if (!empty($sessionToken) && isset($_POST[$secTokenVariable]) && $sessionToken === $_POST[$secTokenVariable]) { return true; } @@ -175,11 +180,9 @@ public static function check_token($request_type = 'post', FormValidator $form = return false; default: - if (!empty($sessionToken) && isset($request_type) && $sessionToken === $request_type) { + if (!empty($sessionToken) && isset($requestType) && $sessionToken === $requestType) { return true; } - - return false; } return false; // Just in case, don't let anything slip. @@ -191,12 +194,12 @@ public static function check_token($request_type = 'post', FormValidator $form = * * @return bool True if the user agent is the same, false otherwise */ - public static function check_ua() + public static function check_ua(): bool { $security = Session::read('sec_ua'); $securitySeed = Session::read('sec_ua_seed'); - if ($_SERVER['HTTP_USER_AGENT'].$securitySeed === $security) { + if ($security === $_SERVER['HTTP_USER_AGENT'].$securitySeed) { return true; } @@ -206,9 +209,11 @@ public static function check_ua() /** * Clear the security token from the session. */ - public static function clear_token() + public static function clear_token(string $prefix = ''): void { - Session::erase('sec_token'); + $secTokenVariable = self::generateSecTokenVariable($prefix); + + Session::erase($secTokenVariable); } /** @@ -221,11 +226,12 @@ public static function clear_token() * * @return string Hidden-type input ready to insert into a form */ - public static function get_HTML_token() + public static function get_HTML_token(string $prefix = ''): string { + $secTokenVariable = self::generateSecTokenVariable($prefix); $token = md5(uniqid(rand(), true)); - $string = ''; - Session::write('sec_token', $token); + $string = ''; + Session::write($secTokenVariable, $token); return $string; } @@ -240,24 +246,23 @@ public static function get_HTML_token() * * @return string Token */ - public static function get_token() + public static function get_token($prefix = ''): string { + $secTokenVariable = self::generateSecTokenVariable($prefix); $token = md5(uniqid(rand(), true)); - Session::write('sec_token', $token); + Session::write($secTokenVariable, $token); return $token; } - /** - * @return string - */ - public static function get_existing_token() + public static function get_existing_token(string $prefix = ''): string { - $token = Session::read('sec_token'); + $secTokenVariable = self::generateSecTokenVariable($prefix); + $token = Session::read($secTokenVariable); if (!empty($token)) { return $token; } else { - return self::get_token(); + return self::get_token($prefix); } } @@ -276,11 +281,11 @@ public static function get_ua() * This function returns a variable from the clean array. If the variable doesn't exist, * it returns null. * - * @param string Variable name + * @param string $varname Variable name * * @return mixed Variable or NULL on error */ - public static function get($varname) + public static function get(string $varname) { if (isset(self::$clean[$varname])) { return self::$clean[$varname]; @@ -294,13 +299,12 @@ public static function get($varname) * Filtering for XSS is very easily done by using the htmlentities() function. * This kind of filtering prevents JavaScript snippets to be understood as such. * - * @param string The variable to filter for XSS, this params can be a string or an array (example : array(x,y)) - * @param int The user status,constant allowed (STUDENT, COURSEMANAGER, ANONYMOUS, COURSEMANAGERLOWSECURITY) - * @param bool $filter_terms + * @param string|array $var The variable to filter for XSS, this params can be a string or an array (example : array(x,y)) + * @param ?int $user_status The user status,constant allowed (STUDENT, COURSEMANAGER, ANONYMOUS, COURSEMANAGERLOWSECURITY) * - * @return string|array Filtered string or array + * @return mixed Filtered string or array */ - public static function remove_XSS($var, $user_status = null, $filter_terms = false) + public static function remove_XSS($var, int $user_status = null, bool $filter_terms = false) { if ($filter_terms) { $var = self::filter_terms($var); @@ -443,11 +447,9 @@ public static function remove_XSS($var, $user_status = null, $filter_terms = fal /** * Filter content. * - * @param string $text to be filter - * - * @return string + * @param string $text to be filtered */ - public static function filter_terms($text) + public static function filter_terms(string $text): string { static $bad_terms = []; @@ -459,7 +461,7 @@ public static function filter_terms($text) if (!empty($list)) { foreach ($list as $term) { $term = str_replace(["\r\n", "\r", "\n", "\t"], '', $term); - $html_entities_value = api_htmlentities($term, ENT_QUOTES); + $html_entities_value = api_htmlentities($term, ENT_QUOTES, api_get_system_encoding()); $bad_terms[] = $term; if ($term != $html_entities_value) { $bad_terms[] = $html_entities_value; @@ -473,7 +475,7 @@ public static function filter_terms($text) $replace = '***'; if (!empty($bad_terms)) { // Fast way - $new_text = str_ireplace($bad_terms, $replace, $text, $count); + $new_text = str_ireplace($bad_terms, $replace, $text); $text = $new_text; } @@ -487,11 +489,13 @@ public static function filter_terms($text) * this method encourages a safe practice for generating icon paths, without using heavy solutions * based on HTMLPurifier for example. * + * @param string $image_path the input path of the image, it could be relative or absolute URL + * * @return string returns sanitized image path or an empty string when the image path is not secure * * @author Ivan Tcholakov, March 2011 */ - public static function filter_img_path($image_path) + public static function filter_img_path(string $image_path): string { static $allowed_extensions = ['png', 'gif', 'jpg', 'jpeg', 'svg', 'webp']; $image_path = htmlspecialchars(trim($image_path)); // No html code is allowed. @@ -532,10 +536,8 @@ public static function filter_img_path($image_path) * Get password requirements * It checks config value 'password_requirements' or uses the "classic" * Chamilo password requirements. - * - * @return array */ - public static function getPasswordRequirements() + public static function getPasswordRequirements(): array { // Default $requirements = [ @@ -553,7 +555,7 @@ public static function getPasswordRequirements() $requirements = $passwordRequirements; } - return $requirements; + return ['min' => $requirements['min']]; } /** @@ -583,4 +585,26 @@ public static function getPasswordRequirementsToString($passedConditions = []) return $output; } + + /** + * Sanitize a string, so it can be used in the exec() command without + * "jail-breaking" to execute other commands. + * + * @param string $param The string to filter + */ + public static function sanitizeExecParam(string $param): string + { + $param = preg_replace('/[`;&|]/', '', $param); + + return escapeshellarg($param); + } + + private static function generateSecTokenVariable(string $prefix = ''): string + { + if (empty($prefix)) { + return 'sec_token'; + } + + return $prefix.'_sec_token'; + } } diff --git a/public/main/inc/lib/sessionmanager.lib.php b/public/main/inc/lib/sessionmanager.lib.php index 074dfaa439a..6cb123323c0 100644 --- a/public/main/inc/lib/sessionmanager.lib.php +++ b/public/main/inc/lib/sessionmanager.lib.php @@ -480,6 +480,10 @@ public static function getSessionsForAdmin( $userId = (int) $userId; + if (!api_is_platform_admin() && !api_is_session_admin() && !api_is_teacher()) { + api_not_allowed(true); + } + $extraFieldModel = new ExtraFieldModel('session'); $conditions = $extraFieldModel->parseConditions($options); @@ -3204,7 +3208,7 @@ public static function create_category_session( $sday_end ) { $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY); - $name = trim($sname); + $name = html_filter(trim($sname)); $year_start = intval($syear_start); $month_start = intval($smonth_start); $day_start = intval($sday_start); @@ -3289,7 +3293,7 @@ public static function edit_category_session( $sday_end ) { $tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY); - $name = trim($sname); + $name = html_filter(trim($sname)); $year_start = intval($syear_start); $month_start = intval($smonth_start); $day_start = intval($sday_start); diff --git a/public/main/inc/lib/social.lib.php b/public/main/inc/lib/social.lib.php index 40f8aea1c45..df73729f7eb 100644 --- a/public/main/inc/lib/social.lib.php +++ b/public/main/inc/lib/social.lib.php @@ -976,28 +976,28 @@ public static function formatWallMessages($messages) /** * verify if Url Exist - Using Curl. - * - * @param $uri url - * - * @return bool */ - public static function verifyUrl($uri) + public static function verifyUrl(string $uri): bool { - $curl = curl_init($uri); - curl_setopt($curl, CURLOPT_FAILONERROR, true); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_TIMEOUT, 15); - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); - $response = curl_exec($curl); - curl_close($curl); - if (!empty($response)) { + $client = new Client(); + + try { + $response = $client->request('GET', $uri, [ + 'timeout' => 15, + 'verify' => false, + 'headers' => [ + 'User-Agent' => $_SERVER['HTTP_USER_AGENT'], + ], + ]); + + if (200 !== $response->getStatusCode()) { + return false; + } + return true; + } catch (Exception $e) { + return false; } - - return false; } /** @@ -1258,6 +1258,7 @@ public static function getWallForm($urlForm) $form->addHtml(''); $form->addHtml(''); $form->addHidden('url_content', ''); + $form->protect(); $html = Display::panel($form->returnForm(), get_lang('Social wall')); return $html; diff --git a/public/main/inc/lib/template.lib.php b/public/main/inc/lib/template.lib.php index daf445b8705..36c0335af7b 100644 --- a/public/main/inc/lib/template.lib.php +++ b/public/main/inc/lib/template.lib.php @@ -918,6 +918,42 @@ public function displayLoginForm() return $html; } + public function enableCookieUsageWarning() + { + $form = new FormValidator( + 'cookiewarning', + 'post', + '', + '', + [ + //'onsubmit' => "$(this).toggle('show')", + ], + FormValidator::LAYOUT_BOX_NO_LABEL + ); + $form->addHidden('acceptCookies', '1'); + $form->addHtml( + '
+ '.get_lang('YouAcceptCookies').' + +
+ '.get_lang('HelpCookieUsageValidation').' +
+ +
' + ); + $form->protect(); + + if ($form->validate()) { + api_set_site_use_cookie_warning_cookie(); + } else { + $this->assign('frmDisplayCookieUsageWarning', $form->returnForm()); + } + } + /** * Returns the tutors names for the current course in session * Function to use in Twig templates. diff --git a/public/main/inc/lib/usergroup.lib.php b/public/main/inc/lib/usergroup.lib.php index d1fcae341eb..6a86f2dc101 100644 --- a/public/main/inc/lib/usergroup.lib.php +++ b/public/main/inc/lib/usergroup.lib.php @@ -1812,10 +1812,7 @@ public function setForm($form, $type = 'add', Usergroup $userGroup = null) $form->addHeader($header); // Name - $form->addElement('text', 'title', get_lang('Title'), ['maxlength' => 255]); - $form->applyFilter('title', 'trim'); - - $form->addRule('title', get_lang('Required field'), 'required'); + $form->addText('title', get_lang('Title'), true, ['maxlength' => 255]); $form->addRule('title', '', 'maxlength', 255); // Description @@ -1840,8 +1837,7 @@ public function setForm($form, $type = 'add', Usergroup $userGroup = null) } // url - $form->addElement('text', 'url', get_lang('URL')); - $form->applyFilter('url', 'trim'); + $form->addText('url', get_lang('Url'), false); // Picture //$allowed_picture_types = $this->getAllowedPictureExtensions(); @@ -1857,7 +1853,7 @@ public function setForm($form, $type = 'add', Usergroup $userGroup = null) if ($userGroup && $repo->hasIllustration($userGroup)) { $picture = $repo->getIllustrationUrl($userGroup); $img = ''; - $form->addElement('label', null, $img); + $form->addLabel(null, $img); $form->addElement('checkbox', 'delete_picture', '', get_lang('Remove picture')); } diff --git a/public/main/inc/lib/usermanager.lib.php b/public/main/inc/lib/usermanager.lib.php index 0b126ae8ec6..417c9652e64 100644 --- a/public/main/inc/lib/usermanager.lib.php +++ b/public/main/inc/lib/usermanager.lib.php @@ -6183,4 +6183,27 @@ public static function countUsersWhoFinishedCoursesInSessions() return $coursesInSessions; } + public static function redirectToResetPassword($userId): void + { + if ('true' !== api_get_setting('platform.force_renew_password_at_first_login')) { + return; + } + $askPassword = self::get_extra_user_data_by_field( + $userId, + 'ask_new_password' + ); + if (!empty($askPassword) && isset($askPassword['ask_new_password']) && + 1 === (int) $askPassword['ask_new_password'] + ) { + $uniqueId = api_get_unique_id(); + $userObj = api_get_user_entity($userId); + $userObj->setConfirmationToken($uniqueId); + $userObj->setPasswordRequestedAt(new \DateTime()); + Database::getManager()->persist($userObj); + Database::getManager()->flush(); + $url = api_get_path(WEB_CODE_PATH).'auth/reset.php?token='.$uniqueId; + api_location($url); + } + } + } diff --git a/public/main/inc/lib/xajax/xajax.inc.php b/public/main/inc/lib/xajax/xajax.inc.php index 6969f734362..5b587caacda 100644 --- a/public/main/inc/lib/xajax/xajax.inc.php +++ b/public/main/inc/lib/xajax/xajax.inc.php @@ -866,7 +866,7 @@ function _detectURI() } if (!empty($aURL['query'])) { - $aURL['query'] = '?'.$aURL['query']; + $aURL['query'] = '?'.Security::remove_XSS($aURL['query']); } // Build the URL: Start with scheme, user and pass diff --git a/public/main/lp/embed.php b/public/main/lp/embed.php index 054180ca80d..30775bf40a3 100644 --- a/public/main/lp/embed.php +++ b/public/main/lp/embed.php @@ -7,8 +7,8 @@ api_protect_course_script(true); -$type = $_REQUEST['type']; -$src = Security::remove_XSS($_REQUEST['source']); +$type = $_REQUEST['type'] ?? ''; +$src = $_REQUEST['source'] ?? ''; if (empty($type) || empty($src)) { api_not_allowed(); } @@ -37,24 +37,26 @@ ); break; case 'youtube': - $src = '//www.youtube.com/embed/'.$src; + $src = "src ='//www.youtube.com/embed/$src'"; + $src = Security::remove_XSS($src); $iframe .= '

'; - $iframe .= ''; + $iframe .= ''; $iframe .= '
'; break; case 'vimeo': - $src = '//player.vimeo.com/video/'.$src; + $src = "src ='//player.vimeo.com/video/$src'"; + $src = Security::remove_XSS($src); $iframe .= '

'; - $iframe .= ''; + $iframe .= ''; $iframe .= '
'; break; case 'nonhttps': $icon = ' '; - $iframe = Display::return_message( + $iframe = Security::remove_XSS(Display::return_message( Display::url($src.$icon, $src, ['class' => 'btn', 'target' => '_blank']), 'normal', false - ); + )); break; } diff --git a/public/main/lp/openoffice_document.class.php b/public/main/lp/openoffice_document.class.php index 9f508903f55..4482439bef2 100644 --- a/public/main/lp/openoffice_document.class.php +++ b/public/main/lp/openoffice_document.class.php @@ -93,8 +93,8 @@ public function convert_document($file, $action_after_conversion = 'make_lp', $s if (!empty($size)) { list($w, $h) = explode('x', $size); if (!empty($w) && !empty($h)) { - $this->slide_width = $w; - $this->slide_height = $h; + $this->slide_width = (int) $w; + $this->slide_height = (int) $h; } } @@ -130,6 +130,7 @@ public function convert_document($file, $action_after_conversion = 'make_lp', $s $files = []; $return = 0; + $cmd = escapeshellcmd($cmd); $shell = exec($cmd, $files, $return); if (0 != $return) { // If the java application returns an error code. @@ -235,7 +236,9 @@ public function convertCopyDocument($originalPath, $convertedPath, $convertedTit $cmd .= ' -p '.api_get_setting('service_ppt2lp', 'port'); // Call to the function implemented by child. - $cmd .= ' "'.$this->base_work_dir.'/'.$this->file_path.'" "'.$this->base_work_dir.'/'.$this->created_dir.'"'; + $cmd .= ' "'.Security::sanitizeExecParam($this->base_work_dir.'/'.$this->file_path) + .'" "' + .Security::sanitizeExecParam($this->base_work_dir.'/'.$this->created_dir).'"'; // To allow openoffice to manipulate docs. @chmod($this->base_work_dir, $permissionFolder); @chmod($this->base_work_dir.'/'.$this->file_path, $permissionFile); @@ -245,6 +248,7 @@ public function convertCopyDocument($originalPath, $convertedPath, $convertedTit $files = []; $return = 0; + $cmd = escapeshellcmd($cmd); $shell = exec($cmd, $files, $return); // TODO: Chown is not working, root keep user privileges, should be www-data @chown($this->base_work_dir.'/'.$this->created_dir, 'www-data'); diff --git a/public/main/lp/openoffice_presentation.class.php b/public/main/lp/openoffice_presentation.class.php index 8f1b1bfc1a9..087f5d269e4 100644 --- a/public/main/lp/openoffice_presentation.class.php +++ b/public/main/lp/openoffice_presentation.class.php @@ -232,13 +232,20 @@ public function add_command_parameters() list($this->slide_width, $this->slide_height) = explode('x', api_get_setting('service_ppt2lp', 'size')); } - return ' -w '.$this->slide_width.' -h '.$this->slide_height.' -d oogie "'.$this->base_work_dir.'/'.$this->file_path.'" "'.$this->base_work_dir.$this->created_dir.'.html"'; + return ' -w ' . Security::sanitizeExecParam($this->slide_width) + . ' -h ' . Security::sanitizeExecParam($this->slide_height) + . ' -d oogie "' + . Security::sanitizeExecParam($this->base_work_dir . '/' . $this->file_path) + . '" "' + . Security::sanitizeExecParam($this->base_work_dir . $this->created_dir . '.html') + . '"'; + } public function set_slide_size($width, $height) { - $this->slide_width = $width; - $this->slide_height = $height; + $this->slide_width = (int) $width; + $this->slide_height = (int) $height; } public function add_docs_to_visio($files = []) diff --git a/public/main/lp/openoffice_text.class.php b/public/main/lp/openoffice_text.class.php index b76218bd006..dd49ddc4046 100644 --- a/public/main/lp/openoffice_text.class.php +++ b/public/main/lp/openoffice_text.class.php @@ -317,7 +317,11 @@ public function dealPerPage($header, $body) */ public function add_command_parameters() { - return ' -d woogie "'.$this->base_work_dir.'/'.$this->file_path.'" "'.$this->base_work_dir.$this->created_dir.'/'.$this->file_name.'.html"'; + return ' -d woogie "' + . Security::sanitizeExecParam($this->base_work_dir . '/' . $this->file_path) + . '" "' + . Security::sanitizeExecParam($this->base_work_dir . $this->created_dir . '/' . $this->file_name . '.html') + . '"'; } /** diff --git a/public/main/lp/openoffice_text_document.class.php b/public/main/lp/openoffice_text_document.class.php index 36470cbaae6..5c20581d8d5 100644 --- a/public/main/lp/openoffice_text_document.class.php +++ b/public/main/lp/openoffice_text_document.class.php @@ -319,7 +319,11 @@ public function dealPerPage($header, $body) */ public function add_command_parameters() { - return ' -d woogie "'.$this->base_work_dir.'/'.$this->file_path.'" "'.$this->base_work_dir.$this->created_dir.'/'.$this->file_name.'.html"'; + return ' -d woogie "' + . Security::sanitizeExecParam($this->base_work_dir . '/' . $this->file_path) + . '" "' + . Security::sanitizeExecParam($this->base_work_dir . $this->created_dir . '/' . $this->file_name . '.html') + . '"'; } /** diff --git a/public/main/my_space/access_details.php b/public/main/my_space/access_details.php index 57e41b95452..7bad61feb2e 100644 --- a/public/main/my_space/access_details.php +++ b/public/main/my_space/access_details.php @@ -53,7 +53,9 @@ ['id' => 'type'] ); $form->addElement('hidden', 'student', $user_id); +$form->applyFilter('student', 'html_filter'); $form->addElement('hidden', 'course', $course_code); +$form->applyFilter('course', 'html_filter'); $form->addRule('from', get_lang('Required field'), 'required'); $form->addRule('to', get_lang('Required field'), 'required'); $group = [ @@ -72,7 +74,7 @@ $to = null; $course = $course_code; if ($form->validate()) { - $values = $form->getSubmitValues(); + $values = $form->exportValues(); $from = $values['from']; $to = $values['to']; $type = $values['type']; diff --git a/public/main/notebook/index.php b/public/main/notebook/index.php index 900f53ed800..6c45eea26c3 100644 --- a/public/main/notebook/index.php +++ b/public/main/notebook/index.php @@ -90,6 +90,7 @@ function setFocus(){ // Setting the form elements $form->addElement('header', '', get_lang('Add new note in my personal notebook')); $form->addElement('text', 'note_title', get_lang('Note title'), ['id' => 'note_title']); + $form->applyFilter('note_title', 'html_filter'); $form->addElement( 'html_editor', 'note_comment', @@ -147,6 +148,7 @@ function setFocus(){ $form->addElement('header', '', get_lang('Edit my personal note')); $form->addElement('hidden', 'notebook_id'); $form->addElement('text', 'note_title', get_lang('Note title'), ['size' => '100']); + $form->applyFilter('note_title', 'html_filter'); $form->addElement( 'html_editor', 'note_comment', diff --git a/public/main/session/add_edit_users_to_session.php b/public/main/session/add_edit_users_to_session.php index b7b1ebfe3c6..4ca22743409 100644 --- a/public/main/session/add_edit_users_to_session.php +++ b/public/main/session/add_edit_users_to_session.php @@ -19,7 +19,7 @@ if (empty($sessionId)) { api_not_allowed(true); } -$addProcess = isset($_GET['add']) ? Security::remove_XSS($_GET['add']) : null; +$addProcess = isset($_GET['add']) && 'true' === $_GET['add'] ? 'true' : null; $session = api_get_session_entity($sessionId); SessionManager::protectSession($session); diff --git a/public/main/session/session_add.php b/public/main/session/session_add.php index eccd3bb61cc..95794616ab4 100644 --- a/public/main/session/session_add.php +++ b/public/main/session/session_add.php @@ -132,8 +132,8 @@ function emptyDuration() { } "; -if (isset($_POST['formSent']) && $_POST['formSent']) { - $formSent = 1; +if (isset($_POST['formSent'])) { + $formSent = (int) $_POST['formSent']; } $tool_name = get_lang('Add a training session'); diff --git a/public/main/session/session_category_list.php b/public/main/session/session_category_list.php index b9168cbfe4a..2c1096f62a1 100644 --- a/public/main/session/session_category_list.php +++ b/public/main/session/session_category_list.php @@ -30,7 +30,7 @@ function selectAll(idCheck,numRows,action) { : 'title'; $idChecked = isset($_REQUEST['idChecked']) ? Security::remove_XSS($_REQUEST['idChecked']) : null; $order = isset($_REQUEST['order']) ? Security::remove_XSS($_REQUEST['order']) : 'ASC'; -$keyword = isset($_REQUEST['keyword']) ? Security::remove_XSS($_REQUEST['keyword']) : null; +$keyword = null; if ('delete_on_session' === $action || 'delete_off_session' === $action) { $delete_session = 'delete_on_session' == $action ? true : false; @@ -40,6 +40,13 @@ function selectAll(idCheck,numRows,action) { exit(); } +$frmSearch = new FormValidator('search', 'get', 'session_category_list.php', '', [], FormValidator::LAYOUT_INLINE); +$frmSearch->addText('keyword', get_lang('Search'), false); +$frmSearch->addButtonSearch(get_lang('Search')); +if ($frmSearch->validate()) { + $keyword = $frmSearch->exportValues()['keyword']; +} + $interbreadcrumb[] = ['url' => 'session_list.php', 'name' => get_lang('Session list')]; if (isset($_GET['search']) && 'advanced' === $_GET['search']) { @@ -106,14 +113,7 @@ function selectAll(idCheck,numRows,action) { Display::getMdiIcon('google-classroom', 'ch-tool-icon-gradient', null, 32, get_lang('Training sessions list')), api_get_path(WEB_CODE_PATH).'session/session_list.php' ); - $actionsRight = ' -
- - -
- '; + $actionsRight = $frmSearch->returnForm(); echo Display::toolbarAction('category', [$actionsLeft, $actionsRight]); ?>
CourseTeacher)); - $sql = "SELECT id, lastname, firstname FROM $tblUser WHERE username='$username'"; - $rs = Database::query($sql); - if (Database::num_rows($rs) > 0) { - [$userId, $lastname, $firstname] = Database::fetch_array($rs); + $rs = Database::select( + ['id', 'lastname', 'firstname'], + $tblUser, + ['where' => ['username = ?' => $username]], + 'first', + 'NUM' + ); + [$userId, $lastname, $firstname] = $rs; + if ($userId > 0) { $params['teachers'] = $userId; } else { $params['teachers'] = api_get_user_id(); diff --git a/public/main/skills/skills_wheel.php b/public/main/skills/skills_wheel.php index 396fd422db2..19d542797d7 100644 --- a/public/main/skills/skills_wheel.php +++ b/public/main/skills/skills_wheel.php @@ -5,11 +5,17 @@ require_once __DIR__.'/../inc/global.inc.php'; +use Symfony\Component\HttpFoundation\Request as HttpRequest; + + $this_section = SECTION_PLATFORM_ADMIN; api_protect_admin_script(false, true); SkillModel::isAllowed(); +$httpRequest = HttpRequest::createFromGlobals(); + + //Adds the JS needed to use the jqgrid $htmlHeadXtra[] = api_get_js('d3/d3.v3.5.4.min.js'); $htmlHeadXtra[] = api_get_js('d3/colorbrewer.js'); @@ -23,9 +29,10 @@ } $skill_condition = ''; -$skillId = isset($_REQUEST['skill_id']) ? (int) $_REQUEST['skill_id'] : 0; -if (!empty($skillId)) { - $skill_condition = '&skill_id='.$skillId; +$skillId = $httpRequest->query->getInt('skill_id', 0); + +if ($skillId > 0) { + $skill_condition = "&skill_id=$skillId"; } $tpl->assign('skill_id_to_load', $skillId); diff --git a/public/main/template/default/agenda/month.html.twig b/public/main/template/default/agenda/month.html.twig index 1e956b3eeca..57093070d10 100644 --- a/public/main/template/default/agenda/month.html.twig +++ b/public/main/template/default/agenda/month.html.twig @@ -89,7 +89,7 @@ 'view': view.type, 'start': date }; - Cookies.set('agenda_cookies', data, 1); // Expires 1 day*/ + Cookies.set('agenda_cookies', data, { expires: 1{% if is_https %}, secure: true{% endif %} }); // Expires 1 day }, viewDidMount: function(dateInfo) { var view = dateInfo.view; @@ -98,7 +98,7 @@ //'start': view.intervalStart.format("YYYY-MM-DD") 'start': dateInfo.currentStart }; - Cookies.set('agenda_cookies', data, 1); // Expires 1 day*/ + Cookies.set('agenda_cookies', data, { expires: 1{% if is_https %}, secure: true{% endif %} }); // Expires 1 day }, // Add event select: function(info) { diff --git a/public/main/tracking/lp_report.php b/public/main/tracking/lp_report.php index 9d702c8ff15..5e2aec050fe 100644 --- a/public/main/tracking/lp_report.php +++ b/public/main/tracking/lp_report.php @@ -85,7 +85,7 @@ function prepare_user_sql_query($getCount) foreach ($keywordList as $keyword) { $keywordListValues[$keyword] = null; if (isset($_GET[$keyword]) && !empty($_GET[$keyword])) { - $keywordListValues[$keyword] = $_GET[$keyword]; + $keywordListValues[$keyword] = Security::remove_XSS($_GET[$keyword]); $atLeastOne = true; } } diff --git a/public/main/work/work.lib.php b/public/main/work/work.lib.php index 86ac5185677..dc131f7a678 100644 --- a/public/main/work/work.lib.php +++ b/public/main/work/work.lib.php @@ -2650,7 +2650,7 @@ function getAllWork( //$visibility = api_get_item_visibility($courseInfo, 'work', $work['id'], $sessionId); $studentPublication = $repo->find($work['iid']); $workId = $studentPublication->getIid(); - $isVisible = $studentPublication->isVisible($courseEntity, $sessionEntity); + $isVisible = $studentPublication->isVisible(api_get_course_entity(), api_get_session_entity()); if (false === $isVisible) { continue; } @@ -5776,7 +5776,7 @@ function getWorkUserListData( $workIdList = []; if (!empty($workParents)) { foreach ($workParents as $work) { - $workIdList[] = $work->id; + $workIdList[] = $work->getIid(); } } @@ -5832,7 +5832,13 @@ function getWorkUserListData( */ function downloadFile($id, $course_info, $isCorrection) { - return getFile($id, $course_info, true, $isCorrection, true); + return getFile( + $id, + $course_info, + true, + $isCorrection, + api_is_course_admin() || api_is_coach() + ); } /** @@ -5917,7 +5923,7 @@ function getFileContents($id, $courseInfo, $sessionId = 0, $correction = false, $forceAccessForCourseAdmins ); - if (empty($isAllow)) { + if (!$isAllow) { return false; } @@ -5955,9 +5961,9 @@ function getFileContents($id, $courseInfo, $sessionId = 0, $correction = false, $is_editor = api_is_allowed_to_edit(true, true, true); $student_is_owner_of_work = user_is_author($studentPublication->getIid(), api_get_user_id()); - if (($forceAccessForCourseAdmins && $isAllow) || - $is_editor || + if ($is_editor || $student_is_owner_of_work || + ($forceAccessForCourseAdmins && $isAllow) || ($doc_visible_for_all && $work_is_visible) ) { $title = $studentPublication->getTitle(); diff --git a/public/plugin/azure_active_directory/index.php b/public/plugin/azure_active_directory/index.php index 289f7cca992..d966e32ebfe 100644 --- a/public/plugin/azure_active_directory/index.php +++ b/public/plugin/azure_active_directory/index.php @@ -2,31 +2,37 @@ /* For licensing terms, see /license.txt */ /** * @author Angel Fernando Quiroz Campos + * + * @package chamilo.plugin.azure_active_directory */ -/** @var AzureActiveDirectory $activeDirectoryPlugin */ -$activeDirectoryPlugin = AzureActiveDirectory::create(); +// Check if AzureActiveDirectory exists, since this is not loaded as a page. +// index.php is shown as a block when showing the region to which the plugin is assigned +if (class_exists(AzureActiveDirectory::class)) { + /** @var AzureActiveDirectory $activeDirectoryPlugin */ + $activeDirectoryPlugin = AzureActiveDirectory::create(); -if ('true' === $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_ENABLE)) { - $_template['block_title'] = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_BLOCK_NAME); + if ($activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_ENABLE) === 'true') { + $_template['block_title'] = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_BLOCK_NAME); - $_template['signin_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_AUTHORIZE); + $_template['signin_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_AUTHORIZE); - if ('true' === $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_FORCE_LOGOUT_BUTTON)) { - $_template['signout_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_LOGOUT); - } + if ('true' === $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_FORCE_LOGOUT_BUTTON)) { + $_template['signout_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_LOGOUT); + } - $managementLoginEnabled = 'true' === $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_ENABLE); + $managementLoginEnabled = 'true' === $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_ENABLE); - $_template['management_login_enabled'] = $managementLoginEnabled; + $_template['management_login_enabled'] = $managementLoginEnabled; - if ($managementLoginEnabled) { - $managementLoginName = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_NAME); + if ($managementLoginEnabled) { + $managementLoginName = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_NAME); - if (empty($managementLoginName)) { - $managementLoginName = $activeDirectoryPlugin->get_lang('ManagementLogin'); - } + if (empty($managementLoginName)) { + $managementLoginName = $activeDirectoryPlugin->get_lang('ManagementLogin'); + } - $_template['management_login_name'] = $managementLoginName; + $_template['management_login_name'] = $managementLoginName; + } } } diff --git a/public/plugin/notebookteacher/src/NotebookTeacher.php b/public/plugin/notebookteacher/src/NotebookTeacher.php index a9a6a9629b7..9371a4d7377 100644 --- a/public/plugin/notebookteacher/src/NotebookTeacher.php +++ b/public/plugin/notebookteacher/src/NotebookTeacher.php @@ -502,11 +502,13 @@ public static function getForm($form, $studentId) ); $form->addElement('text', 'note_title', get_lang('Note title'), ['id' => 'note_title']); - $form->addElement( - 'html_editor', + $form->applyFilter('note_title', 'html_filter'); + $form->applyFilter('note_title', 'attr_on_filter'); + $form->addHtmlEditor( 'note_comment', get_lang('Note details'), - null, + false, + false, api_is_allowed_to_edit() ? ['ToolbarSet' => 'Notebook', 'Width' => '100%', 'Height' => '300'] : ['ToolbarSet' => 'NotebookLearner', 'Width' => '100%', 'Height' => '300', 'UserStatus' => 'student']