diff --git a/lib/classes/scss.php b/lib/classes/scss.php index 5a5e2abbd21..a8aa4a9081e 100644 --- a/lib/classes/scss.php +++ b/lib/classes/scss.php @@ -98,4 +98,58 @@ class core_scss extends \Leafo\ScssPhp\Compiler { return $this->compile($content); } + /** + * Compile child; returns a value to halt execution + * + * @param array $child + * @param \Leafo\ScssPhp\Formatter\OutputBlock $out + * + * @return array|null + */ + protected function compileChild($child, \Leafo\ScssPhp\Formatter\OutputBlock $out) { + switch($child[0]) { + case \Leafo\ScssPhp\Type::T_SCSSPHP_IMPORT_ONCE: + case \Leafo\ScssPhp\Type::T_IMPORT: + list(, $rawpath) = $child; + $rawpath = $this->reduce($rawpath); + $path = $this->compileStringContent($rawpath); + if ($path = $this->findImport($path)) { + if ($this->is_valid_file($path)) { + return parent::compileChild($child, $out); + } else { + // Sneaky stuff, don't let non scss file in. + debugging("Can't import scss file - " . $path, DEBUG_DEVELOPER); + } + } + break; + default: + return parent::compileChild($child, $out); + } + } + + /** + * Is the given file valid for import ? + * + * @param $path + * @return bool + */ + protected function is_valid_file($path) { + global $CFG; + + $realpath = realpath($path); + + // Additional theme directory. + $addthemedirectory = core_component::get_plugin_types()['theme']; + $addrealroot = realpath($addthemedirectory); + + // Original theme directory. + $themedirectory = $CFG->dirroot . "/theme"; + $realroot = realpath($themedirectory); + + // File should end in .scss and must be in sites theme directory, else ignore it. + $pathvalid = $realpath !== false; + $pathvalid = $pathvalid && (substr($path, -5) === '.scss'); + $pathvalid = $pathvalid && (strpos($realpath, $realroot) === 0 || strpos($realpath, $addrealroot) === 0); + return $pathvalid; + } } diff --git a/lib/tests/scss_test.php b/lib/tests/scss_test.php new file mode 100644 index 00000000000..80f3cb54c8e --- /dev/null +++ b/lib/tests/scss_test.php @@ -0,0 +1,81 @@ +. + +/** + * This file contains the unittests for core scss. + * + * @package core + * @category phpunit + * @copyright 2016 onwards Ankit Agarwal + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * This file contains the unittests for core scss. + * + * @package core + * @category phpunit + * @copyright 2016 onwards Ankit Agarwal + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_scss_testcase extends advanced_testcase { + + /** + * Data provider for is_valid_file + * @return array + */ + public function is_valid_file_provider() { + $themedirectory = core_component::get_component_directory('theme_boost'); + $realroot = realpath($themedirectory); + return [ + "File import 1" => [ + "path" => "../test.php", + "valid" => false + ], + "File import 2" => [ + "path" => "../test.py", + "valid" => false + ], + "File import 3" => [ + "path" => $realroot . "/scss/moodle.scss", + "valid" => true + ], + "File import 4" => [ + "path" => $realroot . "/scss/../../../config.php", + "valid" => false + ], + "File import 5" => [ + "path" => "/../../../../etc/passwd", + "valid" => false + ], + "File import 6" => [ + "path" => "random", + "valid" => false + ] + ]; + } + + /** + * @dataProvider is_valid_file_provider + */ + public function test_is_valid_file($path, $valid) { + $scss = new \core_scss(); + $pathvalid = phpunit_util::call_internal_method($scss, 'is_valid_file', [$path], \core_scss::class); + $this->assertSame($valid, $pathvalid); + } +} \ No newline at end of file