CVE-2023-47621 (PHP, published 2023-11-13T20:15Z)
Description: value: 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.
Severity: nan (CVSS v3 base score: 8.8)
Weakness: CWE-434 \u2014 Unrestricted Upload of File with Dangerous Type
The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.
Repository: https://github.com/duncanmcclean/guest-entries
Files changed:
src/Http/Controllers/GuestEntryController.php
src/Http/Requests/DestroyRequest.php
src/Http/Requests/StoreRequest.php
src/Http/Requests/UpdateRequest.php
tests/Http/Controllers/GuestEntryControllerTest.php
tests/Tags/GuestEntriesTagTest.php
Commit message: Prevent uploading .php files (#60)

* Prevent uploading .php files

* Apply Code Style changes

---------

Co-authored-by: duncanmcclean <duncanmcclean@users.noreply.github.com>

Vulnerable code:
        // Filter out any null values.
        $uploadedFiles = collect($uploadedFiles)->filter()->toArray();
    use Concerns\WhitelistedCollections,
        Concerns\HandleFailedValidation;
        Concerns\WhitelistedCollections,
        Concerns\HandleFailedValidation;
        Concerns\WhitelistedCollections,
        Concerns\HandleFailedValidation;
use function PHPUnit\Framework\assertStringContainsString;

Fixed code:
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
        $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();
    use Concerns\HandleFailedValidation,
        Concerns\WhitelistedCollections;
        Concerns\HandleFailedValidation,
        Concerns\WhitelistedCollections;
        Concerns\HandleFailedValidation,
        Concerns\WhitelistedCollections;
use Illuminate\Support\Facades\Storage;
use function PHPUnit\Framework\assertCount;

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('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');
});

use function PHPUnit\Framework\assertStringContainsString;