CVE-2014-3541
Description
The Repositories component in Moodle through 2.3.11, 2.4.x before 2.4.11, 2.5.x before 2.5.7, 2.6.x before 2.6.4, and 2.7.x before 2.7.1 allows remote attackers to conduct PHP object injection attacks and execute arbitrary code via serialized data associated with an add-on.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
moodle/moodlePackagist | < 2.4.11 | 2.4.11 |
moodle/moodlePackagist | >= 2.5.0, < 2.5.7 | 2.5.7 |
moodle/moodlePackagist | >= 2.6.0, < 2.6.4 | 2.6.4 |
moodle/moodlePackagist | >= 2.7.0, < 2.7.1 | 2.7.1 |
Affected products
35cpe:2.3:a:moodle:moodle:2.4.0:*:*:*:*:*:*:*+ 34 more
- cpe:2.3:a:moodle:moodle:2.4.0:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.4.1:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.4.2:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.4.3:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.4.4:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.4.5:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.4.6:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.4.7:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.5.0:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.5.1:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.5.2:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.5.3:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.5.4:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.5.5:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.5.6:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.7.0:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:*:*:*:*:*:*:*:*range: <=2.3.11
- cpe:2.3:a:moodle:moodle:2.3.0:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.3.1:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.3.2:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.3.3:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.3.4:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.3.5:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.3.6:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.3.7:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.3.8:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.3.9:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.3.10:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.6.0:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.6.1:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.6.2:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.6.3:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.4.8:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.4.9:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.4.10:*:*:*:*:*:*:*
Patches
97bcf9b1e2cbdMDL-45616 repositories: use json encoding instead of serialization
7 files changed · +49 −23
repository/coursefiles/lib.php+5 −5 modified@@ -63,7 +63,7 @@ public function get_listing($encodedpath = '', $page = '') { $browser = get_file_browser(); if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params)) { $filepath = is_null($params['filepath']) ? NULL : clean_param($params['filepath'], PARAM_PATH); $filename = is_null($params['filename']) ? NULL : clean_param($params['filename'], PARAM_FILE); @@ -80,12 +80,12 @@ public function get_listing($encodedpath = '', $page = '') { if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) { // build path navigation $pathnodes = array(); - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); $pathnodes[] = array('name'=>$fileinfo->get_visible_name(), 'path'=>$encodedpath); $level = $fileinfo->get_parent(); while ($level) { $params = $level->get_params(); - $encodedpath = base64_encode(serialize($params)); + $encodedpath = base64_encode(json_encode($params)); if ($params['contextid'] != $context->id) { break; } @@ -102,7 +102,7 @@ public function get_listing($encodedpath = '', $page = '') { if ($child->is_directory()) { $params = $child->get_params(); $subdir_children = $child->get_children(); - $encodedpath = base64_encode(serialize($params)); + $encodedpath = base64_encode(json_encode($params)); $node = array( 'title' => $child->get_visible_name(), 'datemodified' => $child->get_timemodified(), @@ -113,7 +113,7 @@ public function get_listing($encodedpath = '', $page = '') { ); $list[] = $node; } else { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'size' => $child->get_filesize(),
repository/equella/callback.php+1 −1 modified@@ -55,7 +55,7 @@ $license = s(clean_param($info->license, PARAM_ALPHAEXT)); } -$source = base64_encode(serialize((object)array('url'=>$url,'filename'=>$filename))); +$source = base64_encode(json_encode(array('url'=>$url,'filename'=>$filename))); $js =<<<EOD <html>
repository/equella/lib.php+10 −5 modified@@ -125,7 +125,11 @@ public function supported_returntypes() { * @return string file referece */ public function get_file_reference($source) { - return $source; + // Internally we store serialized value but user input is json-encoded for security reasons. + $ref = json_decode(base64_decode($source)); + $filename = clean_param($ref->filename, PARAM_FILE); + $url = clean_param($ref->url, PARAM_URL); + return base64_encode(serialize((object)array('url' => $url, 'filename' => $filename))); } /** @@ -414,12 +418,13 @@ private static function to_mime_type($value) { /** * Return the source information * - * @param stdClass $url + * @param string $source * @return string|null */ - public function get_file_source_info($url) { - $ref = unserialize(base64_decode($url)); - return 'EQUELLA: ' . $ref->filename; + public function get_file_source_info($source) { + $ref = json_decode(base64_decode($source)); + $filename = clean_param($ref->filename, PARAM_FILE); + return 'EQUELLA: ' . $filename; } /**
repository/lib.php+23 −3 modified@@ -1691,12 +1691,32 @@ public static function display_instances_list($context, $typename = null) { * Prepare file reference information * * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) - * @return string file referece + * @return string file reference, ready to be stored */ public function get_file_reference($source) { if ($source && $this->has_moodle_files()) { - $params = file_storage::unpack_reference($source); - if (!is_array($params)) { + $params = @json_decode(base64_decode($source), true); + if (!$params && !in_array($this->get_typename(), array('recent', 'user', 'local', 'coursefiles'))) { + // IMPORTANT! Since default format for moodle files was changed in the minor release as a security fix + // we maintain an old code here in order not to break 3rd party repositories that deal + // with moodle files. Repositories are strongly encouraged to be upgraded, see MDL-45616. + // In Moodle 2.8 this fallback will be removed. + $params = file_storage::unpack_reference($source, true); + return file_storage::pack_reference($params); + } + if (!is_array($params) || empty($params['contextid'])) { + throw new repository_exception('invalidparams', 'repository'); + } + $params = array( + 'component' => empty($params['component']) ? '' : clean_param($params['component'], PARAM_COMPONENT), + 'filearea' => empty($params['filearea']) ? '' : clean_param($params['filearea'], PARAM_AREA), + 'itemid' => empty($params['itemid']) ? 0 : clean_param($params['itemid'], PARAM_INT), + 'filename' => empty($params['filename']) ? null : clean_param($params['filename'], PARAM_FILE), + 'filepath' => empty($params['filepath']) ? null : clean_param($params['filepath'], PARAM_PATH), + 'contextid' => clean_param($params['contextid'], PARAM_INT) + ); + // Check if context exists. + if (!context::instance_by_id($params['contextid'], IGNORE_MISSING)) { throw new repository_exception('invalidparams', 'repository'); } return file_storage::pack_reference($params);
repository/local/lib.php+3 −3 modified@@ -56,7 +56,7 @@ public function get_listing($encodedpath = '', $page = '') { $component = null; if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params) && isset($params['contextid'])) { $component = is_null($params['component']) ? NULL : clean_param($params['component'], PARAM_COMPONENT); $filearea = is_null($params['filearea']) ? NULL : clean_param($params['filearea'], PARAM_AREA); @@ -216,7 +216,7 @@ private function can_skip(file_info $fileinfo, $extensions, $parent = -1) { */ private function get_node(file_info $fileinfo) { global $OUTPUT; - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); $node = array( 'title' => $fileinfo->get_visible_name(), 'datemodified' => $fileinfo->get_timemodified(), @@ -256,7 +256,7 @@ private function get_node(file_info $fileinfo) { * @return array */ private function get_node_path(file_info $fileinfo) { - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); return array( 'path' => $encodedpath, 'name' => $fileinfo->get_visible_name()
repository/recent/lib.php+3 −2 modified@@ -126,7 +126,7 @@ public function get_listing($encodedpath = '', $page = '') { $fileinfo = $browser->get_file_info($context, $file['component'], $file['filearea'], $file['itemid'], $file['filepath'], $file['filename']); if ($fileinfo) { - $params = base64_encode(serialize($file)); + $params = base64_encode(json_encode($file)); $node = array( 'title' => $fileinfo->get_visible_name(), 'size' => $fileinfo->get_filesize(), @@ -192,7 +192,8 @@ public function supported_returntypes() { */ public function file_is_accessible($source) { global $USER; - $file = self::get_moodle_file($source); + $reference = $this->get_file_reference($source); + $file = self::get_moodle_file($reference); return (!empty($file) && $file->get_userid() == $USER->id); }
repository/user/lib.php+4 −4 modified@@ -61,7 +61,7 @@ public function get_listing($encodedpath = '', $page = '') { $list = array(); if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params)) { $filepath = clean_param($params['filepath'], PARAM_PATH); $filename = clean_param($params['filename'], PARAM_FILE); @@ -84,7 +84,7 @@ public function get_listing($encodedpath = '', $page = '') { $level = $fileinfo; $params = $fileinfo->get_params(); while ($level && $params['component'] == 'user' && $params['filearea'] == 'private') { - $encodedpath = base64_encode(serialize($level->get_params())); + $encodedpath = base64_encode(json_encode($level->get_params())); $pathnodes[] = array('name'=>$level->get_visible_name(), 'path'=>$encodedpath); $level = $level->get_parent(); $params = $level->get_params(); @@ -95,7 +95,7 @@ public function get_listing($encodedpath = '', $page = '') { $children = $fileinfo->get_children(); foreach ($children as $child) { if ($child->is_directory()) { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'datemodified' => $child->get_timemodified(), @@ -106,7 +106,7 @@ public function get_listing($encodedpath = '', $page = '') { ); $list[] = $node; } else { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'size' => $child->get_filesize(),
5c4ef26c39d3MDL-45616 repositories: use json encoding instead of serialization
7 files changed · +49 −23
repository/coursefiles/lib.php+5 −5 modified@@ -63,7 +63,7 @@ public function get_listing($encodedpath = '', $page = '') { $browser = get_file_browser(); if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params)) { $filepath = is_null($params['filepath']) ? NULL : clean_param($params['filepath'], PARAM_PATH);; $filename = is_null($params['filename']) ? NULL : clean_param($params['filename'], PARAM_FILE); @@ -80,12 +80,12 @@ public function get_listing($encodedpath = '', $page = '') { if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) { // build path navigation $pathnodes = array(); - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); $pathnodes[] = array('name'=>$fileinfo->get_visible_name(), 'path'=>$encodedpath); $level = $fileinfo->get_parent(); while ($level) { $params = $level->get_params(); - $encodedpath = base64_encode(serialize($params)); + $encodedpath = base64_encode(json_encode($params)); if ($params['contextid'] != $context->id) { break; } @@ -102,7 +102,7 @@ public function get_listing($encodedpath = '', $page = '') { if ($child->is_directory()) { $params = $child->get_params(); $subdir_children = $child->get_children(); - $encodedpath = base64_encode(serialize($params)); + $encodedpath = base64_encode(json_encode($params)); $node = array( 'title' => $child->get_visible_name(), 'datemodified' => $child->get_timemodified(), @@ -113,7 +113,7 @@ public function get_listing($encodedpath = '', $page = '') { ); $list[] = $node; } else { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'size' => $child->get_filesize(),
repository/equella/callback.php+1 −1 modified@@ -55,7 +55,7 @@ $license = s(clean_param($info->license, PARAM_ALPHAEXT)); } -$source = base64_encode(serialize((object)array('url'=>$url,'filename'=>$filename))); +$source = base64_encode(json_encode(array('url'=>$url,'filename'=>$filename))); $js =<<<EOD <html>
repository/equella/lib.php+10 −5 modified@@ -119,7 +119,11 @@ public function supported_returntypes() { * @return string file referece */ public function get_file_reference($source) { - return $source; + // Internally we store serialized value but user input is json-encoded for security reasons. + $ref = json_decode(base64_decode($source)); + $filename = clean_param($ref->filename, PARAM_FILE); + $url = clean_param($ref->url, PARAM_URL); + return base64_encode(serialize((object)array('url' => $url, 'filename' => $filename))); } /** @@ -407,12 +411,13 @@ private static function to_mime_type($value) { /** * Return the source information * - * @param stdClass $url + * @param string $source * @return string|null */ - public function get_file_source_info($url) { - $ref = unserialize(base64_decode($url)); - return 'EQUELLA: ' . $ref->filename; + public function get_file_source_info($source) { + $ref = json_decode(base64_decode($source)); + $filename = clean_param($ref->filename, PARAM_FILE); + return 'EQUELLA: ' . $filename; } /**
repository/lib.php+23 −3 modified@@ -1625,12 +1625,32 @@ public static function display_instances_list($context, $typename = null) { * Prepare file reference information * * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) - * @return string file referece + * @return string file reference, ready to be stored */ public function get_file_reference($source) { if ($source && $this->has_moodle_files()) { - $params = file_storage::unpack_reference($source); - if (!is_array($params)) { + $params = @json_decode(base64_decode($source), true); + if (!$params && !in_array($this->get_typename(), array('recent', 'user', 'local', 'coursefiles'))) { + // IMPORTANT! Since default format for moodle files was changed in the minor release as a security fix + // we maintain an old code here in order not to break 3rd party repositories that deal + // with moodle files. Repositories are strongly encouraged to be upgraded, see MDL-45616. + // In Moodle 2.8 this fallback will be removed. + $params = file_storage::unpack_reference($source, true); + return file_storage::pack_reference($params); + } + if (!is_array($params) || empty($params['contextid'])) { + throw new repository_exception('invalidparams', 'repository'); + } + $params = array( + 'component' => empty($params['component']) ? '' : clean_param($params['component'], PARAM_COMPONENT), + 'filearea' => empty($params['filearea']) ? '' : clean_param($params['filearea'], PARAM_AREA), + 'itemid' => empty($params['itemid']) ? 0 : clean_param($params['itemid'], PARAM_INT), + 'filename' => empty($params['filename']) ? null : clean_param($params['filename'], PARAM_FILE), + 'filepath' => empty($params['filepath']) ? null : clean_param($params['filepath'], PARAM_PATH), + 'contextid' => clean_param($params['contextid'], PARAM_INT) + ); + // Check if context exists. + if (!context::instance_by_id($params['contextid'], IGNORE_MISSING)) { throw new repository_exception('invalidparams', 'repository'); } return file_storage::pack_reference($params);
repository/local/lib.php+3 −3 modified@@ -56,7 +56,7 @@ public function get_listing($encodedpath = '', $page = '') { $component = null; if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params) && isset($params['contextid'])) { $component = is_null($params['component']) ? NULL : clean_param($params['component'], PARAM_COMPONENT); $filearea = is_null($params['filearea']) ? NULL : clean_param($params['filearea'], PARAM_AREA); @@ -216,7 +216,7 @@ private function can_skip(file_info $fileinfo, $extensions, $parent = -1) { */ private function get_node(file_info $fileinfo) { global $OUTPUT; - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); $node = array( 'title' => $fileinfo->get_visible_name(), 'datemodified' => $fileinfo->get_timemodified(), @@ -256,7 +256,7 @@ private function get_node(file_info $fileinfo) { * @return array */ private function get_node_path(file_info $fileinfo) { - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); return array( 'path' => $encodedpath, 'name' => $fileinfo->get_visible_name()
repository/recent/lib.php+3 −2 modified@@ -126,7 +126,7 @@ public function get_listing($encodedpath = '', $page = '') { $fileinfo = $browser->get_file_info($context, $file['component'], $file['filearea'], $file['itemid'], $file['filepath'], $file['filename']); if ($fileinfo) { - $params = base64_encode(serialize($file)); + $params = base64_encode(json_encode($file)); $node = array( 'title' => $fileinfo->get_visible_name(), 'size' => $fileinfo->get_filesize(), @@ -191,7 +191,8 @@ public function supported_returntypes() { */ public function file_is_accessible($source) { global $USER; - $file = self::get_moodle_file($source); + $reference = $this->get_file_reference($source); + $file = self::get_moodle_file($reference); return (!empty($file) && $file->get_userid() == $USER->id); }
repository/user/lib.php+4 −4 modified@@ -61,7 +61,7 @@ public function get_listing($encodedpath = '', $page = '') { $list = array(); if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params)) { $filepath = clean_param($params['filepath'], PARAM_PATH);; $filename = clean_param($params['filename'], PARAM_FILE); @@ -84,7 +84,7 @@ public function get_listing($encodedpath = '', $page = '') { $level = $fileinfo; $params = $fileinfo->get_params(); while ($level && $params['component'] == 'user' && $params['filearea'] == 'private') { - $encodedpath = base64_encode(serialize($level->get_params())); + $encodedpath = base64_encode(json_encode($level->get_params())); $pathnodes[] = array('name'=>$level->get_visible_name(), 'path'=>$encodedpath); $level = $level->get_parent(); $params = $level->get_params(); @@ -95,7 +95,7 @@ public function get_listing($encodedpath = '', $page = '') { $children = $fileinfo->get_children(); foreach ($children as $child) { if ($child->is_directory()) { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'datemodified' => $child->get_timemodified(), @@ -106,7 +106,7 @@ public function get_listing($encodedpath = '', $page = '') { ); $list[] = $node; } else { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'size' => $child->get_filesize(),
61961447c29dMDL-45616 repositories: use json encoding instead of serialization
7 files changed · +49 −23
repository/coursefiles/lib.php+5 −5 modified@@ -63,7 +63,7 @@ public function get_listing($encodedpath = '', $page = '') { $browser = get_file_browser(); if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params)) { $filepath = is_null($params['filepath']) ? NULL : clean_param($params['filepath'], PARAM_PATH); $filename = is_null($params['filename']) ? NULL : clean_param($params['filename'], PARAM_FILE); @@ -80,12 +80,12 @@ public function get_listing($encodedpath = '', $page = '') { if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) { // build path navigation $pathnodes = array(); - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); $pathnodes[] = array('name'=>$fileinfo->get_visible_name(), 'path'=>$encodedpath); $level = $fileinfo->get_parent(); while ($level) { $params = $level->get_params(); - $encodedpath = base64_encode(serialize($params)); + $encodedpath = base64_encode(json_encode($params)); if ($params['contextid'] != $context->id) { break; } @@ -102,7 +102,7 @@ public function get_listing($encodedpath = '', $page = '') { if ($child->is_directory()) { $params = $child->get_params(); $subdir_children = $child->get_children(); - $encodedpath = base64_encode(serialize($params)); + $encodedpath = base64_encode(json_encode($params)); $node = array( 'title' => $child->get_visible_name(), 'datemodified' => $child->get_timemodified(), @@ -113,7 +113,7 @@ public function get_listing($encodedpath = '', $page = '') { ); $list[] = $node; } else { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'size' => $child->get_filesize(),
repository/equella/callback.php+1 −1 modified@@ -55,7 +55,7 @@ $license = s(clean_param($info->license, PARAM_ALPHAEXT)); } -$source = base64_encode(serialize((object)array('url'=>$url,'filename'=>$filename))); +$source = base64_encode(json_encode(array('url'=>$url,'filename'=>$filename))); $js =<<<EOD <html>
repository/equella/lib.php+10 −5 modified@@ -125,7 +125,11 @@ public function supported_returntypes() { * @return string file referece */ public function get_file_reference($source) { - return $source; + // Internally we store serialized value but user input is json-encoded for security reasons. + $ref = json_decode(base64_decode($source)); + $filename = clean_param($ref->filename, PARAM_FILE); + $url = clean_param($ref->url, PARAM_URL); + return base64_encode(serialize((object)array('url' => $url, 'filename' => $filename))); } /** @@ -400,12 +404,13 @@ private static function to_mime_type($value) { /** * Return the source information * - * @param stdClass $url + * @param string $source * @return string|null */ - public function get_file_source_info($url) { - $ref = unserialize(base64_decode($url)); - return 'EQUELLA: ' . $ref->filename; + public function get_file_source_info($source) { + $ref = json_decode(base64_decode($source)); + $filename = clean_param($ref->filename, PARAM_FILE); + return 'EQUELLA: ' . $filename; } /**
repository/lib.php+23 −3 modified@@ -1666,12 +1666,32 @@ public static function display_instances_list($context, $typename = null) { * Prepare file reference information * * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) - * @return string file referece + * @return string file reference, ready to be stored */ public function get_file_reference($source) { if ($source && $this->has_moodle_files()) { - $params = file_storage::unpack_reference($source); - if (!is_array($params)) { + $params = @json_decode(base64_decode($source), true); + if (!$params && !in_array($this->get_typename(), array('recent', 'user', 'local', 'coursefiles'))) { + // IMPORTANT! Since default format for moodle files was changed in the minor release as a security fix + // we maintain an old code here in order not to break 3rd party repositories that deal + // with moodle files. Repositories are strongly encouraged to be upgraded, see MDL-45616. + // In Moodle 2.8 this fallback will be removed. + $params = file_storage::unpack_reference($source, true); + return file_storage::pack_reference($params); + } + if (!is_array($params) || empty($params['contextid'])) { + throw new repository_exception('invalidparams', 'repository'); + } + $params = array( + 'component' => empty($params['component']) ? '' : clean_param($params['component'], PARAM_COMPONENT), + 'filearea' => empty($params['filearea']) ? '' : clean_param($params['filearea'], PARAM_AREA), + 'itemid' => empty($params['itemid']) ? 0 : clean_param($params['itemid'], PARAM_INT), + 'filename' => empty($params['filename']) ? null : clean_param($params['filename'], PARAM_FILE), + 'filepath' => empty($params['filepath']) ? null : clean_param($params['filepath'], PARAM_PATH), + 'contextid' => clean_param($params['contextid'], PARAM_INT) + ); + // Check if context exists. + if (!context::instance_by_id($params['contextid'], IGNORE_MISSING)) { throw new repository_exception('invalidparams', 'repository'); } return file_storage::pack_reference($params);
repository/local/lib.php+3 −3 modified@@ -56,7 +56,7 @@ public function get_listing($encodedpath = '', $page = '') { $component = null; if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params) && isset($params['contextid'])) { $component = is_null($params['component']) ? NULL : clean_param($params['component'], PARAM_COMPONENT); $filearea = is_null($params['filearea']) ? NULL : clean_param($params['filearea'], PARAM_AREA); @@ -205,7 +205,7 @@ private function can_skip(file_info $fileinfo, $extensions, $parent = -1) { */ private function get_node(file_info $fileinfo) { global $OUTPUT; - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); $node = array( 'title' => $fileinfo->get_visible_name(), 'datemodified' => $fileinfo->get_timemodified(), @@ -245,7 +245,7 @@ private function get_node(file_info $fileinfo) { * @return array */ private function get_node_path(file_info $fileinfo) { - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); return array( 'path' => $encodedpath, 'name' => $fileinfo->get_visible_name()
repository/recent/lib.php+3 −2 modified@@ -126,7 +126,7 @@ public function get_listing($encodedpath = '', $page = '') { $fileinfo = $browser->get_file_info($context, $file['component'], $file['filearea'], $file['itemid'], $file['filepath'], $file['filename']); if ($fileinfo) { - $params = base64_encode(serialize($file)); + $params = base64_encode(json_encode($file)); $node = array( 'title' => $fileinfo->get_visible_name(), 'size' => $fileinfo->get_filesize(), @@ -192,7 +192,8 @@ public function supported_returntypes() { */ public function file_is_accessible($source) { global $USER; - $file = self::get_moodle_file($source); + $reference = $this->get_file_reference($source); + $file = self::get_moodle_file($reference); return (!empty($file) && $file->get_userid() == $USER->id); }
repository/user/lib.php+4 −4 modified@@ -61,7 +61,7 @@ public function get_listing($encodedpath = '', $page = '') { $list = array(); if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params)) { $filepath = clean_param($params['filepath'], PARAM_PATH); $filename = clean_param($params['filename'], PARAM_FILE); @@ -84,7 +84,7 @@ public function get_listing($encodedpath = '', $page = '') { $level = $fileinfo; $params = $fileinfo->get_params(); while ($level && $params['component'] == 'user' && $params['filearea'] == 'private') { - $encodedpath = base64_encode(serialize($level->get_params())); + $encodedpath = base64_encode(json_encode($level->get_params())); $pathnodes[] = array('name'=>$level->get_visible_name(), 'path'=>$encodedpath); $level = $level->get_parent(); $params = $level->get_params(); @@ -95,7 +95,7 @@ public function get_listing($encodedpath = '', $page = '') { $children = $fileinfo->get_children(); foreach ($children as $child) { if ($child->is_directory()) { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'datemodified' => $child->get_timemodified(), @@ -106,7 +106,7 @@ public function get_listing($encodedpath = '', $page = '') { ); $list[] = $node; } else { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'size' => $child->get_filesize(),
40d52d4067c2MDL-45616 repositories: use json encoding instead of serialization
7 files changed · +49 −23
repository/coursefiles/lib.php+5 −5 modified@@ -63,7 +63,7 @@ public function get_listing($encodedpath = '', $page = '') { $browser = get_file_browser(); if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params)) { $filepath = is_null($params['filepath']) ? NULL : clean_param($params['filepath'], PARAM_PATH); $filename = is_null($params['filename']) ? NULL : clean_param($params['filename'], PARAM_FILE); @@ -80,12 +80,12 @@ public function get_listing($encodedpath = '', $page = '') { if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) { // build path navigation $pathnodes = array(); - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); $pathnodes[] = array('name'=>$fileinfo->get_visible_name(), 'path'=>$encodedpath); $level = $fileinfo->get_parent(); while ($level) { $params = $level->get_params(); - $encodedpath = base64_encode(serialize($params)); + $encodedpath = base64_encode(json_encode($params)); if ($params['contextid'] != $context->id) { break; } @@ -102,7 +102,7 @@ public function get_listing($encodedpath = '', $page = '') { if ($child->is_directory()) { $params = $child->get_params(); $subdir_children = $child->get_children(); - $encodedpath = base64_encode(serialize($params)); + $encodedpath = base64_encode(json_encode($params)); $node = array( 'title' => $child->get_visible_name(), 'datemodified' => $child->get_timemodified(), @@ -113,7 +113,7 @@ public function get_listing($encodedpath = '', $page = '') { ); $list[] = $node; } else { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'size' => $child->get_filesize(),
repository/equella/callback.php+1 −1 modified@@ -55,7 +55,7 @@ $license = s(clean_param($info->license, PARAM_ALPHAEXT)); } -$source = base64_encode(serialize((object)array('url'=>$url,'filename'=>$filename))); +$source = base64_encode(json_encode(array('url'=>$url,'filename'=>$filename))); $js =<<<EOD <html>
repository/equella/lib.php+10 −5 modified@@ -125,7 +125,11 @@ public function supported_returntypes() { * @return string file referece */ public function get_file_reference($source) { - return $source; + // Internally we store serialized value but user input is json-encoded for security reasons. + $ref = json_decode(base64_decode($source)); + $filename = clean_param($ref->filename, PARAM_FILE); + $url = clean_param($ref->url, PARAM_URL); + return base64_encode(serialize((object)array('url' => $url, 'filename' => $filename))); } /** @@ -400,12 +404,13 @@ private static function to_mime_type($value) { /** * Return the source information * - * @param stdClass $url + * @param string $source * @return string|null */ - public function get_file_source_info($url) { - $ref = unserialize(base64_decode($url)); - return 'EQUELLA: ' . $ref->filename; + public function get_file_source_info($source) { + $ref = json_decode(base64_decode($source)); + $filename = clean_param($ref->filename, PARAM_FILE); + return 'EQUELLA: ' . $filename; } /**
repository/lib.php+23 −3 modified@@ -1655,12 +1655,32 @@ public static function display_instances_list($context, $typename = null) { * Prepare file reference information * * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) - * @return string file referece + * @return string file reference, ready to be stored */ public function get_file_reference($source) { if ($source && $this->has_moodle_files()) { - $params = file_storage::unpack_reference($source); - if (!is_array($params)) { + $params = @json_decode(base64_decode($source), true); + if (!$params && !in_array($this->get_typename(), array('recent', 'user', 'local', 'coursefiles'))) { + // IMPORTANT! Since default format for moodle files was changed in the minor release as a security fix + // we maintain an old code here in order not to break 3rd party repositories that deal + // with moodle files. Repositories are strongly encouraged to be upgraded, see MDL-45616. + // In Moodle 2.8 this fallback will be removed. + $params = file_storage::unpack_reference($source, true); + return file_storage::pack_reference($params); + } + if (!is_array($params) || empty($params['contextid'])) { + throw new repository_exception('invalidparams', 'repository'); + } + $params = array( + 'component' => empty($params['component']) ? '' : clean_param($params['component'], PARAM_COMPONENT), + 'filearea' => empty($params['filearea']) ? '' : clean_param($params['filearea'], PARAM_AREA), + 'itemid' => empty($params['itemid']) ? 0 : clean_param($params['itemid'], PARAM_INT), + 'filename' => empty($params['filename']) ? null : clean_param($params['filename'], PARAM_FILE), + 'filepath' => empty($params['filepath']) ? null : clean_param($params['filepath'], PARAM_PATH), + 'contextid' => clean_param($params['contextid'], PARAM_INT) + ); + // Check if context exists. + if (!context::instance_by_id($params['contextid'], IGNORE_MISSING)) { throw new repository_exception('invalidparams', 'repository'); } return file_storage::pack_reference($params);
repository/local/lib.php+3 −3 modified@@ -56,7 +56,7 @@ public function get_listing($encodedpath = '', $page = '') { $component = null; if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params) && isset($params['contextid'])) { $component = is_null($params['component']) ? NULL : clean_param($params['component'], PARAM_COMPONENT); $filearea = is_null($params['filearea']) ? NULL : clean_param($params['filearea'], PARAM_AREA); @@ -205,7 +205,7 @@ private function can_skip(file_info $fileinfo, $extensions, $parent = -1) { */ private function get_node(file_info $fileinfo) { global $OUTPUT; - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); $node = array( 'title' => $fileinfo->get_visible_name(), 'datemodified' => $fileinfo->get_timemodified(), @@ -245,7 +245,7 @@ private function get_node(file_info $fileinfo) { * @return array */ private function get_node_path(file_info $fileinfo) { - $encodedpath = base64_encode(serialize($fileinfo->get_params())); + $encodedpath = base64_encode(json_encode($fileinfo->get_params())); return array( 'path' => $encodedpath, 'name' => $fileinfo->get_visible_name()
repository/recent/lib.php+3 −2 modified@@ -126,7 +126,7 @@ public function get_listing($encodedpath = '', $page = '') { $fileinfo = $browser->get_file_info($context, $file['component'], $file['filearea'], $file['itemid'], $file['filepath'], $file['filename']); if ($fileinfo) { - $params = base64_encode(serialize($file)); + $params = base64_encode(json_encode($file)); $node = array( 'title' => $fileinfo->get_visible_name(), 'size' => $fileinfo->get_filesize(), @@ -192,7 +192,8 @@ public function supported_returntypes() { */ public function file_is_accessible($source) { global $USER; - $file = self::get_moodle_file($source); + $reference = $this->get_file_reference($source); + $file = self::get_moodle_file($reference); return (!empty($file) && $file->get_userid() == $USER->id); }
repository/user/lib.php+4 −4 modified@@ -61,7 +61,7 @@ public function get_listing($encodedpath = '', $page = '') { $list = array(); if (!empty($encodedpath)) { - $params = unserialize(base64_decode($encodedpath)); + $params = json_decode(base64_decode($encodedpath), true); if (is_array($params)) { $filepath = clean_param($params['filepath'], PARAM_PATH); $filename = clean_param($params['filename'], PARAM_FILE); @@ -84,7 +84,7 @@ public function get_listing($encodedpath = '', $page = '') { $level = $fileinfo; $params = $fileinfo->get_params(); while ($level && $params['component'] == 'user' && $params['filearea'] == 'private') { - $encodedpath = base64_encode(serialize($level->get_params())); + $encodedpath = base64_encode(json_encode($level->get_params())); $pathnodes[] = array('name'=>$level->get_visible_name(), 'path'=>$encodedpath); $level = $level->get_parent(); $params = $level->get_params(); @@ -95,7 +95,7 @@ public function get_listing($encodedpath = '', $page = '') { $children = $fileinfo->get_children(); foreach ($children as $child) { if ($child->is_directory()) { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'datemodified' => $child->get_timemodified(), @@ -106,7 +106,7 @@ public function get_listing($encodedpath = '', $page = '') { ); $list[] = $node; } else { - $encodedpath = base64_encode(serialize($child->get_params())); + $encodedpath = base64_encode(json_encode($child->get_params())); $node = array( 'title' => $child->get_visible_name(), 'size' => $child->get_filesize(),
e29bb97c0756MDL-45616 repositories: API changes to avoid serialized data in user input
2 files changed · +6 −8
repository/lib.php+0 −8 modified@@ -1671,14 +1671,6 @@ public static function display_instances_list($context, $typename = null) { public function get_file_reference($source) { if ($source && $this->has_moodle_files()) { $params = @json_decode(base64_decode($source), true); - if (!$params && !in_array($this->get_typename(), array('recent', 'user', 'local', 'coursefiles'))) { - // IMPORTANT! Since default format for moodle files was changed in the minor release as a security fix - // we maintain an old code here in order not to break 3rd party repositories that deal - // with moodle files. Repositories are strongly encouraged to be upgraded, see MDL-45616. - // In Moodle 2.8 this fallback will be removed. - $params = file_storage::unpack_reference($source, true); - return file_storage::pack_reference($params); - } if (!is_array($params) || empty($params['contextid'])) { throw new repository_exception('invalidparams', 'repository'); }
repository/upgrade.txt+6 −0 modified@@ -3,6 +3,12 @@ information provided here is intended especially for developers. Full details of the repository API are available on Moodle docs: http://docs.moodle.org/dev/Repository_API +=== 2.8 === + +* Repositories working with Moodle files must replace serialize() with json_encode() in the + attribute 'source' returned by get_listing(). If repository overrides file_is_accessible(), + get_file_reference() or get_file_source_info() they need to be changed too. See MDL-45616. + === 2.6 === * get_option() now always return null when the first parameter ($config) is not empty, and
cb2b42aed8d9MDL-45616 repositories: more clearly distinguish when we use source and when reference
3 files changed · +15 −12
repository/filepicker.php+1 −1 modified@@ -293,7 +293,7 @@ // note that in this case user may not have permission to access the source file directly // so no file_browser/file_info can be used below if ($repo->has_moodle_files()) { - $file = repository::get_moodle_file($fileurl); + $file = repository::get_moodle_file($reference); if ($file && $file->is_external_file()) { $sourcefield = $file->get_source(); // remember the original source $record->source = $repo::build_source_field($sourcefield);
repository/lib.php+13 −10 modified@@ -717,13 +717,14 @@ public static function draftfile_exists($itemid, $filepath, $filename) { } /** - * Parses the 'source' returned by moodle repositories and returns an instance of stored_file + * Parses the moodle file reference and returns an instance of stored_file * - * @param string $source + * @param string $reference reference to the moodle internal file as retruned by + * {@link repository::get_file_reference()} or {@link file_storage::pack_reference()} * @return stored_file|null */ - public static function get_moodle_file($source) { - $params = file_storage::unpack_reference($source, true); + public static function get_moodle_file($reference) { + $params = file_storage::unpack_reference($reference, true); $fs = get_file_storage(); return $fs->get_file($params['contextid'], $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']); @@ -735,13 +736,14 @@ public static function get_moodle_file($source) { * This is checked when user tries to pick the file from repository to deal with * potential parameter substitutions is request * - * @param string $source + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return bool whether the file is accessible by current user */ public function file_is_accessible($source) { if ($this->has_moodle_files()) { + $reference = $this->get_file_reference($source); try { - $params = file_storage::unpack_reference($source, true); + $params = file_storage::unpack_reference($reference, true); } catch (file_reference_exception $e) { return false; } @@ -1336,12 +1338,13 @@ public function get_file_by_reference($reference) { * again to another file area (also as a copy or as a reference), the value of * files.source is copied. * - * @param string $source the value that repository returned in listing as 'source' + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return string|null */ public function get_file_source_info($source) { if ($this->has_moodle_files()) { - return $this->get_reference_details($source, 0); + $reference = $this->get_file_reference($source); + return $this->get_reference_details($reference, 0); } return $source; } @@ -1621,11 +1624,11 @@ public static function display_instances_list($context, $typename = null) { /** * Prepare file reference information * - * @param string $source + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return string file referece */ public function get_file_reference($source) { - if ($this->has_moodle_files() && ($this->supported_returntypes() & FILE_REFERENCE)) { + if ($source && $this->has_moodle_files()) { $params = file_storage::unpack_reference($source); if (!is_array($params)) { throw new repository_exception('invalidparams', 'repository');
repository/repository_ajax.php+1 −1 modified@@ -239,7 +239,7 @@ // note that in this case user may not have permission to access the source file directly // so no file_browser/file_info can be used below if ($repo->has_moodle_files()) { - $file = repository::get_moodle_file($source); + $file = repository::get_moodle_file($reference); if ($file && $file->is_external_file()) { $sourcefield = $file->get_source(); // remember the original source $record->source = $repo::build_source_field($sourcefield);
68170f0b01ccMDL-45616 repositories: more clearly distinguish when we use source and when reference
3 files changed · +15 −12
repository/filepicker.php+1 −1 modified@@ -293,7 +293,7 @@ // note that in this case user may not have permission to access the source file directly // so no file_browser/file_info can be used below if ($repo->has_moodle_files()) { - $file = repository::get_moodle_file($fileurl); + $file = repository::get_moodle_file($reference); if ($file && $file->is_external_file()) { $sourcefield = $file->get_source(); // remember the original source $record->source = $repo::build_source_field($sourcefield);
repository/lib.php+13 −10 modified@@ -828,13 +828,14 @@ public static function draftfile_exists($itemid, $filepath, $filename) { } /** - * Parses the 'source' returned by moodle repositories and returns an instance of stored_file + * Parses the moodle file reference and returns an instance of stored_file * - * @param string $source + * @param string $reference reference to the moodle internal file as retruned by + * {@link repository::get_file_reference()} or {@link file_storage::pack_reference()} * @return stored_file|null */ - public static function get_moodle_file($source) { - $params = file_storage::unpack_reference($source, true); + public static function get_moodle_file($reference) { + $params = file_storage::unpack_reference($reference, true); $fs = get_file_storage(); return $fs->get_file($params['contextid'], $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']); @@ -846,13 +847,14 @@ public static function get_moodle_file($source) { * This is checked when user tries to pick the file from repository to deal with * potential parameter substitutions is request * - * @param string $source + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return bool whether the file is accessible by current user */ public function file_is_accessible($source) { if ($this->has_moodle_files()) { + $reference = $this->get_file_reference($source); try { - $params = file_storage::unpack_reference($source, true); + $params = file_storage::unpack_reference($reference, true); } catch (file_reference_exception $e) { return false; } @@ -1365,12 +1367,13 @@ public function cache_file_by_reference($reference, $storedfile) { * again to another file area (also as a copy or as a reference), the value of * files.source is copied. * - * @param string $source the value that repository returned in listing as 'source' + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return string|null */ public function get_file_source_info($source) { if ($this->has_moodle_files()) { - return $this->get_reference_details($source, 0); + $reference = $this->get_file_reference($source); + return $this->get_reference_details($reference, 0); } return $source; } @@ -1651,11 +1654,11 @@ public static function display_instances_list($context, $typename = null) { /** * Prepare file reference information * - * @param string $source + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return string file referece */ public function get_file_reference($source) { - if ($this->has_moodle_files() && ($this->supported_returntypes() & FILE_REFERENCE)) { + if ($source && $this->has_moodle_files()) { $params = file_storage::unpack_reference($source); if (!is_array($params)) { throw new repository_exception('invalidparams', 'repository');
repository/repository_ajax.php+1 −1 modified@@ -208,7 +208,7 @@ // note that in this case user may not have permission to access the source file directly // so no file_browser/file_info can be used below if ($repo->has_moodle_files()) { - $file = repository::get_moodle_file($source); + $file = repository::get_moodle_file($reference); if ($file && $file->is_external_file()) { $sourcefield = $file->get_source(); // remember the original source $record->source = $repo::build_source_field($sourcefield);
3fe105953d14MDL-45616 repositories: more clearly distinguish when we use source and when reference
3 files changed · +15 −12
repository/filepicker.php+1 −1 modified@@ -293,7 +293,7 @@ // note that in this case user may not have permission to access the source file directly // so no file_browser/file_info can be used below if ($repo->has_moodle_files()) { - $file = repository::get_moodle_file($fileurl); + $file = repository::get_moodle_file($reference); if ($file && $file->is_external_file()) { $sourcefield = $file->get_source(); // remember the original source $record->source = $repo::build_source_field($sourcefield);
repository/lib.php+13 −10 modified@@ -799,13 +799,14 @@ public static function draftfile_exists($itemid, $filepath, $filename) { } /** - * Parses the 'source' returned by moodle repositories and returns an instance of stored_file + * Parses the moodle file reference and returns an instance of stored_file * - * @param string $source + * @param string $reference reference to the moodle internal file as retruned by + * {@link repository::get_file_reference()} or {@link file_storage::pack_reference()} * @return stored_file|null */ - public static function get_moodle_file($source) { - $params = file_storage::unpack_reference($source, true); + public static function get_moodle_file($reference) { + $params = file_storage::unpack_reference($reference, true); $fs = get_file_storage(); return $fs->get_file($params['contextid'], $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']); @@ -817,13 +818,14 @@ public static function get_moodle_file($source) { * This is checked when user tries to pick the file from repository to deal with * potential parameter substitutions is request * - * @param string $source + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return bool whether the file is accessible by current user */ public function file_is_accessible($source) { if ($this->has_moodle_files()) { + $reference = $this->get_file_reference($source); try { - $params = file_storage::unpack_reference($source, true); + $params = file_storage::unpack_reference($reference, true); } catch (file_reference_exception $e) { return false; } @@ -1401,12 +1403,13 @@ public function get_file_by_reference($reference) { * again to another file area (also as a copy or as a reference), the value of * files.source is copied. * - * @param string $source the value that repository returned in listing as 'source' + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return string|null */ public function get_file_source_info($source) { if ($this->has_moodle_files()) { - return $this->get_reference_details($source, 0); + $reference = $this->get_file_reference($source); + return $this->get_reference_details($reference, 0); } return $source; } @@ -1687,11 +1690,11 @@ public static function display_instances_list($context, $typename = null) { /** * Prepare file reference information * - * @param string $source + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return string file referece */ public function get_file_reference($source) { - if ($this->has_moodle_files() && ($this->supported_returntypes() & FILE_REFERENCE)) { + if ($source && $this->has_moodle_files()) { $params = file_storage::unpack_reference($source); if (!is_array($params)) { throw new repository_exception('invalidparams', 'repository');
repository/repository_ajax.php+1 −1 modified@@ -238,7 +238,7 @@ // note that in this case user may not have permission to access the source file directly // so no file_browser/file_info can be used below if ($repo->has_moodle_files()) { - $file = repository::get_moodle_file($source); + $file = repository::get_moodle_file($reference); if ($file && $file->is_external_file()) { $sourcefield = $file->get_source(); // remember the original source $record->source = $repo::build_source_field($sourcefield);
867f40990bdeMDL-45616 repositories: more clearly distinguish when we use source and when reference
3 files changed · +15 −12
repository/filepicker.php+1 −1 modified@@ -293,7 +293,7 @@ // note that in this case user may not have permission to access the source file directly // so no file_browser/file_info can be used below if ($repo->has_moodle_files()) { - $file = repository::get_moodle_file($fileurl); + $file = repository::get_moodle_file($reference); if ($file && $file->is_external_file()) { $sourcefield = $file->get_source(); // remember the original source $record->source = $repo::build_source_field($sourcefield);
repository/lib.php+13 −10 modified@@ -839,13 +839,14 @@ public static function draftfile_exists($itemid, $filepath, $filename) { } /** - * Parses the 'source' returned by moodle repositories and returns an instance of stored_file + * Parses the moodle file reference and returns an instance of stored_file * - * @param string $source + * @param string $reference reference to the moodle internal file as retruned by + * {@link repository::get_file_reference()} or {@link file_storage::pack_reference()} * @return stored_file|null */ - public static function get_moodle_file($source) { - $params = file_storage::unpack_reference($source, true); + public static function get_moodle_file($reference) { + $params = file_storage::unpack_reference($reference, true); $fs = get_file_storage(); return $fs->get_file($params['contextid'], $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']); @@ -857,13 +858,14 @@ public static function get_moodle_file($source) { * This is checked when user tries to pick the file from repository to deal with * potential parameter substitutions is request * - * @param string $source + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return bool whether the file is accessible by current user */ public function file_is_accessible($source) { if ($this->has_moodle_files()) { + $reference = $this->get_file_reference($source); try { - $params = file_storage::unpack_reference($source, true); + $params = file_storage::unpack_reference($reference, true); } catch (file_reference_exception $e) { return false; } @@ -1376,12 +1378,13 @@ public function cache_file_by_reference($reference, $storedfile) { * again to another file area (also as a copy or as a reference), the value of * files.source is copied. * - * @param string $source the value that repository returned in listing as 'source' + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return string|null */ public function get_file_source_info($source) { if ($this->has_moodle_files()) { - return $this->get_reference_details($source, 0); + $reference = $this->get_file_reference($source); + return $this->get_reference_details($reference, 0); } return $source; } @@ -1662,11 +1665,11 @@ public static function display_instances_list($context, $typename = null) { /** * Prepare file reference information * - * @param string $source + * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned) * @return string file referece */ public function get_file_reference($source) { - if ($this->has_moodle_files() && ($this->supported_returntypes() & FILE_REFERENCE)) { + if ($source && $this->has_moodle_files()) { $params = file_storage::unpack_reference($source); if (!is_array($params)) { throw new repository_exception('invalidparams', 'repository');
repository/repository_ajax.php+1 −1 modified@@ -208,7 +208,7 @@ // note that in this case user may not have permission to access the source file directly // so no file_browser/file_info can be used below if ($repo->has_moodle_files()) { - $file = repository::get_moodle_file($source); + $file = repository::get_moodle_file($reference); if ($file && $file->is_external_file()) { $sourcefield = $file->get_source(); // remember the original source $record->source = $repo::build_source_field($sourcefield);
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
13- github.com/advisories/GHSA-fccf-p8fx-vjj4ghsaADVISORY
- moodle.org/mod/forum/discuss.phpnvdVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2014-3541ghsaADVISORY
- openwall.com/lists/oss-security/2014/07/21/1nvdWEB
- github.com/moodle/moodle/commit/3fe105953d14766393e24372806fcf0a2b77c96dghsaWEB
- github.com/moodle/moodle/commit/40d52d4067c2ee062a5b16c780753c6f97413894ghsaWEB
- github.com/moodle/moodle/commit/5c4ef26c39d3106315f74c26cdcca779ba74254cghsaWEB
- github.com/moodle/moodle/commit/61961447c29d48e5a494e7c02e653d6ff00551b2ghsaWEB
- github.com/moodle/moodle/commit/68170f0b01ccaade799c4cab2312ce6a825fb844ghsaWEB
- github.com/moodle/moodle/commit/7bcf9b1e2cbdd1e877b828da75b17e3f8318fafcghsaWEB
- github.com/moodle/moodle/commit/867f40990bde6152e01604d106ddac8433018f42ghsaWEB
- github.com/moodle/moodle/commit/cb2b42aed8d9ce3c9840ad825f2e0e7e81bfad91ghsaWEB
- github.com/moodle/moodle/commit/e29bb97c0756de511ba287b40790d8275a991d33ghsaWEB
News mentions
0No linked articles in our index yet.