Skip to content

Instantly share code, notes, and snippets.

@lchrusciel
Created March 15, 2022 10:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lchrusciel/5f2f62a36a0f845a11a8420e7c5c0fe1 to your computer and use it in GitHub Desktop.
Save lchrusciel/5f2f62a36a0f845a11a8420e7c5c0fe1 to your computer and use it in GitHub Desktop.
SVG sanitizer security bug fix
composer require enshrined/svg-sanitize
<?php
// src/Uploader/ImageUploader.php
declare(strict_types=1);
namespace App\Uploader;
use enshrined\svgSanitize\Sanitizer;
use Gaufrette\Filesystem;
use Sylius\Component\Core\Generator\ImagePathGeneratorInterface;
use Sylius\Component\Core\Generator\UploadedImagePathGenerator;
use Sylius\Component\Core\Model\ImageInterface;
use Sylius\Component\Core\Uploader\ImageUploaderInterface;
use Symfony\Component\HttpFoundation\File\File;
use Webmozart\Assert\Assert;
final class ImageUploader implements ImageUploaderInterface
{
private const MIME_SVG_XML = 'image/svg+xml';
private const MIME_SVG = 'image/svg';
/** @var Filesystem */
protected $filesystem;
/** @var ImagePathGeneratorInterface */
protected $imagePathGenerator;
/** @var Sanitizer */
protected $sanitizer;
public function __construct(
Filesystem $filesystem,
?ImagePathGeneratorInterface $imagePathGenerator = null
) {
$this->filesystem = $filesystem;
if ($imagePathGenerator === null) {
@trigger_error(sprintf(
'Not passing an $imagePathGenerator to %s constructor is deprecated since Sylius 1.6 and will be not possible in Sylius 2.0.', self::class
), \E_USER_DEPRECATED);
}
$this->imagePathGenerator = $imagePathGenerator ?? new UploadedImagePathGenerator();
$this->sanitizer = new Sanitizer();
}
public function upload(ImageInterface $image): void
{
if (!$image->hasFile()) {
return;
}
/** @var File $file */
$file = $image->getFile();
Assert::isInstanceOf($file, File::class);
$fileContent = $this->sanitizeContent(file_get_contents($file->getPathname()), $file->getMimeType());
if (null !== $image->getPath() && $this->has($image->getPath())) {
$this->remove($image->getPath());
}
do {
$path = $this->imagePathGenerator->generate($image);
} while ($this->isAdBlockingProne($path) || $this->filesystem->has($path));
$image->setPath($path);
$this->filesystem->write($image->getPath(), $fileContent);
}
public function remove(string $path): bool
{
if ($this->filesystem->has($path)) {
return $this->filesystem->delete($path);
}
return false;
}
protected function sanitizeContent(string $fileContent, string $mimeType): string
{
if (self::MIME_SVG_XML === $mimeType || self::MIME_SVG === $mimeType) {
$fileContent = $this->sanitizer->sanitize($fileContent);
}
return $fileContent;
}
private function has(string $path): bool
{
return $this->filesystem->has($path);
}
/**
* Will return true if the path is prone to be blocked by ad blockers
*/
private function isAdBlockingProne(string $path): bool
{
return strpos($path, 'ad') !== false;
}
}
services:
# ...
sylius.image_uploader:
class: App\Uploader\ImageUploader
arguments:
- '@gaufrette.sylius_image_filesystem'
- '@Sylius\Component\Core\Generator\ImagePathGeneratorInterface'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment