CVE-2015-2267
Description
mdeploy.php in Moodle through 2.5.9, 2.6.x before 2.6.9, 2.7.x before 2.7.6, and 2.8.x before 2.8.4 allows remote authenticated users to bypass intended access restrictions and extract archives to arbitrary directories via a crafted dataroot value.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
moodle/moodlePackagist | < 2.6.9 | 2.6.9 |
moodle/moodlePackagist | >= 2.7.0, < 2.7.6 | 2.7.6 |
moodle/moodlePackagist | >= 2.8.0, < 2.8.4 | 2.8.4 |
Affected products
29cpe:2.3:a:moodle:moodle:*:*:*:*:*:*:*:*+ 28 more
- cpe:2.3:a:moodle:moodle:*:*:*:*:*:*:*:*range: <=2.5.9
- 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.5.7:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.5.8:*:*:*:*:*:*:*
- 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.6.4:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.6.5:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.6.6:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.6.7:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.6.8:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.7.0:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.7.1:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.7.2:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.7.3:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.7.4:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.7.5:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.8.0:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.8.1:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.8.2:*:*:*:*:*:*:*
- cpe:2.3:a:moodle:moodle:2.8.3:*:*:*:*:*:*:*
Patches
1012a8fcb5e45cMDL-49087 mdeploy: now relying on config.php documented
1 file changed · +5 −0
lib/upgrade.txt+5 −0 modified@@ -1,6 +1,11 @@ This files describes API changes in core libraries and APIs, information provided here is intended especially for developers. +=== 2.6.9 === + +* The authorization procedure in the mdeploy.php script has been improved. The script + now relies on the main config.php when deploying an available update. + === 2.6.4 / 2.7.1 === * update_internal_user_password() accepts optional boolean $fasthash for fast
84f9f60b67e1MDL-49087 mdeploy: now relying on config.php documented
1 file changed · +5 −0
lib/upgrade.txt+5 −0 modified@@ -1,6 +1,11 @@ This files describes API changes in core libraries and APIs, information provided here is intended especially for developers. +=== 2.7.6 === + +* The authorization procedure in the mdeploy.php script has been improved. The script + now relies on the main config.php when deploying an available update. + === 2.7.4 === * \core_component::fetch_subsystems() now returns a valid path for completion component instead of null.
83866c3c2a5bMDL-49087 mdeploy: now relying on config.php documented
1 file changed · +5 −0
lib/upgrade.txt+5 −0 modified@@ -1,6 +1,11 @@ This files describes API changes in core libraries and APIs, information provided here is intended especially for developers. +=== 2.8.4 === + +* The authorization procedure in the mdeploy.php script has been improved. The script + now relies on the main config.php when deploying an available update. + === 2.8.2 === * \core_component::fetch_subsystems() now returns a valid path for completion component instead of null.
8d9bdd28e049MDL-49087 mdeploy: now relying on config.php documented
1 file changed · +2 −0
lib/upgrade.txt+2 −0 modified@@ -18,6 +18,8 @@ information provided here is intended especially for developers. Moodle specific tags will be ignored. * External function core_files_get_files returns additional fields (timecreated, filesize, author, license) * html_table: new API for adding captions to tables (new field, $table->caption) and subsequently hiding said captions from sighted users using accesshide (enabled using $table->captionhide). +* The authorization procedure in the mdeploy.php script has been improved. The script + now relies on the main config.php when deploying an available update. === 2.8 ===
de169b7944e3MDL-49087 mnet: Ensure typeroot is in dirroot
1 file changed · +15 −2
mdeploy.php+15 −2 modified@@ -775,8 +775,13 @@ public function execute() { } $this->log('MD5 checksum ok'); - // Backup the current version of the plugin + // Check that the specified typeroot is within the current site's dirroot. $plugintyperoot = $this->input->get_option('typeroot'); + if (strpos(realpath($plugintyperoot), realpath($this->get_env('dirroot'))) !== 0) { + throw new backup_folder_exception('Unable to backup the current version of the plugin (typeroot is invalid)'); + } + + // Backup the current version of the plugin $pluginname = $this->input->get_option('name'); $sourcelocation = $plugintyperoot.'/'.$pluginname; $backuplocation = $this->backup_location($sourcelocation); @@ -820,6 +825,10 @@ public function execute() { $source = $this->input->get_option('package'); $md5remote = $this->input->get_option('md5'); + if (strpos(realpath($plugintyperoot), realpath($this->get_env('dirroot'))) !== 0) { + throw new backup_folder_exception('Unable to prepare the plugin location (typeroot is invalid)'); + } + // Check if the plugin location if available for us. $pluginlocation = $plugintyperoot.'/'.$pluginname; @@ -1143,9 +1152,13 @@ protected function get_env($key) { return $CFG->dataroot; } break; + case 'dirroot': + if (isset($CFG->dirroot)) { + return $CFG->dirroot; + } + break; } return null; - } } /**
4475f1e47837MDL-49087 mnet: Ensure typeroot is in dirroot
2 files changed · +12 −1
mdeploy.php+11 −1 modified@@ -736,6 +736,7 @@ class worker extends singleton_pattern { /** @var array the whitelisted config options which can be queried. */ private $validconfigoptions = array( + 'dirroot' => true, 'dataroot' => true, ); @@ -781,8 +782,13 @@ public function execute() { } $this->log('MD5 checksum ok'); - // Backup the current version of the plugin + // Check that the specified typeroot is within the current site's dirroot. $plugintyperoot = $this->input->get_option('typeroot'); + if (strpos(realpath($plugintyperoot), realpath($this->get_env('dirroot'))) !== 0) { + throw new backup_folder_exception('Unable to backup the current version of the plugin (typeroot is invalid)'); + } + + // Backup the current version of the plugin $pluginname = $this->input->get_option('name'); $sourcelocation = $plugintyperoot.'/'.$pluginname; $backuplocation = $this->backup_location($sourcelocation); @@ -826,6 +832,10 @@ public function execute() { $source = $this->input->get_option('package'); $md5remote = $this->input->get_option('md5'); + if (strpos(realpath($plugintyperoot), realpath($this->get_env('dirroot'))) !== 0) { + throw new backup_folder_exception('Unable to prepare the plugin location (typeroot is invalid)'); + } + // Check if the plugin location if available for us. $pluginlocation = $plugintyperoot.'/'.$pluginname;
mdeploytest.php+1 −0 modified@@ -364,6 +364,7 @@ public function test_get_env_valid($key) { public function get_env_valid_provider() { return array( array('dataroot'), + array('dirroot'), ); } }
a47aabc7833dMDL-49087 mnet: Ensure typeroot is in dirroot
2 files changed · +12 −1
mdeploy.php+11 −1 modified@@ -736,6 +736,7 @@ class worker extends singleton_pattern { /** @var array the whitelisted config options which can be queried. */ private $validconfigoptions = array( + 'dirroot' => true, 'dataroot' => true, ); @@ -781,8 +782,13 @@ public function execute() { } $this->log('MD5 checksum ok'); - // Backup the current version of the plugin + // Check that the specified typeroot is within the current site's dirroot. $plugintyperoot = $this->input->get_option('typeroot'); + if (strpos(realpath($plugintyperoot), realpath($this->get_env('dirroot'))) !== 0) { + throw new backup_folder_exception('Unable to backup the current version of the plugin (typeroot is invalid)'); + } + + // Backup the current version of the plugin $pluginname = $this->input->get_option('name'); $sourcelocation = $plugintyperoot.'/'.$pluginname; $backuplocation = $this->backup_location($sourcelocation); @@ -826,6 +832,10 @@ public function execute() { $source = $this->input->get_option('package'); $md5remote = $this->input->get_option('md5'); + if (strpos(realpath($plugintyperoot), realpath($this->get_env('dirroot'))) !== 0) { + throw new backup_folder_exception('Unable to prepare the plugin location (typeroot is invalid)'); + } + // Check if the plugin location if available for us. $pluginlocation = $plugintyperoot.'/'.$pluginname;
mdeploytest.php+1 −0 modified@@ -364,6 +364,7 @@ public function test_get_env_valid($key) { public function get_env_valid_provider() { return array( array('dataroot'), + array('dirroot'), ); } }
c353a6202658MDL-49087 mnet: Use real dataroot instead of user-provided
2 files changed · +104 −13
mdeploy.php+38 −13 modified@@ -31,14 +31,12 @@ * --typeroot=/var/www/moodle/htdocs/blocks * --name=loancalc * --md5=... - * --dataroot=/var/www/moodle/data * * $ sudo -u apache php mdeploy.php --upgrade \ * --package=https://moodle.org/plugins/download.php/...zip \ * --typeroot=/var/www/moodle/htdocs/blocks * --name=loancalc * --md5=... - * --dataroot=/var/www/moodle/data * * When called via HTTP, additional parameters returnurl, passfile and password must be * provided. Optional proxy configuration can be passed using parameters proxy, proxytype @@ -60,6 +58,13 @@ die('This is a standalone utility that should not be included by any other Moodle code.'); } +// This stops immediately at the beginning of lib/setup.php. +define('ABORT_AFTER_CONFIG', true); +if (PHP_SAPI === 'cli') { + // Called from the CLI - we need to set CLI_SCRIPT to ensure that appropriate CLI checks are made in setup.php. + define('CLI_SCRIPT', true); +} +require(__DIR__ . '/config.php'); // Exceptions ////////////////////////////////////////////////////////////////// @@ -72,6 +77,7 @@ class backup_folder_exception extends Exception {} class zip_exception extends Exception {} class filesystem_exception extends Exception {} class checksum_exception extends Exception {} +class invalid_setting_exception extends Exception {} // Various support classes ///////////////////////////////////////////////////// @@ -201,7 +207,6 @@ public function get_option_info($name=null) { array('', 'proxytype', input_manager::TYPE_RAW, 'Proxy type (HTTP or SOCKS5)'), array('', 'proxyuserpwd', input_manager::TYPE_RAW, 'Proxy username and password (e.g. \'username:password\')'), array('', 'returnurl', input_manager::TYPE_URL, 'Return URL (HTTP access only)'), - array('d', 'dataroot', input_manager::TYPE_PATH, 'Full path to the dataroot (moodledata) directory'), array('h', 'help', input_manager::TYPE_FLAG, 'Prints usage information'), array('i', 'install', input_manager::TYPE_FLAG, 'Installation mode'), array('m', 'md5', input_manager::TYPE_MD5, 'Expected MD5 hash of the ZIP package to deploy'), @@ -729,6 +734,11 @@ class worker extends singleton_pattern { /** @var string the full path to the log file */ private $logfile = null; + /** @var array the whitelisted config options which can be queried. */ + private $validconfigoptions = array( + 'dataroot' => true, + ); + /** * Main - the one that actually does something */ @@ -909,17 +919,15 @@ protected function done($exitcode = self::EXIT_OK) { * @throws unauthorized_access_exception */ protected function authorize() { - if (PHP_SAPI === 'cli') { $this->log('Successfully authorized using the CLI SAPI'); return; } - $dataroot = $this->input->get_option('dataroot'); $passfile = $this->input->get_option('passfile'); $password = $this->input->get_option('password'); - $passpath = $dataroot.'/mdeploy/auth/'.$passfile; + $passpath = $this->get_env('dataroot') . '/mdeploy/auth/' . $passfile; if (!is_readable($passpath)) { throw new unauthorized_access_exception('Unable to read the passphrase file.'); @@ -959,12 +967,11 @@ protected function authorize() { * @return string */ protected function log_location() { - if (!is_null($this->logfile)) { return $this->logfile; } - $dataroot = $this->input->get_option('dataroot', ''); + $dataroot = $this->get_env('dataroot'); if (empty($dataroot)) { $this->logfile = false; @@ -988,8 +995,7 @@ protected function log_location() { * @return string */ protected function target_location($source) { - - $dataroot = $this->input->get_option('dataroot'); + $dataroot = $this->get_env('dataroot'); $pool = $dataroot.'/mdeploy/var'; if (!is_dir($pool)) { @@ -1013,8 +1019,7 @@ protected function target_location($source) { * @return string */ protected function backup_location($path) { - - $dataroot = $this->input->get_option('dataroot'); + $dataroot = $this->get_env('dataroot'); $pool = $dataroot.'/mdeploy/archive'; if (!is_dir($pool)) { @@ -1129,12 +1134,32 @@ protected function download_file($source, $target) { return true; } + /** + * Fetch environment settings. + * + * @param string $key The key to fetch + * @return mixed The value of the key if found. + * @throws invalid_setting_exception if the option is not set, or is invalid. + */ + protected function get_env($key) { + global $CFG; + + if (array_key_exists($key, $this->validconfigoptions)) { + if (isset($CFG->$key)) { + return $CFG->$key; + } + throw new invalid_setting_exception("Requested environment setting '{$key}' is not currently set."); + } else { + throw new invalid_setting_exception("Requested environment setting '{$key}' is invalid."); + } + } + /** * Get the location of ca certificates. * @return string absolute file path or empty if default used */ protected function get_cacert() { - $dataroot = $this->input->get_option('dataroot'); + $dataroot = $this->get_env('dataroot'); // Bundle in dataroot always wins. if (is_readable($dataroot.'/moodleorgca.crt')) {
mdeploytest.php+66 −0 modified@@ -113,6 +113,10 @@ public function remove_directory($path, $keeppathroot = false) { public function create_directory_precheck($path) { return parent::create_directory_precheck($path); } + + public function get_env($key) { + return parent::get_env($key); + } } @@ -300,4 +304,66 @@ public function test_create_directory_precheck() { $this->assertTrue($worker->create_directory_precheck($root)); $this->assertFalse(file_exists($root)); // The precheck is supposed to remove it again. } + + /** + * Test that an invalid setting throws an exception. + * + * @dataProvider get_env_unlisted_provider + * @expectedException invalid_setting_exception + * @expectedExceptionMessageRegExp /^Requested environment setting '[^']*' is invalid.$/ + */ + public function test_get_env_unlisted($key) { + $worker = testable_worker::instance(); + $worker->get_env($key); + } + + public function get_env_unlisted_provider() { + return array( + // Completely invalid environment variables. + array('example'), + array('invalid'), + + // Valid ones which have not been whitelisted. + array('noemailever'), + array('dbname'), + ); + } + + /** + * Test that a valid, but unset setting throws an exception. + * + * @dataProvider get_env_valid_provider + * @expectedException invalid_setting_exception + * @expectedExceptionMessageRegExp /^Requested environment setting '[^']*' is not current set.$/ + */ + public function test_get_env_unset($key) { + // Ensure that the setting is currently unset. + global $CFG; + $CFG->$key = null; + + $worker = testable_worker::instance(); + $worker->get_env($key); + } + + /** + * Test that a valid setting with data returns that data. + * + * @dataProvider get_env_valid_provider + */ + public function test_get_env_valid($key) { + // Ensure that the setting is currently unset. + global $CFG; + $CFG->$key = rand(0, 1000); + + $worker = testable_worker::instance(); + $value = $worker->get_env($key); + + $this->assertEquals($CFG->$key, $value); + } + + public function get_env_valid_provider() { + return array( + array('dataroot'), + ); + } }
240e7be7341aMDL-49087 mnet: Use real dataroot instead of user-provided
1 file changed · +32 −13
mdeploy.php+32 −13 modified@@ -31,14 +31,12 @@ * --typeroot=/var/www/moodle/htdocs/blocks * --name=loancalc * --md5=... - * --dataroot=/var/www/moodle/data * * $ sudo -u apache php mdeploy.php --upgrade \ * --package=https://moodle.org/plugins/download.php/...zip \ * --typeroot=/var/www/moodle/htdocs/blocks * --name=loancalc * --md5=... - * --dataroot=/var/www/moodle/data * * When called via HTTP, additional parameters returnurl, passfile and password must be * provided. Optional proxy configuration can be passed using parameters proxy, proxytype @@ -60,6 +58,13 @@ die('This is a standalone utility that should not be included by any other Moodle code.'); } +// This stops immediately at the beginning of lib/setup.php. +define('ABORT_AFTER_CONFIG', true); +if (PHP_SAPI === 'cli') { + // Called from the CLI - we need to set CLI_SCRIPT to ensure that appropriate CLI checks are made in setup.php. + define('CLI_SCRIPT', true); +} +require(__DIR__ . '/config.php'); // Exceptions ////////////////////////////////////////////////////////////////// @@ -201,7 +206,6 @@ public function get_option_info($name=null) { array('', 'proxytype', input_manager::TYPE_RAW, 'Proxy type (HTTP or SOCKS5)'), array('', 'proxyuserpwd', input_manager::TYPE_RAW, 'Proxy username and password (e.g. \'username:password\')'), array('', 'returnurl', input_manager::TYPE_URL, 'Return URL (HTTP access only)'), - array('d', 'dataroot', input_manager::TYPE_PATH, 'Full path to the dataroot (moodledata) directory'), array('h', 'help', input_manager::TYPE_FLAG, 'Prints usage information'), array('i', 'install', input_manager::TYPE_FLAG, 'Installation mode'), array('m', 'md5', input_manager::TYPE_MD5, 'Expected MD5 hash of the ZIP package to deploy'), @@ -909,17 +913,15 @@ protected function done($exitcode = self::EXIT_OK) { * @throws unauthorized_access_exception */ protected function authorize() { - if (PHP_SAPI === 'cli') { $this->log('Successfully authorized using the CLI SAPI'); return; } - $dataroot = $this->input->get_option('dataroot'); $passfile = $this->input->get_option('passfile'); $password = $this->input->get_option('password'); - $passpath = $dataroot.'/mdeploy/auth/'.$passfile; + $passpath = $this->get_env('dataroot') . '/mdeploy/auth/' . $passfile; if (!is_readable($passpath)) { throw new unauthorized_access_exception('Unable to read the passphrase file.'); @@ -959,12 +961,11 @@ protected function authorize() { * @return string */ protected function log_location() { - if (!is_null($this->logfile)) { return $this->logfile; } - $dataroot = $this->input->get_option('dataroot', ''); + $dataroot = $this->get_env('dataroot'); if (empty($dataroot)) { $this->logfile = false; @@ -988,8 +989,7 @@ protected function log_location() { * @return string */ protected function target_location($source) { - - $dataroot = $this->input->get_option('dataroot'); + $dataroot = $this->get_env('dataroot'); $pool = $dataroot.'/mdeploy/var'; if (!is_dir($pool)) { @@ -1013,8 +1013,7 @@ protected function target_location($source) { * @return string */ protected function backup_location($path) { - - $dataroot = $this->input->get_option('dataroot'); + $dataroot = $this->get_env('dataroot'); $pool = $dataroot.'/mdeploy/archive'; if (!is_dir($pool)) { @@ -1129,12 +1128,32 @@ protected function download_file($source, $target) { return true; } + /** + * Fetch environment settings. + * + * @param string $key The key to fetch + * @return mixed The value of the key if found, null if not found. + */ + protected function get_env($key) { + global $CFG; + + switch ($key) { + case 'dataroot': + if (isset($CFG->dataroot)) { + return $CFG->dataroot; + } + break; + } + return null; + } + } + /** * Get the location of ca certificates. * @return string absolute file path or empty if default used */ protected function get_cacert() { - $dataroot = $this->input->get_option('dataroot'); + $dataroot = $this->get_env('dataroot'); // Bundle in dataroot always wins. if (is_readable($dataroot.'/moodleorgca.crt')) {
76da7e9bc886MDL-49087 mnet: Use real dataroot instead of user-provided
2 files changed · +104 −13
mdeploy.php+38 −13 modified@@ -31,14 +31,12 @@ * --typeroot=/var/www/moodle/htdocs/blocks * --name=loancalc * --md5=... - * --dataroot=/var/www/moodle/data * * $ sudo -u apache php mdeploy.php --upgrade \ * --package=https://moodle.org/plugins/download.php/...zip \ * --typeroot=/var/www/moodle/htdocs/blocks * --name=loancalc * --md5=... - * --dataroot=/var/www/moodle/data * * When called via HTTP, additional parameters returnurl, passfile and password must be * provided. Optional proxy configuration can be passed using parameters proxy, proxytype @@ -60,6 +58,13 @@ die('This is a standalone utility that should not be included by any other Moodle code.'); } +// This stops immediately at the beginning of lib/setup.php. +define('ABORT_AFTER_CONFIG', true); +if (PHP_SAPI === 'cli') { + // Called from the CLI - we need to set CLI_SCRIPT to ensure that appropriate CLI checks are made in setup.php. + define('CLI_SCRIPT', true); +} +require(__DIR__ . '/config.php'); // Exceptions ////////////////////////////////////////////////////////////////// @@ -72,6 +77,7 @@ class backup_folder_exception extends Exception {} class zip_exception extends Exception {} class filesystem_exception extends Exception {} class checksum_exception extends Exception {} +class invalid_setting_exception extends Exception {} // Various support classes ///////////////////////////////////////////////////// @@ -201,7 +207,6 @@ public function get_option_info($name=null) { array('', 'proxytype', input_manager::TYPE_RAW, 'Proxy type (HTTP or SOCKS5)'), array('', 'proxyuserpwd', input_manager::TYPE_RAW, 'Proxy username and password (e.g. \'username:password\')'), array('', 'returnurl', input_manager::TYPE_URL, 'Return URL (HTTP access only)'), - array('d', 'dataroot', input_manager::TYPE_PATH, 'Full path to the dataroot (moodledata) directory'), array('h', 'help', input_manager::TYPE_FLAG, 'Prints usage information'), array('i', 'install', input_manager::TYPE_FLAG, 'Installation mode'), array('m', 'md5', input_manager::TYPE_MD5, 'Expected MD5 hash of the ZIP package to deploy'), @@ -729,6 +734,11 @@ class worker extends singleton_pattern { /** @var string the full path to the log file */ private $logfile = null; + /** @var array the whitelisted config options which can be queried. */ + private $validconfigoptions = array( + 'dataroot' => true, + ); + /** * Main - the one that actually does something */ @@ -909,17 +919,15 @@ protected function done($exitcode = self::EXIT_OK) { * @throws unauthorized_access_exception */ protected function authorize() { - if (PHP_SAPI === 'cli') { $this->log('Successfully authorized using the CLI SAPI'); return; } - $dataroot = $this->input->get_option('dataroot'); $passfile = $this->input->get_option('passfile'); $password = $this->input->get_option('password'); - $passpath = $dataroot.'/mdeploy/auth/'.$passfile; + $passpath = $this->get_env('dataroot') . '/mdeploy/auth/' . $passfile; if (!is_readable($passpath)) { throw new unauthorized_access_exception('Unable to read the passphrase file.'); @@ -959,12 +967,11 @@ protected function authorize() { * @return string */ protected function log_location() { - if (!is_null($this->logfile)) { return $this->logfile; } - $dataroot = $this->input->get_option('dataroot', ''); + $dataroot = $this->get_env('dataroot'); if (empty($dataroot)) { $this->logfile = false; @@ -988,8 +995,7 @@ protected function log_location() { * @return string */ protected function target_location($source) { - - $dataroot = $this->input->get_option('dataroot'); + $dataroot = $this->get_env('dataroot'); $pool = $dataroot.'/mdeploy/var'; if (!is_dir($pool)) { @@ -1013,8 +1019,7 @@ protected function target_location($source) { * @return string */ protected function backup_location($path) { - - $dataroot = $this->input->get_option('dataroot'); + $dataroot = $this->get_env('dataroot'); $pool = $dataroot.'/mdeploy/archive'; if (!is_dir($pool)) { @@ -1129,12 +1134,32 @@ protected function download_file($source, $target) { return true; } + /** + * Fetch environment settings. + * + * @param string $key The key to fetch + * @return mixed The value of the key if found. + * @throws invalid_setting_exception if the option is not set, or is invalid. + */ + protected function get_env($key) { + global $CFG; + + if (array_key_exists($key, $this->validconfigoptions)) { + if (isset($CFG->$key)) { + return $CFG->$key; + } + throw new invalid_setting_exception("Requested environment setting '{$key}' is not currently set."); + } else { + throw new invalid_setting_exception("Requested environment setting '{$key}' is invalid."); + } + } + /** * Get the location of ca certificates. * @return string absolute file path or empty if default used */ protected function get_cacert() { - $dataroot = $this->input->get_option('dataroot'); + $dataroot = $this->get_env('dataroot'); // Bundle in dataroot always wins. if (is_readable($dataroot.'/moodleorgca.crt')) {
mdeploytest.php+66 −0 modified@@ -113,6 +113,10 @@ public function remove_directory($path, $keeppathroot = false) { public function create_directory_precheck($path) { return parent::create_directory_precheck($path); } + + public function get_env($key) { + return parent::get_env($key); + } } @@ -300,4 +304,66 @@ public function test_create_directory_precheck() { $this->assertTrue($worker->create_directory_precheck($root)); $this->assertFalse(file_exists($root)); // The precheck is supposed to remove it again. } + + /** + * Test that an invalid setting throws an exception. + * + * @dataProvider get_env_unlisted_provider + * @expectedException invalid_setting_exception + * @expectedExceptionMessageRegExp /^Requested environment setting '[^']*' is invalid.$/ + */ + public function test_get_env_unlisted($key) { + $worker = testable_worker::instance(); + $worker->get_env($key); + } + + public function get_env_unlisted_provider() { + return array( + // Completely invalid environment variables. + array('example'), + array('invalid'), + + // Valid ones which have not been whitelisted. + array('noemailever'), + array('dbname'), + ); + } + + /** + * Test that a valid, but unset setting throws an exception. + * + * @dataProvider get_env_valid_provider + * @expectedException invalid_setting_exception + * @expectedExceptionMessageRegExp /^Requested environment setting '[^']*' is not current set.$/ + */ + public function test_get_env_unset($key) { + // Ensure that the setting is currently unset. + global $CFG; + $CFG->$key = null; + + $worker = testable_worker::instance(); + $worker->get_env($key); + } + + /** + * Test that a valid setting with data returns that data. + * + * @dataProvider get_env_valid_provider + */ + public function test_get_env_valid($key) { + // Ensure that the setting is currently unset. + global $CFG; + $CFG->$key = rand(0, 1000); + + $worker = testable_worker::instance(); + $value = $worker->get_env($key); + + $this->assertEquals($CFG->$key, $value); + } + + public function get_env_valid_provider() { + return array( + array('dataroot'), + ); + } }
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
14- github.com/advisories/GHSA-cm4r-58pj-h2phghsaADVISORY
- moodle.org/mod/forum/discuss.phpnvdVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2015-2267ghsaADVISORY
- openwall.com/lists/oss-security/2015/03/16/1nvdWEB
- github.com/moodle/moodle/commit/12a8fcb5e45c58ee8267ad0472852c2b80a19878ghsaWEB
- github.com/moodle/moodle/commit/240e7be7341afa31096fdbf3f242a7966f6237abghsaWEB
- github.com/moodle/moodle/commit/4475f1e478370fb97933127ec60e40f39e285da1ghsaWEB
- github.com/moodle/moodle/commit/76da7e9bc88669eab62f83f04639ba356a0b0c5aghsaWEB
- github.com/moodle/moodle/commit/83866c3c2a5b1391317172eea0b4f017c6d142d2ghsaWEB
- github.com/moodle/moodle/commit/84f9f60b67e1e20058fbe2afa473607d075aff63ghsaWEB
- github.com/moodle/moodle/commit/8d9bdd28e049ca6b6b2a4ab8f142097c2f907df6ghsaWEB
- github.com/moodle/moodle/commit/a47aabc7833d0c88a83791d99a1204742c33f59bghsaWEB
- github.com/moodle/moodle/commit/c353a6202658f320096a41e94494063393153b7fghsaWEB
- github.com/moodle/moodle/commit/de169b7944e36d374d55e3f396d90ab2b4303afbghsaWEB
News mentions
0No linked articles in our index yet.