CVE-2023-47621 (published 2023-11-13T20:15Z)

Guest Entries is a php library which allows users to create, update & delete entries from the front-end of a site. In affected versions the file uploads feature did not prevent the upload of PHP files. This may lead to code execution on the server by authenticated users. This vulnerability is fixed in v3.1.2. Users are advised to upgrade. There are no known workarounds for this vulnerability.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-434
cwe_name: Unrestricted Upload of File with Dangerous Type
description: The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.
extended_description: 
            
            
            
            
            
         
         

Fix information:
repo_url: https://github.com/duncanmcclean/guest-entries
commit_details: repo_url: https://github.com/duncanmcclean/guest-entries
author: Duncan McClean
author_date: 2023-11-11 10:31:54+00:00
msg: Prevent uploading .php files (#60)

* Prevent uploading .php files

* Apply Code Style changes

---------

Co-authored-by: duncanmcclean <duncanmcclean@users.noreply.github.com>
repository: repo_url: https://github.com/duncanmcclean/guest-entries
repo_name: duncanmcclean/guest-entries
description: Allow users to create, update & delete entries from the front-end of your site
repo_language: PHP
owner: duncanmcclean
file_changes: filename: GuestEntryController.php
change_type: ModificationType.MODIFY
diff: @@ -13,7 +13,9 @@ use DuncanMcClean\GuestEntries\Http\Requests\UpdateRequest;
 use Illuminate\Http\Request;
 use Illuminate\Routing\Controller;
 use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\Validator;
 use Illuminate\Support\Str;
+use Illuminate\Validation\ValidationException;
 use Statamic\Facades\Asset;
 use Statamic\Facades\AssetContainer;
 use Statamic\Facades\Collection;
@@ -240,8 +242,17 @@ class GuestEntryController extends Controller
             $uploadedFiles = [$uploadedFiles];
         }
 
