|
15 | 15 |
|
16 | 16 | use Closure; |
17 | 17 | use CodeIgniter\Exceptions\PageNotFoundException; |
| 18 | +use CodeIgniter\HTTP\Exceptions\BadRequestException; |
18 | 19 | use CodeIgniter\HTTP\Exceptions\RedirectException; |
19 | 20 | use CodeIgniter\HTTP\Method; |
20 | 21 | use CodeIgniter\HTTP\Request; |
@@ -130,11 +131,23 @@ class Router implements RouterInterface |
130 | 131 |
|
131 | 132 | protected ?AutoRouterInterface $autoRouter = null; |
132 | 133 |
|
| 134 | + /** |
| 135 | + * Permitted URI chars |
| 136 | + * |
| 137 | + * The default value is `''` (do not check) for backward compatibility. |
| 138 | + */ |
| 139 | + protected string $permittedURIChars = ''; |
| 140 | + |
133 | 141 | /** |
134 | 142 | * Stores a reference to the RouteCollection object. |
135 | 143 | */ |
136 | 144 | public function __construct(RouteCollectionInterface $routes, ?Request $request = null) |
137 | 145 | { |
| 146 | + $config = config(App::class); |
| 147 | + if (isset($config->permittedURIChars)) { |
| 148 | + $this->permittedURIChars = $config->permittedURIChars; |
| 149 | + } |
| 150 | + |
138 | 151 | $this->collection = $routes; |
139 | 152 |
|
140 | 153 | // These are only for auto-routing |
@@ -187,6 +200,8 @@ public function handle(?string $uri = null) |
187 | 200 | // Decode URL-encoded string |
188 | 201 | $uri = urldecode($uri); |
189 | 202 |
|
| 203 | + $this->checkDisallowedChars($uri); |
| 204 | + |
190 | 205 | // Restart filterInfo |
191 | 206 | $this->filtersInfo = []; |
192 | 207 |
|
@@ -424,7 +439,7 @@ protected function checkRoutes(string $uri): bool |
424 | 439 | }, is_array($handler) ? key($handler) : $handler); |
425 | 440 |
|
426 | 441 | throw new RedirectException( |
427 | | - preg_replace('#^' . $routeKey . '$#u', $redirectTo, $uri), |
| 442 | + preg_replace('#\A' . $routeKey . '\z#u', $redirectTo, $uri), |
428 | 443 | $this->collection->getRedirectCode($routeKey) |
429 | 444 | ); |
430 | 445 | } |
@@ -484,7 +499,7 @@ protected function checkRoutes(string $uri): bool |
484 | 499 |
|
485 | 500 | if (config(Routing::class)->multipleSegmentsOneParam === false) { |
486 | 501 | // Using back-references |
487 | | - $segments = explode('/', preg_replace('#^' . $routeKey . '$#u', $handler, $uri)); |
| 502 | + $segments = explode('/', preg_replace('#\A' . $routeKey . '\z#u', $handler, $uri)); |
488 | 503 | } else { |
489 | 504 | if (str_contains($methodAndParams, '/')) { |
490 | 505 | [$method, $handlerParams] = explode('/', $methodAndParams, 2); |
@@ -708,4 +723,20 @@ protected function setMatchedRoute(string $route, $handler): void |
708 | 723 |
|
709 | 724 | $this->matchedRouteOptions = $this->collection->getRoutesOptions($route); |
710 | 725 | } |
| 726 | + |
| 727 | + /** |
| 728 | + * Checks disallowed characters |
| 729 | + */ |
| 730 | + private function checkDisallowedChars(string $uri): void |
| 731 | + { |
| 732 | + foreach (explode('/', $uri) as $segment) { |
| 733 | + if ($segment !== '' && $this->permittedURIChars !== '' |
| 734 | + && preg_match('/\A[' . $this->permittedURIChars . ']+\z/iu', $segment) !== 1 |
| 735 | + ) { |
| 736 | + throw new BadRequestException( |
| 737 | + 'The URI you submitted has disallowed characters: "' . $segment . '"' |
| 738 | + ); |
| 739 | + } |
| 740 | + } |
| 741 | + } |
711 | 742 | } |
0 commit comments