Skip to content

Commit b79ab08

Browse files
committed
[FEATURE] Return status 400 for invalid FE requests
and set "X-Robots-Tag: noindex, nofollow" for all TypeNum requests
1 parent a3eab5c commit b79ab08

3 files changed

Lines changed: 115 additions & 112 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
namespace In2code\Lux\Controller;
5+
6+
use In2code\Lux\Exception\DisallowedUserAgentException;
7+
use In2code\Lux\Exception\FingerprintMustNotBeEmptyException;
8+
use In2code\Lux\Exception\RateLimitException;
9+
use In2code\Lux\Exception\Validation\IdentificatorFormatException;
10+
use Psr\Http\Message\ResponseInterface;
11+
use Throwable;
12+
use TYPO3\CMS\Core\Http\JsonResponse;
13+
use TYPO3\CMS\Core\Http\PropagateResponseException;
14+
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
15+
16+
abstract class AbstractFrontendController extends ActionController
17+
{
18+
protected const STATUS_BAD_REQUEST = 400;
19+
protected const STATUS_FORBIDDEN = 403;
20+
protected const STATUS_TOO_MANY_REQUESTS = 429;
21+
protected const STATUS_INTERNAL_SERVER_ERROR = 500;
22+
protected const EXCEPTION_STATUS_MAP = [
23+
FingerprintMustNotBeEmptyException::class => self::STATUS_BAD_REQUEST,
24+
IdentificatorFormatException::class => self::STATUS_BAD_REQUEST,
25+
DisallowedUserAgentException::class => self::STATUS_FORBIDDEN,
26+
RateLimitException::class => self::STATUS_TOO_MANY_REQUESTS,
27+
];
28+
protected const HEADERS_ROBOTS = ['X-Robots-Tag' => 'noindex, nofollow'];
29+
30+
protected function assertRequiredArguments(array $argumentNames): void
31+
{
32+
foreach ($argumentNames as $argumentName) {
33+
if ($this->request->hasArgument($argumentName) === false) {
34+
$this->propagateClientError('Required argument "' . $argumentName . '" is not set', 1787577001);
35+
}
36+
}
37+
}
38+
39+
protected function assertNotEmptyArguments(array $argumentNames): void
40+
{
41+
$this->assertRequiredArguments($argumentNames);
42+
foreach ($argumentNames as $argumentName) {
43+
$value = $this->request->getArgument($argumentName);
44+
if ($value === '' || $value === []) {
45+
$this->propagateClientError('Required argument "' . $argumentName . '" is empty', 1787577002);
46+
}
47+
}
48+
}
49+
50+
protected function propagateClientError(string $message, int $code): never
51+
{
52+
throw new PropagateResponseException(
53+
$this->getErrorResponse($message, $code, self::STATUS_BAD_REQUEST),
54+
1787577003
55+
);
56+
}
57+
58+
protected function getStatus(Throwable $exception): int
59+
{
60+
foreach (self::EXCEPTION_STATUS_MAP as $exceptionClassName => $status) {
61+
if ($exception instanceof $exceptionClassName) {
62+
return $status;
63+
}
64+
}
65+
return self::STATUS_INTERNAL_SERVER_ERROR;
66+
}
67+
68+
protected function getErrorResponse(string $message, int $code, int $status): ResponseInterface
69+
{
70+
return new JsonResponse(
71+
[
72+
'error' => [
73+
'message' => $message,
74+
'code' => $code,
75+
],
76+
],
77+
$status,
78+
self::HEADERS_ROBOTS
79+
);
80+
}
81+
}

Classes/Controller/FrontendController.php

Lines changed: 9 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
declare(strict_types=1);
44
namespace In2code\Lux\Controller;
55

6-
use Doctrine\DBAL\Driver\Exception;
7-
use Doctrine\DBAL\Exception as ExceptionDbal;
86
use In2code\Lux\Domain\Factory\VisitorFactory;
97
use In2code\Lux\Domain\Model\Visitor;
108
use In2code\Lux\Domain\Repository\PagevisitRepository;
@@ -22,26 +20,17 @@
2220
use In2code\Lux\Domain\Tracker\PageTracker;
2321
use In2code\Lux\Domain\Tracker\SearchTracker;
2422
use In2code\Lux\Events\AfterTrackingEvent;
25-
use In2code\Lux\Exception\ActionNotAllowedException;
26-
use In2code\Lux\Exception\ConfigurationException;
27-
use In2code\Lux\Exception\EmailValidationException;
2823
use In2code\Lux\Exception\FakeException;
2924
use In2code\Lux\Utility\BackendUtility;
3025
use In2code\Lux\Utility\ConfigurationUtility;
3126
use Psr\Http\Message\ResponseInterface;
3227
use Psr\Log\LoggerInterface;
3328
use Throwable;
34-
use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException;
35-
use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException;
3629
use TYPO3\CMS\Core\Resource\ResourceFactory;
3730
use TYPO3\CMS\Core\Utility\GeneralUtility;
38-
use TYPO3\CMS\Extbase\Configuration\Exception\InvalidConfigurationTypeException;
3931
use TYPO3\CMS\Extbase\Http\ForwardResponse;
40-
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
41-
use TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException;
42-
use TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException;
4332

