Skip to content

Commit c16fac9

Browse files
committed
OXDEV-10199 Remove stoken from specific GET forms
1 parent 5789508 commit c16fac9

17 files changed

Lines changed: 521 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [4.0.0] - Unreleased
88

9+
### Added
10+
- Strip session token (stoken) from GET form URLs (search, attribute filter, product list) via new Form Security admin setting
11+
912
### Changed
1013
- Updated to work with OXID eShop 7.5.x
1114
- Minimum PHP version is now 8.3, tested with up to PHP 8.5

metadata.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*/
1111

1212
use OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Settings\TwoFAShopSettings as TwoFactorAuthModuleSettings;
13+
use OxidEsales\SecurityModule\FormSecurity\Service\ModuleSettingsService as FormSecurityModuleSettings;
1314
use OxidEsales\SecurityModule\PasswordPolicy\Service\ModuleSettingsService as PasswordPolicyModuleSettings;
1415
use OxidEsales\SecurityModule\Captcha\Service\ModuleSettingsService as CaptchaModuleSettings;
1516
use OxidEsales\SecurityModule\Core\Module;
@@ -113,6 +114,14 @@
113114
'value' => '15min'
114115
],
115116

117+
//Form security
118+
[
119+
'group' => 'form_security',
120+
'name' => FormSecurityModuleSettings::GET_FORM_STRIP_STOKEN,
121+
'type' => 'bool',
122+
'value' => false
123+
],
124+
116125
//TwoFactorAuth settings
117126
[
118127
'group' => 'two_factor_auth',

services.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
imports:
22
- { resource: src/Captcha/services.yaml }
3+
- { resource: src/FormSecurity/services.yaml }
34
- { resource: src/PasswordPolicy/services.yaml }
45
- { resource: src/Authentication/services.yaml }
56

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OxidEsales\SecurityModule\FormSecurity\Service;
11+
12+
use OxidEsales\EshopCommunity\Internal\Framework\Module\Facade\ModuleSettingServiceInterface;
13+
use OxidEsales\SecurityModule\Core\Module;
14+
15+
class ModuleSettingsService implements ModuleSettingsServiceInterface
16+
{
17+
public const GET_FORM_STRIP_STOKEN = 'oeSecurityGetFormStripStoken';
18+
19+
public function __construct(
20+
private readonly ModuleSettingServiceInterface $moduleSettingService,
21+
) {
22+
}
23+
24+
public function isGetFormStripStokenEnabled(): bool
25+
{
26+
return $this->moduleSettingService->getBoolean(self::GET_FORM_STRIP_STOKEN, Module::MODULE_ID);
27+
}
28+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OxidEsales\SecurityModule\FormSecurity\Service;
11+
12+
interface ModuleSettingsServiceInterface
13+
{
14+
public function isGetFormStripStokenEnabled(): bool;
15+
}

src/FormSecurity/services.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
services:
2+
_defaults:
3+
public: false
4+
autowire: true
5+
6+
OxidEsales\SecurityModule\FormSecurity\Service\ModuleSettingsServiceInterface:
7+
class: OxidEsales\SecurityModule\FormSecurity\Service\ModuleSettingsService
8+
public: true

src/Shared/Core/ViewConfig.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
use OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Settings\TwoFAShopSettingsInterface;
1313
use OxidEsales\SecurityModule\Captcha\Captcha\Image\Service\ImageCaptchaService;
1414
use OxidEsales\SecurityModule\Captcha\Service\CaptchaServiceInterface;
15+
// phpcs:ignore Generic.Files.LineLength
16+
use OxidEsales\SecurityModule\FormSecurity\Service\ModuleSettingsServiceInterface as FormSecuritySettingsServiceInterface;
1517
use OxidEsales\SecurityModule\PasswordPolicy\Service\ModuleSettingsServiceInterface as PasswordSettingsServiceInterface;
1618
use OxidEsales\SecurityModule\Captcha\Service\ModuleSettingsServiceInterface as CaptchaSettingsServiceInterface;
1719
use OxidEsales\Eshop\Core\Registry;
@@ -57,4 +59,27 @@ public function isTwoFAEnabledForShop(): bool
5759
{
5860
return $this->getService(TwoFAShopSettingsInterface::class)->isTwoFactorAuthEnabled();
5961
}
62+
63+
public function getSecurityModuleFormSettings(): FormSecuritySettingsServiceInterface
64+
{
65+
return $this->getService(FormSecuritySettingsServiceInterface::class);
66+
}
67+
68+
public function getHiddenParamsOnly(): string
69+
{
70+
$value = '';
71+
72+
if (($lang = $this->getFormLang())) {
73+
$value .= "\n{$lang}";
74+
}
75+
76+
$value .= $this->getAdditionalRequestParameters();
77+
78+
return $value;
79+
}
80+
81+
protected function getFormLang(): string
82+
{
83+
return Registry::getLang()->getFormLang();
84+
}
6085
}

tests/Codeception/Acceptance/BaseCest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use OxidEsales\SecurityModule\Authentication\TwoFactorAuth\Settings\TwoFAShopSettings;
1616
use OxidEsales\SecurityModule\Captcha\Service\ModuleSettingsService as CaptchaModuleSettingsService;
1717
use OxidEsales\SecurityModule\Core\Module;
18+
use OxidEsales\SecurityModule\FormSecurity\Service\ModuleSettingsService as FormSecurityModuleSettings;
1819
use OxidEsales\SecurityModule\Tests\Codeception\Support\AcceptanceTester;
1920
use OxidEsales\SecurityModule\PasswordPolicy\Service\ModuleSettingsServiceInterface as PasswordSettingsServiceInterface;
2021

@@ -56,6 +57,15 @@ protected function setTwoFactorAuthState(bool $state)
5657
);
5758
}
5859

