Compare commits

...

6 commits

Author SHA1 Message Date
Jan Böhmer
b8d5b83eee Bumped version 1.17.1
Some checks failed
Build assets artifact / Build assets artifact (push) Has been cancelled
Docker Image Build / docker (push) Has been cancelled
Docker Image Build (FrankenPHP) / docker (push) Has been cancelled
Static analysis / Static analysis (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.1, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.1, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.1, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, sqlite) (push) Has been cancelled
2025-05-18 22:54:26 +02:00
Jan Böhmer
00da2dedc3 Ignore phpstan issue 2025-05-18 22:54:03 +02:00
Jan Böhmer
4ce1de079e Updated dependencies 2025-05-18 22:41:05 +02:00
Jan Böhmer
6b9c125de4 Added console command to sanitize SVG files 2025-05-18 22:38:43 +02:00
Jan Böhmer
2c4f44e808 Sanatize SVG files when uploading 2025-05-18 21:00:19 +02:00
Jan Böhmer
2b694731ad Added content-security policy for SVG files in webserver config 2025-05-18 20:38:53 +02:00
10 changed files with 336 additions and 95 deletions

View file

@ -1 +1 @@
1.17.0
1.17.1

View file

@ -43,6 +43,7 @@
"omines/datatables-bundle": "^0.9.1",
"paragonie/sodium_compat": "^1.21",
"part-db/label-fonts": "^1.0",
"rhukster/dom-sanitizer": "^1.0",
"runtime/frankenphp-symfony": "^0.2.0",
"s9e/text-formatter": "^2.1",
"scheb/2fa-backup-code": "^6.8.0",

