From 71144f64e27cc402f9dc20fca80033fb54001966 Mon Sep 17 00:00:00 2001 From: robertsaternus Date: Mon, 26 Jan 2026 14:28:29 +0100 Subject: [PATCH 1/8] INT-192: Secure proxy endpoint --- CHANGELOG.md | 4 ++++ src/Storefront/Controller/ProxyController.php | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a20d36c..cb28b471 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ # Changelog +## Unreleased +### Fix +- Secure proxy endpoint + ## [v5.4.0] - 2025.03.05 ### Add - Add possibility to disable Web Components integration per sales channel diff --git a/src/Storefront/Controller/ProxyController.php b/src/Storefront/Controller/ProxyController.php index 7c658b1f..2b581c3b 100644 --- a/src/Storefront/Controller/ProxyController.php +++ b/src/Storefront/Controller/ProxyController.php @@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\Annotation\Route; /** @@ -46,6 +47,20 @@ public function execute( ClientBuilder $clientBuilder, EventDispatcherInterface $eventDispatcher, ): Response { + if (!$this->config->isProxyEnabled()) { + throw new NotFoundHttpException('Proxy is disabled.'); + } + +// $ffo = $request->headers->get('_ffo'); +// $fft = $request->headers->get('_fft'); +// +// if (!$ffo || !$fft) { +// return new JsonResponse( +// ['message' => 'UNAUTHORIZED'], +// Response::HTTP_UNAUTHORIZED +// ); +// } + $client = $clientBuilder ->withServerUrl($this->config->getServerUrl()) ->withCredentials(new Credentials(...$this->config->getCredentials())) From 46e67a8df0d1964bbc5d00f2159e832e832f6035 Mon Sep 17 00:00:00 2001 From: robertsaternus Date: Mon, 26 Jan 2026 15:05:32 +0100 Subject: [PATCH 2/8] Add script to check webc request --- .../Controller/ProxyControllerSpec.php | 36 +++++++++++++++---- src/Storefront/Controller/ProxyController.php | 31 +++++++++++----- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/spec/Storefront/Controller/ProxyControllerSpec.php b/spec/Storefront/Controller/ProxyControllerSpec.php index eb73f7e9..fd3ed4b4 100644 --- a/spec/Storefront/Controller/ProxyControllerSpec.php +++ b/spec/Storefront/Controller/ProxyControllerSpec.php @@ -15,10 +15,10 @@ use PhpSpec\Wrapper\Collaborator; use PHPUnit\Framework\Assert; use Prophecy\Argument; -use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\HeaderBag; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -31,7 +31,7 @@ class ProxyControllerSpec extends ObjectBehavior public function let( Communication $config, ClientInterface $client, - ClientBuilder $clientBuilder + ClientBuilder $clientBuilder, ): void { $serverUrl = 'https://example.fact-finder.de/fact-finder'; $config->getServerUrl()->willReturn($serverUrl); @@ -40,11 +40,13 @@ public function let( 'username', 'pass', ]); + $config->isProxyEnabled()->willReturn(true); $this->beConstructedWith($config); $clientBuilder->build()->willReturn($client); $clientBuilder->withServerUrl(Argument::any())->willReturn($clientBuilder); $clientBuilder->withCredentials(Argument::any())->willReturn($clientBuilder); $clientBuilder->withVersion(Argument::any())->willReturn($clientBuilder); + $this->client = $client; $this->clientBuilder = $clientBuilder; } @@ -54,15 +56,23 @@ public function it_should_return_success_response( ResponseInterface $response, EventDispatcherInterface $eventDispatcher, EnrichProxyDataEvent $event, - Stream $stream + Stream $stream, ): void { - // Expect & Given $request->getMethod()->willReturn(Request::METHOD_GET); + $request->headers = new HeaderBag([ + '1234567890abcdef1234' => 'val', + 'abcdef1234567890abcd' => 'val', + '0987654321fedcba0987' => 'val', + ]); + $uri = 'rest/v5/search/example_channel?query=bag&sid=123&format=json'; $_SERVER['REQUEST_URI'] = sprintf('/fact-finder/proxy/%s', $uri); + $this->client->request(Request::METHOD_GET, $uri)->willReturn($response); - $jsonResponse = file_get_contents(dirname(__DIR__, 2) . '/data/proxy/search-bag.json'); + + $jsonResponse = json_encode(['some' => 'data']); // Skrócone dla przykładu $responseData = json_decode($jsonResponse, true); + $stream->__toString()->willReturn($jsonResponse); $response->getBody()->willReturn($stream); $event->getData()->willReturn($responseData); @@ -80,13 +90,25 @@ public function it_should_return_error_response( Request $request, EventDispatcherInterface $eventDispatcher, BeforeProxyErrorResponseEvent $event, - RequestInterface $requestInterface + RequestInterface $requestInterface, ): void { // Expect & Given $request->getMethod()->willReturn(Request::METHOD_GET); + $request->headers = new HeaderBag([ + '1234567890abcdef1234' => 'val', + 'abcdef1234567890abcd' => 'val', + '0987654321fedcba0987' => 'val', + ]); + $uri = 'rest/v5/search/example_channel?query=bag&sid=123&format=json'; $_SERVER['REQUEST_URI'] = sprintf('/fact-finder/proxy/%s', $uri); - $this->client->request(Request::METHOD_GET, $uri)->willThrow(new ConnectException('Unable to connect with server.', $requestInterface->getWrappedObject())); + + $this->client->request(Request::METHOD_GET, $uri)->willThrow( + new ConnectException('Unable to connect with server.', $requestInterface->getWrappedObject()) + ); + + $errorResponse = new JsonResponse(['message' => 'Unable to connect with server.'], Response::HTTP_BAD_REQUEST); + $event->getResponse()->willReturn($errorResponse); $eventDispatcher->dispatch(Argument::type(BeforeProxyErrorResponseEvent::class))->willReturn($event); // When diff --git a/src/Storefront/Controller/ProxyController.php b/src/Storefront/Controller/ProxyController.php index 2b581c3b..08ac83af 100644 --- a/src/Storefront/Controller/ProxyController.php +++ b/src/Storefront/Controller/ProxyController.php @@ -51,15 +51,12 @@ public function execute( throw new NotFoundHttpException('Proxy is disabled.'); } -// $ffo = $request->headers->get('_ffo'); -// $fft = $request->headers->get('_fft'); -// -// if (!$ffo || !$fft) { -// return new JsonResponse( -// ['message' => 'UNAUTHORIZED'], -// Response::HTTP_UNAUTHORIZED -// ); -// } + if (!$this->isWebcRequest($request->headers->all())) { + return new JsonResponse( + ['message' => 'UNAUTHORIZED'], + Response::HTTP_UNAUTHORIZED + ); + } $client = $clientBuilder ->withServerUrl($this->config->getServerUrl()) @@ -99,4 +96,20 @@ public function execute( return $event->getResponse(); } } + + private function isWebcRequest(array $headers): bool + { + $pattern = '/^[0-9a-f]{20}$/i'; + + $matchingHeaders = array_filter( + array_keys($headers), + fn ($headerName) => preg_match($pattern, (string) $headerName) + ); + + if (count($matchingHeaders) >= 3) { + return true; + } + + return false; + } } From ce6471b38978d8cbf9ded30312eed600c50a82da Mon Sep 17 00:00:00 2001 From: robertsaternus Date: Mon, 26 Jan 2026 15:15:15 +0100 Subject: [PATCH 3/8] Add --no-audit to CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b7b573d..791e32af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer - name: Install dependencies - run: composer update --no-ansi --no-interaction --prefer-${{ matrix.deps }} + run: composer update --no-ansi --no-interaction --no-audit --prefer-${{ matrix.deps }} - name: Run tests run: composer test From cd6c5251b0f358b0286e65df6e0a3f0db17641ff Mon Sep 17 00:00:00 2001 From: robertsaternus Date: Mon, 26 Jan 2026 15:20:08 +0100 Subject: [PATCH 4/8] Add --no-audit to CI --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 791e32af..bea1d30f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,9 @@ jobs: key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer - name: Install dependencies - run: composer update --no-ansi --no-interaction --no-audit --prefer-${{ matrix.deps }} + run: + composer config audit.block-insecure false + composer update --no-ansi --no-interaction --no-audit --prefer-${{ matrix.deps }} - name: Run tests run: composer test From 81413fbf1054890e99316b9cf0d27e87dee4a1a9 Mon Sep 17 00:00:00 2001 From: robertsaternus Date: Mon, 26 Jan 2026 15:21:30 +0100 Subject: [PATCH 5/8] Add --no-audit to CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bea1d30f..f14c084f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: - name: Install dependencies run: composer config audit.block-insecure false - composer update --no-ansi --no-interaction --no-audit --prefer-${{ matrix.deps }} + composer update --no-ansi --no-interaction --prefer-${{ matrix.deps }} - name: Run tests run: composer test From 76dfd8407f243422048d5a95f5ae6585d8a9be44 Mon Sep 17 00:00:00 2001 From: robertsaternus Date: Mon, 26 Jan 2026 15:24:19 +0100 Subject: [PATCH 6/8] Add --no-audit to CI --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f14c084f..231dfc58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,9 +31,9 @@ jobs: key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.deps }}-composer - name: Install dependencies - run: + run: | composer config audit.block-insecure false - composer update --no-ansi --no-interaction --prefer-${{ matrix.deps }} + composer update --no-ansi --no-interaction --no-audit --prefer-${{ matrix.deps }} - name: Run tests run: composer test From 9c20929a102fb1e3a9c71bdfcf93202bdbb024b6 Mon Sep 17 00:00:00 2001 From: robertsaternus Date: Mon, 26 Jan 2026 15:37:19 +0100 Subject: [PATCH 7/8] Fix CI --- src/Api/TestConnectionController.php | 7 ------- src/Api/UiFeedExportController.php | 1 - src/Api/UpdateFieldRolesController.php | 1 - 3 files changed, 9 deletions(-) diff --git a/src/Api/TestConnectionController.php b/src/Api/TestConnectionController.php index bc2aaa77..176fc8c7 100644 --- a/src/Api/TestConnectionController.php +++ b/src/Api/TestConnectionController.php @@ -10,7 +10,6 @@ use Omikron\FactFinder\Shopware6\Config\Communication as CommunicationConfig; use Omikron\FactFinder\Shopware6\Upload\UploadService; use Psr\Log\LoggerInterface; -use Shopware\Core\Framework\Routing\Annotation\RouteScope; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Annotation\Route; @@ -33,12 +32,6 @@ public function __construct( */ public function testApiConnection(): JsonResponse { - $client = $this->clientBuilder - ->withCredentials(new Credentials(...$this->config->getCredentials())) - ->withServerUrl($this->config->getServerUrl()) - ->withVersion($this->config->getVersion()) - ->build(); - try { $client = $this->clientBuilder ->withCredentials(new Credentials(...$this->config->getCredentials())) diff --git a/src/Api/UiFeedExportController.php b/src/Api/UiFeedExportController.php index d8e3b2e0..d174d952 100644 --- a/src/Api/UiFeedExportController.php +++ b/src/Api/UiFeedExportController.php @@ -10,7 +10,6 @@ use Omikron\FactFinder\Shopware6\MessageQueue\FeedExportHandler; use Omikron\FactFinder\Shopware6\MessageQueue\RefreshExportCacheHandler; use Psr\Log\LoggerInterface; -use Shopware\Core\Framework\Routing\Annotation\RouteScope; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Api/UpdateFieldRolesController.php b/src/Api/UpdateFieldRolesController.php index 17321d7e..77c7e8b0 100644 --- a/src/Api/UpdateFieldRolesController.php +++ b/src/Api/UpdateFieldRolesController.php @@ -10,7 +10,6 @@ use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; -use Shopware\Core\Framework\Routing\Annotation\RouteScope; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Annotation\Route; From fd1252e3bee6e45b31ecadf29effa52fd085f34b Mon Sep 17 00:00:00 2001 From: robertsaternus Date: Mon, 26 Jan 2026 15:49:17 +0100 Subject: [PATCH 8/8] Add Troubleshooting section in docs --- README.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4c8065dd..fd951aa0 100755 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ modifications in order to fit their needs. For more advanced features please che - [Split ASN on Category Page](#split-asn-on-category-page) - [Set custom Field Roles](#set-custom-field-roles) - [Enrich data received from FACT-Finder in ProxyController](#enrich-data-received-from-fact-finder-in-proxycontroller) +- [Troubleshooting](#troubleshooting) - [Contribute](#contribute) - [License](#license) @@ -55,12 +56,12 @@ modifications in order to fit their needs. For more advanced features please che - Shopware 6.5 - PHP version: 8.1, 8.2 or 8.3 -For Shopware 6.4 please use SDK version 4.x: -https://github.com/FACT-Finder-Web-Components/shopware6-plugin/tree/release/4.x - For Shopware 6.6 please use SDK version 6.x: https://github.com/FACT-Finder-Web-Components/shopware6-plugin/tree/release/6.x +For Shopware 6.7 please use SDK version 7.x: +https://github.com/FACT-Finder-Web-Components/shopware6-plugin/tree/release/7.x + ## FACT-Finder® Supported Sections Version | Compatibility @@ -647,7 +648,21 @@ class EnrichProxyDataEventSubscriber implements EventSubscriberInterface } ``` +## Troubleshooting + +### Composer Security Advisories +When working with **Shopware 6.5.x**, you may encounter a build failure during `composer update` or `composer install`. This is caused by the **Composer Audit** feature (introduced in Composer 2.7+), which automatically blocks the installation of packages with known security vulnerabilities. + +The error message typically looks like this: +`...these were not loaded, because they are affected by security advisories (PKSA-...)` + +### Recommended Solution: Upgrade +The most effective and secure way to resolve this is to **upgrade to the latest version of our plugin** and, if possible, move to **Shopware 6.7+**. +The latest versions provide: +* **Security Patches:** Protection against the vulnerabilities flagged by Composer. +* **Stability:** Improved compatibility with modern PHP versions and server environments. +* **Performance:** Optimized code execution for faster storefront response times. ## Contribute