[BUGFIX] Fix incomplete validation of source_host field
[Packages/TYPO3.CMS.git] / typo3 / sysext / redirects / Classes / Evaluation / SourceHost.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Redirects\Evaluation;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 /**
20 * Class SourceHost
21 * Triggered from DataHandler as TCA formevals hook for validation / sanitation of domain values.
22 */
23 class SourceHost
24 {
25 /**
26 * JavaScript code for client side validation/evaluation
27 *
28 * @return string JavaScript code for client side validation/evaluation
29 */
30 public function returnFieldJS(): string
31 {
32 $jsCode = [];
33 $jsCode[] = 'if (value === \'*\') {return value;}';
34 $jsCode[] = 'var parser = document.createElement(\'a\');';
35 $jsCode[] = 'parser.href = value.indexOf(\'://\') != -1 ? value : \'http://\' + value;';
36 $jsCode[] = 'return parser.host;';
37 return implode(' ', $jsCode);
38 }
39
40 /**
41 * Server-side removing of protocol on save
42 *
43 * @param string $value The field value to be evaluated
44 * @return string Evaluated field value
45 */
46 public function evaluateFieldValue(string $value): string
47 {
48 // 1) Special case: * means any domain
49 if ($value === '*') {
50 return $value;
51 }
52
53 // 2) Check if value contains a protocol like http:// https:// etc...
54 if (strpos($value, '://') !== false) {
55 $tmp = $this->parseUrl($value);
56 if (!empty($tmp)) {
57 return $tmp;
58 }
59 }
60
61 // 3) Check domain name
62 // remove anything after the first "/"
63 $checkValue = $value;
64 if (strpos($value, '/') !== false) {
65 $checkValue = substr($value, 0, strpos($value, '/'));
66 }
67 $validHostnameRegex = '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/';
68 if (preg_match_all($validHostnameRegex, $checkValue, $matches, PREG_SET_ORDER) !== false) {
69 if (!empty($matches)) {
70 return $checkValue;
71 }
72 }
73
74 // 4) IPv4 or IPv6
75 $isIP = filter_var($value, FILTER_VALIDATE_IP) === $value;
76 if ($isIP) {
77 return $value;
78 }
79
80 return '';
81 }
82
83 /**
84 * @param string $value
85 * @return string
86 */
87 protected function parseUrl(string $value): string
88 {
89 $urlParts = parse_url($value);
90 if (!empty($urlParts['host'])) {
91 $value = $urlParts['host'];
92
93 // Special case IPv6 with protocol: http://[2001:0db8:85a3:08d3::0370:7344]/
94 // $urlParts['host'] will be [2001:0db8:85a3:08d3::0370:7344]
95 $ipv6Pattern = '/\[([a-zA-Z0-9:]*)\]/';
96 preg_match_all($ipv6Pattern, $urlParts['host'], $ipv6Matches, PREG_SET_ORDER);
97 if (!empty($ipv6Matches[0][1])) {
98 $value = $ipv6Matches[0][1];
99 }
100 }
101 return $value;
102 }
103 }