162
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "7fb73581b0074c5a79afb3ffa614ed8e",
"content-hash": "27cd0d915eab5e7cb57215a4c0b529fb",
"packages": [
{
"name": "amphp/amp",
@ -456,16 +456,16 @@
},
{
"name": "amphp/http-client",
"version": "v5.3.1",
"version": "v5.3.2",
"source": {
"type": "git",
"url": "https://github.com/amphp/http-client.git",
"reference": "c6a789d5bca0450e125e88c38aa3859c4450b0d6"
"reference": "cf885bf00550d645a27605626528a3b2ce5563ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amphp/http-client/zipball/c6a789d5bca0450e125e88c38aa3859c4450b0d6",
"reference": "c6a789d5bca0450e125e88c38aa3859c4450b0d6",
"url": "https://api.github.com/repos/amphp/http-client/zipball/cf885bf00550d645a27605626528a3b2ce5563ae",
"reference": "cf885bf00550d645a27605626528a3b2ce5563ae",
"shasum": ""
},
"require": {
@ -542,7 +542,7 @@
],
"support": {
"issues": "https://github.com/amphp/http-client/issues",
"source": "https://github.com/amphp/http-client/tree/v5.3.1"
"source": "https://github.com/amphp/http-client/tree/v5.3.2"
},
"funding": [
{
@ -550,7 +550,7 @@
"type": "github"
}
],
"time": "2025-04-30T02:58:16+00:00"
"time": "2025-05-18T18:27:44+00:00"
},
{
"name": "amphp/parser",
@ -7125,6 +7125,51 @@
},
"time": "2025-01-25T19:27:39+00:00"
},
{
"name": "rhukster/dom-sanitizer",
"version": "1.0.7",
"source": {
"type": "git",
"url": "https://github.com/rhukster/dom-sanitizer.git",
"reference": "c2a98f27ad742668b254282ccc5581871d0fb601"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rhukster/dom-sanitizer/zipball/c2a98f27ad742668b254282ccc5581871d0fb601",
"reference": "c2a98f27ad742668b254282ccc5581871d0fb601",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9"
},
"type": "library",
"autoload": {
"psr-4": {
"Rhukster\\DomSanitizer\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Andy Miller",
"email": "rhuk@rhuk.net"
}
],
"description": "A simple but effective DOM/SVG/MathML Sanitizer for PHP 7.4+",
"support": {
"issues": "https://github.com/rhukster/dom-sanitizer/issues",
"source": "https://github.com/rhukster/dom-sanitizer/tree/1.0.7"
},
"time": "2023-11-06T16:46:48+00:00"
},
{
"name": "robrichards/xmlseclibs",
"version": "3.1.3",
@ -13870,16 +13915,16 @@
},
{
"name": "tecnickcom/tc-lib-barcode",
"version": "2.4.5",
"version": "2.4.6",
"source": {
"type": "git",
"url": "https://github.com/tecnickcom/tc-lib-barcode.git",
"reference": "892518d94091156244fe3f4f1e1fbe30106de303"
"reference": "c6130222fdc02a2d7c90682b5c2ca24a059c16f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tecnickcom/tc-lib-barcode/zipball/892518d94091156244fe3f4f1e1fbe30106de303",
"reference": "892518d94091156244fe3f4f1e1fbe30106de303",
"url": "https://api.github.com/repos/tecnickcom/tc-lib-barcode/zipball/c6130222fdc02a2d7c90682b5c2ca24a059c16f4",
"reference": "c6130222fdc02a2d7c90682b5c2ca24a059c16f4",
"shasum": ""
},
"require": {
@ -13958,28 +14003,28 @@
],
"support": {
"issues": "https://github.com/tecnickcom/tc-lib-barcode/issues",
"source": "https://github.com/tecnickcom/tc-lib-barcode/tree/2.4.5"
"source": "https://github.com/tecnickcom/tc-lib-barcode/tree/2.4.6"
},
"funding": [
{
"url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&currency_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tc-lib-barcode%20project",
"url": "https://www.paypal.com/donate/?hosted_button_id=NZUEC5XS8MFBJ",
"type": "custom"
}
],
"time": "2025-04-25T17:50:28+00:00"
"time": "2025-05-13T05:49:21+00:00"
},
{
"name": "tecnickcom/tc-lib-color",
"version": "2.2.10",
"version": "2.2.11",
"source": {
"type": "git",
"url": "https://github.com/tecnickcom/tc-lib-color.git",
"reference": "8fa134e609c13631cb171fd4a8dbc2beeb22c8c7"
"reference": "ead45d76932d936e065062194032bf87f7ea45f8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tecnickcom/tc-lib-color/zipball/8fa134e609c13631cb171fd4a8dbc2beeb22c8c7",
"reference": "8fa134e609c13631cb171fd4a8dbc2beeb22c8c7",
"url": "https://api.github.com/repos/tecnickcom/tc-lib-color/zipball/ead45d76932d936e065062194032bf87f7ea45f8",
"reference": "ead45d76932d936e065062194032bf87f7ea45f8",
"shasum": ""
},
"require": {
@ -14027,15 +14072,15 @@
],
"support": {
"issues": "https://github.com/tecnickcom/tc-lib-color/issues",
"source": "https://github.com/tecnickcom/tc-lib-color/tree/2.2.10"
"source": "https://github.com/tecnickcom/tc-lib-color/tree/2.2.11"
},
"funding": [
{
"url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&currency_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tc-lib-color%20project",
"url": "https://www.paypal.com/donate/?hosted_button_id=NZUEC5XS8MFBJ",
"type": "custom"
}
],
"time": "2025-04-25T17:48:50+00:00"
"time": "2025-05-13T05:47:44+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@ -15764,16 +15809,16 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.14",
"version": "2.1.16",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "8f2e03099cac24ff3b379864d171c5acbfc6b9a2"
"reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/8f2e03099cac24ff3b379864d171c5acbfc6b9a2",
"reference": "8f2e03099cac24ff3b379864d171c5acbfc6b9a2",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9",
"reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9",
"shasum": ""
},
"require": {
@ -15818,7 +15863,7 @@
"type": "github"
}
],
"time": "2025-05-02T15:32:28+00:00"
"time": "2025-05-16T09:40:10+00:00"
},
{
"name": "phpstan/phpstan-doctrine",
@ -15942,22 +15987,22 @@
},
{
"name": "phpstan/phpstan-symfony",
"version": "2.0.4",
"version": "2.0.6",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-symfony.git",
"reference": "648087fb4dd865a09b1828a3b0396eb447665f2e"
"reference": "5005288e07583546ea00b52de4a9ac412eb869d7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/648087fb4dd865a09b1828a3b0396eb447665f2e",
"reference": "648087fb4dd865a09b1828a3b0396eb447665f2e",
"url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/5005288e07583546ea00b52de4a9ac412eb869d7",
"reference": "5005288e07583546ea00b52de4a9ac412eb869d7",
"shasum": ""
},
"require": {
"ext-simplexml": "*",
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.1.2"
"phpstan/phpstan": "^2.1.13"
},
"conflict": {
"symfony/framework-bundle": "<3.0"
@ -15967,7 +16012,7 @@
"phpstan/phpstan-phpunit": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.6",
"psr/container": "1.0 || 1.1.1",
"psr/container": "1.1.2",
"symfony/config": "^5.4 || ^6.1",
"symfony/console": "^5.4 || ^6.1",
"symfony/dependency-injection": "^5.4 || ^6.1",
@ -16007,9 +16052,9 @@
"description": "Symfony Framework extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-symfony/issues",
"source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.4"
"source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.6"
},
"time": "2025-03-28T12:02:03+00:00"
"time": "2025-05-14T07:00:05+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -16443,16 +16488,16 @@
},
{
"name": "rector/rector",
"version": "2.0.15",
"version": "2.0.16",
"source": {
"type": "git",
"url": "https://github.com/rectorphp/rector.git",
"reference": "abbbf32474a67e242d26bffc098a712a05b3d32a"
"reference": "f1366d1f8c7490541c8f7af6e5c7cef7cca1b5a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/abbbf32474a67e242d26bffc098a712a05b3d32a",
"reference": "abbbf32474a67e242d26bffc098a712a05b3d32a",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/f1366d1f8c7490541c8f7af6e5c7cef7cca1b5a2",
"reference": "f1366d1f8c7490541c8f7af6e5c7cef7cca1b5a2",
"shasum": ""
},
"require": {
@ -16490,7 +16535,7 @@
],
"support": {
"issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/2.0.15"
"source": "https://github.com/rectorphp/rector/tree/2.0.16"
},
"funding": [
{
@ -16498,7 +16543,7 @@
"type": "github"
}
],
"time": "2025-05-05T10:00:41+00:00"
"time": "2025-05-12T16:37:16+00:00"
},
{
"name": "roave/security-advisories",
@ -16506,12 +16551,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "59be420c5cdc0c3c9cdae31804d32fefb515a918"
"reference": "0cc529f6cf08a858fcb7a2c5617780fcdc20d1fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/59be420c5cdc0c3c9cdae31804d32fefb515a918",
"reference": "59be420c5cdc0c3c9cdae31804d32fefb515a918",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0cc529f6cf08a858fcb7a2c5617780fcdc20d1fe",
"reference": "0cc529f6cf08a858fcb7a2c5617780fcdc20d1fe",
"shasum": ""
},
"conflict": {
@ -16553,7 +16598,10 @@
"athlon1600/php-proxy-app": "<=3",
"athlon1600/youtube-downloader": "<=4",
"austintoddj/canvas": "<=3.4.2",
"auth0/wordpress": "<=4.6",
"auth0/auth0-php": ">=8.0.0.0-beta1,<8.14",
"auth0/login": "<7.17",
"auth0/symfony": "<5.4",
"auth0/wordpress": "<5.3",
"automad/automad": "<2.0.0.0-alpha5",
"automattic/jetpack": "<9.8",
"awesome-support/awesome-support": "<=6.0.7",
@ -16774,8 +16822,8 @@
"geshi/geshi": "<1.0.8.11-dev",
"getformwork/formwork": "<1.13.1|>=2.0.0.0-beta1,<2.0.0.0-beta4",
"getgrav/grav": "<1.7.46",
"getkirby/cms": "<=3.6.6.5|>=3.7,<=3.7.5.4|>=3.8,<=3.8.4.3|>=3.9,<=3.9.8.1|>=3.10,<=3.10.1|>=4,<=4.3",
"getkirby/kirby": "<=2.5.12",
"getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1",
"getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1",
"getkirby/panel": "<2.5.14",
"getkirby/starterkit": "<=3.7.0.2",
"gilacms/gila": "<=1.15.4",
@ -17006,7 +17054,7 @@
"oro/customer-portal": ">=4.1,<=4.1.13|>=4.2,<=4.2.10|>=5,<=5.0.11|>=5.1,<=5.1.3",
"oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<=4.2.10|>=5,<=5.0.12|>=5.1,<=5.1.3",
"oveleon/contao-cookiebar": "<1.16.3|>=2,<2.1.3",
"oxid-esales/oxideshop-ce": "<4.5",
"oxid-esales/oxideshop-ce": "<=7.0.5",
"oxid-esales/paymorrow-module": ">=1,<1.0.2|>=2,<2.0.1",
"packbackbooks/lti-1-3-php-library": "<5",
"padraic/humbug_get_contents": "<1.1.2",
@ -17115,8 +17163,8 @@
"serluck/phpwhois": "<=4.2.6",
"sfroemken/url_redirect": "<=1.2.1",
"sheng/yiicms": "<1.2.1",
"shopware/core": "<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev",
"shopware/platform": "<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev",
"shopware/core": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev",
"shopware/platform": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev",
"shopware/production": "<=6.3.5.2",
"shopware/shopware": "<=5.7.17",
"shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev",
@ -17179,7 +17227,7 @@
"subhh/libconnect": "<7.0.8|>=8,<8.1",
"sukohi/surpass": "<1",
"sulu/form-bundle": ">=2,<2.5.3",
"sulu/sulu": "<1.6.44|>=2,<2.5.21|>=2.6,<2.6.5",
"sulu/sulu": "<1.6.44|>=2,<2.5.25|>=2.6,<2.6.9|>=3.0.0.0-alpha1,<3.0.0.0-alpha3",
"sumocoders/framework-user-bundle": "<1.4",
"superbig/craft-audit": "<3.0.2",
"svewap/a21glossary": "<=0.4.10",
@ -17429,7 +17477,7 @@
"type": "tidelift"
}
],
"time": "2025-05-08T20:06:04+00:00"
"time": "2025-05-17T16:05:20+00:00"
},
{
"name": "sebastian/cli-parser",
@ -18794,16 +18842,16 @@
},
{
"name": "symplify/easy-coding-standard",
"version": "12.5.16",
"version": "12.5.18",
"source": {
"type": "git",
"url": "https://github.com/easy-coding-standard/easy-coding-standard.git",
"reference": "3e99ec9bd64528cedb7f7e0a9e892a1c3c803935"
"reference": "451dfeba3770f2d7476468b891a789c451ae4b34"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/3e99ec9bd64528cedb7f7e0a9e892a1c3c803935",
"reference": "3e99ec9bd64528cedb7f7e0a9e892a1c3c803935",
"url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/451dfeba3770f2d7476468b891a789c451ae4b34",
"reference": "451dfeba3770f2d7476468b891a789c451ae4b34",
"shasum": ""
},
"require": {
@ -18839,7 +18887,7 @@
],
"support": {
"issues": "https://github.com/easy-coding-standard/easy-coding-standard/issues",
"source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.5.16"
"source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.5.18"
},
"funding": [
{
@ -18851,7 +18899,7 @@
"type": "github"
}
],
"time": "2025-04-28T07:01:07+00:00"
"time": "2025-05-14T09:38:08+00:00"
},
{
"name": "theseer/tokenizer",

View file

@ -52,6 +52,11 @@ server {
location ~ \.php$ {
return 404;
}
# Set Content-Security-Policy for svg files, to block embedded javascript in there
location ~* \.svg$ {
add_header Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';";
}
error_log /var/log/nginx/parts.error.log;
access_log /var/log/nginx/parts.access.log;

View file

@ -118,3 +118,10 @@ DirectoryIndex index.php
# RedirectTemp cannot be used instead
</IfModule>
</IfModule>
# Set Content-Security-Policy for svg files (and compressed variants), to block embedded javascript in there
<IfModule mod_headers.c>
<FilesMatch "\.(svg|svg\.gz|svg\.br)$">
Header set Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';"
</FilesMatch>
</IfModule>

View file

@ -0,0 +1,90 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Command\Attachments;
use App\Entity\Attachments\Attachment;
use App\Services\Attachments\AttachmentSubmitHandler;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand('partdb:attachments:sanitize-svg', "Sanitize uploaded SVG files.")]
class SanitizeSVGAttachmentsCommand extends Command
{
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly AttachmentSubmitHandler $attachmentSubmitHandler, ?string $name = null)
{
parent::__construct($name);
}
public function configure(): void
{
$this->setHelp('This command allows to sanitize SVG files uploaded via attachments. This happens automatically since version 1.17.1, this command is intended to be used for older files.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->info('This command will sanitize all uploaded SVG files. This is only required if you have uploaded (untrusted) SVG files before version 1.17.1. If you are running a newer version, you don\'t need to run this command (again).');
if (!$io->confirm('Do you want to continue?', false)) {
$io->success('Command aborted.');
return Command::FAILURE;
}
$io->info('Sanitizing SVG files...');
//Finding all attachments with svg files
$qb = $this->entityManager->createQueryBuilder();
$qb->select('a')
->from(Attachment::class, 'a')
->where('a.internal_path LIKE :pattern ESCAPE \'#\'')
->orWhere('a.original_filename LIKE :pattern ESCAPE \'#\'')
->setParameter('pattern', '%.svg');
$attachments = $qb->getQuery()->getResult();
$io->note('Found '.count($attachments).' attachments with SVG files.');
if (count($attachments) === 0) {
$io->success('No SVG files found.');
return Command::FAILURE;
}
$io->info('Sanitizing SVG files...');
$io->progressStart(count($attachments));
foreach ($attachments as $attachment) {
/** @var Attachment $attachment */
$io->note('Sanitizing attachment '.$attachment->getId().' ('.($attachment->getFilename() ?? '???').')');
$this->attachmentSubmitHandler->sanitizeSVGAttachment($attachment);
$io->progressAdvance();
}
$io->progressFinish();
$io->success('Sanitization finished. All SVG files have been sanitized.');
return Command::SUCCESS;
}
}

