CVE-2021-32717: Incorrect Permission Assignment for Critical Resource (CWE-732)
value: Shopware is an open source eCommerce platform. In versions prior to 6.4.1.1 private files publicly accessible with Cloud Storage providers when the hashed URL is known. Users are recommend to first change their configuration to set the correct visibility according to the documentation. The visibility must be at the same level as `type`. When the Storage is saved on Amazon AWS we recommending disabling public access to the bucket containing the private files: https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html. Otherwise, update to Shopware 6.4.1.1 or install or update the Security plugin (https://store.shopware.com/en/detail/index/sArticle/518463/number/Swag136939272659) and run the command `./bin/console s3:set-visibility` to correct your cloud file visibilities.
CWE description: The product specifies permissions for a security-critical resource in a way that allows that resource to be read or modified by unintended actors.
CVSS v3 base score: 7.5
Published: 2021-06-24T21:15Z
Repository: https://github.com/shopware/platform
Language: PHP
Files changed: changelog/_unreleased/2021-05-10-fix-visibility-setting-in-s3-storage-fs.md, src/Core/Framework/Adapter/Command/S3FilesystemVisibilityCommand.php, src/Core/Framework/Adapter/Filesystem/FilesystemFactory.php, src/Core/Framework/DependencyInjection/services.xml
Commit date: 2021-06-08 10:17:07 +0200
Commit message: NEXT-14744 - fix visibility setting in s3 storage fs

Diff: diff --git a/changelog/_unreleased/2021-05-10-fix-visibility-setting-in-s3-storage-fs.md b/changelog/_unreleased/2021-05-10-fix-visibility-setting-in-s3-storage-fs.md
new file mode 100644
index 0000000000..f8df86ee6e
--- /dev/null
+++ b/changelog/_unreleased/2021-05-10-fix-visibility-setting-in-s3-storage-fs.md
@@ -0,0 +1,10 @@
+---
+title: fix visibility setting in s3 storage fs
+issue: NEXT-14744
+author: d.neustadt
+author_email: d.neustadt@shopware.com 
+author_github: dneustadt
+---
+# Core
+* Changed setting of visibility from fallback config to nested `options` parameter as per documentation
+* Added `s3:set-visibility` CLI command for retroactively setting visibility of already stored objects
diff --git a/src/Core/Framework/Adapter/Command/S3FilesystemVisibilityCommand.php b/src/Core/Framework/Adapter/Command/S3FilesystemVisibilityCommand.php
new file mode 100644
index 0000000000..c0c13a3b47
--- /dev/null
+++ b/src/Core/Framework/Adapter/Command/S3FilesystemVisibilityCommand.php
@@ -0,0 +1,112 @@
+<?php declare(strict_types=1);
+
+namespace Shopware\Core\Framework\Adapter\Command;
+
+use League\Flysystem\FilesystemInterface;
+use Shopware\Core\Framework\Adapter\Console\ShopwareStyle;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class S3FilesystemVisibilityCommand extends Command
+{
+    protected static $defaultName = 's3:set-visibility';
+
+    /**
+     * @var FilesystemInterface
+     */
+    private $filesystemPrivate;
+
+    /**
+     * @var FilesystemInterface
+     */
+    private $filesystemPublic;
+
+    /**
+     * @var FilesystemInterface
+     */
+    private $filesystemTheme;
+
+    /**
+     * @var FilesystemInterface
+     */
+    private $filesystemSitemap;
+
+    /**
+     * @var FilesystemInterface
+     */
+    private $filesystemAsset;
+
+    public function __construct(
+        FilesystemInterface $filesystemPrivate,
+        FilesystemInterface $filesystemPublic,
+        FilesystemInterface $filesystemTheme,
+        FilesystemInterface $filesystemSitemap,
+        FilesystemInterface $filesystemAsset
+    ) {
+        parent::__construct();
+        $this->filesystemPrivate = $filesystemPrivate;
+        $this->filesystemPublic = $filesystemPublic;
+        $this->filesystemTheme = $filesystemTheme;
+        $this->filesystemSitemap = $filesystemSitemap;
+        $this->filesystemAsset = $filesystemAsset;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure(): void
+    {
+        $this
+            ->setDescription('Sets visibility for all objects in corresponding bucket of S3 storage.');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $style = new ShopwareStyle($input, $output);
+
+        $style->warning('If both private and public objects are stored in the same bucket, this command will set all of them public.');
+        $continue = $style->confirm('Continue?');
+
+        if (!$continue) {
+            return 0;
+        }
+
+        $style->comment('Setting visibility to private in private bucket.');
+        $this->setVisibility($this->filesystemPrivate, $style, 'private');
+        $style->comment('Setting visibility to public in public bucket.');
+        $this->setVisibility($this->filesystemPublic, $style, 'public');
+        $style->comment('Setting visibility to public in theme bucket.');
+        $this->setVisibility($this->filesystemTheme, $style, 'public');
+        $style->comment('Setting visibility to public in sitemap bucket.');
+        $this->setVisibility($this->filesystemSitemap, $style, 'public');
+        $style->comment('Setting visibility to public in asset bucket.');
+        $this->setVisibility($this->filesystemAsset, $style, 'public');
+
+        $style->info('Finished setting visibility of objects in all pre-defined buckets.');
+
+        return 0;
+    }
+
+    private function setVisibility(FilesystemInterface $filesystem, ShopwareStyle $style, string $visibility): void
+    {
+        $files = array_filter($filesystem->listContents('/', true), function (array $object): bool {
+            return $object['type'] === 'file';
+        });
+        ProgressBar::setFormatDefinition('custom', '[%bar%] %current%/%max% -- %message%');
+        $progressBar = new ProgressBar($style, \count($files));
+        $progressBar->setFormat('custom');
+
+        foreach ($files as $file) {
+            if ($file['type'] === 'file') {
+                $filesystem->setVisibility($file['path'], $visibility);
+
+                $progressBar->advance();
+                $progressBar->setMessage($file['path']);
+            }
+        }
+
+        $progressBar->finish();
+    }
+}
diff --git a/src/Core/Framework/Adapter/Filesystem/FilesystemFactory.php b/src/Core/Framework/Adapter/Filesystem/FilesystemFactory.php
index 9ef02ca675..0d953f1f63 100644
--- a/src/Core/Framework/Adapter/Filesystem/FilesystemFactory.php
+++ b/src/Core/Framework/Adapter/Filesystem/FilesystemFactory.php
@@ -39,10 +39,14 @@ class FilesystemFactory
     public function factory(array $config): FilesystemInterface
     {
         $config = $this->resolveFilesystemConfig($config);
         $factory = $this->findAdapterFactory($config['type']);
 
+        if (isset($config['config']['options']['visibility'])) {
+            $config['visibility'] = $config['config']['options']['visibility'];
+        }
+
         $filesystem = new LeagueFilesystem(
             $factory->create($config['config']),
             ['visibility' => $config['visibility']]
         );
 
diff --git a/src/Core/Framework/DependencyInjection/services.xml b/src/Core/Framework/DependencyInjection/services.xml
index 17d8327f28..74cc9761e6 100644
--- a/src/Core/Framework/DependencyInjection/services.xml
+++ b/src/Core/Framework/DependencyInjection/services.xml
@@ -591,10 +591,19 @@ base-uri 'self';
 
             <tag name="console.command"/>
             <tag name="console.command" command="administration:dump:features"/>
         </service>
 
+        <service id="Shopware\Core\Framework\Adapter\Command\S3FilesystemVisibilityCommand">
+            <argument type="service" id="shopware.filesystem.private"/>
+            <argument type="service" id="shopware.filesystem.public"/>
+            <argument type="service" id="shopware.filesystem.theme"/>
+            <argument type="service" id="shopware.filesystem.sitemap"/>
+            <argument type="service" id="shopware.filesystem.asset"/>
+            <tag name="console.command"/>
+        </service>
+
         <service id="Shopware\Core\Framework\Adapter\Cache\CacheInvalidationSubscriber">
             <argument type="service" id="Shopware\Core\Framework\Adapter\Cache\CacheInvalidator"/>
             <argument type="service" id="Doctrine\DBAL\Connection"/>
             <tag name="kernel.event_subscriber"/>
         </service>

Fixed code: ---
title: fix visibility setting in s3 storage fs
issue: NEXT-14744
author: d.neustadt
author_email: d.neustadt@shopware.com 
author_github: dneustadt
---
# Core
* Changed setting of visibility from fallback config to nested `options` parameter as per documentation
* Added `s3:set-visibility` CLI command for retroactively setting visibility of already stored objects
<?php declare(strict_types=1);

namespace Shopware\Core\Framework\Adapter\Command;

use League\Flysystem\FilesystemInterface;
use Shopware\Core\Framework\Adapter\Console\ShopwareStyle;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class S3FilesystemVisibilityCommand extends Command
{
    protected static $defaultName = 's3:set-visibility';

    /**
     * @var FilesystemInterface
     */
    private $filesystemPrivate;

    /**
     * @var FilesystemInterface
     */
    private $filesystemPublic;

    /**
     * @var FilesystemInterface
     */
    private $filesystemTheme;

    /**
     * @var FilesystemInterface
     */
    private $filesystemSitemap;

    /**
     * @var FilesystemInterface
     */
    private $filesystemAsset;

    public function __construct(
        FilesystemInterface $filesystemPrivate,
        FilesystemInterface $filesystemPublic,
        FilesystemInterface $filesystemTheme,
        FilesystemInterface $filesystemSitemap,
        FilesystemInterface $filesystemAsset
    ) {
        parent::__construct();
        $this->filesystemPrivate = $filesystemPrivate;
        $this->filesystemPublic = $filesystemPublic;
        $this->filesystemTheme = $filesystemTheme;
        $this->filesystemSitemap = $filesystemSitemap;
        $this->filesystemAsset = $filesystemAsset;
    }

    /**
     * {@inheritdoc}
     */
    protected function configure(): void
    {
        $this
            ->setDescription('Sets visibility for all objects in corresponding bucket of S3 storage.');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $style = new ShopwareStyle($input, $output);

        $style->warning('If both private and public objects are stored in the same bucket, this command will set all of them public.');
        $continue = $style->confirm('Continue?');

        if (!$continue) {
            return 0;
        }

        $style->comment('Setting visibility to private in private bucket.');
        $this->setVisibility($this->filesystemPrivate, $style, 'private');
        $style->comment('Setting visibility to public in public bucket.');
        $this->setVisibility($this->filesystemPublic, $style, 'public');
        $style->comment('Setting visibility to public in theme bucket.');
        $this->setVisibility($this->filesystemTheme, $style, 'public');
        $style->comment('Setting visibility to public in sitemap bucket.');
        $this->setVisibility($this->filesystemSitemap, $style, 'public');
        $style->comment('Setting visibility to public in asset bucket.');
        $this->setVisibility($this->filesystemAsset, $style, 'public');

        $style->info('Finished setting visibility of objects in all pre-defined buckets.');

        return 0;
    }

    private function setVisibility(FilesystemInterface $filesystem, ShopwareStyle $style, string $visibility): void
    {
        $files = array_filter($filesystem->listContents('/', true), function (array $object): bool {
            return $object['type'] === 'file';
        });
        ProgressBar::setFormatDefinition('custom', '[%bar%] %current%/%max% -- %message%');
        $progressBar = new ProgressBar($style, \count($files));
        $progressBar->setFormat('custom');

        foreach ($files as $file) {
            if ($file['type'] === 'file') {
                $filesystem->setVisibility($file['path'], $visibility);

                $progressBar->advance();
                $progressBar->setMessage($file['path']);
            }
        }

        $progressBar->finish();
    }
}
        if (isset($config['config']['options']['visibility'])) {
            $config['visibility'] = $config['config']['options']['visibility'];
        }

        <service id="Shopware\Core\Framework\Adapter\Command\S3FilesystemVisibilityCommand">
            <argument type="service" id="shopware.filesystem.private"/>
            <argument type="service" id="shopware.filesystem.public"/>
            <argument type="service" id="shopware.filesystem.theme"/>
            <argument type="service" id="shopware.filesystem.sitemap"/>
            <argument type="service" id="shopware.filesystem.asset"/>
            <tag name="console.command"/>
        </service>