diff --git a/.docker/frankenphp/Caddyfile b/.docker/frankenphp/Caddyfile index 83839304..293ab18e 100644 --- a/.docker/frankenphp/Caddyfile +++ b/.docker/frankenphp/Caddyfile @@ -51,5 +51,18 @@ # Disable Topics tracking if not enabled explicitly: https://github.com/jkarlin/topics header ?Permissions-Policy "browsing-topics=()" + # Set a strict CSP and nosniff for all static assets not handled by PHP. + # ? means "set only if not already present", so PHP responses carrying a Nelmio CSP are left untouched. + header ?Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; sandbox;" + header ?X-Content-Type-Options "nosniff" + + # SVG files get a slightly different CSP because they can embed resources and must not be framed. + @svg path *.svg *.svg.gz *.svg.br + header @svg Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'; sandbox;" + + # Prevent PHP execution in the media upload directory + @php_in_media path_regexp (?i)^/media/.*\.(php[3-8]?|phar|phtml|pht|phps)$ + respond @php_in_media 403 + php_server } diff --git a/.docker/symfony.conf b/.docker/symfony.conf index aa88eef2..eb3adb07 100644 --- a/.docker/symfony.conf +++ b/.docker/symfony.conf @@ -15,6 +15,14 @@ AllowOverride All + # Prevent PHP execution in the media upload directory (server-level, not .htaccess, + # because public/media is a Docker volume and .htaccess there may not be present) + + + Require all denied + + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, # error, crit, alert, emerg. # It is also possible to configure the loglevel for particular diff --git a/.env b/.env index 8d5e5a54..8cd39f31 100644 --- a/.env +++ b/.env @@ -1,6 +1,10 @@ #### Part-DB Configuration # See https://docs.part-db.de/configuration.html for documentation of available options +# Change this to a random value to secure your installation! You can generate a random string with "openssl rand -hex 16" +# Share that value with nobody and keep it secret +APP_SECRET=a03498528f5a5fc089273ec9ae5b2849 + ################################################################################### # Database settings ################################################################################### @@ -145,6 +149,16 @@ DISABLE_YEAR2038_BUG_CHECK=0 #TRUSTED_PROXIES=127.0.0.0/8,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 #TRUSTED_HOSTS='^(localhost|example\.com)$' +################################################################################### +# Logging settings +################################################################################### + +# The minimum level a deprecation notice must have to be written to the var/log/_deprecations.log file. +# Deprecation notices are logged with level "info", so this disables the deprecation log by default. +# Set to debug to log all deprecation notices +DEPRECATION_LOG_LEVEL=emergency + + ###> symfony/lock ### # Choose one of the stores below @@ -158,7 +172,6 @@ CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$' ###> symfony/framework-bundle ### APP_ENV=prod -APP_SECRET=a03498528f5a5fc089273ec9ae5b2849 APP_SHARE_DIR=var/share ###< symfony/framework-bundle ### diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5b756228..27dfe173 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -129,7 +129,7 @@ jobs: run: ./bin/phpunit --coverage-clover=coverage.xml - name: Upload coverage - uses: codecov/codecov-action@v6 + uses: codecov/codecov-action@v7 with: env_vars: PHP_VERSION,DB_TYPE token: ${{ secrets.CODECOV_TOKEN }} diff --git a/Dockerfile b/Dockerfile index e848acc1..049de283 100644 --- a/Dockerfile +++ b/Dockerfile @@ -193,7 +193,7 @@ RUN a2dissite 000-default.conf && \ a2enmod proxy_fcgi setenvif && \ a2enconf php${PHP_VERSION}-fpm && \ a2enconf docker-php && \ - a2enmod rewrite + a2enmod rewrite headers # Install composer and yarn dependencies for Part-DB USER www-data diff --git a/VERSION b/VERSION index d8b69897..ccc99d02 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.0 +2.12.3 diff --git a/assets/controllers/common/hide_sidebar_controller.js b/assets/controllers/common/hide_sidebar_controller.js index 4be304ff..c65cdcdf 100644 --- a/assets/controllers/common/hide_sidebar_controller.js +++ b/assets/controllers/common/hide_sidebar_controller.js @@ -51,7 +51,7 @@ export default class extends Controller { //Make the state persistent over reloads if(localStorage.getItem(STORAGE_KEY) === 'true') { - sidebarHide(); + this.hideSidebar(); } } diff --git a/composer.json b/composer.json index a1f15834..f08f824d 100644 --- a/composer.json +++ b/composer.json @@ -70,6 +70,7 @@ "symfony/flex": "^v2.3.1", "symfony/form": "7.4.*", "symfony/framework-bundle": "7.4.*", + "symfony/html-sanitizer": "7.4.*", "symfony/http-client": "7.4.*", "symfony/http-kernel": "7.4.*", "symfony/mailer": "7.4.*", diff --git a/composer.lock b/composer.lock index 34715b7b..467bc8a1 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "d6bda397c505e1e6d540c814a2368fbb", + "content-hash": "1307bf553d1543704e3c03398dec06c7", "packages": [ { "name": "amphp/amp", @@ -682,16 +682,16 @@ }, { "name": "amphp/process", - "version": "v2.0.3", + "version": "v2.1.0", "source": { "type": "git", "url": "https://github.com/amphp/process.git", - "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d" + "reference": "583959df17d00304ad7b0b32285373f985935643" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", - "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "url": "https://api.github.com/repos/amphp/process/zipball/583959df17d00304ad7b0b32285373f985935643", + "reference": "583959df17d00304ad7b0b32285373f985935643", "shasum": "" }, "require": { @@ -705,7 +705,7 @@ "amphp/php-cs-fixer-config": "^2", "amphp/phpunit-util": "^3", "phpunit/phpunit": "^9", - "psalm/phar": "^5.4" + "psalm/phar": "6.16.1" }, "type": "library", "autoload": { @@ -738,7 +738,7 @@ "homepage": "https://amphp.org/process", "support": { "issues": "https://github.com/amphp/process/issues", - "source": "https://github.com/amphp/process/tree/v2.0.3" + "source": "https://github.com/amphp/process/tree/v2.1.0" }, "funding": [ { @@ -746,7 +746,7 @@ "type": "github" } ], - "time": "2024-04-19T03:13:44+00:00" + "time": "2026-05-31T15:11:55+00:00" }, { "name": "amphp/serialization", @@ -976,16 +976,16 @@ }, { "name": "api-platform/doctrine-common", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/doctrine-common.git", - "reference": "089b196c2f8e4d14333aaa3c6db33356e8fd8be0" + "reference": "e4dee10c45bd701c5984321bc98adc0c3760ec48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/doctrine-common/zipball/089b196c2f8e4d14333aaa3c6db33356e8fd8be0", - "reference": "089b196c2f8e4d14333aaa3c6db33356e8fd8be0", + "url": "https://api.github.com/repos/api-platform/doctrine-common/zipball/e4dee10c45bd701c5984321bc98adc0c3760ec48", + "reference": "e4dee10c45bd701c5984321bc98adc0c3760ec48", "shasum": "" }, "require": { @@ -1060,22 +1060,22 @@ "rest" ], "support": { - "source": "https://github.com/api-platform/doctrine-common/tree/v4.3.6" + "source": "https://github.com/api-platform/doctrine-common/tree/v4.3.14" }, - "time": "2026-05-04T13:25:58+00:00" + "time": "2026-06-16T14:59:18+00:00" }, { "name": "api-platform/doctrine-orm", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/doctrine-orm.git", - "reference": "095a4c56cdd9986208100dedd5d28be50a4830ba" + "reference": "6af3eeefc7d483b83e56bcbdbff0dd0dde3c7fc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/doctrine-orm/zipball/095a4c56cdd9986208100dedd5d28be50a4830ba", - "reference": "095a4c56cdd9986208100dedd5d28be50a4830ba", + "url": "https://api.github.com/repos/api-platform/doctrine-orm/zipball/6af3eeefc7d483b83e56bcbdbff0dd0dde3c7fc1", + "reference": "6af3eeefc7d483b83e56bcbdbff0dd0dde3c7fc1", "shasum": "" }, "require": { @@ -1149,13 +1149,13 @@ "rest" ], "support": { - "source": "https://github.com/api-platform/doctrine-orm/tree/v4.3.6" + "source": "https://github.com/api-platform/doctrine-orm/tree/v4.3.14" }, - "time": "2026-05-07T11:45:31+00:00" + "time": "2026-06-16T13:14:05+00:00" }, { "name": "api-platform/documentation", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/documentation.git", @@ -1212,22 +1212,22 @@ ], "description": "API Platform documentation controller.", "support": { - "source": "https://github.com/api-platform/documentation/tree/v4.3.6" + "source": "https://github.com/api-platform/documentation/tree/v4.3.14" }, "time": "2026-04-30T12:21:24+00:00" }, { "name": "api-platform/http-cache", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/http-cache.git", - "reference": "dd7c092b9abee06e72fd58544fe714b6c2a61efa" + "reference": "8e71916de766f503dd60f1a1e886b4d704f881a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/http-cache/zipball/dd7c092b9abee06e72fd58544fe714b6c2a61efa", - "reference": "dd7c092b9abee06e72fd58544fe714b6c2a61efa", + "url": "https://api.github.com/repos/api-platform/http-cache/zipball/8e71916de766f503dd60f1a1e886b4d704f881a8", + "reference": "8e71916de766f503dd60f1a1e886b4d704f881a8", "shasum": "" }, "require": { @@ -1292,22 +1292,22 @@ "rest" ], "support": { - "source": "https://github.com/api-platform/http-cache/tree/v4.3.6" + "source": "https://github.com/api-platform/http-cache/tree/v4.3.14" }, - "time": "2026-04-30T12:21:24+00:00" + "time": "2026-06-09T14:20:49+00:00" }, { "name": "api-platform/hydra", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/hydra.git", - "reference": "317a696e396b80ba87de2560679c362923ef0a14" + "reference": "c9c53cfc9cdf9a2b8499d49dc70cf3ea9dbf8463" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/hydra/zipball/317a696e396b80ba87de2560679c362923ef0a14", - "reference": "317a696e396b80ba87de2560679c362923ef0a14", + "url": "https://api.github.com/repos/api-platform/hydra/zipball/c9c53cfc9cdf9a2b8499d49dc70cf3ea9dbf8463", + "reference": "c9c53cfc9cdf9a2b8499d49dc70cf3ea9dbf8463", "shasum": "" }, "require": { @@ -1315,7 +1315,7 @@ "api-platform/json-schema": "^4.3", "api-platform/jsonld": "^4.3", "api-platform/metadata": "^4.3", - "api-platform/serializer": "^4.3", + "api-platform/serializer": "^4.3.12", "api-platform/state": "^4.3", "php": ">=8.2", "symfony/type-info": "^7.3 || ^8.0", @@ -1379,29 +1379,29 @@ "rest" ], "support": { - "source": "https://github.com/api-platform/hydra/tree/v4.3.6" + "source": "https://github.com/api-platform/hydra/tree/v4.3.14" }, - "time": "2026-05-11T11:50:19+00:00" + "time": "2026-06-13T05:11:46+00:00" }, { "name": "api-platform/json-api", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/json-api.git", - "reference": "3a562e7f1bb1bc802e58eff674a20b78fe107275" + "reference": "30f70ddc6d865e9c36d99c0255bb1f407c4d4258" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/json-api/zipball/3a562e7f1bb1bc802e58eff674a20b78fe107275", - "reference": "3a562e7f1bb1bc802e58eff674a20b78fe107275", + "url": "https://api.github.com/repos/api-platform/json-api/zipball/30f70ddc6d865e9c36d99c0255bb1f407c4d4258", + "reference": "30f70ddc6d865e9c36d99c0255bb1f407c4d4258", "shasum": "" }, "require": { "api-platform/documentation": "^4.3", "api-platform/json-schema": "^4.3", "api-platform/metadata": "^4.3", - "api-platform/serializer": "^4.3", + "api-platform/serializer": "^4.3.12", "api-platform/state": "^4.3", "php": ">=8.2", "symfony/error-handler": "^6.4 || ^7.0 || ^8.0", @@ -1461,22 +1461,22 @@ "rest" ], "support": { - "source": "https://github.com/api-platform/json-api/tree/v4.3.6" + "source": "https://github.com/api-platform/json-api/tree/v4.3.14" }, - "time": "2026-05-22T11:06:32+00:00" + "time": "2026-06-17T18:14:46+00:00" }, { "name": "api-platform/json-schema", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/json-schema.git", - "reference": "23dc2c388a08f2006b9189a0883a08f8837d7249" + "reference": "2136965a30643056d09b10ae5e9fab1f7232a028" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/json-schema/zipball/23dc2c388a08f2006b9189a0883a08f8837d7249", - "reference": "23dc2c388a08f2006b9189a0883a08f8837d7249", + "url": "https://api.github.com/repos/api-platform/json-schema/zipball/2136965a30643056d09b10ae5e9fab1f7232a028", + "reference": "2136965a30643056d09b10ae5e9fab1f7232a028", "shasum": "" }, "require": { @@ -1542,27 +1542,27 @@ "swagger" ], "support": { - "source": "https://github.com/api-platform/json-schema/tree/v4.3.6" + "source": "https://github.com/api-platform/json-schema/tree/v4.3.14" }, - "time": "2026-04-30T12:21:24+00:00" + "time": "2026-06-13T05:06:55+00:00" }, { "name": "api-platform/jsonld", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/jsonld.git", - "reference": "20ca6d7b5c11674c3046d710aaa0c9bc1795e54b" + "reference": "026a380c3c85c4210028da43e0cea1b64211bbf5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/jsonld/zipball/20ca6d7b5c11674c3046d710aaa0c9bc1795e54b", - "reference": "20ca6d7b5c11674c3046d710aaa0c9bc1795e54b", + "url": "https://api.github.com/repos/api-platform/jsonld/zipball/026a380c3c85c4210028da43e0cea1b64211bbf5", + "reference": "026a380c3c85c4210028da43e0cea1b64211bbf5", "shasum": "" }, "require": { "api-platform/metadata": "^4.3", - "api-platform/serializer": "^4.3", + "api-platform/serializer": "^4.3.12", "api-platform/state": "^4.3", "php": ">=8.2" }, @@ -1622,22 +1622,22 @@ "rest" ], "support": { - "source": "https://github.com/api-platform/jsonld/tree/v4.3.6" + "source": "https://github.com/api-platform/jsonld/tree/v4.3.14" }, - "time": "2026-04-30T12:21:24+00:00" + "time": "2026-06-13T05:11:46+00:00" }, { "name": "api-platform/metadata", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/metadata.git", - "reference": "e9e8a7b85d2d513edff3b108072f8ab23a9d6344" + "reference": "86efa5375e994f4e46f3e4336dc4dc5ecc9456a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/metadata/zipball/e9e8a7b85d2d513edff3b108072f8ab23a9d6344", - "reference": "e9e8a7b85d2d513edff3b108072f8ab23a9d6344", + "url": "https://api.github.com/repos/api-platform/metadata/zipball/86efa5375e994f4e46f3e4336dc4dc5ecc9456a8", + "reference": "86efa5375e994f4e46f3e4336dc4dc5ecc9456a8", "shasum": "" }, "require": { @@ -1720,22 +1720,22 @@ "swagger" ], "support": { - "source": "https://github.com/api-platform/metadata/tree/v4.3.6" + "source": "https://github.com/api-platform/metadata/tree/v4.3.14" }, - "time": "2026-05-22T12:00:17+00:00" + "time": "2026-06-13T05:03:21+00:00" }, { "name": "api-platform/openapi", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/openapi.git", - "reference": "1562617e7500a50c2b6e6f43a0fb29a6a47e83a2" + "reference": "c72470132f2eb35a4f8f252e60342f0f7c487704" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/openapi/zipball/1562617e7500a50c2b6e6f43a0fb29a6a47e83a2", - "reference": "1562617e7500a50c2b6e6f43a0fb29a6a47e83a2", + "url": "https://api.github.com/repos/api-platform/openapi/zipball/c72470132f2eb35a4f8f252e60342f0f7c487704", + "reference": "c72470132f2eb35a4f8f252e60342f0f7c487704", "shasum": "" }, "require": { @@ -1753,7 +1753,7 @@ "api-platform/doctrine-common": "^4.3", "api-platform/doctrine-odm": "^4.3", "api-platform/doctrine-orm": "^4.3", - "api-platform/serializer": "^4.3", + "api-platform/serializer": "^4.3.12", "phpspec/prophecy-phpunit": "^2.2", "phpunit/phpunit": "^11.5 || ^12.2", "symfony/type-info": "^7.3 || ^8.0" @@ -1811,22 +1811,22 @@ "swagger" ], "support": { - "source": "https://github.com/api-platform/openapi/tree/v4.3.6" + "source": "https://github.com/api-platform/openapi/tree/v4.3.14" }, - "time": "2026-04-30T12:21:24+00:00" + "time": "2026-06-16T10:01:53+00:00" }, { "name": "api-platform/serializer", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/serializer.git", - "reference": "2c4f996bb6e5fef49106df0c48d0c1954e10998b" + "reference": "8a6e6a719a401be470b958ba1ae63f4eb0b22992" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/serializer/zipball/2c4f996bb6e5fef49106df0c48d0c1954e10998b", - "reference": "2c4f996bb6e5fef49106df0c48d0c1954e10998b", + "url": "https://api.github.com/repos/api-platform/serializer/zipball/8a6e6a719a401be470b958ba1ae63f4eb0b22992", + "reference": "8a6e6a719a401be470b958ba1ae63f4eb0b22992", "shasum": "" }, "require": { @@ -1905,22 +1905,22 @@ "serializer" ], "support": { - "source": "https://github.com/api-platform/serializer/tree/v4.3.6" + "source": "https://github.com/api-platform/serializer/tree/v4.3.14" }, - "time": "2026-05-12T10:07:44+00:00" + "time": "2026-06-19T08:12:49+00:00" }, { "name": "api-platform/state", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/state.git", - "reference": "6e3f6d75e605ba7171a7590c82da5126979a936b" + "reference": "3bb76df0857dd1a6e706dd1c5c4b574432a2fa8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/state/zipball/6e3f6d75e605ba7171a7590c82da5126979a936b", - "reference": "6e3f6d75e605ba7171a7590c82da5126979a936b", + "url": "https://api.github.com/repos/api-platform/state/zipball/3bb76df0857dd1a6e706dd1c5c4b574432a2fa8d", + "reference": "3bb76df0857dd1a6e706dd1c5c4b574432a2fa8d", "shasum": "" }, "require": { @@ -1933,7 +1933,7 @@ "symfony/translation-contracts": "^3.0" }, "require-dev": { - "api-platform/serializer": "^4.3", + "api-platform/serializer": "^4.3.12", "api-platform/validator": "^4.3.1", "phpunit/phpunit": "^11.5 || ^12.2", "symfony/http-foundation": "^6.4.14 || ^7.0 || ^8.0", @@ -2002,22 +2002,22 @@ "swagger" ], "support": { - "source": "https://github.com/api-platform/state/tree/v4.3.6" + "source": "https://github.com/api-platform/state/tree/v4.3.14" }, - "time": "2026-05-22T12:02:28+00:00" + "time": "2026-06-13T05:11:46+00:00" }, { "name": "api-platform/symfony", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/symfony.git", - "reference": "13308ad99dd1479e70fe79c20519d8135df8e7b9" + "reference": "8b20ef6262b2d557e2c286df48b78e66e7f8c000" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/symfony/zipball/13308ad99dd1479e70fe79c20519d8135df8e7b9", - "reference": "13308ad99dd1479e70fe79c20519d8135df8e7b9", + "url": "https://api.github.com/repos/api-platform/symfony/zipball/8b20ef6262b2d557e2c286df48b78e66e7f8c000", + "reference": "8b20ef6262b2d557e2c286df48b78e66e7f8c000", "shasum": "" }, "require": { @@ -2028,7 +2028,7 @@ "api-platform/jsonld": "^4.3", "api-platform/metadata": "^4.3", "api-platform/openapi": "^4.3", - "api-platform/serializer": "^4.3", + "api-platform/serializer": "^4.3.12", "api-platform/state": "^4.3", "api-platform/validator": "^4.3.1", "php": ">=8.2", @@ -2131,13 +2131,13 @@ "symfony" ], "support": { - "source": "https://github.com/api-platform/symfony/tree/v4.3.6" + "source": "https://github.com/api-platform/symfony/tree/v4.3.14" }, - "time": "2026-05-18T09:34:32+00:00" + "time": "2026-06-17T18:14:46+00:00" }, { "name": "api-platform/validator", - "version": "v4.3.6", + "version": "v4.3.14", "source": { "type": "git", "url": "https://github.com/api-platform/validator.git", @@ -2207,22 +2207,22 @@ "validator" ], "support": { - "source": "https://github.com/api-platform/validator/tree/v4.3.6" + "source": "https://github.com/api-platform/validator/tree/v4.3.14" }, "time": "2026-05-07T11:45:31+00:00" }, { "name": "beberlei/assert", - "version": "v3.3.3", + "version": "v3.3.4", "source": { "type": "git", "url": "https://github.com/beberlei/assert.git", - "reference": "b5fd8eacd8915a1b627b8bfc027803f1939734dd" + "reference": "f193f4613c7d7fbcee2c05e4daff4061d49c040e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/b5fd8eacd8915a1b627b8bfc027803f1939734dd", - "reference": "b5fd8eacd8915a1b627b8bfc027803f1939734dd", + "url": "https://api.github.com/repos/beberlei/assert/zipball/f193f4613c7d7fbcee2c05e4daff4061d49c040e", + "reference": "f193f4613c7d7fbcee2c05e4daff4061d49c040e", "shasum": "" }, "require": { @@ -2274,9 +2274,9 @@ ], "support": { "issues": "https://github.com/beberlei/assert/issues", - "source": "https://github.com/beberlei/assert/tree/v3.3.3" + "source": "https://github.com/beberlei/assert/tree/v3.3.4" }, - "time": "2024-07-15T13:18:35+00:00" + "time": "2026-06-10T19:47:05+00:00" }, { "name": "beberlei/doctrineextensions", @@ -2342,16 +2342,16 @@ }, { "name": "brick/math", - "version": "0.17.1", + "version": "0.17.2", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "6aef71a9fbbd1ee7be0e313cd627f8e6f7125a5b" + "reference": "8189e751995f9e15729c1aa2f89fa8f166ffe818" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/6aef71a9fbbd1ee7be0e313cd627f8e6f7125a5b", - "reference": "6aef71a9fbbd1ee7be0e313cd627f8e6f7125a5b", + "url": "https://api.github.com/repos/brick/math/zipball/8189e751995f9e15729c1aa2f89fa8f166ffe818", + "reference": "8189e751995f9e15729c1aa2f89fa8f166ffe818", "shasum": "" }, "require": { @@ -2389,7 +2389,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.17.1" + "source": "https://github.com/brick/math/tree/0.17.2" }, "funding": [ { @@ -2397,7 +2397,7 @@ "type": "github" } ], - "time": "2026-04-19T20:55:20+00:00" + "time": "2026-05-25T20:34:43+00:00" }, { "name": "brick/schema", @@ -2653,32 +2653,34 @@ "type": "tidelift" } ], + "abandoned": true, "time": "2022-01-17T14:14:24+00:00" }, { "name": "composer/pcre", - "version": "3.3.2", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, "conflict": { - "phpstan/phpstan": "<1.11.10" + "phpstan/phpstan": "<2.2.2" }, "require-dev": { - "phpstan/phpstan": "^1.12 || ^2", - "phpstan/phpstan-strict-rules": "^1 || ^2", - "phpunit/phpunit": "^8 || ^9" + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" }, "type": "library", "extra": { @@ -2716,7 +2718,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.3.2" + "source": "https://github.com/composer/pcre/tree/3.4.0" }, "funding": [ { @@ -2726,13 +2728,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-11-12T16:29:46+00:00" + "time": "2026-06-07T11:47:49+00:00" }, { "name": "composer/semver", @@ -3348,16 +3346,16 @@ }, { "name": "doctrine/doctrine-bundle", - "version": "2.18.2", + "version": "2.18.3", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineBundle.git", - "reference": "0ff098b29b8b3c68307c8987dcaed7fd829c6546" + "reference": "241d61f6bbc77275d5a9f95d514241c058bf2d0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/0ff098b29b8b3c68307c8987dcaed7fd829c6546", - "reference": "0ff098b29b8b3c68307c8987dcaed7fd829c6546", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/241d61f6bbc77275d5a9f95d514241c058bf2d0a", + "reference": "241d61f6bbc77275d5a9f95d514241c058bf2d0a", "shasum": "" }, "require": { @@ -3394,6 +3392,7 @@ "psr/log": "^1.1.4 || ^2.0 || ^3.0", "symfony/doctrine-messenger": "^6.4 || ^7.0", "symfony/expression-language": "^6.4 || ^7.0", + "symfony/http-kernel": "^6.4 || ^7.0", "symfony/messenger": "^6.4 || ^7.0", "symfony/property-info": "^6.4 || ^7.0", "symfony/security-bundle": "^6.4 || ^7.0", @@ -3449,7 +3448,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineBundle/issues", - "source": "https://github.com/doctrine/DoctrineBundle/tree/2.18.2" + "source": "https://github.com/doctrine/DoctrineBundle/tree/2.18.3" }, "funding": [ { @@ -3465,7 +3464,7 @@ "type": "tidelift" } ], - "time": "2025-12-20T21:35:32+00:00" + "time": "2026-06-08T08:22:50+00:00" }, { "name": "doctrine/doctrine-migrations-bundle", @@ -4636,25 +4635,26 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.10.4", + "version": "7.12.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "aec528da477062d3af11f51e6b33402be233b21f" + "reference": "d34627490fbc03bf5c5d7cfed81f2faa19519425" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/aec528da477062d3af11f51e6b33402be233b21f", - "reference": "aec528da477062d3af11f51e6b33402be233b21f", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d34627490fbc03bf5c5d7cfed81f2faa19519425", + "reference": "d34627490fbc03bf5c5d7cfed81f2faa19519425", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^2.3", - "guzzlehttp/psr7": "^2.8", + "guzzlehttp/promises": "^2.5", + "guzzlehttp/psr7": "^2.12.1", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.24" }, "provide": { "psr/http-client-implementation": "1.0" @@ -4663,7 +4663,7 @@ "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", "guzzle/client-integration-tests": "3.0.2", - "guzzlehttp/test-server": "^0.3.2", + "guzzlehttp/test-server": "^0.5.1", "php-http/message-factory": "^1.1", "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" @@ -4743,7 +4743,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.10.4" + "source": "https://github.com/guzzle/guzzle/tree/7.12.1" }, "funding": [ { @@ -4759,24 +4759,25 @@ "type": "tidelift" } ], - "time": "2026-05-22T19:00:53+00:00" + "time": "2026-06-18T14:12:49+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.4.1", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "09e8a212562fb1fb6a512c4156ed71525969d6c2" + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/09e8a212562fb1fb6a512c4156ed71525969d6c2", - "reference": "09e8a212562fb1fb6a512c4156ed71525969d6c2", + "url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e", + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0" + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", @@ -4826,7 +4827,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.4.1" + "source": "https://github.com/guzzle/promises/tree/2.5.0" }, "funding": [ { @@ -4842,27 +4843,29 @@ "type": "tidelift" } ], - "time": "2026-05-20T22:57:30+00:00" + "time": "2026-06-02T12:23:43+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.10.1", + "version": "2.12.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "73ab136360b5dfd858006eae9795e8fe43c80361" + "reference": "172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/73ab136360b5dfd858006eae9795e8fe43c80361", - "reference": "73ab136360b5dfd858006eae9795e8fe43c80361", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7", + "reference": "172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.24" }, "provide": { "psr/http-factory-implementation": "1.0", @@ -4943,7 +4946,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.10.1" + "source": "https://github.com/guzzle/psr7/tree/2.12.1" }, "funding": [ { @@ -4959,7 +4962,7 @@ "type": "tidelift" } ], - "time": "2026-05-20T09:27:36+00:00" + "time": "2026-06-18T09:49:37+00:00" }, { "name": "hshn/base64-encoded-file", @@ -5025,16 +5028,16 @@ }, { "name": "imagine/imagine", - "version": "1.5.2", + "version": "1.5.4", "source": { "type": "git", "url": "https://github.com/php-imagine/Imagine.git", - "reference": "f9ed796eefb77c2f0f2167e1d4e36bc2b5ed6b0c" + "reference": "dd57a4c290ff4d223d17bcd219dac9ac8bf1cc16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-imagine/Imagine/zipball/f9ed796eefb77c2f0f2167e1d4e36bc2b5ed6b0c", - "reference": "f9ed796eefb77c2f0f2167e1d4e36bc2b5ed6b0c", + "url": "https://api.github.com/repos/php-imagine/Imagine/zipball/dd57a4c290ff4d223d17bcd219dac9ac8bf1cc16", + "reference": "dd57a4c290ff4d223d17bcd219dac9ac8bf1cc16", "shasum": "" }, "require": { @@ -5081,9 +5084,9 @@ ], "support": { "issues": "https://github.com/php-imagine/Imagine/issues", - "source": "https://github.com/php-imagine/Imagine/tree/1.5.2" + "source": "https://github.com/php-imagine/Imagine/tree/1.5.4" }, - "time": "2026-01-09T10:45:12+00:00" + "time": "2026-06-04T10:05:48+00:00" }, { "name": "jbtronics/2fa-webauthn", @@ -7972,16 +7975,16 @@ }, { "name": "omines/datatables-bundle", - "version": "0.10.7", + "version": "0.10.8", "source": { "type": "git", "url": "https://github.com/omines/datatables-bundle.git", - "reference": "4cd6d27b12c79a1ed72b4953a86aedf289e8701d" + "reference": "eace7648205f7595ccb289037897597d9c7bb9ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/omines/datatables-bundle/zipball/4cd6d27b12c79a1ed72b4953a86aedf289e8701d", - "reference": "4cd6d27b12c79a1ed72b4953a86aedf289e8701d", + "url": "https://api.github.com/repos/omines/datatables-bundle/zipball/eace7648205f7595ccb289037897597d9c7bb9ec", + "reference": "eace7648205f7595ccb289037897597d9c7bb9ec", "shasum": "" }, "require": { @@ -7994,12 +7997,13 @@ "symfony/translation": "^6.4|^7.3|^8.0" }, "conflict": { - "doctrine/orm": "^3.0 <3.3" + "doctrine/orm": "^3.0 <3.3", + "symfony/twig-bridge": "<6.4.19" }, "require-dev": { "doctrine/common": "^3.5.0", - "doctrine/doctrine-bundle": "^2.18.1|^3.0.0@dev", - "doctrine/orm": "^2.19.3|^3.5.7@dev", + "doctrine/doctrine-bundle": "^2.18.1|^3.2.2", + "doctrine/orm": "^2.19.3|^3.6.7", "doctrine/persistence": "^3.4.0|^4.1.1", "ext-curl": "*", "ext-json": "*", @@ -8007,28 +8011,28 @@ "ext-mongodb": "*", "ext-pdo_sqlite": "*", "ext-zip": "*", - "friendsofphp/php-cs-fixer": "^3.90.0", + "friendsofphp/php-cs-fixer": "^3.93.0", "mongodb/mongodb": "^1.20.0|^2.1.2", "openspout/openspout": "^4.28.5", - "phpoffice/phpspreadsheet": "^2.3.3|^3.9.2|^4.5.0|^5.2.0", + "phpoffice/phpspreadsheet": "^2.3.3|^3.9.2|^4.5.0|^5.4.0", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.32", - "phpstan/phpstan-doctrine": "^2.0.11", - "phpstan/phpstan-phpunit": "^2.0.8", - "phpstan/phpstan-symfony": "^2.0.8", - "phpunit/phpunit": "^11.5.44|^12.4.4", + "phpstan/phpstan": "^2.1.37", + "phpstan/phpstan-doctrine": "^2.0.14", + "phpstan/phpstan-phpunit": "^2.0.12", + "phpstan/phpstan-symfony": "^2.0.12", + "phpunit/phpunit": "^11.5.44|^12.5.7", "ruflin/elastica": "^7.3.2", - "symfony/browser-kit": "^6.4.13|^7.3|^8.0", + "symfony/browser-kit": "^6.4.13|^7.3|^8.0.4", "symfony/css-selector": "^6.4.13|^7.3|^8.0", - "symfony/doctrine-bridge": "^6.4.13|^7.3|^8.0", - "symfony/dom-crawler": "^6.4.13|^7.3|^8.0", - "symfony/intl": "^6.4.13|^7.3|^8.0", - "symfony/mime": "^6.4.13|^7.3|^8.0", - "symfony/phpunit-bridge": "^7.3|^8.0", - "symfony/twig-bundle": "^6.4|^7.3|^8.0", - "symfony/var-dumper": "^6.4.13|^7.3|^8.0", - "symfony/var-exporter": "^v6.4.26|^7.3", - "symfony/yaml": "^6.4.13|^7.3|^8.0" + "symfony/doctrine-bridge": "^6.4.13|^7.3|^8.0.4", + "symfony/dom-crawler": "^6.4.13|^7.3|^8.0.4", + "symfony/intl": "^6.4.13|^7.3|^8.0.4", + "symfony/mime": "^6.4.13|^7.3|^8.0.4", + "symfony/phpunit-bridge": "^7.3|^8.0.3", + "symfony/twig-bundle": "^6.4.32|^7.3|^8.0.4", + "symfony/var-dumper": "^6.4.13|^7.3|^8.0.4", + "symfony/var-exporter": "^v6.4.26|^7.4|^8.0", + "symfony/yaml": "^6.4.13|^7.3|^8.0.1" }, "suggest": { "doctrine/doctrine-bundle": "For integrated access to Doctrine object managers", @@ -8080,7 +8084,7 @@ ], "support": { "issues": "https://github.com/omines/datatables-bundle/issues", - "source": "https://github.com/omines/datatables-bundle/tree/0.10.7" + "source": "https://github.com/omines/datatables-bundle/tree/0.10.8" }, "funding": [ { @@ -8088,7 +8092,7 @@ "type": "github" } ], - "time": "2025-11-28T21:20:14+00:00" + "time": "2026-06-02T09:41:50+00:00" }, { "name": "onelogin/php-saml", @@ -8954,16 +8958,16 @@ }, { "name": "phpoffice/phpspreadsheet", - "version": "5.7.0", + "version": "5.8.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "9f55d3b9b7bcb1084fda8340e4b7ce4ed10cd0c8" + "reference": "01964d92536edf1a3a874b9580a52824bebf6fbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/9f55d3b9b7bcb1084fda8340e4b7ce4ed10cd0c8", - "reference": "9f55d3b9b7bcb1084fda8340e4b7ce4ed10cd0c8", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/01964d92536edf1a3a874b9580a52824bebf6fbb", + "reference": "01964d92536edf1a3a874b9580a52824bebf6fbb", "shasum": "" }, "require": { @@ -9057,9 +9061,9 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.7.0" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.8.0" }, - "time": "2026-04-20T02:42:17+00:00" + "time": "2026-06-07T03:51:10+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -9993,16 +9997,16 @@ }, { "name": "sabberworm/php-css-parser", - "version": "v9.3.0", + "version": "v9.4.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", - "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949" + "reference": "fd3bf9fb173e0df649bc4e3e0d088a1b2417c08f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949", - "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/fd3bf9fb173e0df649bc4e3e0d088a1b2417c08f", + "reference": "fd3bf9fb173e0df649bc4e3e0d088a1b2417c08f", "shasum": "" }, "require": { @@ -10013,15 +10017,15 @@ "require-dev": { "php-parallel-lint/php-parallel-lint": "1.4.0", "phpstan/extension-installer": "1.4.3", - "phpstan/phpstan": "1.12.32 || 2.1.32", - "phpstan/phpstan-phpunit": "1.4.2 || 2.0.8", - "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.7", + "phpstan/phpstan": "1.12.33 || 2.2.2", + "phpstan/phpstan-phpunit": "1.4.2 || 2.0.16", + "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.11", "phpunit/phpunit": "8.5.52", "rawr/phpunit-data-provider": "3.3.1", - "rector/rector": "1.2.10 || 2.2.8", - "rector/type-perfect": "1.0.0 || 2.1.0", + "rector/rector": "1.2.10 || 2.4.6", + "rector/type-perfect": "1.0.0 || 2.1.3", "squizlabs/php_codesniffer": "4.0.1", - "thecodingmachine/phpstan-safe-rule": "1.2.0 || 1.4.1" + "thecodingmachine/phpstan-safe-rule": "1.2.0 || 1.4.3" }, "suggest": { "ext-mbstring": "for parsing UTF-8 CSS" @@ -10029,7 +10033,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "9.4.x-dev" + "dev-main": "9.5.x-dev" } }, "autoload": { @@ -10067,9 +10071,9 @@ ], "support": { "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", - "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.3.0" + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.4.0" }, - "time": "2026-03-03T17:31:43+00:00" + "time": "2026-06-18T15:10:53+00:00" }, { "name": "sabre/uri", @@ -10134,16 +10138,16 @@ }, { "name": "scheb/2fa-backup-code", - "version": "v7.13.1", + "version": "v7.14.0", "source": { "type": "git", "url": "https://github.com/scheb/2fa-backup-code.git", - "reference": "35f1ace4be7be2c10158d2bb8284208499111db8" + "reference": "73946182322f43cf82c5ee8c5481481a570ade40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scheb/2fa-backup-code/zipball/35f1ace4be7be2c10158d2bb8284208499111db8", - "reference": "35f1ace4be7be2c10158d2bb8284208499111db8", + "url": "https://api.github.com/repos/scheb/2fa-backup-code/zipball/73946182322f43cf82c5ee8c5481481a570ade40", + "reference": "73946182322f43cf82c5ee8c5481481a570ade40", "shasum": "" }, "require": { @@ -10177,22 +10181,22 @@ "two-step" ], "support": { - "source": "https://github.com/scheb/2fa-backup-code/tree/v7.13.1" + "source": "https://github.com/scheb/2fa-backup-code/tree/v7.14.0" }, - "time": "2025-11-20T13:35:24+00:00" + "time": "2026-01-24T13:47:32+00:00" }, { "name": "scheb/2fa-bundle", - "version": "v7.13.1", + "version": "v7.14.0", "source": { "type": "git", "url": "https://github.com/scheb/2fa-bundle.git", - "reference": "edcc14456b508aab37ec792cfc36793d04226784" + "reference": "65fa9eb61d1205f2024f14c8c38b53e7bb20927c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scheb/2fa-bundle/zipball/edcc14456b508aab37ec792cfc36793d04226784", - "reference": "edcc14456b508aab37ec792cfc36793d04226784", + "url": "https://api.github.com/repos/scheb/2fa-bundle/zipball/65fa9eb61d1205f2024f14c8c38b53e7bb20927c", + "reference": "65fa9eb61d1205f2024f14c8c38b53e7bb20927c", "shasum": "" }, "require": { @@ -10245,22 +10249,22 @@ "two-step" ], "support": { - "source": "https://github.com/scheb/2fa-bundle/tree/v7.13.1" + "source": "https://github.com/scheb/2fa-bundle/tree/v7.14.0" }, - "time": "2025-12-18T15:29:07+00:00" + "time": "2026-06-12T18:31:07+00:00" }, { "name": "scheb/2fa-google-authenticator", - "version": "v7.13.1", + "version": "v7.14.0", "source": { "type": "git", "url": "https://github.com/scheb/2fa-google-authenticator.git", - "reference": "7ad34bbde343a0770571464127ee072aacb70a58" + "reference": "23b0bda5b924b8752047c4994f10e1c9e1532179" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scheb/2fa-google-authenticator/zipball/7ad34bbde343a0770571464127ee072aacb70a58", - "reference": "7ad34bbde343a0770571464127ee072aacb70a58", + "url": "https://api.github.com/repos/scheb/2fa-google-authenticator/zipball/23b0bda5b924b8752047c4994f10e1c9e1532179", + "reference": "23b0bda5b924b8752047c4994f10e1c9e1532179", "shasum": "" }, "require": { @@ -10298,22 +10302,22 @@ "two-step" ], "support": { - "source": "https://github.com/scheb/2fa-google-authenticator/tree/v7.13.1" + "source": "https://github.com/scheb/2fa-google-authenticator/tree/v7.14.0" }, - "time": "2025-12-04T15:55:14+00:00" + "time": "2026-01-24T13:53:55+00:00" }, { "name": "scheb/2fa-trusted-device", - "version": "v7.13.1", + "version": "v7.14.0", "source": { "type": "git", "url": "https://github.com/scheb/2fa-trusted-device.git", - "reference": "ae3a5819faccbf151af078f432e4e6c97bb44ebf" + "reference": "590f400bac58bff70af24adbc702e57a30e493a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scheb/2fa-trusted-device/zipball/ae3a5819faccbf151af078f432e4e6c97bb44ebf", - "reference": "ae3a5819faccbf151af078f432e4e6c97bb44ebf", + "url": "https://api.github.com/repos/scheb/2fa-trusted-device/zipball/590f400bac58bff70af24adbc702e57a30e493a3", + "reference": "590f400bac58bff70af24adbc702e57a30e493a3", "shasum": "" }, "require": { @@ -10349,9 +10353,9 @@ "two-step" ], "support": { - "source": "https://github.com/scheb/2fa-trusted-device/tree/v7.13.1" + "source": "https://github.com/scheb/2fa-trusted-device/tree/v7.14.0" }, - "time": "2025-12-01T15:40:59+00:00" + "time": "2026-01-24T13:47:32+00:00" }, { "name": "shivas/versioning-bundle", @@ -10549,16 +10553,16 @@ }, { "name": "spomky-labs/otphp", - "version": "11.4.2", + "version": "11.5.0", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", - "reference": "2a1b503fd1c1a5c751ab3c5cd37f2d2d26ab74ad" + "reference": "877683d6352b80cdc7020fd43a725629c2524435" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/2a1b503fd1c1a5c751ab3c5cd37f2d2d26ab74ad", - "reference": "2a1b503fd1c1a5c751ab3c5cd37f2d2d26ab74ad", + "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/877683d6352b80cdc7020fd43a725629c2524435", + "reference": "877683d6352b80cdc7020fd43a725629c2524435", "shasum": "" }, "require": { @@ -10603,7 +10607,7 @@ ], "support": { "issues": "https://github.com/Spomky-Labs/otphp/issues", - "source": "https://github.com/Spomky-Labs/otphp/tree/11.4.2" + "source": "https://github.com/Spomky-Labs/otphp/tree/11.5.0" }, "funding": [ { @@ -10615,7 +10619,7 @@ "type": "patreon" } ], - "time": "2026-01-23T10:53:01+00:00" + "time": "2026-06-06T23:41:24+00:00" }, { "name": "spomky-labs/pki-framework", @@ -11377,16 +11381,16 @@ }, { "name": "symfony/cache", - "version": "v7.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "902d621e0b6ef0ebeaa133770b5c339a19328589" + "reference": "4c09e18a92cce126cc0d1155825279fca8cd0673" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/902d621e0b6ef0ebeaa133770b5c339a19328589", - "reference": "902d621e0b6ef0ebeaa133770b5c339a19328589", + "url": "https://api.github.com/repos/symfony/cache/zipball/4c09e18a92cce126cc0d1155825279fca8cd0673", + "reference": "4c09e18a92cce126cc0d1155825279fca8cd0673", "shasum": "" }, "require": { @@ -11457,7 +11461,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.4.12" + "source": "https://github.com/symfony/cache/tree/v7.4.13" }, "funding": [ { @@ -11477,7 +11481,7 @@ "type": "tidelift" } ], - "time": "2026-05-20T07:20:23+00:00" + "time": "2026-05-24T08:43:14+00:00" }, { "name": "symfony/cache-contracts", @@ -11718,16 +11722,16 @@ }, { "name": "symfony/console", - "version": "v7.4.11", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "ed0107e43ab452aa77ae99e005b95e56b556e075" + "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/ed0107e43ab452aa77ae99e005b95e56b556e075", - "reference": "ed0107e43ab452aa77ae99e005b95e56b556e075", + "url": "https://api.github.com/repos/symfony/console/zipball/85095d2573eaefaf35e40b9513a9bf09f72cd217", + "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217", "shasum": "" }, "require": { @@ -11792,7 +11796,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.11" + "source": "https://github.com/symfony/console/tree/v7.4.13" }, "funding": [ { @@ -11812,7 +11816,7 @@ "type": "tidelift" } ], - "time": "2026-05-13T12:04:42+00:00" + "time": "2026-05-24T08:56:14+00:00" }, { "name": "symfony/css-selector", @@ -11885,16 +11889,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v7.4.10", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "4eb0d9dfa9d4f7c59216baf49b3ed6b1fb72293d" + "reference": "f299e20ce983be6c0744952533c6dfeaaa1448e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/4eb0d9dfa9d4f7c59216baf49b3ed6b1fb72293d", - "reference": "4eb0d9dfa9d4f7c59216baf49b3ed6b1fb72293d", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f299e20ce983be6c0744952533c6dfeaaa1448e2", + "reference": "f299e20ce983be6c0744952533c6dfeaaa1448e2", "shasum": "" }, "require": { @@ -11945,7 +11949,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.4.10" + "source": "https://github.com/symfony/dependency-injection/tree/v7.4.13" }, "funding": [ { @@ -11965,7 +11969,7 @@ "type": "tidelift" } ], - "time": "2026-05-06T11:55:30+00:00" + "time": "2026-05-20T14:07:29+00:00" }, { "name": "symfony/deprecation-contracts", @@ -12756,16 +12760,16 @@ }, { "name": "symfony/flex", - "version": "v2.10.0", + "version": "v2.11.0", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d" + "reference": "4a6d98eea3ebc7f68d82810cb682eedca2649e99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/9cd384775973eabbf6e8b05784dda279fc67c28d", - "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d", + "url": "https://api.github.com/repos/symfony/flex/zipball/4a6d98eea3ebc7f68d82810cb682eedca2649e99", + "reference": "4a6d98eea3ebc7f68d82810cb682eedca2649e99", "shasum": "" }, "require": { @@ -12778,9 +12782,9 @@ }, "require-dev": { "composer/composer": "^2.1", - "symfony/dotenv": "^6.4|^7.4|^8.0", + "phpunit/phpunit": "^12.4", + "symfony/dotenv": "^6.4.41|^7.4.13|^8.0.13", "symfony/filesystem": "^6.4|^7.4|^8.0", - "symfony/phpunit-bridge": "^6.4|^7.4|^8.0", "symfony/process": "^6.4|^7.4|^8.0" }, "type": "composer-plugin", @@ -12805,7 +12809,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.10.0" + "source": "https://github.com/symfony/flex/tree/v2.11.0" }, "funding": [ { @@ -12825,7 +12829,7 @@ "type": "tidelift" } ], - "time": "2025-11-16T09:38:19+00:00" + "time": "2026-05-29T17:25:22+00:00" }, { "name": "symfony/form", @@ -12932,16 +12936,16 @@ }, { "name": "symfony/framework-bundle", - "version": "v7.4.11", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "637f5cac1ac2698a012b41610215bf366004295f" + "reference": "8be39c7bf9e6f58fe49c07927572a9df7c961c95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/637f5cac1ac2698a012b41610215bf366004295f", - "reference": "637f5cac1ac2698a012b41610215bf366004295f", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/8be39c7bf9e6f58fe49c07927572a9df7c961c95", + "reference": "8be39c7bf9e6f58fe49c07927572a9df7c961c95", "shasum": "" }, "require": { @@ -13066,7 +13070,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.4.11" + "source": "https://github.com/symfony/framework-bundle/tree/v7.4.13" }, "funding": [ { @@ -13086,20 +13090,94 @@ "type": "tidelift" } ], - "time": "2026-05-13T12:04:42+00:00" + "time": "2026-05-23T18:04:28+00:00" }, { - "name": "symfony/http-client", - "version": "v7.4.9", + "name": "symfony/html-sanitizer", + "version": "v7.4.13", "source": { "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6" + "url": "https://github.com/symfony/html-sanitizer.git", + "reference": "761f6c49dfd103ee08b3cd09ece588b069e18ec9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6", - "reference": "7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6", + "url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/761f6c49dfd103ee08b3cd09ece588b069e18ec9", + "reference": "761f6c49dfd103ee08b3cd09ece588b069e18ec9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "league/uri": "^6.5|^7.0", + "masterminds/html5": "^2.7.2", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HtmlSanitizer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Titouan Galopin", + "email": "galopintitouan@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to sanitize untrusted HTML input for safe insertion into a document's DOM.", + "homepage": "https://symfony.com", + "keywords": [ + "Purifier", + "html", + "sanitizer" + ], + "support": { + "source": "https://github.com/symfony/html-sanitizer/tree/v7.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-24T11:20:33+00:00" + }, + { + "name": "symfony/http-client", + "version": "v7.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "e8a112b8415707265a7e614278136a9d92989a6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/e8a112b8415707265a7e614278136a9d92989a6a", + "reference": "e8a112b8415707265a7e614278136a9d92989a6a", "shasum": "" }, "require": { @@ -13167,7 +13245,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.4.9" + "source": "https://github.com/symfony/http-client/tree/v7.4.13" }, "funding": [ { @@ -13187,7 +13265,7 @@ "type": "tidelift" } ], - "time": "2026-04-29T13:25:15+00:00" + "time": "2026-05-24T09:57:54+00:00" }, { "name": "symfony/http-client-contracts", @@ -13273,16 +13351,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.4.8", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "9381209597ec66c25be154cbf2289076e64d1eab" + "reference": "bc354f47c62301e990b7874fa662326368508e2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9381209597ec66c25be154cbf2289076e64d1eab", - "reference": "9381209597ec66c25be154cbf2289076e64d1eab", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/bc354f47c62301e990b7874fa662326368508e2c", + "reference": "bc354f47c62301e990b7874fa662326368508e2c", "shasum": "" }, "require": { @@ -13331,7 +13409,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.4.8" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.13" }, "funding": [ { @@ -13351,20 +13429,20 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-05-24T11:20:33+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "7922b53e70d2ba2027af8bb6a59d91eb3541ea4d" + "reference": "9df847980c436451f4f51d1284491bb4356dd989" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/7922b53e70d2ba2027af8bb6a59d91eb3541ea4d", - "reference": "7922b53e70d2ba2027af8bb6a59d91eb3541ea4d", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9df847980c436451f4f51d1284491bb4356dd989", + "reference": "9df847980c436451f4f51d1284491bb4356dd989", "shasum": "" }, "require": { @@ -13450,7 +13528,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.4.12" + "source": "https://github.com/symfony/http-kernel/tree/v7.4.13" }, "funding": [ { @@ -13470,7 +13548,7 @@ "type": "tidelift" } ], - "time": "2026-05-20T09:27:11+00:00" + "time": "2026-05-27T08:31:43+00:00" }, { "name": "symfony/intl", @@ -13648,16 +13726,16 @@ }, { "name": "symfony/mime", - "version": "v7.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "b198dd66c211c97119bcaaff7c13431dbbb5e470" + "reference": "a845722765c4f6b2ce88beaf4f4479975b186770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/b198dd66c211c97119bcaaff7c13431dbbb5e470", - "reference": "b198dd66c211c97119bcaaff7c13431dbbb5e470", + "url": "https://api.github.com/repos/symfony/mime/zipball/a845722765c4f6b2ce88beaf4f4479975b186770", + "reference": "a845722765c4f6b2ce88beaf4f4479975b186770", "shasum": "" }, "require": { @@ -13713,7 +13791,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.4.12" + "source": "https://github.com/symfony/mime/tree/v7.4.13" }, "funding": [ { @@ -13733,7 +13811,7 @@ "type": "tidelift" } ], - "time": "2026-05-20T07:20:23+00:00" + "time": "2026-05-23T16:22:37+00:00" }, { "name": "symfony/monolog-bridge", @@ -14129,16 +14207,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.38.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "9c862df890f7c833b1101ac5578ec4dcf199efb5" + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/9c862df890f7c833b1101ac5578ec4dcf199efb5", - "reference": "9c862df890f7c833b1101ac5578ec4dcf199efb5", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", "shasum": "" }, "require": { @@ -14187,7 +14265,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" }, "funding": [ { @@ -14207,7 +14285,7 @@ "type": "tidelift" } ], - "time": "2026-05-25T12:39:52+00:00" + "time": "2026-05-26T05:58:03+00:00" }, { "name": "symfony/polyfill-intl-icu", @@ -14299,16 +14377,16 @@ }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.37.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + "reference": "dc21118016c039a66235cf93d96b435ffb282412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412", + "reference": "dc21118016c039a66235cf93d96b435ffb282412", "shasum": "" }, "require": { @@ -14362,7 +14440,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1" }, "funding": [ { @@ -14382,7 +14460,7 @@ "type": "tidelift" } ], - "time": "2024-09-10T14:38:51+00:00" + "time": "2026-05-25T15:22:23+00:00" }, { "name": "symfony/polyfill-intl-normalizer", @@ -14471,16 +14549,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.38.0", + "version": "v1.38.2", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "c4406e07046227db8844a25f89d111da078aaa9c" + "reference": "796a26abb75ce49f3a84433cd81bf1009d73d5f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/c4406e07046227db8844a25f89d111da078aaa9c", - "reference": "c4406e07046227db8844a25f89d111da078aaa9c", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/796a26abb75ce49f3a84433cd81bf1009d73d5f8", + "reference": "796a26abb75ce49f3a84433cd81bf1009d73d5f8", "shasum": "" }, "require": { @@ -14527,7 +14605,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.38.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.38.2" }, "funding": [ { @@ -14547,20 +14625,20 @@ "type": "tidelift" } ], - "time": "2026-05-25T12:39:52+00:00" + "time": "2026-05-27T06:51:48+00:00" }, { "name": "symfony/polyfill-php84", - "version": "v1.38.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "a0e0aca0368801ec79f8791dea9a7c12af527c93" + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/a0e0aca0368801ec79f8791dea9a7c12af527c93", - "reference": "a0e0aca0368801ec79f8791dea9a7c12af527c93", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", "shasum": "" }, "require": { @@ -14607,7 +14685,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" }, "funding": [ { @@ -14627,20 +14705,20 @@ "type": "tidelift" } ], - "time": "2026-05-25T12:12:52+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { "name": "symfony/polyfill-php85", - "version": "v1.38.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "3e851ffb42624b64b3b9ec234a0e2074b0f880e3" + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/3e851ffb42624b64b3b9ec234a0e2074b0f880e3", - "reference": "3e851ffb42624b64b3b9ec234a0e2074b0f880e3", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", "shasum": "" }, "require": { @@ -14687,7 +14765,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.38.0" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.38.1" }, "funding": [ { @@ -14707,7 +14785,7 @@ "type": "tidelift" } ], - "time": "2026-05-25T11:50:50+00:00" + "time": "2026-05-26T02:25:22+00:00" }, { "name": "symfony/polyfill-uuid", @@ -14794,16 +14872,16 @@ }, { "name": "symfony/process", - "version": "v7.4.11", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d9593c9efa40499eb078b81144de42cbc28a31f0" + "reference": "f5804be144caceb570f6747519999636b664f24c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d9593c9efa40499eb078b81144de42cbc28a31f0", - "reference": "d9593c9efa40499eb078b81144de42cbc28a31f0", + "url": "https://api.github.com/repos/symfony/process/zipball/f5804be144caceb570f6747519999636b664f24c", + "reference": "f5804be144caceb570f6747519999636b664f24c", "shasum": "" }, "require": { @@ -14835,7 +14913,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.11" + "source": "https://github.com/symfony/process/tree/v7.4.13" }, "funding": [ { @@ -14855,7 +14933,7 @@ "type": "tidelift" } ], - "time": "2026-05-11T16:55:21+00:00" + "time": "2026-05-23T16:05:06+00:00" }, { "name": "symfony/property-access", @@ -15118,16 +15196,16 @@ }, { "name": "symfony/rate-limiter", - "version": "v7.4.10", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/rate-limiter.git", - "reference": "778c5239c7fd6bf9b886dedf3d84ddb156ddb888" + "reference": "8b162768544e5a8895c52161d63c999aca91f4a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/rate-limiter/zipball/778c5239c7fd6bf9b886dedf3d84ddb156ddb888", - "reference": "778c5239c7fd6bf9b886dedf3d84ddb156ddb888", + "url": "https://api.github.com/repos/symfony/rate-limiter/zipball/8b162768544e5a8895c52161d63c999aca91f4a9", + "reference": "8b162768544e5a8895c52161d63c999aca91f4a9", "shasum": "" }, "require": { @@ -15168,7 +15246,7 @@ "rate-limiter" ], "support": { - "source": "https://github.com/symfony/rate-limiter/tree/v7.4.10" + "source": "https://github.com/symfony/rate-limiter/tree/v7.4.13" }, "funding": [ { @@ -15188,20 +15266,20 @@ "type": "tidelift" } ], - "time": "2026-05-04T13:25:50+00:00" + "time": "2026-05-23T16:05:06+00:00" }, { "name": "symfony/routing", - "version": "v7.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "3b04a5ec4887a8135a12ebf0f4cbc5b8fc8ee204" + "reference": "3a162171bb008e5e0f15dce6581373a4c0e8390d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/3b04a5ec4887a8135a12ebf0f4cbc5b8fc8ee204", - "reference": "3b04a5ec4887a8135a12ebf0f4cbc5b8fc8ee204", + "url": "https://api.github.com/repos/symfony/routing/zipball/3a162171bb008e5e0f15dce6581373a4c0e8390d", + "reference": "3a162171bb008e5e0f15dce6581373a4c0e8390d", "shasum": "" }, "require": { @@ -15253,7 +15331,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.4.12" + "source": "https://github.com/symfony/routing/tree/v7.4.13" }, "funding": [ { @@ -15273,20 +15351,20 @@ "type": "tidelift" } ], - "time": "2026-05-20T07:20:23+00:00" + "time": "2026-05-24T11:20:33+00:00" }, { "name": "symfony/runtime", - "version": "v7.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "0b032fa77359745db793df5aff626779180c5f3b" + "reference": "1a24cf8aab3a9378117718b35525c4126ad3adec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/0b032fa77359745db793df5aff626779180c5f3b", - "reference": "0b032fa77359745db793df5aff626779180c5f3b", + "url": "https://api.github.com/repos/symfony/runtime/zipball/1a24cf8aab3a9378117718b35525c4126ad3adec", + "reference": "1a24cf8aab3a9378117718b35525c4126ad3adec", "shasum": "" }, "require": { @@ -15337,7 +15415,7 @@ "runtime" ], "support": { - "source": "https://github.com/symfony/runtime/tree/v7.4.12" + "source": "https://github.com/symfony/runtime/tree/v7.4.13" }, "funding": [ { @@ -15357,20 +15435,20 @@ "type": "tidelift" } ], - "time": "2026-05-20T07:20:23+00:00" + "time": "2026-05-23T18:04:28+00:00" }, { "name": "symfony/security-bundle", - "version": "v7.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "6f6f859b437fb95028addfa21b417d25daca86d5" + "reference": "0cbc6528aa583795ab44e43b4e92a09acf927c6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/6f6f859b437fb95028addfa21b417d25daca86d5", - "reference": "6f6f859b437fb95028addfa21b417d25daca86d5", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/0cbc6528aa583795ab44e43b4e92a09acf927c6f", + "reference": "0cbc6528aa583795ab44e43b4e92a09acf927c6f", "shasum": "" }, "require": { @@ -15449,7 +15527,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v7.4.12" + "source": "https://github.com/symfony/security-bundle/tree/v7.4.13" }, "funding": [ { @@ -15469,20 +15547,20 @@ "type": "tidelift" } ], - "time": "2026-05-15T07:14:02+00:00" + "time": "2026-05-23T16:05:06+00:00" }, { "name": "symfony/security-core", - "version": "v7.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "efff84605474ec682c7d9c6278088811e6f3caaa" + "reference": "25db686fcf2a3fe00e1cf6dcab1fcb7aac71ba9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/efff84605474ec682c7d9c6278088811e6f3caaa", - "reference": "efff84605474ec682c7d9c6278088811e6f3caaa", + "url": "https://api.github.com/repos/symfony/security-core/zipball/25db686fcf2a3fe00e1cf6dcab1fcb7aac71ba9b", + "reference": "25db686fcf2a3fe00e1cf6dcab1fcb7aac71ba9b", "shasum": "" }, "require": { @@ -15540,7 +15618,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v7.4.12" + "source": "https://github.com/symfony/security-core/tree/v7.4.13" }, "funding": [ { @@ -15560,7 +15638,7 @@ "type": "tidelift" } ], - "time": "2026-05-15T06:48:59+00:00" + "time": "2026-05-23T16:05:06+00:00" }, { "name": "symfony/security-csrf", @@ -15638,16 +15716,16 @@ }, { "name": "symfony/security-http", - "version": "v7.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "1fc7ca636cbd2cad29b42cc13c9fd0c681c6efee" + "reference": "da3c28025a664e6a88e1af104a74457d99301161" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/1fc7ca636cbd2cad29b42cc13c9fd0c681c6efee", - "reference": "1fc7ca636cbd2cad29b42cc13c9fd0c681c6efee", + "url": "https://api.github.com/repos/symfony/security-http/zipball/da3c28025a664e6a88e1af104a74457d99301161", + "reference": "da3c28025a664e6a88e1af104a74457d99301161", "shasum": "" }, "require": { @@ -15706,7 +15784,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v7.4.12" + "source": "https://github.com/symfony/security-http/tree/v7.4.13" }, "funding": [ { @@ -15726,7 +15804,7 @@ "type": "tidelift" } ], - "time": "2026-05-20T07:20:23+00:00" + "time": "2026-05-25T06:06:12+00:00" }, { "name": "symfony/serializer", @@ -15921,16 +15999,16 @@ }, { "name": "symfony/stimulus-bundle", - "version": "v2.35.0", + "version": "v2.36.0", "source": { "type": "git", "url": "https://github.com/symfony/stimulus-bundle.git", - "reference": "05af0259f201dbbd15c103bea289989a4b483b5b" + "reference": "377a3d1ec5834631a7db53bd275276ff3c5b49df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/05af0259f201dbbd15c103bea289989a4b483b5b", - "reference": "05af0259f201dbbd15c103bea289989a4b483b5b", + "url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/377a3d1ec5834631a7db53bd275276ff3c5b49df", + "reference": "377a3d1ec5834631a7db53bd275276ff3c5b49df", "shasum": "" }, "require": { @@ -15970,7 +16048,7 @@ "symfony-ux" ], "support": { - "source": "https://github.com/symfony/stimulus-bundle/tree/v2.35.0" + "source": "https://github.com/symfony/stimulus-bundle/tree/v2.36.0" }, "funding": [ { @@ -15990,7 +16068,7 @@ "type": "tidelift" } ], - "time": "2026-03-22T22:21:50+00:00" + "time": "2026-05-06T04:31:36+00:00" }, { "name": "symfony/stopwatch", @@ -16060,16 +16138,16 @@ }, { "name": "symfony/string", - "version": "v7.4.11", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "965f7306a43383d02c6aca1e3f3bd2f0ea5dee15" + "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/965f7306a43383d02c6aca1e3f3bd2f0ea5dee15", - "reference": "965f7306a43383d02c6aca1e3f3bd2f0ea5dee15", + "url": "https://api.github.com/repos/symfony/string/zipball/961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", + "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", "shasum": "" }, "require": { @@ -16127,7 +16205,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.11" + "source": "https://github.com/symfony/string/tree/v7.4.13" }, "funding": [ { @@ -16147,7 +16225,7 @@ "type": "tidelift" } ], - "time": "2026-05-13T12:04:42+00:00" + "time": "2026-05-23T15:23:29+00:00" }, { "name": "symfony/translation", @@ -16699,16 +16777,16 @@ }, { "name": "symfony/ux-translator", - "version": "v2.35.0", + "version": "v2.36.0", "source": { "type": "git", "url": "https://github.com/symfony/ux-translator.git", - "reference": "5a56d25237393e865e3df94a39d2c8f0ce94b50c" + "reference": "eb5f11b9a84491a93635798f039f474bc4ab0af9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-translator/zipball/5a56d25237393e865e3df94a39d2c8f0ce94b50c", - "reference": "5a56d25237393e865e3df94a39d2c8f0ce94b50c", + "url": "https://api.github.com/repos/symfony/ux-translator/zipball/eb5f11b9a84491a93635798f039f474bc4ab0af9", + "reference": "eb5f11b9a84491a93635798f039f474bc4ab0af9", "shasum": "" }, "require": { @@ -16756,7 +16834,7 @@ "symfony-ux" ], "support": { - "source": "https://github.com/symfony/ux-translator/tree/v2.35.0" + "source": "https://github.com/symfony/ux-translator/tree/v2.36.0" }, "funding": [ { @@ -16776,20 +16854,20 @@ "type": "tidelift" } ], - "time": "2026-03-22T22:21:50+00:00" + "time": "2026-05-06T04:31:36+00:00" }, { "name": "symfony/ux-turbo", - "version": "v2.35.0", + "version": "v2.36.0", "source": { "type": "git", "url": "https://github.com/symfony/ux-turbo.git", - "reference": "4309a4299f5f1b9b7ce4c13ed6d1b77a5472c216" + "reference": "c16f0fdcc8eb22d80a02949447e561c145fa1bc8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/4309a4299f5f1b9b7ce4c13ed6d1b77a5472c216", - "reference": "4309a4299f5f1b9b7ce4c13ed6d1b77a5472c216", + "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/c16f0fdcc8eb22d80a02949447e561c145fa1bc8", + "reference": "c16f0fdcc8eb22d80a02949447e561c145fa1bc8", "shasum": "" }, "require": { @@ -16859,7 +16937,7 @@ "turbo-stream" ], "support": { - "source": "https://github.com/symfony/ux-turbo/tree/v2.35.0" + "source": "https://github.com/symfony/ux-turbo/tree/v2.36.0" }, "funding": [ { @@ -16879,7 +16957,7 @@ "type": "tidelift" } ], - "time": "2026-04-03T05:13:59+00:00" + "time": "2026-05-06T04:31:36+00:00" }, { "name": "symfony/validator", @@ -17318,16 +17396,16 @@ }, { "name": "symfony/yaml", - "version": "v7.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "8b6952b56ca6417f25f7a65758cadd0ce02edc51" + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/8b6952b56ca6417f25f7a65758cadd0ce02edc51", - "reference": "8b6952b56ca6417f25f7a65758cadd0ce02edc51", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c", + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c", "shasum": "" }, "require": { @@ -17370,7 +17448,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.12" + "source": "https://github.com/symfony/yaml/tree/v7.4.13" }, "funding": [ { @@ -17390,20 +17468,20 @@ "type": "tidelift" } ], - "time": "2026-05-20T07:20:23+00:00" + "time": "2026-05-25T06:06:12+00:00" }, { "name": "symplify/easy-coding-standard", - "version": "13.1.3", + "version": "13.2.3", "source": { "type": "git", - "url": "https://github.com/easy-coding-standard/ecs.git", - "reference": "d894d088d7ebb9326f9eed28bf251481c813b89f" + "url": "https://github.com/ecsphp/ecs.git", + "reference": "94f56bce0420d4e837a85c4b2c6501293a5974eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/easy-coding-standard/ecs/zipball/d894d088d7ebb9326f9eed28bf251481c813b89f", - "reference": "d894d088d7ebb9326f9eed28bf251481c813b89f", + "url": "https://api.github.com/repos/ecsphp/ecs/zipball/94f56bce0420d4e837a85c4b2c6501293a5974eb", + "reference": "94f56bce0420d4e837a85c4b2c6501293a5974eb", "shasum": "" }, "require": { @@ -17438,22 +17516,22 @@ "static analysis" ], "support": { - "source": "https://github.com/easy-coding-standard/ecs/tree/13.1.3" + "source": "https://github.com/ecsphp/ecs/tree/13.2.3" }, - "time": "2026-05-04T21:45:57+00:00" + "time": "2026-06-15T22:08:41+00:00" }, { "name": "tecnickcom/tc-lib-barcode", - "version": "2.7.0", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/tecnickcom/tc-lib-barcode.git", - "reference": "4e53047a4ba4ed592ae677b3729ce9bfeae1cfbb" + "reference": "3d7b04b0a905b1bcd7ce65e364ab228c3a11a463" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/tc-lib-barcode/zipball/4e53047a4ba4ed592ae677b3729ce9bfeae1cfbb", - "reference": "4e53047a4ba4ed592ae677b3729ce9bfeae1cfbb", + "url": "https://api.github.com/repos/tecnickcom/tc-lib-barcode/zipball/3d7b04b0a905b1bcd7ce65e364ab228c3a11a463", + "reference": "3d7b04b0a905b1bcd7ce65e364ab228c3a11a463", "shasum": "" }, "require": { @@ -17462,12 +17540,11 @@ "ext-gd": "*", "ext-pcre": "*", "php": ">=8.2", - "tecnickcom/tc-lib-color": "^2.7" + "tecnickcom/tc-lib-color": "^2.11" }, "require-dev": { "pdepend/pdepend": "^2.16", - "phpcompatibility/php-compatibility": "^10.0.0@dev", - "phpunit/phpunit": "^13.1 || ^12.5 || ^11.5" + "phpunit/phpunit": "^11.5 || ^12.5 || ^13.2" }, "type": "library", "autoload": { @@ -17539,20 +17616,20 @@ "type": "github" } ], - "time": "2026-05-22T07:09:18+00:00" + "time": "2026-06-17T18:04:27+00:00" }, { "name": "tecnickcom/tc-lib-color", - "version": "2.8.0", + "version": "2.11.0", "source": { "type": "git", "url": "https://github.com/tecnickcom/tc-lib-color.git", - "reference": "6947cc9fffe23a21642279b8ab73a43f3311c5f9" + "reference": "b7b326913c21fd695c4a189e3632c0d001b51c86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/tc-lib-color/zipball/6947cc9fffe23a21642279b8ab73a43f3311c5f9", - "reference": "6947cc9fffe23a21642279b8ab73a43f3311c5f9", + "url": "https://api.github.com/repos/tecnickcom/tc-lib-color/zipball/b7b326913c21fd695c4a189e3632c0d001b51c86", + "reference": "b7b326913c21fd695c4a189e3632c0d001b51c86", "shasum": "" }, "require": { @@ -17561,8 +17638,7 @@ }, "require-dev": { "pdepend/pdepend": "^2.16", - "phpcompatibility/php-compatibility": "^10.0.0@dev", - "phpunit/phpunit": "^13.1 || ^12.5 || ^11.5" + "phpunit/phpunit": "^11.5 || ^12.5 || ^13.2" }, "type": "library", "autoload": { @@ -17607,7 +17683,7 @@ "type": "github" } ], - "time": "2026-05-22T06:55:57+00:00" + "time": "2026-06-17T17:58:21+00:00" }, { "name": "thecodingmachine/safe", @@ -18341,16 +18417,16 @@ }, { "name": "twig/twig", - "version": "v3.26.0", + "version": "v3.27.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "1fcae487b180d78e6351f4e0afa91f9eab96a2bc" + "reference": "ae2071bffb38f04847fc0864d730c94b9cb8ab74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/1fcae487b180d78e6351f4e0afa91f9eab96a2bc", - "reference": "1fcae487b180d78e6351f4e0afa91f9eab96a2bc", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/ae2071bffb38f04847fc0864d730c94b9cb8ab74", + "reference": "ae2071bffb38f04847fc0864d730c94b9cb8ab74", "shasum": "" }, "require": { @@ -18405,7 +18481,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.26.0" + "source": "https://github.com/twigphp/Twig/tree/v3.27.1" }, "funding": [ { @@ -18417,7 +18493,7 @@ "type": "tidelift" } ], - "time": "2026-05-20T07:31:59+00:00" + "time": "2026-05-30T17:09:26+00:00" }, { "name": "ua-parser/uap-php", @@ -18555,16 +18631,16 @@ }, { "name": "web-auth/webauthn-lib", - "version": "5.3.4", + "version": "5.3.5", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-lib.git", - "reference": "dbb2d7a03db5893da2ef1f2898063ab8f7792838" + "reference": "9e0986d999f4102e24ac8a598d3a80d98b56c19f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/dbb2d7a03db5893da2ef1f2898063ab8f7792838", - "reference": "dbb2d7a03db5893da2ef1f2898063ab8f7792838", + "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/9e0986d999f4102e24ac8a598d3a80d98b56c19f", + "reference": "9e0986d999f4102e24ac8a598d3a80d98b56c19f", "shasum": "" }, "require": { @@ -18625,7 +18701,7 @@ "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-lib/tree/5.3.4" + "source": "https://github.com/web-auth/webauthn-lib/tree/5.3.5" }, "funding": [ { @@ -18637,11 +18713,11 @@ "type": "patreon" } ], - "time": "2026-05-18T11:59:46+00:00" + "time": "2026-05-31T15:00:08+00:00" }, { "name": "web-auth/webauthn-symfony-bundle", - "version": "5.3.4", + "version": "5.3.5", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-symfony-bundle.git", @@ -18708,7 +18784,7 @@ "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-symfony-bundle/tree/5.3.4" + "source": "https://github.com/web-auth/webauthn-symfony-bundle/tree/5.3.5" }, "funding": [ { @@ -18724,16 +18800,16 @@ }, { "name": "webmozart/assert", - "version": "2.4.0", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "9007ea6f45ecf352a9422b36644e4bfc039b9155" + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9007ea6f45ecf352a9422b36644e4bfc039b9155", - "reference": "9007ea6f45ecf352a9422b36644e4bfc039b9155", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", "shasum": "" }, "require": { @@ -18784,9 +18860,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.4.0" + "source": "https://github.com/webmozarts/assert/tree/2.4.1" }, - "time": "2026-05-20T13:07:01+00:00" + "time": "2026-06-15T15:31:57+00:00" }, { "name": "willdurand/negotiation", @@ -19423,11 +19499,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.55", + "version": "2.2.2", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9eaac3826ed5e9b8427350a43cac825eeca3f566", - "reference": "9eaac3826ed5e9b8427350a43cac825eeca3f566", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e5cc34d491a90e79c216d824f60fe21fd4d93bd6", + "reference": "e5cc34d491a90e79c216d824f60fe21fd4d93bd6", "shasum": "" }, "require": { @@ -19450,6 +19526,17 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ "dev", @@ -19472,25 +19559,25 @@ "type": "github" } ], - "time": "2026-05-18T11:57:34+00:00" + "time": "2026-06-05T09:00:01+00:00" }, { "name": "phpstan/phpstan-doctrine", - "version": "2.0.22", + "version": "2.0.27", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "e87516b034749432d51653c0147e053e476e8c53" + "reference": "39b4ca45a07cdd6366eeefa2f7a993cddf3b9f9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/e87516b034749432d51653c0147e053e476e8c53", - "reference": "e87516b034749432d51653c0147e053e476e8c53", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/39b4ca45a07cdd6366eeefa2f7a993cddf3b9f9f", + "reference": "39b4ca45a07cdd6366eeefa2f7a993cddf3b9f9f", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.34" + "phpstan/phpstan": "^2.2.2" }, "conflict": { "doctrine/collections": "<1.0", @@ -19547,9 +19634,9 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.22" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.27" }, - "time": "2026-05-09T08:10:48+00:00" + "time": "2026-06-10T10:39:35+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -19604,16 +19691,16 @@ }, { "name": "phpstan/phpstan-symfony", - "version": "2.0.18", + "version": "2.0.20", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "a12176b639dec54e8bfd0a5ebf5fc36ffe003b5d" + "reference": "53f1a6462dbe71fad36ce054caf5e1b725b740fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/a12176b639dec54e8bfd0a5ebf5fc36ffe003b5d", - "reference": "a12176b639dec54e8bfd0a5ebf5fc36ffe003b5d", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/53f1a6462dbe71fad36ce054caf5e1b725b740fd", + "reference": "53f1a6462dbe71fad36ce054caf5e1b725b740fd", "shasum": "" }, "require": { @@ -19672,9 +19759,9 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.18" + "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.20" }, - "time": "2026-05-18T14:51:49+00:00" + "time": "2026-06-16T09:17:35+00:00" }, { "name": "phpunit/php-code-coverage", @@ -20135,21 +20222,21 @@ }, { "name": "rector/rector", - "version": "2.4.4", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "4661c582a20f03df585d2e3fdc4af1b83d67a091" + "reference": "7526beadb3da0b88cfa9e8290d74944799d1f3a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/4661c582a20f03df585d2e3fdc4af1b83d67a091", - "reference": "4661c582a20f03df585d2e3fdc4af1b83d67a091", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/7526beadb3da0b88cfa9e8290d74944799d1f3a4", + "reference": "7526beadb3da0b88cfa9e8290d74944799d1f3a4", "shasum": "" }, "require": { "php": "^7.4|^8.0", - "phpstan/phpstan": "^2.1.48" + "phpstan/phpstan": "^2.2.2" }, "conflict": { "rector/rector-doctrine": "*", @@ -20183,7 +20270,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/2.4.4" + "source": "https://github.com/rectorphp/rector/tree/2.5.0" }, "funding": [ { @@ -20191,7 +20278,7 @@ "type": "github" } ], - "time": "2026-05-20T19:30:21+00:00" + "time": "2026-06-20T17:02:36+00:00" }, { "name": "roave/security-advisories", @@ -20199,18 +20286,18 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "f9f1a88a11437cacd4d26b4953416af5c5425389" + "reference": "bfdbd26a02c71b3b00586f056c8cf5efea22a27a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/f9f1a88a11437cacd4d26b4953416af5c5425389", - "reference": "f9f1a88a11437cacd4d26b4953416af5c5425389", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/bfdbd26a02c71b3b00586f056c8cf5efea22a27a", + "reference": "bfdbd26a02c71b3b00586f056c8cf5efea22a27a", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", "adaptcms/adaptcms": "<=1.3", - "admidio/admidio": "<=5.0.8", + "admidio/admidio": "<=5.0.9", "adodb/adodb-php": "<=5.22.9", "aheinze/cockpit": "<2.2", "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", @@ -20259,7 +20346,7 @@ "auth0/login": "<=7.20", "auth0/symfony": "<=5.7", "auth0/wordpress": "<=5.5", - "automad/automad": "<2.0.0.0-alpha5", + "automad/automad": "<=2.0.0.0-beta27", "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", "aws/aws-sdk-php": "<=3.371.3", @@ -20267,7 +20354,7 @@ "azuracast/azuracast": "<=0.23.5", "b13/seo_basics": "<0.8.2", "backdrop/backdrop": "<=1.32", - "backpack/crud": "<3.4.9", + "backpack/crud": "<4.0.63|>=4.1,<4.1.69|>=5,<5.0.13", "backpack/filemanager": "<2.0.2|>=3,<3.0.9", "bacula-web/bacula-web": "<9.7.1", "badaso/core": "<=2.9.11", @@ -20284,6 +20371,7 @@ "bedita/bedita": "<4", "bednee/cooluri": "<1.0.30", "bigfork/silverstripe-form-capture": ">=3,<3.1.1", + "billabear/billabear": "<=2025.01.03", "billz/raspap-webgui": "<3.3.6", "binarytorch/larecipe": "<2.8.1", "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", @@ -20304,13 +20392,14 @@ "bytefury/crater": "<6.0.2", "cachethq/cachet": "<2.5.1", "cadmium-org/cadmium-cms": "<=0.4.9", + "cakephp/authentication": "<3.3.6|>=4,<4.1.1", "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10|>=5.2.10,<5.2.12|==5.3", "cakephp/database": ">=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", "cardgate/magento2": "<2.0.33", "cardgate/woocommerce": "<=3.1.15", - "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", + "cart2quote/module-quotation": ">=4.1.6,<4.4.6|>=5,<5.4.4", "cart2quote/module-quotation-encoded": ">=4.1.6,<=4.4.5|>=5,<5.4.4", - "cartalyst/sentry": "<=2.1.6", + "cartalyst/sentry": "<2.1.7", "catfan/medoo": "<1.7.5", "causal/oidc": "<4", "cecil/cecil": "<7.47.1", @@ -20328,7 +20417,7 @@ "code16/sharp": "<9.22", "codeception/codeception": "<3.1.3|>=4,<4.1.22", "codeigniter/framework": "<3.1.10", - "codeigniter4/framework": "<4.6.2", + "codeigniter4/framework": "<4.7.2", "codeigniter4/shield": "<1.0.0.0-beta8", "codiad/codiad": "<=2.8.4", "codingms/additional-tca": ">=1.7,<1.15.17|>=1.16,<1.16.9", @@ -20348,12 +20437,13 @@ "coreshop/core-shop": "<4.1.9|==5", "corveda/phpsandbox": "<1.3.5", "cosenary/instagram": "<=2.3", + "cotonti/cotonti": "<=1", "couleurcitron/tarteaucitron-wp": "<0.3", "cpsit/typo3-mailqueue": "<0.4.5|>=0.5,<0.5.2", "craftcms/aws-s3": ">=2.0.2,<=2.2.4", "craftcms/azure-blob": ">=2.0.0.0-beta1,<=2.1", - "craftcms/cms": "<4.17.12|>=5,<5.9.18", - "craftcms/commerce": ">=4,<4.11|>=5,<5.6", + "craftcms/cms": "<4.18|>=5,<5.10", + "craftcms/commerce": ">=4,<=4.11.1|>=5,<=5.6.4", "craftcms/composer": ">=4.0.0.0-RC1-dev,<=4.10|>=5.0.0.0-RC1-dev,<=5.5.1", "craftcms/craft": ">=3.5,<=4.16.17|>=5.0.0.0-RC1-dev,<=5.8.21", "craftcms/google-cloud": ">=2.0.0.0-beta1,<=2.2", @@ -20408,7 +20498,7 @@ "drupal/commerce_alphabank_redirect": "<1.0.3", "drupal/commerce_eurobank_redirect": "<2.1.1", "drupal/config_split": "<1.10|>=2,<2.0.2", - "drupal/core": ">=6,<6.38|>=7,<7.103|>=8,<10.4.9|>=10.5,<10.5.6|>=11,<11.1.9|>=11.2,<11.2.8", + "drupal/core": ">=6,<6.38|>=7,<7.103|>=8,<10.5.10|>=10.6,<10.6.9|>=11,<11.2.12|>=11.3,<11.3.10", "drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", "drupal/currency": "<3.5", "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", @@ -20435,6 +20525,7 @@ "drupal/umami_analytics": "<1.0.1", "duncanmcclean/guest-entries": "<3.1.2", "dweeves/magmi": "<=0.7.24", + "easycorp/easyadmin-bundle": ">=4,<4.29.10|>=5,<5.0.13", "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.3.1", "ecodev/newsletter": "<=4", "ectouch/ectouch": "<=2.7.2", @@ -20478,10 +20569,11 @@ "feehi/cms": "<=2.1.1", "feehi/feehicms": "<=2.1.1", "fenom/fenom": "<=2.12.1", - "filament/actions": ">=3.2,<3.2.123", + "filament/actions": ">=3.2,<3.2.123|>=4,<=4.11.3|>=5,<=5.6.3", "filament/filament": ">=4,<4.3.1", + "filament/forms": ">=3,<=3.3.52", "filament/infolists": ">=3,<3.2.115", - "filament/tables": ">=3,<3.2.115|>=4,<4.8.5|>=5,<5.3.5", + "filament/tables": ">=3,<=3.3.50|>=4,<4.8.5|>=5,<5.3.5", "filegator/filegator": "<7.8", "filp/whoops": "<2.1.13", "fineuploader/php-traditional-server": "<=1.2.2", @@ -20518,19 +20610,19 @@ "friendsoftypo3/tt-address": "<8.1.2|>=9,<9.1.1|>=10,<10.0.1", "froala/wysiwyg-editor": "<=4.3", "frosh/adminer-platform": "<2.2.1", - "froxlor/froxlor": "<2.3.6", + "froxlor/froxlor": "<2.3.7", "frozennode/administrator": "<=5.0.12", "fuel/core": "<1.8.1", "funadmin/funadmin": "<=7.1.0.0-RC6", "gaoming13/wechat-php-sdk": "<=1.10.2", "genix/cms": "<=1.1.11", - "georgringer/news": "<11.4.4|>=12,<12.3.2|>=13,<13.0.2|>=14,<14.0.3", + "georgringer/news": "<10.0.4|>=11,<11.4.4|>=12,<12.3.2|>=13,<13.0.2|>=14,<14.0.3", "geshi/geshi": "<=1.0.9.1", "getformwork/formwork": "<=2.3.3", - "getgrav/grav": "<=2.0.0.0-RC1", + "getgrav/grav": "<=2.0.0.0-RC8", "getgrav/grav-plugin-api": "<1.0.0.0-beta15", "getgrav/grav-plugin-form": "<9.1", - "getkirby/cms": "<4.9|>=5,<5.4", + "getkirby/cms": "<=4.9.3|>=5,<=5.4.3", "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", @@ -20545,11 +20637,12 @@ "gp247/core": "<1.1.24", "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", - "grumpydictator/firefly-iii": "<6.1.17|>=6.4.23,<=6.5", + "grumpydictator/firefly-iii": "<=6.6.2", "gugoan/economizzer": "<=0.9.0.0-beta1", - "guzzlehttp/guzzle": "<6.5.8|>=7,<7.4.5", + "guzzlehttp/guzzle": "<7.12.1", + "guzzlehttp/guzzle-services": "<1.5.4", "guzzlehttp/oauth-subscriber": "<0.8.1", - "guzzlehttp/psr7": "<1.9.1|>=2,<2.4.5", + "guzzlehttp/psr7": "<2.12.1", "haffner/jh_captcha": "<=2.1.3|>=3,<=3.0.2", "handcraftedinthealps/goodby-csv": "<1.4.3", "harvesthq/chosen": "<1.8.7", @@ -20578,6 +20671,7 @@ "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<6.18.31|>=7,<7.22.4", "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "illuminate/mail": ">=9,<12.60|>=13,<13.10", "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", "imdbphp/imdbphp": "<=5.1.1", "impresscms/impresscms": "<=1.4.5", @@ -20591,7 +20685,7 @@ "inter-mediator/inter-mediator": "==5.5", "intercom/intercom-php": "==5.0.2", "invoiceninja/invoiceninja": "<5.13.4", - "ipl/web": "<=0.13", + "ipl/web": "<=0.10.2|>=0.11,<=0.13", "islandora/crayfish": "<4.1", "islandora/islandora": ">=2,<2.4.1", "ivankristianto/phpwhois": "<=4.3", @@ -20603,6 +20697,7 @@ "jasig/phpcas": "<1.3.3", "jbartels/wec-map": "<3.0.3", "jcbrand/converse.js": "<3.3.3", + "jleehr/canto-saas-api": "<=2", "joedolson/my-calendar": "<3.7.7", "joelbutcher/socialstream": "<5.6|>=6,<6.2", "johnbillion/query-monitor": "<3.20.4", @@ -20646,7 +20741,7 @@ "lara-zeus/artemis": ">=1,<=1.0.6", "lara-zeus/dynamic-dashboard": ">=3,<=3.0.1", "laravel/fortify": "<1.11.1", - "laravel/framework": "<10.48.29|>=11,<11.44.1|>=12,<12.1.1", + "laravel/framework": "<12.61.1|>=13,<13.12", "laravel/laravel": ">=5.4,<5.4.22", "laravel/passport": ">=13,<13.7.1", "laravel/pulse": "<1.3.1", @@ -20737,6 +20832,7 @@ "movim/moxl": ">=0.8,<=0.10", "movingbytes/social-network": "<=1.2.1", "mpdf/mpdf": "<=7.1.7", + "mtdowling/jmespath.php": "<2.9.1", "munkireport/comment": "<4", "munkireport/managedinstalls": "<2.6", "munkireport/munki_facts": "<1.5", @@ -20818,11 +20914,12 @@ "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", "personnummer/personnummer": "<3.0.2", "ph7software/ph7builder": "<=17.9.1", - "phanan/koel": "<5.1.4", + "phanan/koel": "<=9.3.4", + "pheditor/pheditor": ">=2.0.1,<=2.0.3", "phenx/php-svg-lib": "<0.5.2", "php-censor/php-censor": "<2.0.13|>=2.1,<2.1.5", "php-mod/curl": "<2.3.2", - "phpbb/phpbb": "<3.3.11", + "phpbb/phpbb": "<3.3.16|==4.0.0.0-alpha1", "phpems/phpems": ">=6,<=6.1.3", "phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7", "phpmailer/phpmailer": "<6.5", @@ -20832,9 +20929,9 @@ "phpoffice/common": "<0.2.9", "phpoffice/math": "<=0.2", "phpoffice/phpexcel": "<=1.8.2", - "phpoffice/phpspreadsheet": "<=1.30.3|>=2,<=2.1.15|>=2.2,<=2.4.4|>=3,<=3.10.4|>=4,<=5.6", + "phpoffice/phpspreadsheet": "<=1.30.4|>=2,<=2.1.15|>=2.2,<=2.4.4|>=3,<=3.10.4|>=4,<=5.6", "phppgadmin/phppgadmin": "<=7.13", - "phpseclib/phpseclib": "<=2.0.53|>=3,<=3.0.51", + "phpseclib/phpseclib": "<=2.0.54|>=3,<=3.0.53", "phpservermon/phpservermon": "<3.6", "phpsysinfo/phpsysinfo": "<3.4.3", "phpunit/phpunit": "<8.5.52|>=9,<9.6.33|>=10,<10.5.62|>=11,<11.5.50|>=12,<12.5.8|>=12.5.21,<12.5.22|>=13.1.5,<13.1.6", @@ -20843,14 +20940,14 @@ "phpxmlrpc/phpxmlrpc": "<4.9.2", "phraseanet/phraseanet": "==4.0.3", "pi/pi": "<=2.5", - "pimcore/admin-ui-classic-bundle": "<=1.7.15|>=2.0.0.0-RC1-dev,<=2.2.2", + "pimcore/admin-ui-classic-bundle": "<=2.3.5", "pimcore/customer-management-framework-bundle": "<4.2.1", "pimcore/data-hub": "<1.2.4", "pimcore/data-importer": "<1.8.9|>=1.9,<1.9.3", "pimcore/demo": "<10.3", "pimcore/ecommerce-framework-bundle": "<1.0.10", "pimcore/perspective-editor": "<1.5.1", - "pimcore/pimcore": "<=11.5.14.1|>=12,<12.3.3|==12.3.3", + "pimcore/pimcore": "<=12.3.8", "pimcore/web2print-tools-bundle": "<=5.2.1|>=6.0.0.0-RC1-dev,<=6.1", "piwik/piwik": "<1.11", "pixelfed/pixelfed": "<0.12.5", @@ -20858,6 +20955,7 @@ "pocketmine/bedrock-protocol": "<8.0.2", "pocketmine/pocketmine-mp": "<5.42.1", "pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1", + "poweradmin/poweradmin": "<4.2.4|>=4.3,<4.3.3", "pressbooks/pressbooks": "<5.18", "prestashop/autoupgrade": ">=4,<4.10.1", "prestashop/blockreassurance": "<=5.1.3", @@ -20873,10 +20971,10 @@ "prestashop/ps_linklist": "<3.1", "privatebin/privatebin": "<1.4|>=1.5,<1.7.4|>=1.7.7,<2.0.3", "processwire/processwire": "<=3.0.255", - "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7", - "propel/propel1": ">=1,<=1.7.1", + "propel/propel": ">=2.0.0.0-alpha1,<2.0.0.0-alpha8", + "propel/propel1": ">=1,<1.7.2", "psy/psysh": "<=0.11.22|>=0.12,<=0.12.18", - "pterodactyl/panel": "<1.12.1", + "pterodactyl/panel": "<1.12.3", "ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2", "ptrofimov/beanstalk_console": "<1.7.14", "pubnub/pubnub": "<6.1", @@ -20924,10 +21022,10 @@ "sheng/yiicms": "<1.2.1", "shopper/cart": "<2.8", "shopper/framework": "<2.8", - "shopware/core": "<6.6.10.15-dev|>=6.7,<6.7.8.1-dev", - "shopware/platform": "<6.6.10.15-dev|>=6.7,<6.7.8.1-dev", + "shopware/core": "<6.6.10.18-dev|>=6.7,<6.7.10.1-dev", + "shopware/platform": "<6.6.10.18-dev|>=6.7,<6.7.10.1-dev", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<=5.7.17|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.6.1-dev", + "shopware/shopware": "<=6.3.5.2|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.6.1-dev", "shopware/storefront": "<6.6.10.10-dev|>=6.7,<6.7.5.1-dev", "shopxo/shopxo": "<=6.4", "showdoc/showdoc": "<3.8.1", @@ -20937,7 +21035,7 @@ "silverstripe/assets": "<2.4.5|>=3,<3.1.3", "silverstripe/cms": "<4.11.3", "silverstripe/comments": ">=1.3,<3.1.1", - "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", + "silverstripe/forum": "<0.6.2|>=0.7,<0.7.4", "silverstripe/framework": "<5.3.23", "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.8.2|>=4,<4.3.7|>=5,<5.1.3", "silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1", @@ -20977,9 +21075,11 @@ "soosyze/soosyze": "<=2", "spatie/browsershot": "<5.0.5", "spatie/image-optimizer": "<1.7.3", + "spatie/schema-org": ">=3.23.1,<3.23.2|>=4,<4.0.2", "spencer14420/sp-php-email-handler": "<1", "spipu/html2pdf": "<5.2.8", "spiral/roadrunner": "<2025.1", + "spomky-labs/otphp": "<11.4.3", "spoon/library": "<1.4.1", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", @@ -21013,51 +21113,55 @@ "symbiote/silverstripe-seed": "<6.0.3", "symbiote/silverstripe-versionedfiles": "<=2.0.3", "symfont/process": ">=0", - "symfony/cache": ">=2,<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/cache": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", - "symfony/dom-crawler": ">=2,<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/dom-crawler": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4", - "symfony/html-sanitizer": ">=6.1,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", - "symfony/http-client": ">=4.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", - "symfony/http-foundation": "<5.4.50|>=6,<6.4.29|>=7,<7.3.7", + "symfony/html-sanitizer": ">=6.1,<6.4.41|>=7,<7.4.13|>=8,<8.0.13", + "symfony/http-client": ">=4.3,<5.4.53|>=6,<6.4.15|>=7,<7.1.8", + "symfony/http-foundation": "<5.4.50|>=6,<6.4.41|>=7,<7.4.13|>=8,<8.0.13", "symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6|>=7.4,<7.4.12|>=8,<8.0.12", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", "symfony/json-path": ">=7.3,<7.4.12|>=8,<8.0.12", "symfony/lox24-notifier": ">=7.1,<7.4.12|>=8,<8.0.12", - "symfony/mailer": ">=2,<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/mailer": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", "symfony/mailjet-mailer": ">=6.4,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/mailomat-mailer": ">=7.2,<7.4.13|>=8,<8.0.13", "symfony/mailtrap-mailer": ">=7.2,<7.4.12|>=8,<8.0.12", "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", - "symfony/mime": ">=2,<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", - "symfony/monolog-bridge": ">=2,<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/mime": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/monolog-bridge": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", - "symfony/polyfill": ">=1,<1.10", + "symfony/polyfill": ">=1,<1.10|>=1.17.1,<1.38.1", + "symfony/polyfill-intl-idn": ">=1.17.1,<1.38.1", "symfony/polyfill-php55": ">=1,<1.10", "symfony/process": "<5.4.51|>=6,<6.4.33|>=7,<7.1.7|>=7.3,<7.3.11|>=7.4,<7.4.5|>=8,<8.0.5", "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", - "symfony/routing": ">=2,<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/routing": "<5.4.53|>=6,<6.4.41|>=7,<7.4.13|>=8,<8.0.13", "symfony/runtime": ">=5.3,<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", "symfony/security": ">=2,<2.7.51|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.8", "symfony/security-bundle": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.4.10|>=7,<7.0.10|>=7.1,<7.1.3", "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", - "symfony/security-http": ">=2,<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/security-http": "<5.4.53|>=6,<6.4.41|>=7,<7.4.13|>=8,<8.0.13", "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", - "symfony/symfony": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/symfony": "<5.4.53|>=6,<6.4.41|>=7,<7.4.13|>=8,<8.0.13", "symfony/translation": ">=2,<2.0.17", "symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8|>=6.4.24,<6.4.40", "symfony/twilio-notifier": ">=6.4,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", - "symfony/ux-autocomplete": "<2.11.2", - "symfony/ux-live-component": "<2.25.1", + "symfony/ux-autocomplete": "<2.36|>=3,<3.1", + "symfony/ux-icons": ">=2.17,<2.36.1|>=3,<3.2", + "symfony/ux-live-component": "<2.36|>=3,<3.1", + "symfony/ux-toolkit": ">=2.32,<2.36.1|>=3,<3.2", "symfony/ux-twig-component": "<2.25.1", "symfony/validator": "<5.4.43|>=6,<6.4.11|>=7,<7.1.4", "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4|>=7.2.9,<7.4.12|>=8,<8.0.12", "symfony/webhook": ">=6.3,<6.3.8", - "symfony/yaml": ">=2,<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/yaml": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", "symphonycms/symphony-2": "<2.6.4", "t3/dce": "<0.11.5|>=2.2,<2.6.2", "t3g/svg-sanitizer": "<1.0.3", @@ -21074,7 +21178,7 @@ "thorsten/phpmyfaq": "<4.1.3", "tikiwiki/tiki-manager": "<=17.1", "timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1", - "tinymce/tinymce": "<7.2", + "tinymce/tinymce": "<7.9.3|>=8,<8.5.1", "tinymighty/wiki-seo": "<1.2.2", "titon/framework": "<9.9.99", "tltneon/lgsl": "<7", @@ -21092,25 +21196,26 @@ "twig/cssinliner-extra": "<3.26", "twig/intl-extra": "<3.26", "twig/markdown-extra": "<3.26", - "twig/twig": "<3.26", + "twig/twig": "<3.27", "typicms/core": "<16.1.7", "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", - "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1|==14.2", + "typo3/cms-backend": "<10.4.57|>=11,<11.5.51|>=12,<12.4.46|>=13,<13.4.31|>=14,<14.3.3", "typo3/cms-belog": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", "typo3/cms-beuser": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", - "typo3/cms-core": "<=8.7.56|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", + "typo3/cms-core": "<10.4.57|>=11,<11.5.51|>=12,<12.4.46|>=13,<13.4.31|>=14,<14.3.3", "typo3/cms-dashboard": ">=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1", "typo3/cms-extensionmanager": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", "typo3/cms-felogin": ">=4.2,<4.2.3", + "typo3/cms-filelist": ">=11,<11.5.51|>=12,<12.4.46|>=13,<13.4.31|>=14,<14.3.3", "typo3/cms-fluid": "<4.3.4|>=4.4,<4.4.1", - "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-form": "<10.4.57|>=11,<11.5.51|>=12,<12.4.46|>=13,<13.4.31|>=14,<14.3.3", "typo3/cms-frontend": "<4.3.9|>=4.4,<4.4.5", - "typo3/cms-indexed-search": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-indexed-search": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<13.4.31|>=14,<14.3.3", "typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8|==13.4.2", "typo3/cms-lowlevel": ">=11,<=11.5.41", "typo3/cms-recordlist": ">=11,<11.5.48", - "typo3/cms-recycler": ">=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", + "typo3/cms-recycler": "<10.4.57|>=11,<11.5.51|>=12,<12.4.46|>=13,<13.4.31|>=14,<14.3.3", "typo3/cms-redirects": ">=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", "typo3/cms-rte-ckeditor": ">=9.5,<9.5.42|>=10,<10.4.39|>=11,<11.5.30", "typo3/cms-scheduler": ">=11,<=11.5.41", @@ -21118,7 +21223,7 @@ "typo3/cms-webhooks": ">=12,<=12.4.30|>=13,<=13.4.11", "typo3/cms-workspaces": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", - "typo3/html-sanitizer": ">=1,<=1.5.2|>=2,<=2.1.3", + "typo3/html-sanitizer": "<2.3.2", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", "typo3/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", @@ -21134,7 +21239,7 @@ "uvdesk/core-framework": "<=1.1.1", "vanilla/safecurl": "<0.9.2", "verbb/comments": "<1.5.5", - "verbb/formie": "<2.2.20|>=3.0.0.0-beta1,<3.1.24", + "verbb/formie": "<2.2.21|>=3,<3.1.26", "verbb/image-resizer": "<2.0.9", "verbb/knock-knock": "<1.2.8", "verot/class.upload.php": "<=2.1.6", @@ -21152,6 +21257,9 @@ "web-auth/webauthn-lib": ">=4.5,<4.9|>=5.2,<5.2.4", "web-auth/webauthn-symfony-bundle": ">=5.2,<5.2.4", "web-feet/coastercms": "==5.5", + "web-token/jwt-experimental": "<=4.1.6", + "web-token/jwt-framework": "<=4.2.99", + "web-token/jwt-library": "<3.4.10|>=4,<4.0.7|>=4.1,<4.1.7", "web-tp3/wec_map": "<3.0.3", "webbuilders-group/silverstripe-kapost-bridge": "<0.4", "webcoast/deferred-image-processing": "<1.0.2", @@ -21277,7 +21385,7 @@ "type": "tidelift" } ], - "time": "2026-05-24T20:23:40+00:00" + "time": "2026-06-19T21:28:22+00:00" }, { "name": "sebastian/cli-parser", @@ -21391,7 +21499,6 @@ "type": "github" } ], - "abandoned": true, "time": "2025-03-19T07:56:08+00:00" }, { @@ -21448,7 +21555,6 @@ "type": "github" } ], - "abandoned": true, "time": "2024-07-03T04:45:54+00:00" }, { @@ -22653,16 +22759,16 @@ }, { "name": "symfony/web-profiler-bundle", - "version": "v7.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "558fe81a383302318d9b92f7661deb731153c86e" + "reference": "153076bb3f0690fff0e95e55cc06358b22f236a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/558fe81a383302318d9b92f7661deb731153c86e", - "reference": "558fe81a383302318d9b92f7661deb731153c86e", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/153076bb3f0690fff0e95e55cc06358b22f236a5", + "reference": "153076bb3f0690fff0e95e55cc06358b22f236a5", "shasum": "" }, "require": { @@ -22719,7 +22825,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.4.12" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.4.13" }, "funding": [ { @@ -22739,7 +22845,7 @@ "type": "tidelift" } ], - "time": "2026-05-20T07:20:23+00:00" + "time": "2026-05-23T16:05:06+00:00" }, { "name": "theseer/tokenizer", diff --git a/config/packages/monolog.yaml b/config/packages/monolog.yaml index 387d71ad..17f8f4c2 100644 --- a/config/packages/monolog.yaml +++ b/config/packages/monolog.yaml @@ -51,6 +51,7 @@ when@prod: type: stream channels: [deprecation] path: "%kernel.logs_dir%/%kernel.environment%_deprecations.log" + level: "%env(DEPRECATION_LOG_LEVEL)%" when@docker: monolog: @@ -75,3 +76,4 @@ when@docker: type: stream channels: [deprecation] path: "%kernel.logs_dir%/%kernel.environment%_deprecations.log" + level: "%env(DEPRECATION_LOG_LEVEL)%" diff --git a/config/reference.php b/config/reference.php index 38a275d6..71b08412 100644 --- a/config/reference.php +++ b/config/reference.php @@ -653,7 +653,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param; * time_based_uuid_node?: scalar|Param|null, * }, * html_sanitizer?: bool|array{ // HtmlSanitizer configuration - * enabled?: bool|Param, // Default: false + * enabled?: bool|Param, // Default: true * sanitizers?: array> .env.local + ``` + For Docker, pass it in the `environment` section of your `docker-compose.yaml`: + ```yaml + environment: + - APP_SECRET= + ``` + Part-DB displays a warning on the homepage (visible to administrators only) as long as the default value is in use. * `SHOW_PART_IMAGE_OVERLAY`: Set to 0 to disable the part image overlay, which appears if you hover over an image in the part image gallery * `IPN_SUGGEST_REGEX`: A global regular expression, that part IPNs have to fulfill. Enforce your own format for your users. @@ -268,9 +279,13 @@ See the [information providers]({% link usage/information_provider_system.md %}) * `BANNER`: You can configure the text that should be shown as the banner on the homepage. Useful especially for docker containers. In all other applications you can just change the `config/banner.md` file. * `DISABLE_YEAR2038_BUG_CHECK` (env only): If set to `1`, the year 2038 bug check is disabled on 32-bit systems, and dates after -2038 are no longer forbidden. However this will lead to 500 error messages when rendering dates after 2038 as all current +2038 are no longer forbidden. However, this will lead to 500 error messages when rendering dates after 2038 as all current 32-bit PHP versions can not format these dates correctly. This setting is for the case that future PHP versions will handle this correctly on 32-bit systems. 64-bit systems are not affected by this bug, and the check is always disabled. +* `DEPRECATION_LOG_LEVEL` (default `emergency`) (env only): In the `prod` and `docker` environments, PHP/Symfony + deprecation notices are written to their own `var/log/_deprecations.log` file. This option sets the minimum log + level a deprecation notice must have to be written there. Since deprecation notices are logged with level `info`, + the default value of `emergency` effectively disables this dedicated deprecation log. Set it to `debug` to enable it. ## Banner diff --git a/docs/installation/installation_docker.md b/docs/installation/installation_docker.md index ab07e010..97b1e662 100644 --- a/docs/installation/installation_docker.md +++ b/docs/installation/installation_docker.md @@ -47,7 +47,10 @@ services: - DATABASE_URL=sqlite:///%kernel.project_dir%/var/db/app.db # In docker env logs will be redirected to stderr - APP_ENV=docker - + # Secret key used to sign cookies and CSRF tokens. MUST be changed to a unique random value before going live! + # Generate one with: openssl rand -hex 32 + - APP_SECRET=CHANGE_ME + # Uncomment this, if you want to use the automatic database migration feature. With this you have you do not have to # run the doctrine:migrations:migrate commands on installation or upgrade. A database backup is written to the uploads/ # folder (under .automigration-backup), so you can restore it, if the migration fails. @@ -89,7 +92,11 @@ services: 4. Customize the settings by changing the environment variables (or adding new ones). See [Configuration]({% link configuration.md %}) for more information. -5. Inside the folder, run +5. Make sure to change the `APP_SECRET` variable to a unique random value (32 characters). You can generate one with the following command: +```bash +openssl rand -hex 32 +``` +6. Inside the folder, run ```bash docker-compose up -d @@ -100,7 +107,7 @@ services: > Otherwise Part-DB console might use the wrong configuration to execute commands. -6. Create the initial database with +7. Create the initial database with ```bash docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate @@ -108,7 +115,7 @@ docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate and watch for the password output -6. Part-DB is available under `http://localhost:8080` and you can log in with the username `admin` and the password shown +8. Part-DB is available under `http://localhost:8080` and you can log in with the username `admin` and the password shown before The docker image uses a SQLite database and all data (database, uploads, and other media) is put into folders relative to @@ -121,6 +128,7 @@ If you want to use MySQL as a database, you can use the following docker-compose {: .warning } > You have to replace the values for MYSQL_ROOT_PASSWORD and MYSQL_PASSWORD with your own passwords!! > You have to change MYSQL_PASSWORD in the database section and for the DATABASE_URL in the partdb section. +> Generate a random string for APP_SECRET. ```yaml version: '3.3' @@ -142,14 +150,19 @@ services: environment: # Replace SECRET_USER_PASSWORD with the value of MYSQL_PASSWORD from below - DATABASE_URL=mysql://partdb:SECRET_USER_PASSWORD@database:3306/partdb + + # Secret key used to sign cookies. MUST be changed to a unique random value before going live! + # Generate one with: openssl rand -hex 32 + - APP_SECRET=CHANGE_ME + # In docker env logs will be redirected to stderr - APP_ENV=docker - - # Uncomment this, if you want to use the automatic database migration feature. With this you do not have to - # run the doctrine:migrations:migrate commands on installation or upgrade. A database backup is written to the uploads/ - # folder (under .automigration-backup), so you can restore it, if the migration fails. - # This feature is currently experimental, so use it at your own risk! - # - DB_AUTOMIGRATE=true + + # Uncomment this, if you want to use the automatic database migration feature. With this you do not have to + # run the doctrine:migrations:migrate commands on installation or upgrade. A database backup is written to the uploads/ + # folder (under .automigration-backup), so you can restore it, if the migration fails. + # This feature is currently experimental, so use it at your own risk! + # - DB_AUTOMIGRATE=true # You can configure Part-DB using the webUI or environment variables # However you can add any other environment configuration you want here diff --git a/docs/installation/installation_guide-debian.md b/docs/installation/installation_guide-debian.md index b3c61126..2915adbb 100644 --- a/docs/installation/installation_guide-debian.md +++ b/docs/installation/installation_guide-debian.md @@ -136,6 +136,15 @@ cp .env .env.local In your `.env.local` you can configure Part-DB according to your wishes and overwrite web interface settings. A full list of configuration options can be found [here](../configuration.md). +{: .important } +> **Change `APP_SECRET` before going live.** The default value shipped with Part-DB is publicly known and must not be +> used in production — it would allow an attacker to forge signed cookies and bypass CSRF protection. +> Generate a new value and add it to your `.env.local`: +> ```bash +> echo "APP_SECRET=$(openssl rand -hex 32)" >> .env.local +> ``` +> or edit the file with a text editor and add a new value for `APP_SECRET` (you can generate a random value with `openssl rand -hex 32`). + Please check that the configured base currency matches your mainly used currency, as this can not be changed after creating price information. @@ -223,7 +232,7 @@ sudo ln -s /etc/apache2/sites-available/partdb.conf /etc/apache2/sites-enabled/p Configure apache to show pretty URL paths for Part-DB (`/label/dialog` instead of `/index.php/label/dialog`): ```bash -sudo a2enmod rewrite +sudo a2enmod rewrite headers ``` If you want to access Part-DB via the IP-Address of the server, instead of the domain name, you have to remove the diff --git a/docs/installation/nginx.md b/docs/installation/nginx.md index db209d92..1ae1d32c 100644 --- a/docs/installation/nginx.md +++ b/docs/installation/nginx.md @@ -36,6 +36,10 @@ server { root /var/www/partdb/public; location / { + # Headers are set here for static assets. PHP responses are served via the index.php location + # below and inherit neither of these headers, so Nelmio's PHP-side CSP is unaffected. + add_header Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; sandbox;" always; + add_header X-Content-Type-Options "nosniff" always; try_files $uri /index.php$is_args$args; } @@ -52,10 +56,17 @@ server { location ~ \.php$ { return 404; } - - # Set Content-Security-Policy for svg files, to block embedded javascript in there + + # Prevent PHP execution in the media upload directory + location ~* ^/media/.*\.(php[3-8]?|phar|phtml|pht|phps)$ { + return 403; + } + + # SVG files get a slightly different CSP because they can embed resources and must not be framed. + # This regex location takes precedence over location /, so headers must be repeated here. 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';"; + add_header Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'; sandbox;" always; + add_header X-Content-Type-Options "nosniff" always; } error_log /var/log/nginx/parts.error.log; diff --git a/public/.htaccess b/public/.htaccess index a13baeee..0493298f 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -119,9 +119,14 @@ DirectoryIndex index.php -# Set Content-Security-Policy for svg files (and compressed variants), to block embedded javascript in there + # Set a strict CSP for all static assets not handled by PHP. + # PHP responses already carry their own CSP via NelmioSecurityBundle, so setifempty leaves those untouched. + Header always setifempty Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; sandbox;" + Header always setifempty X-Content-Type-Options "nosniff" + + # SVG files get a slightly different CSP because they can embed resources and must not be framed. - Header set Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';" + Header always set Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'; sandbox;" - \ No newline at end of file + diff --git a/public/kicad/footprints.txt b/public/kicad/footprints.txt index 779a7751..a0b41279 100644 --- a/public/kicad/footprints.txt +++ b/public/kicad/footprints.txt @@ -1,4 +1,4 @@ -# Generated on Mon May 25 06:41:46 UTC 2026 +# Generated on Mon Jun 15 07:28:00 UTC 2026 # This file contains all footprints available in the offical KiCAD library Audio_Module:Reverb_BTDR-1H Audio_Module:Reverb_BTDR-1V @@ -12012,7 +12012,6 @@ Package_DFN_QFN:WDFN-8-1EP_4x3mm_P0.65mm_EP2.4x1.8mm_ThermalVias Package_DFN_QFN:WDFN-8-1EP_6x5mm_P1.27mm_EP3.4x4mm Package_DFN_QFN:WDFN-8-1EP_8x6mm_P1.27mm_EP6x4.8mm Package_DFN_QFN:WDFN-8-1EP_8x6mm_P1.27mm_EP6x4.8mm_ThermalVias -Package_DFN_QFN:WDFN-8_2x2mm_P0.5mm Package_DFN_QFN:WFDFPN-8-1EP_3x2mm_P0.5mm_EP1.25x1.35mm Package_DFN_QFN:WQFN-14-1EP_2.5x2.5mm_P0.5mm_EP1.45x1.45mm Package_DFN_QFN:WQFN-14-1EP_2.5x2.5mm_P0.5mm_EP1.45x1.45mm_ThermalVias @@ -12358,7 +12357,6 @@ Package_DirectFET:DirectFET_SQ Package_DirectFET:DirectFET_ST Package_LCC:Analog_LCC-8_5x5mm_P1.27mm Package_LCC:MO047AD_PLCC-52_19.1x19.1mm_P1.27mm -Package_LCC:PLCC-20 Package_LCC:PLCC-20_9.0x9.0mm_P1.27mm Package_LCC:PLCC-20_SMD-Socket Package_LCC:PLCC-20_THT-Socket diff --git a/public/kicad/symbols.txt b/public/kicad/symbols.txt index 46a15ee1..b07fbf28 100644 --- a/public/kicad/symbols.txt +++ b/public/kicad/symbols.txt @@ -1,4 +1,4 @@ -# Generated on Mon May 25 06:42:27 UTC 2026 +# Generated on Mon Jun 15 07:28:38 UTC 2026 # This file contains all symbols available in the offical KiCAD library 4xxx:14528 4xxx:14529 @@ -2255,11 +2255,11 @@ Battery_Management:BQ76200PW Battery_Management:BQ76920PW Battery_Management:BQ76930DBT Battery_Management:BQ76940DBT -Battery_Management:BQ7695201PFBR -Battery_Management:BQ7695202PFBR -Battery_Management:BQ7695203PFBR -Battery_Management:BQ7695204PFBR -Battery_Management:BQ76952PFBR +Battery_Management:BQ7695201PFB +Battery_Management:BQ7695202PFB +Battery_Management:BQ7695203PFB +Battery_Management:BQ7695204PFB +Battery_Management:BQ76952PFB Battery_Management:BQ78350DBT Battery_Management:BQ78350DBT-R1 Battery_Management:CN3063 @@ -5300,21 +5300,39 @@ Converter_DCDC:TMR-4812 Converter_DCDC:TMR-4821 Converter_DCDC:TMR-4822 Converter_DCDC:TMR-4823 +Converter_DCDC:TMR10-1211WI +Converter_DCDC:TMR10-1212WI +Converter_DCDC:TMR10-1213WI +Converter_DCDC:TMR10-1215WI +Converter_DCDC:TMR10-1222WI +Converter_DCDC:TMR10-1223WI Converter_DCDC:TMR10-2410WIR +Converter_DCDC:TMR10-2411WI Converter_DCDC:TMR10-2411WIR +Converter_DCDC:TMR10-2412WI Converter_DCDC:TMR10-2412WIR +Converter_DCDC:TMR10-2413WI Converter_DCDC:TMR10-2413WIR +Converter_DCDC:TMR10-2415WI Converter_DCDC:TMR10-2415WIR Converter_DCDC:TMR10-2421WIR +Converter_DCDC:TMR10-2422WI Converter_DCDC:TMR10-2422WIR +Converter_DCDC:TMR10-2423WI Converter_DCDC:TMR10-2423WIR Converter_DCDC:TMR10-4810WIR +Converter_DCDC:TMR10-4811WI Converter_DCDC:TMR10-4811WIR +Converter_DCDC:TMR10-4812WI Converter_DCDC:TMR10-4812WIR +Converter_DCDC:TMR10-4813WI Converter_DCDC:TMR10-4813WIR +Converter_DCDC:TMR10-4815WI Converter_DCDC:TMR10-4815WIR Converter_DCDC:TMR10-4821WIR +Converter_DCDC:TMR10-4822WI Converter_DCDC:TMR10-4822WIR +Converter_DCDC:TMR10-4823WI Converter_DCDC:TMR10-4823WIR Converter_DCDC:TMR10-7210WIR Converter_DCDC:TMR10-7211WIR @@ -14593,6 +14611,8 @@ MCU_Texas:LM4F111C4QR MCU_Texas:LM4F111E5QR MCU_Texas:LM4F111H5QR MCU_Texas:MSP432E401Y +MCU_Texas:MSPM0C110xSDDF +MCU_Texas:MSPM0C110xSDSG MCU_Texas:TM4C1230C3PM MCU_Texas:TM4C1230D5PM MCU_Texas:TM4C1230E6PM @@ -15115,6 +15135,7 @@ Memory_Flash:AM29F400Bx-xxEx Memory_Flash:AM29F400Bx-xxSx Memory_Flash:AM29PDL128G Memory_Flash:AT25DF041x-UxN-x +Memory_Flash:AT25SF041B-SSHD-X Memory_Flash:AT25SF081-SSHD-X Memory_Flash:AT25SF081-SSHF-X Memory_Flash:AT25SF081-XMHD-X @@ -15670,6 +15691,7 @@ Power_Management:LM5069MM-1 Power_Management:LM5069MM-2 Power_Management:LM66100DCK Power_Management:LM74700 +Power_Management:LM74701-Q1 Power_Management:LMG3410 Power_Management:LMG5200 Power_Management:LT1641-1 @@ -15771,6 +15793,7 @@ Power_Management:TPS22810DBV Power_Management:TPS22810DRV Power_Management:TPS22917DBV Power_Management:TPS22917LDBV +Power_Management:TPS22919DCK Power_Management:TPS22929D Power_Management:TPS22993 Power_Management:TPS2412D @@ -16037,6 +16060,7 @@ Power_Supervisor:TPS3831 Power_Supervisor:TPS3839DBZ Power_Supervisor:TPS3839DQN RF:0900PC15J0013 +RF:AD8302xRU RF:ADC-10-1R RF:ADCH-80 RF:ADCH-80A @@ -16479,6 +16503,7 @@ RF_Module:DWM3000 RF_Module:E18-MS1-PCB RF_Module:E73-2G4M04S-52810 RF_Module:E73-2G4M04S-52832 +RF_Module:ESP-01 RF_Module:ESP-07 RF_Module:ESP-12E RF_Module:ESP-12F @@ -16680,6 +16705,19 @@ Reference_Voltage:LM4040LP-4.1 Reference_Voltage:LM4040LP-5 Reference_Voltage:LM4040LP-8.2 Reference_Voltage:LM4041LP-ADJ +Reference_Voltage:LM4050xEM3-2.1 +Reference_Voltage:LM4050xEM3-2.5 +Reference_Voltage:LM4050xEM3-3.0 +Reference_Voltage:LM4050xEM3-3.3 +Reference_Voltage:LM4050xEM3-4.1 +Reference_Voltage:LM4050xEM3-5.0 +Reference_Voltage:LM4050xEX3-2.1 +Reference_Voltage:LM4050xEX3-2.5 +Reference_Voltage:LM4050xEX3-3.3 +Reference_Voltage:LM4050xEX3-4.1 +Reference_Voltage:LM4050xEX3-5.0 +Reference_Voltage:LM4051xEM3-1.2 +Reference_Voltage:LM4051xEX3-1.2 Reference_Voltage:LM4125AIM5-2.5 Reference_Voltage:LM4125IM5-2.0 Reference_Voltage:LM4125IM5-2.5 @@ -16827,7 +16865,6 @@ Reference_Voltage:MCP1501-25xCH Reference_Voltage:MCP1501-25xRW Reference_Voltage:MCP1501-25xSN Reference_Voltage:MCP1501-30xCH -Reference_Voltage:MCP1501-30xRW Reference_Voltage:MCP1501-30xSN Reference_Voltage:MCP1501-33xCH Reference_Voltage:MCP1501-33xRW @@ -20988,6 +21025,7 @@ Sensor_Temperature:MCP9501 Sensor_Temperature:MCP9502 Sensor_Temperature:MCP9503 Sensor_Temperature:MCP9504 +Sensor_Temperature:MCP96xx01x-x-MX Sensor_Temperature:MCP9700Ax-ELT Sensor_Temperature:MCP9700Ax-ETT Sensor_Temperature:MCP9700Ax-HLT diff --git a/public/media/.gitignore b/public/media/.gitignore index e4343963..d296e2d4 100644 --- a/public/media/.gitignore +++ b/public/media/.gitignore @@ -1,3 +1,4 @@ # Ignore everything except this .gitignore * -!.gitignore \ No newline at end of file +!.gitignore +!.htaccess diff --git a/public/media/.htaccess b/public/media/.htaccess new file mode 100644 index 00000000..5f567a9a --- /dev/null +++ b/public/media/.htaccess @@ -0,0 +1,10 @@ +# Deny access to PHP and PHP-like files to prevent remote code execution + + + Require all denied + + + Order deny,allow + Deny from all + + diff --git a/src/Command/CheckRequirementsCommand.php b/src/Command/CheckRequirementsCommand.php index f9080c42..97a2074d 100644 --- a/src/Command/CheckRequirementsCommand.php +++ b/src/Command/CheckRequirementsCommand.php @@ -22,6 +22,7 @@ declare(strict_types=1); */ namespace App\Command; +use App\Services\System\AppSecretChecker; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -33,7 +34,9 @@ use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface; #[AsCommand('partdb:check-requirements', 'Checks if the requirements Part-DB needs or recommends are fulfilled.')] class CheckRequirementsCommand extends Command { - public function __construct(protected ContainerBagInterface $params) + public function __construct(protected ContainerBagInterface $params, + private readonly AppSecretChecker $appSecretChecker + ) { parent::__construct(); } @@ -121,6 +124,16 @@ class CheckRequirementsCommand extends Command $io->success('Debug mode disabled.'); } + //Check if APP_SECRET has been changed from the default + if ($io->isVerbose()) { + $io->comment('Checking APP_SECRET...'); + } + if ($this->appSecretChecker->isInsecureSecret()) { + $io->warning('APP_SECRET is set to the default value shipped with Part-DB. This is a security risk! Generate a new secret (e.g. using "openssl rand -hex 32") and set it as APP_SECRET in your .env.local file.'); + } elseif (!$only_issues) { + $io->success('APP_SECRET has been changed from the default value.'); + } + } protected function checkPHPExtensions(SymfonyStyle $io, bool $only_issues = false): void diff --git a/src/Controller/AttachmentFileController.php b/src/Controller/AttachmentFileController.php index 01aeab11..7f48e661 100644 --- a/src/Controller/AttachmentFileController.php +++ b/src/Controller/AttachmentFileController.php @@ -93,6 +93,8 @@ class AttachmentFileController extends AbstractController //Set header content disposition, so that the file will be downloaded $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $attachment->getFilename()); + $this->setAttachmentCSPHeaders($response); + return $response; } @@ -112,6 +114,16 @@ class AttachmentFileController extends AbstractController //Set header content disposition, so that the file will be downloaded $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $attachment->getFilename()); + $this->setAttachmentCSPHeaders($response); + + return $response; + } + + private function setAttachmentCSPHeaders(Response $response): Response + { + //Set an CSP that disallow to run any scripts, styles or images from the attachment render page, as it is not used anywhere else for now and can be a security risk if used without proper precautions, so it should be opt-in + $response->headers->set('Content-Security-Policy', "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; sandbox;"); + return $response; } diff --git a/src/Controller/HomepageController.php b/src/Controller/HomepageController.php index 6f863a3c..f64f281f 100644 --- a/src/Controller/HomepageController.php +++ b/src/Controller/HomepageController.php @@ -24,6 +24,7 @@ namespace App\Controller; use App\DataTables\LogDataTable; use App\Entity\Parts\Part; +use App\Services\System\AppSecretChecker; use App\Services\System\BannerHelper; use App\Services\System\GitVersionInfoProvider; use App\Services\System\UpdateAvailableFacade; @@ -36,8 +37,11 @@ use Symfony\Component\Routing\Attribute\Route; class HomepageController extends AbstractController { - public function __construct(private readonly DataTableFactory $dataTable, private readonly BannerHelper $bannerHelper) - { + public function __construct( + private readonly DataTableFactory $dataTable, + private readonly BannerHelper $bannerHelper, + private readonly AppSecretChecker $appSecretChecker, + ) { } @@ -84,6 +88,8 @@ class HomepageController extends AbstractController 'new_version_available' => $updateAvailableManager->isUpdateAvailable(), 'new_version' => $updateAvailableManager->getLatestVersionString(), 'new_version_url' => $updateAvailableManager->getLatestVersionUrl(), + 'insecure_app_secret' => $this->appSecretChecker->isInsecureSecret(), + 'suggested_app_secret' => $this->appSecretChecker->isInsecureSecret() ? $this->appSecretChecker->generateSecret() : null, ]); } } diff --git a/src/Controller/PartImportExportController.php b/src/Controller/PartImportExportController.php index 45f90d75..c9b6f245 100644 --- a/src/Controller/PartImportExportController.php +++ b/src/Controller/PartImportExportController.php @@ -90,7 +90,7 @@ class PartImportExportController extends AbstractController goto ret; } - if (!isset($errors) || $errors) { + if (!isset($errors) || $errors) { //@phpstan-ignore-line $this->addFlash('error', 'parts.import.flash.error'); } else { $this->addFlash('success', 'parts.import.flash.success'); diff --git a/src/Controller/TreeController.php b/src/Controller/TreeController.php index 71f8ba5c..b8c50d9b 100644 --- a/src/Controller/TreeController.php +++ b/src/Controller/TreeController.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace App\Controller; +use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\Response; use App\Entity\ProjectSystem\Project; use App\Entity\Parts\Category; @@ -55,7 +56,7 @@ class TreeController extends AbstractController #[Route(path: '/category/{id}', name: 'tree_category')] #[Route(path: '/categories', name: 'tree_category_root')] - public function categoryTree(?Category $category = null): JsonResponse + public function categoryTree(#[MapEntity(id: 'id')] ?Category $category = null): JsonResponse { if ($this->isGranted('@parts.read') && $this->isGranted('@categories.read')) { $tree = $this->treeGenerator->getTreeView(Category::class, $category, 'list_parts_root'); @@ -68,7 +69,7 @@ class TreeController extends AbstractController #[Route(path: '/footprint/{id}', name: 'tree_footprint')] #[Route(path: '/footprints', name: 'tree_footprint_root')] - public function footprintTree(?Footprint $footprint = null): JsonResponse + public function footprintTree(#[MapEntity(id: 'id')] ?Footprint $footprint = null): JsonResponse { if ($this->isGranted('@parts.read') && $this->isGranted('@footprints.read')) { $tree = $this->treeGenerator->getTreeView(Footprint::class, $footprint, 'list_parts_root'); @@ -80,7 +81,7 @@ class TreeController extends AbstractController #[Route(path: '/location/{id}', name: 'tree_location')] #[Route(path: '/locations', name: 'tree_location_root')] - public function locationTree(?StorageLocation $location = null): JsonResponse + public function locationTree(#[MapEntity(id: 'id')] ?StorageLocation $location = null): JsonResponse { if ($this->isGranted('@parts.read') && $this->isGranted('@storelocations.read')) { $tree = $this->treeGenerator->getTreeView(StorageLocation::class, $location, 'list_parts_root'); @@ -93,7 +94,7 @@ class TreeController extends AbstractController #[Route(path: '/manufacturer/{id}', name: 'tree_manufacturer')] #[Route(path: '/manufacturers', name: 'tree_manufacturer_root')] - public function manufacturerTree(?Manufacturer $manufacturer = null): JsonResponse + public function manufacturerTree(#[MapEntity(id: 'id')] ?Manufacturer $manufacturer = null): JsonResponse { if ($this->isGranted('@parts.read') && $this->isGranted('@manufacturers.read')) { $tree = $this->treeGenerator->getTreeView(Manufacturer::class, $manufacturer, 'list_parts_root'); @@ -106,7 +107,7 @@ class TreeController extends AbstractController #[Route(path: '/supplier/{id}', name: 'tree_supplier')] #[Route(path: '/suppliers', name: 'tree_supplier_root')] - public function supplierTree(?Supplier $supplier = null): JsonResponse + public function supplierTree(#[MapEntity(id: 'id')] ?Supplier $supplier = null): JsonResponse { if ($this->isGranted('@parts.read') && $this->isGranted('@suppliers.read')) { $tree = $this->treeGenerator->getTreeView(Supplier::class, $supplier, 'list_parts_root'); @@ -119,7 +120,7 @@ class TreeController extends AbstractController #[Route(path: '/device/{id}', name: 'tree_device')] #[Route(path: '/devices', name: 'tree_device_root')] - public function deviceTree(?Project $device = null): JsonResponse + public function deviceTree(#[MapEntity(id: 'id')] ?Project $device = null): JsonResponse { if ($this->isGranted('@projects.read')) { $tree = $this->treeGenerator->getTreeView(Project::class, $device, 'devices'); diff --git a/src/DataTables/AttachmentDataTable.php b/src/DataTables/AttachmentDataTable.php index 16e6a7a7..6c4c905a 100644 --- a/src/DataTables/AttachmentDataTable.php +++ b/src/DataTables/AttachmentDataTable.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace App\DataTables; +use App\DataTables\Column\HTMLColumn; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\PrettyBoolColumn; use App\DataTables\Column\RowClassColumn; @@ -40,14 +41,19 @@ use Omines\DataTablesBundle\DataTable; use Omines\DataTablesBundle\DataTableTypeInterface; use Symfony\Contracts\Translation\TranslatorInterface; -final class AttachmentDataTable implements DataTableTypeInterface +final readonly class AttachmentDataTable implements DataTableTypeInterface { - public function __construct(private readonly TranslatorInterface $translator, private readonly EntityURLGenerator $entityURLGenerator, private readonly AttachmentManager $attachmentHelper, private readonly AttachmentURLGenerator $attachmentURLGenerator, private readonly ElementTypeNameGenerator $elementTypeNameGenerator) + public function __construct(private TranslatorInterface $translator, private EntityURLGenerator $entityURLGenerator, private AttachmentManager $attachmentHelper, private AttachmentURLGenerator $attachmentURLGenerator, private ElementTypeNameGenerator $elementTypeNameGenerator) { } public function configure(DataTable $dataTable, array $options): void { + /************************************************************************************************************* + * Avoid using render, as it has no escaping, and is a potential security risk. Use data on TextColumn or the + * HTMLColumn, if necessary + ************************************************************************************************************/ + $dataTable->add('dont_matter', RowClassColumn::class, [ 'render' => function ($value, Attachment $context): string { //Mark attachments yellow which have an internal file linked that doesn't exist @@ -59,10 +65,10 @@ final class AttachmentDataTable implements DataTableTypeInterface }, ]); - $dataTable->add('picture', TextColumn::class, [ + $dataTable->add('picture', HTMLColumn::class, [ 'label' => '', 'className' => 'no-colvis', - 'render' => function ($value, Attachment $context): string { + 'data' => function (Attachment $context): string { if ($context->isPicture() && $this->attachmentHelper->isInternalFileExisting($context)) { @@ -95,65 +101,65 @@ final class AttachmentDataTable implements DataTableTypeInterface 'orderField' => 'NATSORT(attachment.name)', ]); - $dataTable->add('attachment_type', TextColumn::class, [ + $dataTable->add('attachment_type', HTMLColumn::class, [ 'label' => 'attachment.table.type', 'field' => 'attachment_type.name', 'orderField' => 'NATSORT(attachment_type.name)', - 'render' => fn($value, Attachment $context): string => sprintf( + 'data' => fn(Attachment $context, $value): string => sprintf( '%s', $this->entityURLGenerator->editURL($context->getAttachmentType()), htmlspecialchars((string) $value) ), ]); - $dataTable->add('element', TextColumn::class, [ + $dataTable->add('element', HTMLColumn::class, [ 'label' => 'attachment.table.element', //'propertyPath' => 'element.name', - 'render' => fn($value, Attachment $context): string => sprintf( + 'data' => fn(Attachment $context): string => sprintf( '%s', $this->entityURLGenerator->infoURL($context->getElement()), $this->elementTypeNameGenerator->getTypeNameCombination($context->getElement(), true) ), ]); - $dataTable->add('internal_link', TextColumn::class, [ + $dataTable->add('internal_link', HTMLColumn::class, [ 'label' => 'attachment.table.internal_file', 'propertyPath' => 'filename', 'orderField' => 'NATSORT(attachment.original_filename)', - 'render' => function ($value, Attachment $context) { + 'data' => function (Attachment $context, $value) { if ($this->attachmentHelper->isInternalFileExisting($context)) { return sprintf( '%s', $this->entityURLGenerator->viewURL($context), - htmlspecialchars($value) + htmlspecialchars((string) $value) ); } - return $value; - } + return htmlspecialchars((string) $value); + }, ]); - $dataTable->add('external_link', TextColumn::class, [ + $dataTable->add('external_link', HTMLColumn::class, [ 'label' => 'attachment.table.external_link', 'propertyPath' => 'host', 'orderField' => 'attachment.external_path', - 'render' => function ($value, Attachment $context) { + 'data' => function (Attachment $context, $value) { if ($context->hasExternal()) { return sprintf( '%s', htmlspecialchars((string) $context->getExternalPath()), htmlspecialchars((string) $context->getExternalPath()), - htmlspecialchars($value), + htmlspecialchars((string) $value), ); } - return $value; - } + return htmlspecialchars((string) $value); + }, ]); - $dataTable->add('filesize', TextColumn::class, [ + $dataTable->add('filesize', HTMLColumn::class, [ 'label' => $this->translator->trans('attachment.table.filesize'), - 'render' => function ($value, Attachment $context) { + 'data' => function (Attachment $context) { if (!$context->hasInternal()) { return sprintf( ' @@ -168,7 +174,7 @@ final class AttachmentDataTable implements DataTableTypeInterface ' %s ', - $this->attachmentHelper->getHumanFileSize($context) + htmlspecialchars($this->attachmentHelper->getHumanFileSize($context)) ); } diff --git a/src/DataTables/Column/EntityColumn.php b/src/DataTables/Column/EntityColumn.php index 54ae3fb3..b5d71a08 100644 --- a/src/DataTables/Column/EntityColumn.php +++ b/src/DataTables/Column/EntityColumn.php @@ -78,7 +78,7 @@ class EntityColumn extends AbstractColumn ); } - return sprintf('%s', $value); + return sprintf('%s', htmlspecialchars($value)); } return ''; diff --git a/src/DataTables/Column/HTMLColumn.php b/src/DataTables/Column/HTMLColumn.php new file mode 100644 index 00000000..a1220dd3 --- /dev/null +++ b/src/DataTables/Column/HTMLColumn.php @@ -0,0 +1,37 @@ +. + */ +namespace App\DataTables\Column; + +use Omines\DataTablesBundle\Column\TextColumn; + +/** + * A TextColumn whose value is always treated as raw HTML and therefore never passed through htmlspecialchars(). + * The value returned by the 'data' option must already contain properly escaped/sanitized HTML, as it is output as-is. + */ +class HTMLColumn extends TextColumn +{ + public function isRaw(): bool + { + return true; + } +} diff --git a/src/DataTables/Column/IconLinkColumn.php b/src/DataTables/Column/IconLinkColumn.php index 6704cb4a..47b35d82 100644 --- a/src/DataTables/Column/IconLinkColumn.php +++ b/src/DataTables/Column/IconLinkColumn.php @@ -87,9 +87,9 @@ class IconLinkColumn extends AbstractColumn return sprintf( '', $disabled ? 'disabled' : '', - $href, - $title, - $icon + htmlspecialchars($href), + htmlspecialchars($title ?? ''), + htmlspecialchars($icon ?? '') ); } diff --git a/src/DataTables/ErrorDataTable.php b/src/DataTables/ErrorDataTable.php index 833ea934..a16b453e 100644 --- a/src/DataTables/ErrorDataTable.php +++ b/src/DataTables/ErrorDataTable.php @@ -22,9 +22,9 @@ declare(strict_types=1); */ namespace App\DataTables; +use App\DataTables\Column\HTMLColumn; use App\DataTables\Column\RowClassColumn; use Omines\DataTablesBundle\Adapter\ArrayAdapter; -use Omines\DataTablesBundle\Column\TextColumn; use Omines\DataTablesBundle\DataTable; use Omines\DataTablesBundle\DataTableFactory; use Omines\DataTablesBundle\DataTableTypeInterface; @@ -32,7 +32,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\OptionsResolver\OptionsResolver; -class ErrorDataTable implements DataTableTypeInterface +final readonly class ErrorDataTable implements DataTableTypeInterface { public function configureOptions(OptionsResolver $optionsResolver): void { @@ -49,6 +49,11 @@ class ErrorDataTable implements DataTableTypeInterface public function configure(DataTable $dataTable, array $options): void { + /************************************************************************************************************* + * Avoid using render, as it has no escaping, and is a potential security risk. Use data on TextColumn or the + * HTMLColumn, if necessary + ************************************************************************************************************/ + $optionsResolver = new OptionsResolver(); $this->configureOptions($optionsResolver); $options = $optionsResolver->resolve($options); @@ -58,9 +63,9 @@ class ErrorDataTable implements DataTableTypeInterface 'render' => fn($value, $context): string => 'table-warning', ]) - ->add('error', TextColumn::class, [ + ->add('error', HTMLColumn::class, [ 'label' => 'error_table.error', - 'render' => fn($value, $context): string => ' ' . $value, + 'data' => fn($context, $value): string => ' ' . htmlspecialchars((string) $value), ]) ; diff --git a/src/DataTables/Helpers/PartDataTableHelper.php b/src/DataTables/Helpers/PartDataTableHelper.php index 54094ff1..2f40dbd2 100644 --- a/src/DataTables/Helpers/PartDataTableHelper.php +++ b/src/DataTables/Helpers/PartDataTableHelper.php @@ -62,7 +62,7 @@ class PartDataTableHelper } if ($context->getBuiltProject() instanceof Project) { $icon = sprintf('', - $this->translator->trans('part.info.projectBuildPart.hint').': '.$context->getBuiltProject()->getName()); + $this->translator->trans('part.info.projectBuildPart.hint').': '.htmlspecialchars($context->getBuiltProject()->getName())); } diff --git a/src/DataTables/LogDataTable.php b/src/DataTables/LogDataTable.php index 2c37767b..5c4ca88b 100644 --- a/src/DataTables/LogDataTable.php +++ b/src/DataTables/LogDataTable.php @@ -25,6 +25,7 @@ namespace App\DataTables; use App\DataTables\Column\EnumColumn; use App\Entity\LogSystem\LogTargetType; use Symfony\Bundle\SecurityBundle\Security; +use App\DataTables\Column\HTMLColumn; use App\DataTables\Column\IconLinkColumn; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\LogEntryExtraColumn; @@ -59,7 +60,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\Translation\TranslatorInterface; -class LogDataTable implements DataTableTypeInterface +final readonly class LogDataTable implements DataTableTypeInterface { protected LogEntryRepository $logRepo; @@ -95,6 +96,11 @@ class LogDataTable implements DataTableTypeInterface public function configure(DataTable $dataTable, array $options): void { + /************************************************************************************************************* + * Avoid using render, as it has no escaping, and is a potential security risk. Use data on TextColumn or the + * HTMLColumn, if necessary + ************************************************************************************************************/ + $resolver = new OptionsResolver(); $this->configureOptions($resolver); $options = $resolver->resolve($options); @@ -104,10 +110,10 @@ class LogDataTable implements DataTableTypeInterface 'render' => fn($value, AbstractLogEntry $context) => $this->logLevelHelper->logLevelToTableColorClass($context->getLevelString()), ]); - $dataTable->add('symbol', TextColumn::class, [ + $dataTable->add('symbol', HTMLColumn::class, [ 'label' => '', 'className' => 'no-colvis', - 'render' => fn($value, AbstractLogEntry $context): string => sprintf( + 'data' => fn(AbstractLogEntry $context): string => sprintf( '', $this->logLevelHelper->logLevelToIconClass($context->getLevelString()), $context->getLevelString() @@ -128,10 +134,10 @@ class LogDataTable implements DataTableTypeInterface ) ]); - $dataTable->add('type', TextColumn::class, [ + $dataTable->add('type', HTMLColumn::class, [ 'label' => 'log.type', 'propertyPath' => 'type', - 'render' => function (string $value, AbstractLogEntry $context) { + 'data' => function (AbstractLogEntry $context, string $value) { $text = $this->translator->trans('log.type.'.$value); if ($context instanceof PartStockChangedLogEntry) { @@ -149,20 +155,20 @@ class LogDataTable implements DataTableTypeInterface 'label' => 'log.level', 'visible' => 'system_log' === $options['mode'], 'propertyPath' => 'levelString', - 'render' => fn(string $value, AbstractLogEntry $context) => $this->translator->trans('log.level.'.$value), + 'data' => fn(AbstractLogEntry $context, string $value) => $this->translator->trans('log.level.'.$value), ]); - $dataTable->add('user', TextColumn::class, [ + $dataTable->add('user', HTMLColumn::class, [ 'label' => 'log.user', 'orderField' => 'NATSORT(user.name)', - 'render' => function ($value, AbstractLogEntry $context): string { + 'data' => function (AbstractLogEntry $context): string { $user = $context->getUser(); //If user was deleted, show the info from the username field if (!$user instanceof User) { if ($context->isCLIEntry()) { return sprintf('%s [%s]', - htmlentities((string) $context->getCLIUsername()), + htmlspecialchars((string) $context->getCLIUsername()), $this->translator->trans('log.cli_user') ); } @@ -170,7 +176,7 @@ class LogDataTable implements DataTableTypeInterface //Else we just deal with a deleted user return sprintf( '@%s [%s]', - htmlentities($context->getUsername()), + htmlspecialchars($context->getUsername()), $this->translator->trans('log.target_deleted'), ); } @@ -182,7 +188,7 @@ class LogDataTable implements DataTableTypeInterface $img_url, $this->userAvatarHelper->getAvatarMdURL($user), $this->urlGenerator->generate('user_info', ['id' => $user->getID()]), - htmlentities($user->getFullName(true)) + htmlspecialchars($user->getFullName(true)) ); }, ]); @@ -194,7 +200,7 @@ class LogDataTable implements DataTableTypeInterface 'render' => function (LogTargetType $value, AbstractLogEntry $context) { $class = $value->toClass(); if (null !== $class) { - return $this->elementTypeNameGenerator->getLocalizedTypeLabel($class); + return $this->elementTypeNameGenerator->typeLabel($class); } return ''; @@ -216,9 +222,9 @@ class LogDataTable implements DataTableTypeInterface 'icon' => 'fas fa-fw fa-eye', 'href' => function ($value, AbstractLogEntry $context) { if ( + $context instanceof CollectionElementDeleted || ($context instanceof TimeTravelInterface && $context->hasOldDataInformation()) - || $context instanceof CollectionElementDeleted ) { try { $target = $this->logRepo->getTargetElement($context); diff --git a/src/DataTables/PartsDataTable.php b/src/DataTables/PartsDataTable.php index b3ff841b..c989c37d 100644 --- a/src/DataTables/PartsDataTable.php +++ b/src/DataTables/PartsDataTable.php @@ -25,6 +25,7 @@ namespace App\DataTables; use App\DataTables\Adapters\TwoStepORMAdapter; use App\DataTables\Column\EntityColumn; use App\DataTables\Column\EnumColumn; +use App\DataTables\Column\HTMLColumn; use App\DataTables\Column\IconLinkColumn; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\MarkdownColumn; @@ -58,7 +59,7 @@ use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Contracts\Translation\TranslatorInterface; -final class PartsDataTable implements DataTableTypeInterface +final readonly class PartsDataTable implements DataTableTypeInterface { public const LENGTH_MENU = [[10, 25, 50, 100, 250, 500, -1], [10, 25, 50, 100, 250, 500, "All"]]; @@ -94,6 +95,11 @@ final class PartsDataTable implements DataTableTypeInterface * When adding columns here, add them also to PartTableColumns enum, to make them configurable in the settings! *************************************************************************************************************/ + /************************************************************************************************************* + * Avoid using render, as it has no escaping, and is a potential security risk. Use data on TextColumn or the + * HTMLColumn, if necessary + ************************************************************************************************************/ + $this->csh //Color the table rows depending on the review and favorite status ->add('row_color', RowClassColumn::class, [ @@ -109,23 +115,23 @@ final class PartsDataTable implements DataTableTypeInterface }, ], visibility_configurable: false) ->add('select', SelectColumn::class, visibility_configurable: false) - ->add('picture', TextColumn::class, [ + ->add('picture', HTMLColumn::class, [ 'label' => '', 'className' => 'no-colvis', - 'render' => fn($value, Part $context) => $this->partDataTableHelper->renderPicture($context), + 'data' => fn(Part $context) => $this->partDataTableHelper->renderPicture($context), ], visibility_configurable: false) - ->add('name', TextColumn::class, [ + ->add('name', HTMLColumn::class, [ 'label' => $this->translator->trans('part.table.name'), - 'render' => fn($value, Part $context) => $this->partDataTableHelper->renderName($context), + 'data' => fn(Part $context) => $this->partDataTableHelper->renderName($context), 'orderField' => 'NATSORT(part.name)' ]) ->add('si_value', TextColumn::class, [ 'label' => $this->translator->trans('part.table.si_value'), - 'render' => function ($value, Part $context): string { + 'data' => function (Part $context): string { $siValue = SiValueSort::sqliteSiValue($context->getName()); if ($siValue !== null) { //Output it as scientific number with a big E - return htmlspecialchars(sprintf('%G', $siValue)); + return sprintf('%G', $siValue); } return ''; }, @@ -156,24 +162,24 @@ final class PartsDataTable implements DataTableTypeInterface 'label' => $this->translator->trans('part.table.manufacturer'), 'orderField' => 'NATSORT(_manufacturer.name)' ]) - ->add('storelocation', TextColumn::class, [ + ->add('storelocation', HTMLColumn::class, [ 'label' => $this->translator->trans('part.table.storeLocations'), //We need to use a aggregate function to get the first store location, as we have a one-to-many relation 'orderField' => 'NATSORT(MIN(_storelocations.name))', - 'render' => fn($value, Part $context) => $this->partDataTableHelper->renderStorageLocations($context), + 'data' => fn(Part $context) => $this->partDataTableHelper->renderStorageLocations($context), ], alias: 'storage_location') - ->add('amount', TextColumn::class, [ + ->add('amount', HTMLColumn::class, [ 'label' => $this->translator->trans('part.table.amount'), - 'render' => fn($value, Part $context) => $this->partDataTableHelper->renderAmount($context), + 'data' => fn(Part $context) => $this->partDataTableHelper->renderAmount($context), 'orderField' => 'amountSum' ]) ->add('minamount', TextColumn::class, [ 'label' => $this->translator->trans('part.table.minamount'), - 'render' => fn($value, Part $context): string => htmlspecialchars($this->amountFormatter->format( + 'data' => fn(Part $context, $value): string => $this->amountFormatter->format( $value, $context->getPartUnit() - )), + ), ]) ->add('orderamount', TextColumn::class, [ 'label' => $this->translator->trans('part.table.orderamount'), @@ -187,16 +193,16 @@ final class PartsDataTable implements DataTableTypeInterface ->add('partUnit', TextColumn::class, [ 'label' => $this->translator->trans('part.table.partUnit'), 'orderField' => 'NATSORT(_partUnit.name)', - 'render' => function ($value, Part $context): string { + 'data' => function (Part $context): string { $partUnit = $context->getPartUnit(); if ($partUnit === null) { return ''; } - $tmp = htmlspecialchars($partUnit->getName()); + $tmp = $partUnit->getName(); if ($partUnit->getUnit()) { - $tmp .= ' (' . htmlspecialchars($partUnit->getUnit()) . ')'; + $tmp .= ' (' . $partUnit->getUnit() . ')'; } return $tmp; } @@ -204,14 +210,14 @@ final class PartsDataTable implements DataTableTypeInterface ->add('partCustomState', TextColumn::class, [ 'label' => $this->translator->trans('part.table.partCustomState'), 'orderField' => 'NATSORT(_partCustomState.name)', - 'render' => function($value, Part $context): string { + 'data' => function(Part $context): string { $partCustomState = $context->getPartCustomState(); if ($partCustomState === null) { return ''; } - return htmlspecialchars($partCustomState->getName()); + return $partCustomState->getName(); } ]) ->add('addedDate', LocaleDateTimeColumn::class, [ @@ -257,25 +263,25 @@ final class PartsDataTable implements DataTableTypeInterface ]) ->add('eda_reference', TextColumn::class, [ 'label' => $this->translator->trans('part.table.eda_reference'), - 'render' => static fn($value, Part $context) => htmlspecialchars($context->getEdaInfo()->getReferencePrefix() ?? ''), + 'data' => static fn(Part $context) => $context->getEdaInfo()->getReferencePrefix() ?? '', 'orderField' => 'NATSORT(part.eda_info.reference_prefix)' ]) ->add('eda_value', TextColumn::class, [ 'label' => $this->translator->trans('part.table.eda_value'), - 'render' => static fn($value, Part $context) => htmlspecialchars($context->getEdaInfo()->getValue() ?? ''), + 'data' => static fn(Part $context) => $context->getEdaInfo()->getValue() ?? '', 'orderField' => 'NATSORT(part.eda_info.value)' ]) - ->add('eda_status', TextColumn::class, [ + ->add('eda_status', HTMLColumn::class, [ 'label' => $this->translator->trans('part.table.eda_status'), - 'render' => fn($value, Part $context) => $this->partDataTableHelper->renderEdaStatus($context), + 'data' => fn(Part $context) => $this->partDataTableHelper->renderEdaStatus($context), 'className' => 'text-center', ]); //Add a column to list the projects where the part is used, when the user has the permission to see the projects if ($this->security->isGranted('read', Project::class)) { - $this->csh->add('projects', TextColumn::class, [ + $this->csh->add('projects', HTMLColumn::class, [ 'label' => $this->translator->trans('project.labelp'), - 'render' => function ($value, Part $context): string { + 'data' => function (Part $context): string { //Only show the first 5 projects names $projects = $context->getProjects(); $tmp = ""; @@ -295,7 +301,7 @@ final class PartsDataTable implements DataTableTypeInterface } return $tmp; - } + }, ]); } diff --git a/src/DataTables/ProjectBomEntriesDataTable.php b/src/DataTables/ProjectBomEntriesDataTable.php index 2f0766ee..651e3d46 100644 --- a/src/DataTables/ProjectBomEntriesDataTable.php +++ b/src/DataTables/ProjectBomEntriesDataTable.php @@ -25,6 +25,7 @@ namespace App\DataTables; use App\DataTables\Adapters\TwoStepORMAdapter; use App\DataTables\Column\EntityColumn; use App\DataTables\Column\EnumColumn; +use App\DataTables\Column\HTMLColumn; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\MarkdownColumn; use App\DataTables\Helpers\PartDataTableHelper; @@ -48,7 +49,7 @@ use Omines\DataTablesBundle\DataTable; use Omines\DataTablesBundle\DataTableTypeInterface; use Symfony\Contracts\Translation\TranslatorInterface; -class ProjectBomEntriesDataTable implements DataTableTypeInterface +final readonly class ProjectBomEntriesDataTable implements DataTableTypeInterface { public function __construct( protected EntityURLGenerator $entityURLGenerator, @@ -63,17 +64,22 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface public function configure(DataTable $dataTable, array $options): void { + /************************************************************************************************************* + * Avoid using render, as it has no escaping, and is a potential security risk. Use data on TextColumn or the + * HTMLColumn, if necessary + ************************************************************************************************************/ + $dataTable //->add('select', SelectColumn::class) - ->add('picture', TextColumn::class, [ + ->add('picture', HTMLColumn::class, [ 'label' => '', 'className' => 'no-colvis', - 'render' => function ($value, ProjectBOMEntry $context) { + 'data' => function (ProjectBOMEntry $context) { if(!$context->getPart() instanceof Part) { return ''; } return $this->partDataTableHelper->renderPicture($context->getPart()); - } + }, ]) ->add('id', TextColumn::class, [ @@ -85,27 +91,27 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface 'label' => $this->translator->trans('project.bom.quantity'), 'className' => 'text-center', 'orderField' => 'bom_entry.quantity', - 'render' => function ($value, ProjectBOMEntry $context): float|string { + 'data' => function (ProjectBOMEntry $context): float|string { //If we have a non-part entry, only show the rounded quantity if (!$context->getPart() instanceof Part) { return round($context->getQuantity()); } //Otherwise use the unit of the part to format the quantity - return htmlspecialchars($this->amountFormatter->format($context->getQuantity(), $context->getPart()->getPartUnit())); + return $this->amountFormatter->format($context->getQuantity(), $context->getPart()->getPartUnit()); }, ]) ->add('partId', TextColumn::class, [ 'label' => $this->translator->trans('project.bom.part_id'), 'visible' => true, 'orderField' => 'part.id', - 'render' => function ($value, ProjectBOMEntry $context) { + 'data' => function (ProjectBOMEntry $context) { return $context->getPart() instanceof Part ? (string) $context->getPart()->getId() : ''; }, ]) - ->add('name', TextColumn::class, [ + ->add('name', HTMLColumn::class, [ 'label' => $this->translator->trans('part.table.name'), 'orderField' => 'NATSORT(part.name)', - 'render' => function ($value, ProjectBOMEntry $context) { + 'data' => function (ProjectBOMEntry $context) { if(!$context->getPart() instanceof Part) { return htmlspecialchars((string) $context->getName()); } @@ -123,11 +129,7 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface 'label' => $this->translator->trans('part.table.ipn'), 'orderField' => 'NATSORT(part.ipn)', 'visible' => false, - 'render' => function ($value, ProjectBOMEntry $context) { - if($context->getPart() instanceof Part) { - return $context->getPart()->getIpn(); - } - } + 'data' => fn (ProjectBOMEntry $context) => $context->getPart()?->getIpn() ]) ->add('description', MarkdownColumn::class, [ 'label' => $this->translator->trans('part.table.description'), @@ -172,9 +174,9 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface }, ]) - ->add('mountnames', TextColumn::class, [ + ->add('mountnames', HTMLColumn::class, [ 'label' => 'project.bom.mountnames', - 'render' => function ($value, ProjectBOMEntry $context) { + 'data' => function (ProjectBOMEntry $context) { $html = ''; foreach (explode(',', $context->getMountnames()) as $mountname) { @@ -184,58 +186,58 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface }, ]) - ->add('instockAmount', TextColumn::class, [ + ->add('instockAmount', HTMLColumn::class, [ 'label' => 'project.bom.instockAmount', 'visible' => false, - 'render' => function ($value, ProjectBOMEntry $context) { + 'data' => function (ProjectBOMEntry $context) { if ($context->getPart() !== null) { return $this->partDataTableHelper->renderAmount($context->getPart()); } return ''; - } + }, ]) - ->add('minAmount', TextColumn::class, [ + ->add('minAmount', HTMLColumn::class, [ 'label' => $this->translator->trans('part.table.minamount'), 'visible' => false, 'orderField' => 'part.minamount', - 'render' => function ($value, ProjectBOMEntry $context): string { + 'data' => function (ProjectBOMEntry $context): string { if (!$context->getPart() instanceof Part) { return ''; } - return htmlspecialchars($this->amountFormatter->format($context->getPart()->getMinAmount(), $context->getPart()->getPartUnit())); + return $this->amountFormatter->format($context->getPart()->getMinAmount(), $context->getPart()->getPartUnit()); }, ]) - ->add('orderAmount', TextColumn::class, [ + ->add('orderAmount', HTMLColumn::class, [ 'label' => $this->translator->trans('part.table.orderamount'), 'visible' => false, 'orderField' => 'part.orderamount', - 'render' => function ($value, ProjectBOMEntry $context): string { + 'data' => function (ProjectBOMEntry $context): string { if (!$context->getPart() instanceof Part) { return ''; } - return htmlspecialchars($this->amountFormatter->format($context->getPart()->getOrderAmount(), $context->getPart()->getPartUnit())); + return $this->amountFormatter->format($context->getPart()->getOrderAmount(), $context->getPart()->getPartUnit()); }, ]) - ->add('storelocation', TextColumn::class, [ + ->add('storelocation', HTMLColumn::class, [ 'label' => $this->translator->trans('part.table.storeLocations'), //We need to use a aggregate function to get the first store location, as we have a one-to-many relation 'orderField' => 'NATSORT(MIN(_storelocations.name))', 'visible' => false, - 'render' => function ($value, ProjectBOMEntry $context) { + 'data' => function (ProjectBOMEntry $context) { if ($context->getPart() !== null) { return $this->partDataTableHelper->renderStorageLocations($context->getPart()); } return ''; - } + }, ]) ->add('price', TextColumn::class, [ 'label' => 'project.bom.price', 'visible' => false, - 'render' => function ($value, ProjectBOMEntry $context) { + 'data' => function (ProjectBOMEntry $context) { $price = $this->projectBuildHelper->getEntryUnitPrice($context); return $this->moneyFormatter->format($price->toScale(2, RoundingMode::Up)->toFloat(), null, 2, true); }, @@ -243,7 +245,7 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface ->add('ext_price', TextColumn::class, [ 'label' => 'project.bom.ext_price', 'visible' => false, - 'render' => function ($value, ProjectBOMEntry $context) { + 'data' => function (ProjectBOMEntry $context) { $price = $this->projectBuildHelper->getEntryUnitPrice($context); return $this->moneyFormatter->format( $price->multipliedBy(BigDecimal::fromFloatShortest($context->getQuantity())) diff --git a/src/Doctrine/Middleware/SetSQLModeMiddlewareDriver.php b/src/Doctrine/Middleware/SetSQLModeMiddlewareDriver.php index d05b6b9c..b8e70ff2 100644 --- a/src/Doctrine/Middleware/SetSQLModeMiddlewareDriver.php +++ b/src/Doctrine/Middleware/SetSQLModeMiddlewareDriver.php @@ -35,8 +35,10 @@ class SetSQLModeMiddlewareDriver extends AbstractDriverMiddleware { //Only set this on MySQL connections, as other databases don't support this parameter if($params['driver'] === 'pdo_mysql') { - //1002 is \PDO::MYSQL_ATTR_INIT_COMMAND constant value - $params['driverOptions'][\PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode, \'ONLY_FULL_GROUP_BY\', \'\'))'; + //PDO::MYSQL_ATTR_INIT_COMMAND is deprecated since PHP 8.5 in favor of Pdo\Mysql::ATTR_INIT_COMMAND, + //but the Pdo\Mysql class only exists since PHP 8.4. Both constants have the same value (1002). + $initCommandAttr = class_exists(\Pdo\Mysql::class) ? \Pdo\Mysql::ATTR_INIT_COMMAND : \PDO::MYSQL_ATTR_INIT_COMMAND; + $params['driverOptions'][$initCommandAttr] = 'SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode, \'ONLY_FULL_GROUP_BY\', \'\'))'; } return parent::connect($params); diff --git a/src/Entity/Parts/StorageLocation.php b/src/Entity/Parts/StorageLocation.php index 6c455ae5..7ba400d9 100644 --- a/src/Entity/Parts/StorageLocation.php +++ b/src/Entity/Parts/StorageLocation.php @@ -80,7 +80,7 @@ use Symfony\Component\Validator\Constraints as Assert; ) ], uriVariables: [ - 'id' => new Link(fromProperty: 'children', fromClass: Manufacturer::class) + 'id' => new Link(fromProperty: 'children', fromClass: StorageLocation::class) ], normalizationContext: ['groups' => ['location:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] )] diff --git a/src/Services/Attachments/AttachmentSubmitHandler.php b/src/Services/Attachments/AttachmentSubmitHandler.php index 25f6142f..1b90091f 100644 --- a/src/Services/Attachments/AttachmentSubmitHandler.php +++ b/src/Services/Attachments/AttachmentSubmitHandler.php @@ -69,7 +69,7 @@ class AttachmentSubmitHandler protected const BLACKLISTED_EXTENSIONS = ['php', 'phtml', 'php3', 'ph3', 'php4', 'ph4', 'php5', 'ph5', 'phtm', 'sh', 'asp', 'cgi', 'py', 'pl', 'exe', 'aspx', 'js', 'mjs', 'jsp', 'css', 'jar', 'html', 'htm', 'shtm', 'shtml', 'htaccess', - 'htpasswd', '']; + 'htpasswd', 'phar', 'phps', '']; public function __construct( protected AttachmentPathResolver $pathResolver, @@ -543,8 +543,10 @@ class AttachmentSubmitHandler return $attachment; } + $guessed_mime_type = $this->mimeTypes->guessMimeType($path); + //Check if the file is an SVG - if ($attachment->getExtension() === "svg") { + if ($guessed_mime_type === "image/svg+xml" || $attachment->getExtension() === "svg") { $this->SVGSanitizer->sanitizeFile($path); } diff --git a/src/Services/ImportExportSystem/BOMValidationService.php b/src/Services/ImportExportSystem/BOMValidationService.php index 74f81fe3..9f4cf5b8 100644 --- a/src/Services/ImportExportSystem/BOMValidationService.php +++ b/src/Services/ImportExportSystem/BOMValidationService.php @@ -29,13 +29,13 @@ use Symfony\Contracts\Translation\TranslatorInterface; /** * Service for validating BOM import data with comprehensive validation rules - * and user-friendly error messages. + * and user-friendly error messages. The results are not HTML safe, and must be escaped before display! */ -class BOMValidationService +readonly class BOMValidationService { public function __construct( - private readonly EntityManagerInterface $entityManager, - private readonly TranslatorInterface $translator + private EntityManagerInterface $entityManager, + private TranslatorInterface $translator ) { } @@ -473,4 +473,4 @@ class BOMValidationService : 0, ]; } -} \ No newline at end of file +} diff --git a/src/Services/LogSystem/LogEntryExtraFormatter.php b/src/Services/LogSystem/LogEntryExtraFormatter.php index ae2a5eba..13394d98 100644 --- a/src/Services/LogSystem/LogEntryExtraFormatter.php +++ b/src/Services/LogSystem/LogEntryExtraFormatter.php @@ -169,8 +169,8 @@ class LogEntryExtraFormatter $array['log.collection_deleted.deleted'] = sprintf( '%s: %s (%s)', $this->elementTypeNameGenerator->getLocalizedTypeLabel($context->getDeletedElementClass()), - $context->getOldName() ?? (string) $context->getDeletedElementID(), - $context->getCollectionName() + htmlspecialchars($context->getOldName() ?? (string) $context->getDeletedElementID()), + htmlspecialchars($context->getCollectionName()) ); } @@ -218,4 +218,4 @@ class LogEntryExtraFormatter return implode(', ', $output); } -} +} \ No newline at end of file diff --git a/src/Services/System/AppSecretChecker.php b/src/Services/System/AppSecretChecker.php new file mode 100644 index 00000000..7a4b1fcf --- /dev/null +++ b/src/Services/System/AppSecretChecker.php @@ -0,0 +1,63 @@ +. + */ + +declare(strict_types=1); + +namespace App\Services\System; + +use Symfony\Component\DependencyInjection\Attribute\Autowire; + +/** + * Checks whether APP_SECRET has been changed from the default value shipped with Part-DB. + */ +final readonly class AppSecretChecker +{ + /** Known default/example secrets that must not be used in production. */ + public const INSECURE_SECRETS = [ + 'a03498528f5a5fc089273ec9ae5b2849', // default in .env + '318b5d659e07a0b3f96d9b3a83b254ca', // default in .env.dev + 'CHANGE_ME' //example secret used in documentation and error messages + ]; + + public function __construct( + #[Autowire('%kernel.secret%')] + private string $appSecret, + ) { + } + + /** + * @return bool True if the app secret is one of the known insecure default secrets, false otherwise. + */ + public function isInsecureSecret(): bool + { + return in_array($this->appSecret, self::INSECURE_SECRETS, true); + } + + /** + * Generates a new random app secret that can be used to replace the default insecure one. + * @return string + * @throws \Random\RandomException + */ + public function generateSecret(): string + { + //Symfony docs recommend 32 characters for the app secret, which are 16 random bytes when hex-encoded. + return bin2hex(random_bytes(16)); + } +} diff --git a/templates/homepage.html.twig b/templates/homepage.html.twig index 6e7aa360..2a191f14 100644 --- a/templates/homepage.html.twig +++ b/templates/homepage.html.twig @@ -85,6 +85,16 @@ {% block content %} + {% if insecure_app_secret and is_granted('@system.server_infos') %} + + {% endif %} + {% if is_granted('@system.show_updates') %} {{ nv.new_version_alert(new_version_available, new_version, new_version_url) }} {% endif %} diff --git a/templates/log_system/details/log_details.html.twig b/templates/log_system/details/log_details.html.twig index aff127f4..2255dd97 100644 --- a/templates/log_system/details/log_details.html.twig +++ b/templates/log_system/details/log_details.html.twig @@ -58,7 +58,7 @@ {% trans %}log.target{% endtrans %} - {{ target_html|raw }} + {{ target_html|sanitize_html }} @@ -111,7 +111,7 @@ {% elseif log_entry is instanceof('App\\Entity\\LogSystem\\CollectionElementDeleted') %} {% include "log_system/details/_extra_collection_element_deleted.html.twig" %} {% else %} - {{ extra_html | raw }} + {{ extra_html | sanitize_html }} {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/projects/_bom_validation_results.html.twig b/templates/projects/_bom_validation_results.html.twig index 68f1b827..cb92e7bc 100644 --- a/templates/projects/_bom_validation_results.html.twig +++ b/templates/projects/_bom_validation_results.html.twig @@ -68,7 +68,7 @@