View file

@ -318,6 +318,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
return new ArrayCollection();
}
//@phpstan-ignore-next-line
return $this->children ?? new ArrayCollection();
}

View file

@ -65,7 +65,7 @@ class AttachmentSubmitHandler
'htpasswd', ''];
public function __construct(protected AttachmentPathResolver $pathResolver, protected bool $allow_attachments_downloads,
protected HttpClientInterface $httpClient, protected MimeTypesInterface $mimeTypes,
protected HttpClientInterface $httpClient, protected MimeTypesInterface $mimeTypes, protected readonly SVGSanitizer $SVGSanitizer,
protected FileTypeFilterTools $filterTools, /**
* @var string The user configured maximum upload size. This is a string like "10M" or "1G" and will be converted to
*/
@ -214,6 +214,9 @@ class AttachmentSubmitHandler
//Move the attachment files to secure location (and back) if needed
$this->moveFile($attachment, $secure_attachment);
//Sanitize the SVG if needed
$this->sanitizeSVGAttachment($attachment);
//Rename blacklisted (unsecure) files to a better extension
$this->renameBlacklistedExtensions($attachment);
@ -498,4 +501,32 @@ class AttachmentSubmitHandler
return $this->max_upload_size_bytes;
}
/**
* Sanitizes the given SVG file, if the attachment is an internal SVG file.
* @param Attachment $attachment
* @return Attachment
*/
public function sanitizeSVGAttachment(Attachment $attachment): Attachment
{
//We can not do anything on builtins or external ressources
if ($attachment->isBuiltIn() || !$attachment->hasInternal()) {
return $attachment;
}
//Resolve the path to the file
$path = $this->pathResolver->placeholderToRealPath($attachment->getInternalPath());
//Check if the file exists
if (!file_exists($path)) {
return $attachment;
}
//Check if the file is an SVG
if ($attachment->getExtension() === "svg") {
$this->SVGSanitizer->sanitizeFile($path);
}
return $attachment;
}
}