-        // Filter out any null values.
-        $uploadedFiles = collect($uploadedFiles)->filter()->toArray();
+        $uploadedFiles = collect($uploadedFiles)
+            ->each(function ($file) use ($key) {
+                if (in_array(trim(strtolower($file->getClientOriginalExtension())), ['php', 'php3', 'php4', 'php5', 'phtml'])) {
+                    $validator = Validator::make([], []);
+                    $validator->errors()->add($key, __('Failed to upload.'));
+
+                    throw new ValidationException($validator);
+                }
+            })
+            ->filter()
+            ->toArray();
 
         /* @var \Illuminate\Http\Testing\File $file */
         foreach ($uploadedFiles as $uploadedFile) {

programming_language: PHP
filename: DestroyRequest.php
change_type: ModificationType.MODIFY
diff: @@ -8,8 +8,8 @@ use Illuminate\Foundation\Http\FormRequest;
 
 class DestroyRequest extends FormRequest
 {
-    use Concerns\WhitelistedCollections,
-        Concerns\HandleFailedValidation;
+    use Concerns\HandleFailedValidation,
+        Concerns\WhitelistedCollections;
 
     public function authorize()
     {

programming_language: PHP
filename: StoreRequest.php
change_type: ModificationType.MODIFY
diff: @@ -9,8 +9,8 @@ use Statamic\Facades\Collection;
 class StoreRequest extends FormRequest
 {
     use Concerns\AcceptsFormRequests,
-        Concerns\WhitelistedCollections,
-        Concerns\HandleFailedValidation;
+        Concerns\HandleFailedValidation,
+        Concerns\WhitelistedCollections;
 
     public function authorize()
     {

programming_language: PHP
filename: UpdateRequest.php
change_type: ModificationType.MODIFY
diff: @@ -9,8 +9,8 @@ use Illuminate\Foundation\Http\FormRequest;
 class UpdateRequest extends FormRequest
 {
     use Concerns\AcceptsFormRequests,
-        Concerns\WhitelistedCollections,
-        Concerns\HandleFailedValidation;
+        Concerns\HandleFailedValidation,
+        Concerns\WhitelistedCollections;
 
     public function authorize()
     {

programming_language: PHP
filename: GuestEntryControllerTest.php
change_type: ModificationType.MODIFY
diff: @@ -9,6 +9,7 @@ use Illuminate\Http\UploadedFile;
 use Illuminate\Support\Facades\Config;
 use Illuminate\Support\Facades\Event;
 use Illuminate\Support\Facades\File;
+use Illuminate\Support\Facades\Storage;
 use Spatie\TestTime\TestTime;
 use Statamic\Events\EntrySaved;
 use Statamic\Facades\Asset;
@@ -18,6 +19,8 @@ use Statamic\Facades\Collection;
 use Statamic\Facades\Entry;
 use Statamic\Facades\Site;
 
+use function PHPUnit\Framework\assertCount;
+
 beforeEach(function () {
     File::deleteDirectory(app('stache')->store('entries')->directory());
 
@@ -462,6 +465,66 @@ it('can store entry and ensure file can be uploaded', function () {
     $this->assertIsString($entry->get('attachment'));
 });
 
+it('cant store an entry when uploading a PHP file', function () {
+    AssetContainer::make('assets')->disk('local')->save();
+
+    Blueprint::make('comments')
+        ->setNamespace('collections.comments')
+        ->setContents([
+            'title' => 'Comments',
+            'sections' => [
+                'main' => [
+                    'display' => 'main',
+                    'fields' => [
+                        [
+                            'handle' => 'title',
+                            'field' => [
+                                'type' => 'text',
+                            ],
+                        ],
+                        [
+                            'handle' => 'slug',
+                            'field' => [
+                                'type' => 'slug',
+                            ],
+                        ],
+                        [
+                            'handle' => 'attachment',
+                            'field' => [
+                                'mode' => 'list',
+                                'container' => 'assets',
+                                'restrict' => false,
+                                'allow_uploads' => true,
+                                'show_filename' => true,
+                                'display' => 'Attachment',
+                                'type' => 'assets',
+                                'icon' => 'assets',
+                                'listable' => 'hidden',
+                                'max_items' => 1,
+                            ],
+                        ],
+                    ],
+                ],
+            ],
+        ])
+        ->save();
+
+    Collection::make('comments')->save();
+
+    $this
+        ->post(route('statamic.guest-entries.store'), [
+            '_collection' => encrypt('comments'),
+            'title' => 'This is great',
+            'slug' => 'this-is-great',
+            'attachment' => UploadedFile::fake()->image('foobar.php'),
+        ])
+        ->assertSessionHasErrors('attachment');
+
+    assertCount(0, Entry::all());
+
+    Storage::disk('local')->assertMissing('assets/foobar.php');
+});
+
 it('can store entry and ensure multiple files can be uploaded', function () {
     AssetContainer::make('assets')->disk('local')->save();
 
@@ -1465,6 +1528,89 @@ it('can update entry and ensure file can be uploaded', function () {
     $this->assertIsString($entry->get('attachment'));
 });
 
+it('cant update entry when uploading a PHP file', function () {
+    AssetContainer::make('assets')->disk('local')->save();
+
+    Blueprint::make('albums')
+        ->setNamespace('collections.albums')
+        ->setContents([
+            'title' => 'Albums',
+            'sections' => [
+                'main' => [
+                    'display' => 'main',
+                    'fields' => [
+                        [
+                            'handle' => 'title',
+                            'field' => [
+                                'type' => 'text',
+                            ],
+                        ],
+                        [
+                            'handle' => 'artist',
+                            'field' => [
+                                'type' => 'text',
+                            ],
+                        ],
+                        [
+                            'handle' => 'slug',
+                            'field' => [
+                                'type' => 'slug',
+                            ],
+                        ],
+                        [
+                            'handle' => 'record_label',
+                            'field' => [
+                                'type' => 'text',
+                            ],
+                        ],
+                        [
+                            'handle' => 'attachment',
+                            'field' => [
+                                'mode' => 'list',
+                                'container' => 'assets',
+                                'restrict' => false,
+                                'allow_uploads' => true,
+                                'show_filename' => true,
+                                'display' => 'Attachment',
+                                'type' => 'assets',
+                                'icon' => 'assets',
+                                'listable' => 'hidden',
+                                'max_items' => 1,
+                            ],
+                        ],
+                    ],
+                ],
+            ],
+        ])
+        ->save();
+
+    Collection::make('albums')->save();
+
+    Entry::make()
+        ->id('allo-mate-idee')
+        ->collection('albums')
+        ->slug('allo-mate')
+        ->data([
+            'title' => 'Allo Mate!',
+            'artist' => 'Guvna B',
+        ])
+        ->save();
+
+    $this
+        ->post(route('statamic.guest-entries.update'), [
+            '_collection' => encrypt('albums'),
+            '_id' => encrypt('allo-mate-idee'),
+            'attachment' => UploadedFile::fake()->image('something.php'),
+        ])
+        ->assertSessionHasErrors('attachment');
+
+    $entry = Entry::find('allo-mate-idee');
+
+    $this->assertNull($entry->get('attachment'));
+
+    Storage::disk('local')->assertMissing('something.php');
+});
+
 it('can update entry and ensure multiple files can be uploaded', function () {
     AssetContainer::make('assets')->disk('local')->save();
 

programming_language: PHP
filename: GuestEntriesTagTest.php
change_type: ModificationType.MODIFY
diff: @@ -3,7 +3,6 @@
 use DuncanMcClean\GuestEntries\Tags\GuestEntriesTag;
 use Illuminate\Container\EntryNotFoundException;
 use Illuminate\Support\Facades\Config;
-use function PHPUnit\Framework\assertStringContainsString;
 use Statamic\Exceptions\CollectionNotFoundException;
 use Statamic\Facades\Antlers;
 use Statamic\Facades\Collection;
@@ -11,6 +10,8 @@ use Statamic\Facades\Entry;
 use Statamic\Statamic;
 use Statamic\Tags\Tags;
 
+use function PHPUnit\Framework\assertStringContainsString;
+
 $tag = null;
 
 beforeEach(function () use (&$tag) {

programming_language: PHP