{% trans %}project.bom_import.validation.errors.description{% endtrans %}

    {% for error in validation_result.errors %} -
  • {{ error|raw }}
  • +
  • {{ error }}
  • {% endfor %}
@@ -80,7 +80,7 @@

{% trans %}project.bom_import.validation.warnings.description{% endtrans %}

    {% for warning in validation_result.warnings %} -
  • {{ warning|raw }}
  • +
  • {{ warning }}
  • {% endfor %}
@@ -91,7 +91,7 @@

{% trans %}project.bom_import.validation.info.title{% endtrans %}

    {% for info in validation_result.info %} -
  • {{ info|raw }}
  • +
  • {{ info }}
  • {% endfor %}
@@ -139,21 +139,21 @@ {% if line_result.errors is not empty %}
{% for error in line_result.errors %} -
{{ error|raw }}
+
{{ error }}
{% endfor %}
{% endif %} {% if line_result.warnings is not empty %}
{% for warning in line_result.warnings %} -
{{ warning|raw }}
+
{{ warning }}
{% endfor %}
{% endif %} {% if line_result.info is not empty %}
{% for info in line_result.info %} -
{{ info|raw }}
+
{{ info }}
{% endfor %}
{% endif %} diff --git a/templates/projects/import_bom_map_fields.html.twig b/templates/projects/import_bom_map_fields.html.twig index ee1e23ef..b272be64 100644 --- a/templates/projects/import_bom_map_fields.html.twig +++ b/templates/projects/import_bom_map_fields.html.twig @@ -132,8 +132,8 @@