44-
class FrontendController extends ActionController
33+
class FrontendController extends AbstractFrontendController
4534
{
4635
protected array $allowedActions = [
4736
'pageRequest',
@@ -68,27 +57,14 @@ public function __construct(
6857
) {
6958
}
7059

71-
/**
72-
* @return void
73-
* @throws ActionNotAllowedException
74-
* @noinspection PhpUnused
75-
*/
7660
public function initializeDispatchRequestAction(): void
7761
{
78-
$action = $this->request->getArgument('dispatchAction');
79-
if (!in_array($action, $this->allowedActions)) {
80-
throw new ActionNotAllowedException('Action not allowed', 1518815149);
62+
$this->assertRequiredArguments(['dispatchAction', 'identificator', 'arguments']);
63+
if (in_array($this->request->getArgument('dispatchAction'), $this->allowedActions) === false) {
64+
$this->propagateClientError('Action not allowed', 1518815149);
8165
}
8266
}
8367

84-
/**
85-
* @param string $dispatchAction
86-
* @param string $identificator Fingerprint or Local storage hash
87-
* @param array $arguments
88-
* @return ResponseInterface
89-
* @noinspection PhpUnused
90-
* @throws InvalidConfigurationTypeException
91-
*/
9268
public function dispatchRequestAction(
9369
string $dispatchAction,
9470
string $identificator,
@@ -101,12 +77,6 @@ public function dispatchRequestAction(
10177
return $this->jsonResponse(json_encode(['error' => true, 'status' => 'disabled']));
10278
}
10379

104-
/**
105-
* @param string $identificator
106-
* @param array $arguments
107-
* @return ResponseInterface
108-
* @noinspection PhpUnused
109-
*/
11080
public function pageRequestAction(string $identificator, array $arguments): ResponseInterface
11181
{
11282
try {
@@ -162,12 +132,6 @@ public function searchRequestAction(string $identificator, array $arguments): Re
162132
}
163133
}
164134

165-
/**
166-
* @param string $identificator
167-
* @param array $arguments
168-
* @return ResponseInterface
169-
* @noinspection PhpUnused
170-
*/
171135
public function fieldListeningRequestAction(string $identificator, array $arguments): ResponseInterface
172136
{
173137
try {
@@ -185,12 +149,6 @@ public function fieldListeningRequestAction(string $identificator, array $argume
185149
}
186150
}
187151

188-
/**
189-
* @param string $identificator
190-
* @param array $arguments
191-
* @return ResponseInterface
192-
* @noinspection PhpUnused
193-
*/
194152
public function formListeningRequestAction(string $identificator, array $arguments): ResponseInterface
195153
{
196154
try {
@@ -211,12 +169,6 @@ public function formListeningRequestAction(string $identificator, array $argumen
211169
}
212170
}
213171

214-
/**
215-
* @param string $identificator
216-
* @param array $arguments
217-
* @return ResponseInterface
218-
* @noinspection PhpUnused
219-
*/
220172
public function email4LinkRequestAction(string $identificator, array $arguments): ResponseInterface
221173
{
222174
try {
@@ -254,12 +206,6 @@ public function email4LinkRequestAction(string $identificator, array $arguments)
254206
}
255207
}
256208

257-
/**
258-
* @param string $identificator
259-
* @param array $arguments
260-
* @return ResponseInterface
261-
* @noinspection PhpUnused
262-
*/
263209
public function downloadRequestAction(string $identificator, array $arguments): ResponseInterface
264210
{
265211
try {
@@ -272,12 +218,6 @@ public function downloadRequestAction(string $identificator, array $arguments):
272218
}
273219
}
274220

275-
/**
276-
* @param string $identificator
277-
* @param array $arguments
278-
* @return ResponseInterface
279-
* @noinspection PhpUnused
280-
*/
281221
public function linkClickRequestAction(string $identificator, array $arguments): ResponseInterface
282222
{
283223
try {
@@ -309,13 +249,6 @@ public function redirectRequestAction(string $identificator): ResponseInterface
309249
return $this->jsonResponse(json_encode($this->afterAction($visitor)));
310250
}
311251

312-
/**
313-
* @param string $identificator
314-
* @param array $arguments
315-
* @return ResponseInterface
316-
* @throws IllegalObjectTypeException
317-
* @throws UnknownObjectException
318-
*/
319252
public function abTestingRequestAction(string $identificator, array $arguments): ResponseInterface
320253
{
321254
try {
@@ -336,12 +269,6 @@ public function abTestingRequestAction(string $identificator, array $arguments):
336269
return $this->jsonResponse(json_encode($result));
337270
}
338271

339-
/**
340-
* @param string $identificator
341-
* @param array $arguments
342-
* @return ResponseInterface
343-
* @throws ExceptionDbal
344-
*/
345272
public function abTestingConversionFulfilledRequestAction(
346273
string $identificator,
347274
array $arguments
@@ -362,14 +289,11 @@ public function abTestingConversionFulfilledRequestAction(
362289
return $this->jsonResponse(json_encode($this->afterAction($visitor)));
363290
}
364291

365-
/**
366-
* @param string $title
367-
* @param string $text
368-
* @param string $href
369-
* @param array $arguments
370-
* @return ResponseInterface
371-
* @noinspection PhpUnused
372-
*/
292+
public function initializeEmail4linkAction(): void
293+
{
294+
$this->assertRequiredArguments(['title', 'text', 'href']);
295+
}
296+
373297
public function email4linkAction(
374298
string $title,
375299
string $text,
@@ -387,10 +311,6 @@ public function email4linkAction(
387311
return $this->jsonResponse(json_encode(['html' => $this->view->render()]));
388312
}
389313

390-
/**
391-
* @return ResponseInterface
392-
* @noinspection PhpUnused
393-
*/
394314
public function trackingOptOutAction(): ResponseInterface
395315
{
396316
return $this->htmlResponse();
@@ -404,10 +324,6 @@ public function trackingOptOutAction(): ResponseInterface
404324
* @param Visitor $visitor
405325
* @param array $arguments
406326
* @return void
407-
* @throws EmailValidationException
408-
* @throws IllegalObjectTypeException
409-
* @throws InvalidConfigurationTypeException
410-
* @throws UnknownObjectException
411327
*/
412328
protected function callAdditionalTrackers(Visitor $visitor, array $arguments): void
413329
{
@@ -457,19 +373,6 @@ protected function getError(Throwable $exception): array
457373
];
458374
}
459375

460-
/**
461-
* @param string $identificator
462-
* @param bool $tempVisitor
463-
* @return Visitor
464-
* @throws ConfigurationException
465-
* @throws ExceptionDbal
466-
* @throws ExtensionConfigurationExtensionNotConfiguredException
467-
* @throws ExtensionConfigurationPathDoesNotExistException
468-
* @throws IllegalObjectTypeException
469-
* @throws InvalidConfigurationTypeException
470-
* @throws UnknownObjectException
471-
* @throws Exception
472-
*/
473376
protected function getVisitor(string $identificator, bool $tempVisitor = false): Visitor
474377
{
475378
$visitorFactory = GeneralUtility::makeInstance(VisitorFactory::class, $identificator, $tempVisitor);

Configuration/TypoScript/Lux/12_PageTypeNumConfiguration.typoscript

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ luxPageRequestAjax = PAGE
33
luxPageRequestAjax {
44
typeNum = 1518815717
55
config {
6-
additionalHeaders.10 {
7-
header = Content-Type: application/json
8-
replace = 1
6+
additionalHeaders {
7+
10 {
8+
header = Content-Type: application/json
9+
replace = 1
10+
}
11+
20 {
12+
header = X-Robots-Tag: noindex, nofollow
13+
replace = 1
14+
}
915
}
1016
disableAllHeaderCode = 1
1117
disablePrefixComment = 1
@@ -50,6 +56,13 @@ luxRedirect {
5056
robots = noindex,nofollow
5157
}
5258

59+
config {
60+
additionalHeaders.20 {
61+
header = X-Robots-Tag: noindex, nofollow
62+
replace = 1
63+
}
64+
}
65+
5366
includeJSFooter {
5467
lux = EXT:lux/Resources/Public/JavaScript/Lux/Lux.min.js
5568
lux {
@@ -167,9 +180,15 @@ luxConfigurationFieldIdentification = PAGE
167180
luxConfigurationFieldIdentification {
168181
typeNum = 1517985223
169182
config {
170-
additionalHeaders.10 {
171-
header = Content-Type: text/javascript
172-
replace = 1
183+
additionalHeaders {
184+
10 {
185+
header = Content-Type: text/javascript
186+
replace = 1
187+
}
188+
20 {
189+
header = X-Robots-Tag: noindex, nofollow
190+
replace = 1
191+
}
173192
}
174193
disableAllHeaderCode = 1
175194
disablePrefixComment = 1

0 commit comments

Comments
 (0)