60+
protected function setGetFormStripStokenState(bool $state): void
61+
{
62+
ContainerFacade::get(ModuleSettingServiceInterface::class)->saveBoolean(
63+
FormSecurityModuleSettings::GET_FORM_STRIP_STOKEN,
64+
$state,
65+
Module::MODULE_ID
66+
);
67+
}
68+
5969
protected function setUserTwoFAState(AcceptanceTester $I, bool $state): void
6070
{
6171
$userData = $this->getExistingUserData();
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
/**
4+
* Copyright © OXID eSales AG. All rights reserved.
5+
* See LICENSE file for license details.
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace OxidEsales\SecurityModule\Tests\Codeception\Acceptance;
11+
12+
use OxidEsales\Codeception\Page\Account\UserLogin;
13+
use OxidEsales\SecurityModule\Tests\Codeception\Support\AcceptanceTester;
14+
15+
/**
16+
* @group oe_security_module
17+
* @group oe_security_module_form_security
18+
*/
19+
class GetFormSecurityCest extends BaseCest
20+
{
21+
private const SEARCH_TERM = '1000';
22+
private const CATEGORY = 'Test category 0 [EN] šÄßüл';
23+
private const ATTRIBUTE_NAME = 'Test attribute 1 [EN] šÄßüл';
24+
private const ATTRIBUTE_VALUE = 'attr value 1 [EN] šÄßüл';
25+
26+
public function _before(AcceptanceTester $I): void
27+
{
28+
$this->setCaptchaState(false);
29+
$this->setPasswordState(false);
30+
$this->setGetFormStripStokenState(false);
31+
$this->loginUser($I);
32+
}
33+
34+
private function loginUser(AcceptanceTester $I): void
35+
{
36+
$userData = $this->getExistingUserData();
37+
$loginPage = new UserLogin($I);
38+
$I->amOnPage($loginPage->URL);
39+
$loginPage->login($userData['userLoginName'], $userData['userPassword']);
40+
}
41+
42+
public function _after(AcceptanceTester $I): void
43+
{
44+
$this->setGetFormStripStokenState(false);
45+
}
46+
47+
public function testSearchFormFunctionsByDefault(AcceptanceTester $I): void
48+
{
49+
$I->wantToTest('search form works and includes stoken in URL when setting is disabled');
50+
51+
$I->openShop()
52+
->searchFor(self::SEARCH_TERM)
53+
->seeSearchCount(1);
54+
55+
$I->seeInCurrentUrl('stoken=');
56+
}
57+
58+
public function testSearchFormStripsStokenWhenSettingIsEnabled(AcceptanceTester $I): void
59+
{
60+
$I->wantToTest('search form works and stoken is absent from URL when setting is enabled');
61+
62+
$this->setGetFormStripStokenState(true);
63+
64+
$I->openShop()
65+
->searchFor(self::SEARCH_TERM)
66+
->seeSearchCount(1);
67+
68+
$I->dontSeeInCurrentUrl('stoken=');
69+
}
70+
71+
public function testAttributeFilterFunctionsByDefault(AcceptanceTester $I): void
72+
{
73+
$I->wantToTest('attribute filter works and includes stoken in URL when setting is disabled');
74+
75+
$I->openShop()
76+
->openCategoryPage(self::CATEGORY)
77+
->selectFilter(self::ATTRIBUTE_NAME, self::ATTRIBUTE_VALUE);
78+
79+
$I->seeInCurrentUrl('stoken=');
80+
}
81+
82+
public function testAttributeFilterStripsStokenWhenSettingIsEnabled(AcceptanceTester $I): void
83+
{
84+
$I->wantToTest('attribute filter works and stoken is absent from URL when setting is enabled');
85+
86+
$this->setGetFormStripStokenState(true);
87+
88+
$I->openShop()
89+
->openCategoryPage(self::CATEGORY)
90+
->selectFilter(self::ATTRIBUTE_NAME, self::ATTRIBUTE_VALUE);
91+
92+
$I->dontSeeInCurrentUrl('stoken=');
93+
}
94+
95+
public function testResetFilterFunctionsByDefault(AcceptanceTester $I): void
96+
{
97+
$I->wantToTest('reset filter works and includes stoken in URL when setting is disabled');
98+
99+
$I->openShop()
100+
->openCategoryPage(self::CATEGORY)
101+
->selectFilter(self::ATTRIBUTE_NAME, self::ATTRIBUTE_VALUE)
102+
->resetFilter();
103+
104+
$I->seeInCurrentUrl('stoken=');
105+
}
106+
107+
public function testResetFilterStripsStokenWhenSettingIsEnabled(AcceptanceTester $I): void
108+
{
109+
$I->wantToTest('reset filter works and stoken is absent from URL when setting is enabled');
110+
111+
$this->setGetFormStripStokenState(true);
112+
113+
$I->openShop()
114+
->openCategoryPage(self::CATEGORY)
115+
->selectFilter(self::ATTRIBUTE_NAME, self::ATTRIBUTE_VALUE)
116+
->resetFilter();
117+
118+
$I->dontSeeInCurrentUrl('stoken=');
119+
}
120+
}

tests/Codeception/Support/Data/fixtures.sql

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,25 @@ REPLACE INTO `oxarticles` (`OXID`, `OXSHOPID`, `OXPARENTID`, `OXACTIVE`, `OXARTN
77
VALUES ('1000', 1, '', 1, '1000', '[DE 4] Test product 0 šÄßüл', 'Test product 0 short desc [DE]', 50, 35, 45, 55, 0, 'kg', 2, NULL, 2, 15, 1, 'In stock [DE]', 'Out of stock [DE]', '0000-00-00', '2008-02-04', '2008-02-04 17:07:48', 1, 2, 2, 'search1000', 1, '', 0, 0, '', 50, 0, '', '', 'Test product 0 [EN] šÄßüл', 'Test product 0 short desc [EN] šÄßüл', 'šÄßüл1000', '', 'In stock [EN] šÄßüл', 'Out of stock [EN] šÄßüл', 0, 'testdistributor', 'testmanufacturer', 1, 1, 'DAY');
88

99
UPDATE `oxcountry` SET `OXACTIVE` = 1 , `OXID` = 'testcountry_de' WHERE `OXISOALPHA2` = 'DE';
10+
11+
-- Form Security test data for GetFormSecurityCest
12+
13+
REPLACE INTO `oxarticles` (`OXID`, `OXSHOPID`, `OXPARENTID`, `OXACTIVE`, `OXARTNUM`, `OXTITLE`, `OXSHORTDESC`, `OXPRICE`, `OXPRICEA`, `OXPRICEB`, `OXPRICEC`, `OXTPRICE`, `OXUNITNAME`, `OXUNITQUANTITY`, `OXVAT`, `OXWEIGHT`, `OXSTOCK`, `OXSTOCKFLAG`, `OXSTOCKTEXT`, `OXNOSTOCKTEXT`, `OXDELIVERY`, `OXINSERT`, `OXTIMESTAMP`, `OXLENGTH`, `OXWIDTH`, `OXHEIGHT`, `OXSEARCHKEYS`, `OXISSEARCH`, `OXVARNAME`, `OXVARSTOCK`, `OXVARCOUNT`, `OXVARSELECT`, `OXVARMINPRICE`, `OXVARMAXPRICE`, `OXVARNAME_1`, `OXVARSELECT_1`, `OXTITLE_1`, `OXSHORTDESC_1`, `OXSEARCHKEYS_1`, `OXBUNDLEID`, `OXSTOCKTEXT_1`, `OXNOSTOCKTEXT_1`, `OXSORT`, `OXVENDORID`, `OXMANUFACTURERID`, `OXMINDELTIME`, `OXMAXDELTIME`, `OXDELTIMEUNIT`)
14+
VALUES ('1001', 1, '', 1, '1001', '[DE] Test product 1 šÄßüл', 'Test product 1 short desc [DE]', 100, 70, 90, 110, 0, 'kg', 1, NULL, 1, 10, 1, 'In stock [DE]', 'Out of stock [DE]', '0000-00-00', '2008-02-04', '2008-02-04 17:07:48', 1, 1, 1, 'search1001', 1, '', 0, 0, '', 100, 0, '', '', 'Test product 1 [EN] šÄßüл', 'Test product 1 short desc [EN] šÄßüл', 'šÄßüл1001', '', 'In stock [EN] šÄßüл', 'Out of stock [EN] šÄßüл', 0, 'testdistributor', 'testmanufacturer', 1, 1, 'DAY');
15+
16+
REPLACE INTO `oxcategories` (`OXID`, `OXPARENTID`, `OXLEFT`, `OXRIGHT`, `OXROOTID`, `OXSORT`, `OXACTIVE`, `OXACTIVE_1`, `OXHIDDEN`, `OXSHOPID`, `OXTITLE`, `OXTITLE_1`, `OXDESC`, `OXDESC_1`, `OXLONGDESC`, `OXLONGDESC_1`)
17+
VALUES ('oesm_testcat', 'oxrootid', 1, 2, 'oesm_testcat', 0, 1, 1, 0, 1, '[DE] Test Kategorie 0 šÄßüл', 'Test category 0 [EN] šÄßüл', '', '', '', '');
18+
19+
REPLACE INTO `oxattribute` (`OXID`, `OXSHOPID`, `OXTITLE`, `OXTITLE_1`, `OXPOS`)
20+
VALUES ('oesm_testattr1', 1, '[DE] Test Attribut 1 šÄßüл', 'Test attribute 1 [EN] šÄßüл', 1);
21+
22+
REPLACE INTO `oxcategory2attribute` (`OXID`, `OXOBJECTID`, `OXATTRID`, `OXSORT`)
23+
VALUES ('oesm_cat2attr1', 'oesm_testcat', 'oesm_testattr1', 1);
24+
25+
REPLACE INTO `oxobject2category` (`OXID`, `OXOBJECTID`, `OXCATNID`, `OXPOS`, `OXTIME`)
26+
VALUES ('oesm_p1000incat', '1000', 'oesm_testcat', 0, 0),
27+
('oesm_p1001incat', '1001', 'oesm_testcat', 0, 0);
28+
29+
-- Product 1000 has attribute value → survives filter; product 1001 has no value → filtered out
30+
REPLACE INTO `oxobject2attribute` (`OXID`, `OXOBJECTID`, `OXATTRID`, `OXVALUE`, `OXVALUE_1`, `OXPOS`)
31+
VALUES ('oesm_1000attr1', '1000', 'oesm_testattr1', '[DE] Attributwert 1 šÄßüл', 'attr value 1 [EN] šÄßüл', 1);

0 commit comments

Comments
 (0)