Validate file extensions in `UploadFormField`
authorCyperghost <olaf_schmitz_1@t-online.de>
Tue, 29 Oct 2024 09:45:13 +0000 (10:45 +0100)
committerCyperghost <olaf_schmitz_1@t-online.de>
Tue, 29 Oct 2024 09:45:13 +0000 (10:45 +0100)
wcfsetup/install/files/lib/system/form/builder/field/UploadFormField.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 02b4c69fc3874c4ada9cbacaba7abbd70463442a..bb60dc0e16d1de297e578c76c95ab2d1ab5a1680 100644 (file)
@@ -106,6 +106,14 @@ class UploadFormField extends AbstractFormField
      */
     private $isRegistered = false;
 
+    /**
+     * List of allowed file extensions or `null` if the extension is not validated.
+     *
+     * @var string[]|null
+     * @since 6.2
+     */
+    private ?array $allowedFileExtensions = null;
+
     /**
      * Unregisters the current field in the upload handler.
      */
@@ -296,6 +304,41 @@ class UploadFormField extends AbstractFormField
                 }
             }
         }
+
+        if ($this->allowedFileExtensions !== null) {
+            // File extensions that contain a dot must be checked otherwise,
+            // the function `UploadFile::getFilenameExtension()` only returns the last part of the file extension.
+            //
+            // For example, if the file is called `blub.tar.gz`, only `gz` is returned.
+            // In this case, we check whether the file ends with `.tar.gz`.
+            $specialFileExtensions = \array_filter($this->allowedFileExtensions, function ($extension) {
+                return \str_contains($extension, '.');
+            });
+            $allowedFileExtensions = \array_filter($this->allowedFileExtensions, function ($extension) {
+                return !\str_contains($extension, '.');
+            });
+
+            foreach ($this->getValue() as $file) {
+                if (!\in_array($file->getFilenameExtension(), $allowedFileExtensions)) {
+                    foreach ($specialFileExtensions as $extension) {
+                        if (\str_ends_with(".{$file->getFilename()}", $extension)) {
+                            continue 2;
+                        }
+                    }
+
+                    $this->addValidationError(
+                        new FormFieldValidationError(
+                            'acceptableFileExtensions',
+                            'wcf.form.field.upload.error.fileExtension',
+                            [
+                                'allowedFileExtensions' => $this->allowedFileExtensions,
+                                'file' => $file,
+                            ]
+                        )
+                    );
+                }
+            }
+        }
     }
 
     /**
@@ -953,4 +996,26 @@ class UploadFormField extends AbstractFormField
     {
         return $this->acceptableFiles;
     }
+
+    /**
+     * Returns the allowed file extensions or `null` if the extension is not validated.
+     *
+     * @since  6.2
+     */
+    public function getAllowedFileExtensions(): ?array
+    {
+        return $this->allowedFileExtensions;
+    }
+
+    /**
+     * Specifies the allowed file extensions or `null` if the extension is not to be validated.
+     *
+     * @since  6.2
+     */
+    public function setAllowedFileExtensions(?array $allowedFileExtensions = null): static
+    {
+        $this->allowedFileExtensions = $allowedFileExtensions;
+
+        return $this;
+    }
 }
index 9894e4b4d57576be61e7ec7dd7be2aec484874a5..b38da4ad785ce6b286dd55b1ea915861a2b1caac 100644 (file)
@@ -4100,6 +4100,7 @@ Dateianhänge:
                <item name="wcf.form.field.upload.error.maximumImageHeight"><![CDATA[Die Datei „{$file->getFilename()}“ darf maximal {#$maximumImageHeight} Pixel hoch sein.]]></item>
                <item name="wcf.form.field.upload.error.minimum"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du musst{else}Sie müssen{/if} mindestens {if $minimum > 1}{#$minimum} Dateien{else}eine Datei{/if} hochladen.]]></item>
                <item name="wcf.form.field.upload.error.maximum"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du darfst{else}Sie dürfen{/if} maximal {if $maximum > 1}{#$maximum} Dateien{else}eine Datei{/if} hochladen.]]></item>
+               <item name="wcf.form.field.upload.error.fileExtension"><![CDATA[Die Datei „{$file->getFilename()}“ hat eine ungültige Dateiendung. Erlaubt sind: {implode from=$allowedFileExtensions item=extension}{$extension}{/implode}.]]></item>
        </category>
        <category name="wcf.image">
                <item name="wcf.image.coverPhoto"><![CDATA[Titelbild]]></item>
index 6ab91420388c77c16d60d641c279c2bb8ca03590..5fab4999e3f2eb1eaa50d8706d5f74030b8226eb 100644 (file)
@@ -4046,6 +4046,7 @@ Attachments:
                <item name="wcf.form.field.upload.error.maximumImageHeight"><![CDATA[The file “{$file->getFilename()}” may have a maximum height of {#$maximumImageHeight} pixels.]]></item>
                <item name="wcf.form.field.upload.error.minimum"><![CDATA[You must upload at least {if $minimum > 1}{#$minimum} files{else}one file{/if}.]]></item>
                <item name="wcf.form.field.upload.error.maximum"><![CDATA[You can upload a maximum of {if $maximum > 1}{#$maximum} files{else}one file{/if}.]]></item>
+               <item name="wcf.form.field.upload.error.fileExtension"><![CDATA[The file “{$file->getFilename()}” has an invalid file extension. Allowed are: {implode from=$allowedFileExtensions item=extension}{$extension}{/implode}.]]></item>
        </category>
        <category name="wcf.image">
                <item name="wcf.image.coverPhoto"><![CDATA[Cover Photo]]></item>