Skip to content

Commit e1bd60e

Browse files
authored
Merge pull request #39 from eliashaeussler/feature/typo3-v14
[!!!][FEATURE] Add support for TYPO3 v14 and drop support for v13
2 parents 3e11333 + 2824a37 commit e1bd60e

14 files changed

Lines changed: 747 additions & 732 deletions

File tree

.Build/composer.json

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,25 @@
99
],
1010
"require": {
1111
"derhansen/form_crshield": "@dev",
12-
"typo3/cms-core": "^13.4",
13-
"typo3/cms-extbase": "^13.4",
14-
"typo3/cms-form": "^13.4",
15-
"typo3/cms-frontend": "^13.4"
12+
"typo3/cms-core": "^14.0",
13+
"typo3/cms-extbase": "^14.0",
14+
"typo3/cms-form": "^14.0",
15+
"typo3/cms-frontend": "^14.0"
1616
},
1717
"require-dev": {
18-
"typo3/cms-backend": " ^13.4",
19-
"typo3/cms-fluid-styled-content": "^13.4",
20-
"typo3/cms-tstemplate": "^13.4",
21-
"typo3/cms-install": "^13.4",
22-
"typo3/cms-extensionmanager": "^13.4",
18+
"typo3/cms-backend": " ^14.0",
19+
"typo3/cms-fluid-styled-content": "^14.0",
20+
"typo3/cms-tstemplate": "^14.0",
21+
"typo3/cms-install": "^14.0",
22+
"typo3/cms-extensionmanager": "^14.0",
2323
"codeception/codeception": "^5.0",
2424
"codeception/module-webdriver": "^3.2.0",
2525
"codeception/module-db": "^3.0.1",
2626
"codeception/module-phpbrowser": "^3.0.0",
2727
"codeception/module-asserts": "^3.0.0"
2828
},
2929
"config": {
30+
"lock": false,
3031
"allow-plugins": {
3132
"typo3/class-alias-loader": true,
3233
"typo3/cms-composer-installers": true

.github/workflows/AcceptanceTests.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- name: Set up PHP Version
1414
uses: shivammathur/setup-php@v2
1515
with:
16-
php-version: 8.2
16+
php-version: 8.4
1717
tools: composer:v2
1818

1919
- name: Start MySQL
@@ -84,7 +84,9 @@ jobs:
8484
uses: actions/upload-artifact@v4
8585
with:
8686
name: codeception-results
87-
path: Tests/Acceptance/_output/
87+
path: |
88+
Tests/Acceptance/WithChromeBrowser/_output/
89+
Tests/Acceptance/WithPhpBrowser/_output/
8890
8991
- name: Upload logs
9092
if: ${{ failure() }}

Classes/Hooks/Form.php

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77
use Derhansen\FormCrshield\Service\ChallengeResponseService;
88
use Psr\Http\Message\ServerRequestInterface;
99
use Psr\Log\LoggerInterface;
10+
use TYPO3\CMS\Core\Attribute\AsEventListener;
1011
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
1112
use TYPO3\CMS\Core\Context\Context;
1213
use TYPO3\CMS\Core\Http\ApplicationType;
1314
use TYPO3\CMS\Core\Utility\GeneralUtility;
1415
use TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator;
1516
use TYPO3\CMS\Form\Domain\Model\FormElements\Page;
1617
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
18+
use TYPO3\CMS\Form\Event\AfterCurrentPageIsResolvedEvent;
19+
use TYPO3\CMS\Form\Event\BeforeRenderableIsValidatedEvent;
1720
use TYPO3\CMS\Frontend\Cache\CacheLifetimeCalculator;
1821
use TYPO3\CMS\Frontend\Page\PageInformation;
1922

@@ -22,7 +25,6 @@ class Form
2225
private const FIELD_ID = 'cr-field';
2326

2427
private int $currentTimestamp;
25-
private int $cacheTimeOutDefault = 86400;
2628
private array $settings;
2729

2830
public function __construct(
@@ -34,65 +36,63 @@ public function __construct(
3436
$this->settings = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('form_crshield');
3537
}
3638

37-
public function afterInitializeCurrentPage(FormRuntime $runtime, ?Page $currentPage, ?Page $page, array $args): ?Page
39+
#[AsEventListener('derhansen/form_crshield/form/after-initialize-current-page')]
40+
public function afterInitializeCurrentPage(AfterCurrentPageIsResolvedEvent $event): void
3841
{
3942
// If the form is in preview mode or we are in backend context, do not add the cr-field
40-
if (($runtime->getFormDefinition()->getRenderingOptions()['previewMode'] ?? false) ||
41-
ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()
43+
if (($event->formRuntime->getFormDefinition()->getRenderingOptions()['previewMode'] ?? false) ||
44+
ApplicationType::fromRequest($event->request)->isBackend()
4245
) {
43-
return $currentPage;
46+
return;
4447
}
4548

46-
$pageObject = $currentPage ?? $page;
49+
$pageObject = $event->currentPage ?? $event->lastDisplayedPage;
4750

48-
if ($pageObject && !$this->crFieldHasBeenVerified($runtime)) {
51+
if ($pageObject && !$this->crFieldHasBeenVerified($event->formRuntime)) {
4952
// Set delay for initial form (no delay for re-submission of form)
50-
$delay = $runtime->getFormSession() === null ? (int)($this->settings['crJavaScriptDelay'] ?? 3) : 0;
53+
$delay = $event->formRuntime->getFormSession() === null ? (int)($this->settings['crJavaScriptDelay'] ?? 3) : 0;
5154
$challenge = $this->challengeResponseService->getChallenge(
5255
(string)($this->settings['obfuscationMethod'] ?? '1'),
53-
$this->getPageExpirationTime($runtime),
56+
$this->getPageExpirationTime($event->formRuntime),
5457
$delay,
55-
$this->getHmacSalt($runtime)
58+
$this->getHmacSalt($event->formRuntime)
5659
);
5760

5861
$newElement = $pageObject->createElement(self::FIELD_ID, 'Hidden');
5962
$newElement->addValidator(new NotEmptyValidator());
6063
$newElement->setDefaultValue(base64_encode($challenge));
6164
$newElement->setProperty('fluidAdditionalAttributes', ['autocomplete' => 'off']);
6265
}
63-
64-
return $currentPage;
6566
}
6667

67-
public function afterSubmit(FormRuntime $runtime, $element, $value, $requestArguments)
68+
#[AsEventListener('derhansen/form_crshield/form/after-submit')]
69+
public function afterSubmit(BeforeRenderableIsValidatedEvent $event): void
6870
{
71+
$requestArguments = $event->request->getArguments();
72+
6973
// Write all POST data for the current page to debug log
70-
if (is_a($element, Page::class)) {
74+
if (is_a($event->renderable, Page::class)) {
7175
$this->logger->debug('Submitted data', $requestArguments);
7276
}
7377

74-
if (!(is_a($element, Page::class) || $element->getIdentifier() === self::FIELD_ID)) {
75-
return $value;
78+
if (!(is_a($event->renderable, Page::class) || $event->renderable->getIdentifier() === self::FIELD_ID)) {
79+
return;
7680
}
7781

7882
$submittedResponse = $requestArguments[self::FIELD_ID] ?? '';
79-
if (!$this->challengeResponseService->isValidResponse($submittedResponse, $this->getHmacSalt($runtime), '1')) {
83+
if (!$this->challengeResponseService->isValidResponse($submittedResponse, $this->getHmacSalt($event->formRuntime))) {
8084
$this->logger->debug('CR response validation failed', $requestArguments);
81-
return '';
85+
$event->value = '';
86+
return;
8287
}
8388

8489
// Save sha1 of HmacSalt to formstate for cr-field
85-
if ($runtime->getFormState()) {
86-
$runtime->getFormState()->setFormValue(self::FIELD_ID, sha1($this->getHmacSalt($runtime)));
87-
}
88-
89-
return $value;
90+
$event->formRuntime->getFormState()?->setFormValue(self::FIELD_ID, sha1($this->getHmacSalt($event->formRuntime)));
9091
}
9192

9293
protected function crFieldHasBeenVerified(FormRuntime $runtime): bool
9394
{
94-
return $runtime->getFormState() &&
95-
$runtime->getFormState()->getFormValue(self::FIELD_ID) === sha1($this->getHmacSalt($runtime));
95+
return $runtime->getFormState()?->getFormValue(self::FIELD_ID) === sha1($this->getHmacSalt($runtime));
9696
}
9797

9898
protected function getPageExpirationTime(FormRuntime $runtime): int
@@ -122,7 +122,6 @@ protected function getCacheTimeout(ServerRequestInterface $request): int
122122
$pageInformation->getId(),
123123
$pageInformation->getPageRecord(),
124124
$typoScriptConfigArray,
125-
$this->cacheTimeOutDefault,
126125
$this->context
127126
);
128127
}

Classes/Service/ChallengeResponseService.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,21 @@ public function __construct(
1919

2020
public function getChallenge(string $method, int $expirationTime, int $delay, string $salt): string
2121
{
22-
return $method . '|' . $expirationTime . '|' . $this->hashService->hmac((string)$expirationTime, $salt) . '|' .
22+
$challenge = $method . '|' . $expirationTime . '|' . $this->hashService->hmac((string)$expirationTime, $salt) . '|' .
2323
$delay;
24+
25+
$this->logger->debug(
26+
'Challenge generated using the following parameters: ',
27+
[
28+
'method' => $method,
29+
'expirationTime' => $expirationTime,
30+
'delay' => $delay,
31+
'salt' => $salt,
32+
'challenge' => $challenge
33+
]
34+
);
35+
36+
return $challenge;
2437
}
2538

2639
public function isValidResponse(string $submittedResponse, string $salt): bool

Configuration/Services.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,3 @@ services:
66

77
Derhansen\FormCrshield\:
88
resource: '../Classes/*'
9-
10-
Derhansen\FormCrshield\Hooks\Form:
11-
public: true
12-

README.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ Challenge/response spam shield for TYPO3 ext:form
88

99
## What is it?
1010

11-
Form challenge/response spambot shield is a TYPO3 extension to prevent automated form submissions
12-
in TYPO3 form extension.
11+
This TYPO3 extension provides a Challenge/response spam shield for TYPO3 ext:form to prevent automated
12+
form submissions.
1313

1414
## How does it work?
1515

16-
Form challenge/response spam shield adds a hidden input field to every form generated by the TYPO3 form
16+
Challenge/response spam shield adds a hidden input field to every form generated by the TYPO3 form
1717
extension. The input field has a precalculated value with a challenge. The included JavaScript will
1818
use the challenge to calculate an expected response which will be checked on every form submission.
1919
If the expected response is not submitted, validation for the hidden input field is considered as
@@ -92,9 +92,21 @@ snippet to implement the challenge/response protection in their own forms (e.g.
9292

9393
| Version | TYPO3 | PHP | Support/Development |
9494
|---------|-------------|-----------|---------------------------------------- |
95+
| 4.x | 14.x | 8.2 - 8.5 | Features, Bugfixes, Security Updates |
96+
| 3.x | 13.x | 8.2 - 8.4 | Features, Bugfixes, Security Updates |
9597
| 2.x | 13.x | 8.2 - 8.4 | Features, Bugfixes, Security Updates |
96-
| 1.x | 10.4 - 12.4 | 7.2 - 8.3 | Features, Bugfixes, Security Updates |
98+
| 1.x | 10.4 - 12.4 | 7.2 - 8.4 | Features, Bugfixes, Security Updates |
9799

100+
## Reporting a Vulnerability
101+
102+
Please report vulnerabilities to [security@typo3.org](mailto:security@typo3.org).
103+
104+
## Extensions using the challenge/response algorithm
105+
106+
The following extensions use the challenge/response algorithm:
107+
108+
* https://github.com/derhansen/form_crshield
109+
* https://github.com/derhansen/powermail_crshield
98110

99111
## Thanks for sponsoring
100112

Tests/Acceptance/Fixtures/config/system/settings.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,25 +80,25 @@
8080
'imagesizes' => [
8181
'backend' => 'TYPO3\\CMS\\Core\\Cache\\Backend\\Typo3DatabaseBackend',
8282
'options' => [
83-
'compression' => 1,
83+
'compression' => true,
8484
],
8585
],
8686
'pages' => [
8787
'backend' => 'TYPO3\\CMS\\Core\\Cache\\Backend\\Typo3DatabaseBackend',
8888
'options' => [
89-
'compression' => 1,
89+
'compression' => true,
9090
],
9191
],
9292
'pagesection' => [
9393
'backend' => 'TYPO3\\CMS\\Core\\Cache\\Backend\\Typo3DatabaseBackend',
9494
'options' => [
95-
'compression' => 1,
95+
'compression' => true,
9696
],
9797
],
9898
'rootline' => [
9999
'backend' => 'TYPO3\\CMS\\Core\\Cache\\Backend\\Typo3DatabaseBackend',
100100
'options' => [
101-
'compression' => 1,
101+
'compression' => true,
102102
],
103103
],
104104
],
Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
#!/bin/sh
22

3-
mysqldump --no-data -u$DB_USERNAME -p$DB_PASSWORD_V8 -h127.0.0.1 --port 3406 typo3_formcrshield_acceptance_v13 > typo3.sql
3+
mysqldump --no-data -u$DB_USERNAME -p$DB_PASSWORD -h127.0.0.1 --port 33066 typo3_formcrshield_acceptance_v14 > typo3.sql
44
mysqldump --no-create-info \
5-
--ignore-table=typo3_formcrshield_acceptance_v13.sys_log \
6-
--ignore-table=typo3_formcrshield_acceptance_v13.cache_hash \
7-
--ignore-table=typo3_formcrshield_acceptance_v13.cache_hash_tags \
8-
--ignore-table=typo3_formcrshield_acceptance_v13.cache_pages \
9-
--ignore-table=typo3_formcrshield_acceptance_v13.cache_pages_tags \
10-
--ignore-table=typo3_formcrshield_acceptance_v13.cache_imagesizes \
11-
--ignore-table=typo3_formcrshield_acceptance_v13.cache_imagesizes_tags \
12-
--ignore-table=typo3_formcrshield_acceptance_v13.cache_pagesection \
13-
--ignore-table=typo3_formcrshield_acceptance_v13.cache_pagesection_tags \
14-
--ignore-table=typo3_formcrshield_acceptance_v13.cache_rootline \
15-
--ignore-table=typo3_formcrshield_acceptance_v13.cache_rootline_tags \
16-
--ignore-table=typo3_formcrshield_acceptance_v13.tx_extensionmanager_domain_model_extension \
17-
--ignore-table=typo3_formcrshield_acceptance_v13.sys_http_report \
18-
-u$DB_USERNAME -p$DB_PASSWORD_V8 -h127.0.0.1 --port 3406 typo3_formcrshield_acceptance_v13 >> typo3.sql
5+
--ignore-table=typo3_formcrshield_acceptance_v14.sys_log \
6+
--ignore-table=typo3_formcrshield_acceptance_v14.cache_hash \
7+
--ignore-table=typo3_formcrshield_acceptance_v14.cache_hash_tags \
8+
--ignore-table=typo3_formcrshield_acceptance_v14.cache_pages \
9+
--ignore-table=typo3_formcrshield_acceptance_v14.cache_pages_tags \
10+
--ignore-table=typo3_formcrshield_acceptance_v14.cache_imagesizes \
11+
--ignore-table=typo3_formcrshield_acceptance_v14.cache_imagesizes_tags \
12+
--ignore-table=typo3_formcrshield_acceptance_v14.cache_pagesection \
13+
--ignore-table=typo3_formcrshield_acceptance_v14.cache_pagesection_tags \
14+
--ignore-table=typo3_formcrshield_acceptance_v14.cache_rootline \
15+
--ignore-table=typo3_formcrshield_acceptance_v14.cache_rootline_tags \
16+
--ignore-table=typo3_formcrshield_acceptance_v14.tx_extensionmanager_domain_model_extension \
17+
--ignore-table=typo3_formcrshield_acceptance_v14.sys_http_report \
18+
-u$DB_USERNAME -p$DB_PASSWORD -h127.0.0.1 --port 33066 typo3_formcrshield_acceptance_v14 >> typo3.sql

0 commit comments

Comments
 (0)