View file

@ -0,0 +1,58 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Services\Attachments;
use Rhukster\DomSanitizer\DOMSanitizer;
class SVGSanitizer
{
/**
* Sanitizes the given SVG string by removing any potentially harmful content (like inline scripts).
* @param string $input
* @return string
*/
public function sanitizeString(string $input): string
{
return (new DOMSanitizer(DOMSanitizer::SVG))->sanitize($input);
}
/**
* Sanitizes the given SVG file by removing any potentially harmful content (like inline scripts).
* The sanitized content is written back to the file.
* @param string $filepath
*/
public function sanitizeFile(string $filepath): void
{
//Open the file and read the content
$content = file_get_contents($filepath);
if ($content === false) {
throw new \RuntimeException('Could not read file: ' . $filepath);
}
//Sanitize the content
$sanitizedContent = $this->sanitizeString($content);
//Write the sanitized content back to the file
file_put_contents($filepath, $sanitizedContent);
}
}

View file

@ -2078,9 +2078,9 @@
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
"@types/node@*":
version "22.15.17"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.17.tgz#355ccec95f705b664e4332bb64a7f07db30b7055"
integrity sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==
version "22.15.18"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.18.tgz#2f8240f7e932f571c2d45f555ba0b6c3f7a75963"
integrity sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==
dependencies:
undici-types "~6.21.0"
@ -2301,7 +2301,7 @@ acorn-walk@^8.0.0:
dependencies:
acorn "^8.11.0"
acorn@^8.0.4, acorn@^8.0.5, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2:
acorn@^8.0.4, acorn@^8.0.5, acorn@^8.11.0, acorn@^8.14.0:
version "8.14.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb"
integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==
@ -2675,9 +2675,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001716:
version "1.0.30001717"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz#5d9fec5ce09796a1893013825510678928aca129"
integrity sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==
version "1.0.30001718"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz#dae13a9c80d517c30c6197515a96131c194d8f82"
integrity sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==
chalk@^2.4.2:
version "2.4.2"
@ -3217,11 +3217,11 @@ data-view-byte-offset@^1.0.1:
is-data-view "^1.0.1"
datatables.net-bs5@^2, datatables.net-bs5@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/datatables.net-bs5/-/datatables.net-bs5-2.3.0.tgz#36344ee116d8ad644476d47974eb7b26cfe256e5"
integrity sha512-JvcYrf2kEVzHY7NKsJxV132kehhc5+iRIpZAYmYqSW52yCcMiQYWtw1Z4+Wb/FMbJDQwP9exCtTwroLYPKwkeQ==
version "2.3.1"
resolved "https://registry.yarnpkg.com/datatables.net-bs5/-/datatables.net-bs5-2.3.1.tgz#167a86c4ab138fe6eb7f653fb4c64e3ade0d3d7a"
integrity sha512-bUwsW7cIR8EDyiedQv6OKGF5ouxkFjaODmtEd7NRtpoZixAZDScmIrxe9cddjS0EybpC2gYslKs+o03E8tkCEA==
dependencies:
datatables.net "2.3.0"
datatables.net "2.3.1"
jquery ">=1.7"
datatables.net-buttons-bs5@^3.0.0:
@ -3259,18 +3259,18 @@ datatables.net-colreorder@2.1.0:
jquery ">=1.7"
datatables.net-fixedheader-bs5@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/datatables.net-fixedheader-bs5/-/datatables.net-fixedheader-bs5-4.0.1.tgz#c593b2402b5736e571973c6ceba1a6220a674718"
integrity sha512-rA02QKEDvzj9dtwoUDl8hdSFDi4THpZeqCpCne0sVul1AM4OBy0O4zcx2WMSiEc5yEjFVZwKHmkvcDyhioV5IQ==
version "4.0.2"
resolved "https://registry.yarnpkg.com/datatables.net-fixedheader-bs5/-/datatables.net-fixedheader-bs5-4.0.2.tgz#f2c347853cfdf5c720fde61408e427ae96282ce7"
integrity sha512-CT8chF78LZg0zk8C21XEKrJv1i6DxyN1hFS80UHLGnh9bTBn7BzOG3781cZKfyUc2alazVLxZgCiLDreakrgZQ==
dependencies:
datatables.net-bs5 "^2"
datatables.net-fixedheader "4.0.1"
datatables.net-fixedheader "4.0.2"
jquery ">=1.7"
datatables.net-fixedheader@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/datatables.net-fixedheader/-/datatables.net-fixedheader-4.0.1.tgz#d3c4866fa96fc468002f72b9e8efa3276aa4807b"
integrity sha512-jdwvGvI/WBKjNDJ5JwcTqmh7ehLkzXmTqvVCWJ6ig3+P9EHasoTq6Ys08WfICXpCHZgDKwDYLiW2rqVLK5TWOg==
datatables.net-fixedheader@4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/datatables.net-fixedheader/-/datatables.net-fixedheader-4.0.2.tgz#29321374aa771babe920a80bd2a365611d1d851c"
integrity sha512-pjOs3m0vRQArVZWG/LYfsyi/P9cjuNer0BuG6v2hkn/LT2DAiIe6l543p7IHgTGQXn/my2s/M397DY7U7txavg==
dependencies:
datatables.net "^2"
jquery ">=1.7"
@ -3309,10 +3309,10 @@ datatables.net-select@2.1.0:
datatables.net "^2"
jquery ">=1.7"
datatables.net@2.3.0, datatables.net@^2, datatables.net@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.3.0.tgz#2d9a040dbbf03b8fe6afb6134f57a94fa7744982"
integrity sha512-IX5Dsc7bTNqI3RyT4B7imtFicLMCg7ZLwwx/gvR6cbwYgfG+mLX2KAVm7BNmnwOhUdMMcGSGSE19RpTn31TTGQ==
datatables.net@2.3.1, datatables.net@^2, datatables.net@^2.0.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.3.1.tgz#fcb61efd42fe64627fb9042637f1d93872268e70"
integrity sha512-pRXZuk3oR7P5kTl/CRvopcTihlvmZiY+xSBjbNI6e8MjYtgZQodWEqLUUOrIZeLH8CfxapPR/Bmb0NKAvILP5g==
dependencies:
jquery ">=1.7"
@ -3329,9 +3329,9 @@ debug@3.2.6:
ms "^2.1.1"
debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
version "4.4.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
version "4.4.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b"
integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==
dependencies:
ms "^2.1.3"
@ -3495,9 +3495,9 @@ duplexer@^0.1.2:
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
electron-to-chromium@^1.5.149:
version "1.5.151"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.151.tgz#5edd6c17e1b2f14b4662c41b9379f96cc8c2bb7c"
integrity sha512-Rl6uugut2l9sLojjS4H4SAr3A4IgACMLgpuEMPYCVcKydzfyPrn5absNRju38IhQOf/NwjJY8OGWjlteqYeBCA==
version "1.5.155"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz#809dd0ae9ae1db87c358e0c0c17c09a2ffc432d1"
integrity sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==
emoji-regex@^7.0.1:
version "7.0.3"
@ -6106,9 +6106,9 @@ semver@^6.0.0, semver@^6.3.1:
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.5.4:
version "7.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
version "7.7.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
serialize-javascript@^5.0.1:
version "5.0.1"
@ -6529,12 +6529,12 @@ terser-webpack-plugin@^5.3.0, terser-webpack-plugin@^5.3.11:
terser "^5.31.1"
terser@^5.3.4, terser@^5.31.1:
version "5.39.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.39.0.tgz#0e82033ed57b3ddf1f96708d123cca717d86ca3a"
integrity sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==
version "5.39.2"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.39.2.tgz#5a1626030724a672e2e5b5c9cd9070308c20e8f9"
integrity sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==
dependencies:
"@jridgewell/source-map" "^0.3.3"
acorn "^8.8.2"
acorn "^8.14.0"
commander "^2.20.0"
source-map-support "~0.5.20"