diff --git a/wire/config.php b/wire/config.php index ed0c7ba9..5553ee26 100644 --- a/wire/config.php +++ b/wire/config.php @@ -985,7 +985,7 @@ $config->protectCSRF = true; * @var int * */ -$config->maxUrlSegments = 4; +$config->maxUrlSegments = 20; /** * Maximum length for any individual URL segment (default=128) @@ -1004,7 +1004,23 @@ $config->maxUrlSegmentLength = 128; * @var int * */ -$config->maxUrlDepth = 30; +$config->maxUrlDepth = 30; + +/** + * Long URL response (URL depth, length or segments overflow) + * + * HTTP code that ProcessWire should respond with when it receives more URL segments, + * more URL depth, or longer URL length than what is allowed. Suggested values: + * + * - `404`: Page not found + * - `301`: Redirect to closest allowed URL (permanent) + * - `302`: Redirect to closest allowed URL (temporary) + * + * @var int + * @since 3.0.243 + * + */ +$config->longUrlResponse = 404; /** * Pagination URL prefix diff --git a/wire/core/Config.php b/wire/core/Config.php index 86439d6f..3bf67ba1 100644 --- a/wire/core/Config.php +++ b/wire/core/Config.php @@ -91,6 +91,7 @@ * @property int $maxUrlSegments Maximum number of extra stacked URL segments allowed in a page's URL (including page numbers) #pw-group-URLs * @property int $maxUrlSegmentLength Maximum length of any individual URL segment (default=128). #pw-group-URLs * @property int $maxUrlDepth Maximum URL/path slashes (depth) for request URLs. (Min=10, Max=60) #pw-group-URLs + * @property int $longUrlResponse Response code when URL segments, depth or length exceeds max allowed. #pw-group-URLs @since 3.0.243 * @property string $wireInputOrder Order that variables with the $input API var are handled when you access $input->var. #pw-group-HTTP-and-input * @property bool $wireInputLazy Specify true for $input API var to load input data in a lazy fashion and potentially use less memory. Default is false. #pw-group-HTTP-and-input * @property int $wireInputArrayDepth Maximum multi-dimensional array depth for input variables accessed from $input or 1 to only allow single dimension arrays. #pw-group-HTTP-and-input @since 3.0.178 diff --git a/wire/core/PagesPathFinder.php b/wire/core/PagesPathFinder.php index 0c27edae..d8844afb 100644 --- a/wire/core/PagesPathFinder.php +++ b/wire/core/PagesPathFinder.php @@ -380,8 +380,10 @@ class PagesPathFinder extends Wire { * */ protected function applyPagesRow(array $parts, $row) { - - $maxUrlSegmentLength = $this->wire()->config->maxUrlSegmentLength; + + $config = $this->wire()->config; + $maxUrlSegmentLength = $config->maxUrlSegmentLength; + $maxUrlSegments = $config->maxUrlSegments; $result = &$this->result; // array of [language name] => [ 'a', 'b', 'c' ] (from /a/b/c/) @@ -396,14 +398,28 @@ class PagesPathFinder extends Wire { if(!$id) { // if it didn’t resolve to DB page name then it is a URL segment - if(strlen($name) > $maxUrlSegmentLength) $name = substr($name, 0, $maxUrlSegmentLength); - $result['urlSegments'][] = $name; - if($this->verbose) { - $result['parts'][] = array( - 'type' => 'urlSegment', - 'value' => $name, - 'language' => '' - ); + if(strlen($name) > $maxUrlSegmentLength) { + $name = substr($name, 0, $maxUrlSegmentLength); + if($config->longUrlResponse >= 300) { + $result['response'] = $config->longUrlResponse; + $this->addResultError('urlSegmentLength', 'URL segment length > config.maxUrlSegmentLength'); + } + } + if(count($result['urlSegments']) + 1 > $maxUrlSegments) { + if($config->longUrlResponse >= 300) { + $this->addResultError('urlSegmentMAX', 'Number of URL segments exceeds config.maxUrlSegments'); + $result['response'] = $config->longUrlResponse; + break; + } + } else { + $result['urlSegments'][] = $name; + if($this->verbose) { + $result['parts'][] = array( + 'type' => 'urlSegment', + 'value' => $name, + 'language' => '' + ); + } } continue; } @@ -480,7 +496,7 @@ class PagesPathFinder extends Wire { * If language segment detected then remove it and populate language to result * * @param string $path - * @return array|bool + * @return array * */ protected function getPathParts($path) { @@ -497,7 +513,7 @@ class PagesPathFinder extends Wire { $lastPart = ''; if($this->strlen($path) > $maxPathLength) { - $result['response'] = 414; // 414=URI too long + $result['response'] = $config->longUrlResponse; // 414=URI too long $this->addResultError('pathLengthMAX', "Path length exceeds max allowed $maxPathLength"); $path = substr($path, 0, $maxPathLength); } @@ -506,7 +522,7 @@ class PagesPathFinder extends Wire { if(count($parts) > $maxDepth) { $parts = array_slice($parts, 0, $maxDepth); - $result['response'] = 414; + $result['response'] = $config->longUrlResponse; $this->addResultError('pathDepthMAX', 'Path depth exceeds config.maxUrlDepth'); } else if($path === '/' || $path === '' || !count($parts)) { return array(); @@ -1484,7 +1500,7 @@ class PagesPathFinder extends Wire { * */ protected function addResultError($name, $message, $force = false) { - if(!$this->verbose && !$force) return; + //if(!$this->verbose && !$force) return; $this->result['errors'][$name] = $message; } diff --git a/wire/core/PagesRequest.php b/wire/core/PagesRequest.php index cb47dad5..4675ecf5 100644 --- a/wire/core/PagesRequest.php +++ b/wire/core/PagesRequest.php @@ -593,9 +593,15 @@ class PagesRequest extends Wire { } $maxUrlDepth = $config->maxUrlDepth; - if($maxUrlDepth > 0 && substr_count($it, '/') > $config->maxUrlDepth) { - $this->setResponseCode(414, 'Request URL exceeds max depth set in $config->maxUrlDepth'); - return false; + if($maxUrlDepth > 0 && substr_count($it, '/') > $maxUrlDepth) { + if(in_array($config->longUrlResponse, [ 302, 301 ])) { + $parts = array_slice(explode('/', $it), 0, $maxUrlDepth); + $it = '/' . trim(implode('/', $parts), '/') . '/'; + $this->setRedirectPath($it, $config->longUrlResponse); + } else { + $this->setResponseCode($config->longUrlResponse, 'Request URL exceeds max depth set in $config->maxUrlDepth'); + return false; + } } if(!isset($it[0]) || $it[0] != '/') $it = "/$it";