MDL-53240 filetypes: Add functions to be used for input validation
authorJohn Okely <john@moodle.com>
Thu, 30 Mar 2017 04:14:52 +0000 (12:14 +0800)
committerDavid Mudrák <david@moodle.com>
Thu, 1 Jun 2017 07:18:38 +0000 (09:18 +0200)
lib/form/classes/filetypes_util.php
lib/form/tests/filetypes_util_test.php

index 7d6f136..82a56c4 100644 (file)
@@ -432,4 +432,64 @@ class filetypes_util {
 
         return !empty($intersection);
     }
+
+    /**
+     * Is the given filename of an allowed file type?
+     *
+     * Empty whitelist is interpretted as "any file type is allowed" rather
+     * than "no file can be uploaded".
+     *
+     * @param string $filename the file name
+     * @param string|array $whitelist list of allowed file extensions
+     * @return boolean True if the file type is allowed, false if not
+     */
+    public function is_allowed_file_type($filename, $whitelist) {
+
+        $allowedextensions = $this->expand($whitelist);
+
+        if (empty($allowedextensions) || $allowedextensions == ['*']) {
+            return true;
+        }
+
+        $haystack = strrev(trim(strtolower($filename)));
+
+        foreach ($allowedextensions as $extension) {
+            if (strpos($haystack, strrev($extension)) === 0) {
+                // The file name ends with the extension.
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns file types from the list that are not recognized
+     *
+     * @param string|array $types list of user-defined file types
+     * @return array A list of unknown file types.
+     */
+    public function get_unknown_file_types($types) {
+        $unknown = [];
+
+        foreach ($this->normalize_file_types($types) as $type) {
+            if ($this->is_filetype_group($type)) {
+                // The type is a group that exists.
+            } else if ($this->looks_like_mimetype($type)) {
+                // If there's no extension associated with that mimetype, we consider it unknown.
+                if (empty(file_get_typegroup('extension', [$type]))) {
+                    $unknown[$type] = true;
+                }
+            } else {
+                $coretypes = core_filetypes::get_types();
+                $typecleaned = str_replace(".", "", $type);
+                if (empty($coretypes[$typecleaned])) {
+                    // If there's no extension, it doesn't exist.
+                    $unknown[$type] = true;
+                }
+            }
+        }
+
+        return array_keys($unknown);
+    }
 }
index 91e17cf..45dec2f 100644 (file)
@@ -306,4 +306,123 @@ class filetypes_util_testcase extends advanced_testcase {
             }
         }
     }
+
+    /**
+     * Data provider for testing test_is_allowed_file_type.
+     *
+     * @return array
+     */
+    public function is_allowed_file_type_provider() {
+        return [
+            'Filetype not in extension whitelist' => [
+                'filename' => 'test.xml',
+                'whitelist' => '.png .jpg',
+                'expected' => false
+            ],
+            'Filetype not in mimetype whitelist' => [
+                'filename' => 'test.xml',
+                'whitelist' => 'image/png',
+                'expected' => false
+            ],
+            'Filetype not in group whitelist' => [
+                'filename' => 'test.xml',
+                'whitelist' => 'web_file',
+                'expected' => false
+            ],
+            'Filetype in whitelist as extension' => [
+                'filename' => 'test.xml',
+                'whitelist' => 'xml',
+                'expected' => true
+            ],
+            'Empty whitelist should allow all' => [
+                'filename' => 'test.xml',
+                'whitelist' => '',
+                'expected' => true
+            ],
+            'Filetype in whitelist but later on' => [
+                'filename' => 'test.xml',
+                'whitelist' => 'gif;jpeg,image/png xml xlsx',
+                'expected' => true
+            ],
+            'Filetype in whitelist as mimetype' => [
+                'filename' => 'test.xml',
+                'whitelist' => 'image/png application/xml',
+                'expected' => true
+            ],
+            'Filetype in whitelist as group' => [
+                'filename' => 'test.html',
+                'whitelist' => 'video,web_file',
+                'expected' => true
+            ],
+        ];
+    }
+
+    /**
+     * Test is_allowed_file_type().
+     * @dataProvider is_allowed_file_type_provider
+     * @param string $filename The filename to check
+     * @param string $whitelist The space , or ; separated list of types supported
+     * @param boolean $expected The expected result. True if the file is allowed, false if not.
+     */
+    public function test_is_allowed_file_type($filename, $whitelist, $expected) {
+        $util = new filetypes_util();
+        $this->assertSame($expected, $util->is_allowed_file_type($filename, $whitelist));
+    }
+
+    /**
+     * Data provider for testing test_get_unknown_file_types.
+     *
+     * @return array
+     */
+    public function get_unknown_file_types_provider() {
+        return [
+            'Unknown extension' => [
+                'filetypes' => '.rat',
+                'expected' => ['.rat']
+            ],
+            'Multiple unknown extensions' => [
+                'filetypes' => '.ricefield .rat',
+                'expected' => ['.ricefield', '.rat']
+            ],
+            'Existant extension' => [
+                'filetypes' => '.xml',
+                'expected' => []
+            ],
+            'Existant group' => [
+                'filetypes' => 'web_file',
+                'expected' => []
+            ],
+            'Nonexistant mimetypes' => [
+                'filetypes' => 'ricefield/rat',
+                'expected' => ['ricefield/rat']
+            ],
+            'Existant mimetype' => [
+                'filetypes' => 'application/xml',
+                'expected' => []
+            ],
+            'Multiple unknown mimetypes' => [
+                'filetypes' => 'ricefield/rat cam/ball',
+                'expected' => ['ricefield/rat', 'cam/ball']
+            ],
+            'Strange characters in unknown extension/group' => [
+                'filetypes' => '©ç√√ß∂å√©åß©√',
+                'expected' => ['.©ç√√ß∂å√©åß©√']
+            ],
+            'Some existant some not' => [
+                'filetypes' => '.txt application/xml web_file ©ç√√ß∂å√©åß©√ .png ricefield/rat document',
+                'expected' => ['.©ç√√ß∂å√©åß©√', 'ricefield/rat']
+            ],
+        ];
+    }
+
+    /**
+     * Test get_unknown_file_types().
+     * @dataProvider get_unknown_file_types_provider
+     * @param string $filetypes The filetypes to check
+     * @param array $expected The expected result. The list of non existant file types.
+     */
+    public function test_get_unknown_file_types($filetypes, $expected) {
+        $util = new filetypes_util();
+        $this->assertSame($expected, $util->get_unknown_file_types($filetypes));
+    }
 }