MDL-56992 core_scss: Don't allow invalid files to be included
authorAnkit Agarwal <ankit@moodle.com>
Thu, 8 Dec 2016 06:03:55 +0000 (11:33 +0530)
committerDan Poltawski <dan@moodle.com>
Wed, 4 Jan 2017 11:33:57 +0000 (11:33 +0000)
We allow only .scss files so allow files to be included only and only if they end in .scss and they exist on server under theme directory.

lib/classes/scss.php
lib/tests/scss_test.php [new file with mode: 0644]

index 5a5e2ab..a8aa4a9 100644 (file)
@@ -98,4 +98,58 @@ class core_scss extends \Leafo\ScssPhp\Compiler {
         return $this->compile($content);
     }
 
         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 (file)
index 0000000..80f3cb5
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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