CVE-2022-24739 (published 2022-03-08T22:15Z)

alltube is an html front end for youtube-dl. On releases prior to 3.0.3, an attacker could craft a special HTML page to trigger either an open redirect attack or a Server-Side Request Forgery attack (depending on how AllTube is configured). The impact is mitigated by the fact the SSRF attack is only possible when the `stream` option is enabled in the configuration. (This option is disabled by default.) 3.0.3 contains a fix for this vulnerability.

Severity: MEDIUM
CVSS v2 base score: 4.0 (AV:N/AC:H/Au:N/C:P/I:P/A:N)
CVSS v3 base score: 6.1, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N)

Weakness information:
cwe_id: CWE-601
cwe_name: URL Redirection to Untrusted Site ('Open Redirect')
description: A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This simplifies phishing attacks.
extended_description: An http parameter may contain a URL value and could cause the web application to redirect the request to the specified URL. By modifying the URL value to a malicious site, an attacker may successfully launch a phishing scam and steal user credentials. Because the server name in the modified link is identical to the original site, phishing attempts have a more trustworthy appearance. Whether this issue poses a vulnerability will be subject to the intended behavior of the application. For example, a search engine might intentionally provide redirects to arbitrary URLs.
         
cwe_id: CWE-918
cwe_name: Server-Side Request Forgery (SSRF)
description: The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
extended_description: By providing URLs to unexpected hosts or ports, attackers can make it appear that the server is sending the request, possibly bypassing access controls such as firewalls that prevent the attackers from accessing the URLs directly. The server can be used as a proxy to conduct port scanning of hosts in internal networks, use other URLs such as that can access documents on the system (using file://), or use other protocols such as gopher:// or tftp://, which may provide greater control over the contents of requests.
         

Fix information:
repo_url: https://github.com/Rudloff/alltube
commit_details: repo_url: https://github.com/Rudloff/alltube
author: Pierre Rudloff
author_date: 2022-03-08 09:29:57+01:00
msg: Disable the generic extractor entirely
It can be used for SSRF attacks even when redirects are disabled
repository: repo_url: https://github.com/Rudloff/alltube
repo_name: Rudloff/alltube
description: Web GUI for youtube-dl
repo_language: PHP
owner: Rudloff
file_changes: filename: composer.json
change_type: ModificationType.MODIFY
diff: @@ -78,7 +78,7 @@
     ],
     "patches": {
       "ytdl-org/youtube-dl": {
-        "Disable redirects in generic extractor": "patches/youtube-dl-redirect.diff"
+        "Disable the generic extractor": "patches/youtube-dl-disable-generic.diff"
       }
     }
   },

programming_language: Jupyter Notebook
filename: composer.lock
change_type: ModificationType.MODIFY
diff: @@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "b5c12c24e723e5246b8003cd84de873f",
+    "content-hash": "87b2074ab1d8a7ee59f719e26130ca36",
     "packages": [
         {
             "name": "aura/session",
@@ -4086,12 +4086,12 @@
             },
             "type": "library",
             "autoload": {
-                "psr-4": {
-                    "Amp\\Serialization\\": "src"
-                },
                 "files": [
                     "src/functions.php"
-                ]
+                ],
+                "psr-4": {
+                    "Amp\\Serialization\\": "src"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [

programming_language: JavaScript
filename: youtube-dl-disable-generic.diff
change_type: ModificationType.ADD
diff: @@ -0,0 +1,12 @@
+diff --git a/youtube_dl/extractor/__init__.py b/youtube_dl/extractor/__init__.py
+index 18d8dbcd6..4d3edfac3 100644
+--- a/youtube_dl/extractor/__init__.py
++++ b/youtube_dl/extractor/__init__.py
+@@ -13,7 +13,6 @@ except ImportError:
+         for name, klass in globals().items()
+         if name.endswith('IE') and name != 'GenericIE'
+     ]
+-    _ALL_CLASSES.append(GenericIE)
+ 
+ 
+ def gen_extractor_classes():

programming_language: Python
filename: youtube-dl-redirect.diff
change_type: ModificationType.DELETE
diff: @@ -1,12 +0,0 @@
-diff --git a/youtube_dl/extractor/generic.py b/youtube_dl/extractor/generic.py
-index f99d887ca..749ed6ecf 100644
---- a/youtube_dl/extractor/generic.py
-+++ b/youtube_dl/extractor/generic.py
-@@ -2252,6 +2252,7 @@ class GenericIE(InfoExtractor):
- 
-     def report_following_redirect(self, new_url):
-         """Report information extraction."""
-+        raise UnsupportedError('Redirects are not allowed')
-         self._downloader.to_screen('[redirect] Following redirect to %s' % new_url)
- 
-     def _extract_rss(self, url, video_id, doc):

programming_language: unknown
repo_url: https://github.com/Rudloff/alltube
commit_details: repo_url: https://github.com/Rudloff/alltube
author: Pierre Rudloff
author_date: 2022-02-27 10:54:56+01:00
msg: Prevent SSRF requests
By validating the provided URL before passing it to youtube-dl
repository: repo_url: https://github.com/Rudloff/alltube
repo_name: Rudloff/alltube
description: Web GUI for youtube-dl
repo_language: PHP
owner: Rudloff
file_changes: filename: BaseController.php
change_type: ModificationType.MODIFY
diff: @@ -11,6 +11,9 @@ use Alltube\Library\Downloader;
 use Alltube\Library\Video;
 use Alltube\LocaleManager;
 use Aura\Session\Segment;
+use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
+use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Options;
+use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Url;
 use Psr\Container\ContainerInterface;
 use Psr\Log\LoggerInterface;
 use Slim\Http\Request;
@@ -127,10 +130,11 @@ abstract class BaseController
      * @param Request $request PSR-7 request
      *
      * @return string|null Password
+     * @throws InvalidURLException
      */
     protected function getPassword(Request $request): ?string
     {
-        $url = $request->getQueryParam('url');
+        $url = $this->getVideoPageUrl($request);
 
         $password = $request->getParam('password');
         if (isset($password)) {
@@ -157,4 +161,19 @@ abstract class BaseController
 
         return $controller->displayError($request, $response, $message);
     }
+
+    /**
+     * @param Request $request
+     * @return string
+     * @throws InvalidURLException
+     */
+    protected function getVideoPageUrl(Request $request): string
+    {
+        $url = $request->getQueryParam('url') ?: $request->getQueryParam('v');
+
+        // Prevent SSRF attacks.
+        $parts = Url::validateUrl($url, new Options());
+
+        return $parts['url'];
+    }
 }

programming_language: PHP
filename: DownloadController.php
change_type: ModificationType.MODIFY
diff: @@ -19,6 +19,7 @@ use Alltube\Library\Exception\YoutubedlException;
 use Alltube\Stream\ConvertedPlaylistArchiveStream;
 use Alltube\Stream\PlaylistArchiveStream;
 use Alltube\Stream\YoutubeStream;
+use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
 use Slim\Http\Request;
 use Slim\Http\Response;
 use Slim\Http\StatusCode;
@@ -37,56 +38,53 @@ class DownloadController extends BaseController
      *
      * @return Response HTTP response
      * @throws AlltubeLibraryException
+     * @throws InvalidURLException
      */
     public function download(Request $request, Response $response): Response
     {
-        $url = $request->getQueryParam('url');
+        $url = $this->getVideoPageUrl($request);
 
-        if (isset($url)) {
-            $this->video = $this->downloader->getVideo($url, $this->getFormat($request), $this->getPassword($request));
+        $this->video = $this->downloader->getVideo($url, $this->getFormat($request), $this->getPassword($request));
 
-            try {
-                if ($this->config->convert && $request->getQueryParam('audio')) {
-                    // Audio convert.
-                    return $this->getAudioResponse($request, $response);
-                } elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) {
-                    // Advance convert.
-                    return $this->getConvertedResponse($request, $response);
-                }
+        try {
+            if ($this->config->convert && $request->getQueryParam('audio')) {
+                // Audio convert.
+                return $this->getAudioResponse($request, $response);
+            } elseif ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) {
+                // Advance convert.
+                return $this->getConvertedResponse($request, $response);
+            }
 
-                // Regular download.
-                return $this->getDownloadResponse($request, $response);
-            } catch (PasswordException $e) {
-                $frontController = new FrontController($this->container);
+            // Regular download.
+            return $this->getDownloadResponse($request, $response);
+        } catch (PasswordException $e) {
+            $frontController = new FrontController($this->container);
 
-                return $frontController->password($request, $response);
-            } catch (WrongPasswordException $e) {
-                return $this->displayError($request, $response, $this->localeManager->t('Wrong password'));
-            } catch (PlaylistConversionException $e) {
+            return $frontController->password($request, $response);
+        } catch (WrongPasswordException $e) {
+            return $this->displayError($request, $response, $this->localeManager->t('Wrong password'));
+        } catch (PlaylistConversionException $e) {
+            return $this->displayError(
+                $request,
+                $response,
+                $this->localeManager->t('Conversion of playlists is not supported.')
+            );
+        } catch (InvalidProtocolConversionException $e) {
+            if (in_array($this->video->protocol, ['m3u8', 'm3u8_native'])) {
                 return $this->displayError(
                     $request,
                     $response,
-                    $this->localeManager->t('Conversion of playlists is not supported.')
+                    $this->localeManager->t('Conversion of M3U8 files is not supported.')
                 );
-            } catch (InvalidProtocolConversionException $e) {
-                if (in_array($this->video->protocol, ['m3u8', 'm3u8_native'])) {
-                    return $this->displayError(
-                        $request,
-                        $response,
-                        $this->localeManager->t('Conversion of M3U8 files is not supported.')
-                    );
-                } elseif ($this->video->protocol == 'http_dash_segments') {
-                    return $this->displayError(
-                        $request,
-                        $response,
-                        $this->localeManager->t('Conversion of DASH segments is not supported.')
-                    );
-                } else {
-                    throw $e;
-                }
+            } elseif ($this->video->protocol == 'http_dash_segments') {
+                return $this->displayError(
+                    $request,
+                    $response,
+                    $this->localeManager->t('Conversion of DASH segments is not supported.')
+                );
+            } else {
+                throw $e;
             }
-        } else {
-            return $response->withRedirect($this->router->pathFor('index'));
         }
     }
 

programming_language: PHP
filename: FrontController.php
change_type: ModificationType.MODIFY
diff: @@ -12,6 +12,7 @@ use Alltube\Library\Exception\WrongPasswordException;
 use Alltube\Locale;
 use Alltube\Middleware\CspMiddleware;
 use Exception;
+use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
 use Slim\Http\StatusCode;
 use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
 use Throwable;
@@ -198,24 +199,21 @@ class FrontController extends BaseController
      *
      * @return Response HTTP response
      * @throws AlltubeLibraryException
+     * @throws InvalidURLException
      */
     public function info(Request $request, Response $response): Response
     {
-        $url = $request->getQueryParam('url') ?: $request->getQueryParam('v');
+        $url = $this->getVideoPageUrl($request);
 
-        if (isset($url) && !empty($url)) {
-            $this->video = $this->downloader->getVideo($url, $this->getFormat($request), $this->getPassword($request));
+        $this->video = $this->downloader->getVideo($url, $this->getFormat($request), $this->getPassword($request));
 
-            if ($this->config->convert && $request->getQueryParam('audio')) {
-                // We skip the info page and get directly to the download.
-                return $response->withRedirect(
-                    $this->router->pathFor('download', [], $request->getQueryParams())
-                );
-            } else {
-                return $this->getInfoResponse($request, $response);
-            }
+        if ($this->config->convert && $request->getQueryParam('audio')) {
+            // We skip the info page and get directly to the download.
+            return $response->withRedirect(
+                $this->router->pathFor('download', [], $request->getQueryParams())
+            );
         } else {
-            return $response->withRedirect($this->router->pathFor('index'));
+            return $this->getInfoResponse($request, $response);
         }
     }
 

programming_language: PHP
filename: JsonController.php
change_type: ModificationType.MODIFY
diff: @@ -7,6 +7,8 @@
 namespace Alltube\Controller;
 
 use Alltube\Library\Exception\AlltubeLibraryException;
+use Exception;
+use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
 use Slim\Http\Request;
 use Slim\Http\Response;
 use Slim\Http\StatusCode;
@@ -23,13 +25,12 @@ class JsonController extends BaseController
      * @param Response $response PSR-7 response
      *
      * @return Response HTTP response
-     * @throws AlltubeLibraryException
      */
     public function json(Request $request, Response $response): Response
     {
-        $url = $request->getQueryParam('url');
+        try {
+            $url = $this->getVideoPageUrl($request);
 
-        if (isset($url)) {
             $this->video = $this->downloader->getVideo(
                 $url,
                 $this->getFormat($request),
@@ -37,8 +38,8 @@ class JsonController extends BaseController
             );
 
             return $response->withJson($this->video->getJson());
-        } else {
-            return $response->withJson(['error' => 'You need to provide the url parameter'])
+        } catch (InvalidURLException $e) {
+            return $response->withJson(['error' => $e->getMessage()])
                 ->withStatus(StatusCode::HTTP_BAD_REQUEST);
         }
     }

programming_language: PHP
filename: composer.json
change_type: ModificationType.MODIFY
diff: @@ -25,6 +25,7 @@
     "aura/session": "^2.1",
     "barracudanetworks/archivestream-php": "^1.0",
     "consolidation/log": "^2.0",
+    "j0k3r/httplug-ssrf-plugin": "^2.0",
     "jawira/case-converter": "^3.4",
     "jean85/pretty-package-versions": "^1.3",
     "mathmarques/smarty-view": "^1.1",

programming_language: Jupyter Notebook
filename: composer.lock
change_type: ModificationType.MODIFY
diff: @@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "93cbba477de92eb8ec3d913b8cd83c82",
+    "content-hash": "06423e8f32df697ae32be27a2c7a97b2",
     "packages": [
         {
             "name": "aura/session",
@@ -108,6 +108,72 @@
             ],
             "time": "2018-08-10T13:58:33+00:00"
         },
+        {
+            "name": "clue/stream-filter",
+            "version": "v1.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/clue/stream-filter.git",
+                "reference": "d6169430c7731d8509da7aecd0af756a5747b78e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/clue/stream-filter/zipball/d6169430c7731d8509da7aecd0af756a5747b78e",
+                "reference": "d6169430c7731d8509da7aecd0af756a5747b78e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "Clue\\StreamFilter\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering"
+                }
+            ],
+            "description": "A simple and modern approach to stream filtering in PHP",
+            "homepage": "https://github.com/clue/php-stream-filter",
+            "keywords": [
+                "bucket brigade",
+                "callback",
+                "filter",
+                "php_user_filter",
+                "stream",
+                "stream_filter_append",
+                "stream_filter_register"
+            ],
+            "support": {
+                "issues": "https://github.com/clue/stream-filter/issues",
+                "source": "https://github.com/clue/stream-filter/tree/v1.6.0"
+            },
+            "funding": [
+                {
+                    "url": "https://clue.engineering/support",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/clue",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-02-21T13:15:14+00:00"
+        },
         {
             "name": "composer/installers",
             "version": "v1.9.0",
@@ -540,6 +606,77 @@
             ],
             "time": "2019-07-01T23:21:34+00:00"
         },
+        {
+            "name": "j0k3r/httplug-ssrf-plugin",
+            "version": "v2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/j0k3r/httplug-ssrf-plugin.git",
+                "reference": "b60e8068054bdb15fdf1e62cd314d941c62a3b72"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/j0k3r/httplug-ssrf-plugin/zipball/b60e8068054bdb15fdf1e62cd314d941c62a3b72",
+                "reference": "b60e8068054bdb15fdf1e62cd314d941c62a3b72",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.9",
+                "php-http/client-common": "^2.0",
+                "php-http/discovery": "^1.5",
+                "php-http/message": "^1.7",
+                "php-http/message-factory": "^1.0.2"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.14",
+                "guzzlehttp/psr7": "^1.0",
+                "php-http/guzzle6-adapter": "^2.0",
+                "php-http/mock-client": "^1.0",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^0.12",
+                "phpstan/phpstan-phpunit": "^0.12",
+                "symfony/phpunit-bridge": "~5.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Graby\\HttpClient\\Plugin\\ServerSideRequestForgeryProtection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jeremy Benoist",
+                    "email": "jeremy.benoist@gmail.com"
+                },
+                {
+                    "name": "aaa2000",
+                    "email": "adev2000@gmail.com"
+                },
+                {
+                    "name": "Jack W",
+                    "email": "jack@fin1te.net",
+                    "role": "Developer (SafeCurl original version)"
+                }
+            ],
+            "description": "Server-Side Request Forgery (SSRF) protection plugin for HTTPlug",
+            "homepage": "https://github.com/j0k3r/httplug-ssrf-plugin",
+            "keywords": [
+                "http",
+                "httplug",
+                "plugin",
+                "security",
+                "ssrf"
+            ],
+            "support": {
+                "issues": "https://github.com/j0k3r/httplug-ssrf-plugin/issues",
+                "source": "https://github.com/j0k3r/httplug-ssrf-plugin/tree/master"
+            },
+            "time": "2020-06-26T06:05:06+00:00"
+        },
         {
             "name": "jawira/case-converter",
             "version": "v3.4.1",
@@ -916,6 +1053,396 @@
             ],
             "time": "2020-09-02T14:53:15+00:00"
         },
+        {
+            "name": "php-http/client-common",
+            "version": "2.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-http/client-common.git",
+                "reference": "d135751167d57e27c74de674d6a30cef2dc8e054"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-http/client-common/zipball/d135751167d57e27c74de674d6a30cef2dc8e054",
+                "reference": "d135751167d57e27c74de674d6a30cef2dc8e054",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0",
+                "php-http/httplug": "^2.0",
+                "php-http/message": "^1.6",
+                "php-http/message-factory": "^1.0",
+                "psr/http-client": "^1.0",
+                "psr/http-factory": "^1.0",
+                "psr/http-message": "^1.0",
+                "symfony/options-resolver": "~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0 || ^6.0",
+                "symfony/polyfill-php80": "^1.17"
+            },
+            "require-dev": {
+                "doctrine/instantiator": "^1.1",
+                "guzzlehttp/psr7": "^1.4",
+                "nyholm/psr7": "^1.2",
+                "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1",
+                "phpspec/prophecy": "^1.10.2",
+                "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3"
+            },
+            "suggest": {
+                "ext-json": "To detect JSON responses with the ContentTypePlugin",
+                "ext-libxml": "To detect XML responses with the ContentTypePlugin",
+                "php-http/cache-plugin": "PSR-6 Cache plugin",
+                "php-http/logger-plugin": "PSR-3 Logger plugin",
+                "php-http/stopwatch-plugin": "Symfony Stopwatch plugin"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Http\\Client\\Common\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com"
+                }
+            ],
+            "description": "Common HTTP Client implementations and tools for HTTPlug",
+            "homepage": "http://httplug.io",
+            "keywords": [
+                "client",
+                "common",
+                "http",
+                "httplug"
+            ],
+            "support": {
+                "issues": "https://github.com/php-http/client-common/issues",
+                "source": "https://github.com/php-http/client-common/tree/2.5.0"
+            },
+            "time": "2021-11-26T15:01:24+00:00"
+        },
+        {
+            "name": "php-http/discovery",
+            "version": "1.14.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-http/discovery.git",
+                "reference": "de90ab2b41d7d61609f504e031339776bc8c7223"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-http/discovery/zipball/de90ab2b41d7d61609f504e031339776bc8c7223",
+                "reference": "de90ab2b41d7d61609f504e031339776bc8c7223",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "conflict": {
+                "nyholm/psr7": "<1.0"
+            },
+            "require-dev": {
+                "graham-campbell/phpspec-skip-example-extension": "^5.0",
+                "php-http/httplug": "^1.0 || ^2.0",
+                "php-http/message-factory": "^1.0",
+                "phpspec/phpspec": "^5.1 || ^6.1",
+                "puli/composer-plugin": "1.0.0-beta10"
+            },
+            "suggest": {
+                "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.9-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Http\\Discovery\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com"
+                }
+            ],
+            "description": "Finds installed HTTPlug implementations and PSR-7 message factories",
+            "homepage": "http://php-http.org",
+            "keywords": [
+                "adapter",
+                "client",
+                "discovery",
+                "factory",
+                "http",
+                "message",
+                "psr7"
+            ],
+            "support": {
+                "issues": "https://github.com/php-http/discovery/issues",
+                "source": "https://github.com/php-http/discovery/tree/1.14.1"
+            },
+            "time": "2021-09-18T07:57:46+00:00"
+        },
+        {
+            "name": "php-http/httplug",
+            "version": "2.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-http/httplug.git",
+                "reference": "f640739f80dfa1152533976e3c112477f69274eb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-http/httplug/zipball/f640739f80dfa1152533976e3c112477f69274eb",
+                "reference": "f640739f80dfa1152533976e3c112477f69274eb",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0",
+                "php-http/promise": "^1.1",
+                "psr/http-client": "^1.0",
+                "psr/http-message": "^1.0"
+            },
+            "require-dev": {
+                "friends-of-phpspec/phpspec-code-coverage": "^4.1",
+                "phpspec/phpspec": "^5.1 || ^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Http\\Client\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Eric GELOEN",
+                    "email": "geloen.eric@gmail.com"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://sagikazarmark.hu"
+                }
+            ],
+            "description": "HTTPlug, the HTTP client abstraction for PHP",
+            "homepage": "http://httplug.io",
+            "keywords": [
+                "client",
+                "http"
+            ],
+            "support": {
+                "issues": "https://github.com/php-http/httplug/issues",
+                "source": "https://github.com/php-http/httplug/tree/2.3.0"
+            },
+            "time": "2022-02-21T09:52:22+00:00"
+        },
+        {
+            "name": "php-http/message",
+            "version": "1.13.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-http/message.git",
+                "reference": "7886e647a30a966a1a8d1dad1845b71ca8678361"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-http/message/zipball/7886e647a30a966a1a8d1dad1845b71ca8678361",
+                "reference": "7886e647a30a966a1a8d1dad1845b71ca8678361",
+                "shasum": ""
+            },
+            "require": {
+                "clue/stream-filter": "^1.5",
+                "php": "^7.1 || ^8.0",
+                "php-http/message-factory": "^1.0.2",
+                "psr/http-message": "^1.0"
+            },
+            "provide": {
+                "php-http/message-factory-implementation": "1.0"
+            },
+            "require-dev": {
+                "ergebnis/composer-normalize": "^2.6",
+                "ext-zlib": "*",
+                "guzzlehttp/psr7": "^1.0",
+                "laminas/laminas-diactoros": "^2.0",
+                "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1",
+                "slim/slim": "^3.0"
+            },
+            "suggest": {
+                "ext-zlib": "Used with compressor/decompressor streams",
+                "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories",
+                "laminas/laminas-diactoros": "Used with Diactoros Factories",
+                "slim/slim": "Used with Slim Framework PSR-7 implementation"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.10-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/filters.php"
+                ],
+                "psr-4": {
+                    "Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com"
+                }
+            ],
+            "description": "HTTP Message related tools",
+            "homepage": "http://php-http.org",
+            "keywords": [
+                "http",
+                "message",
+                "psr-7"
+            ],
+            "support": {
+                "issues": "https://github.com/php-http/message/issues",
+                "source": "https://github.com/php-http/message/tree/1.13.0"
+            },
+            "time": "2022-02-11T13:41:14+00:00"
+        },
+        {
+            "name": "php-http/message-factory",
+            "version": "v1.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-http/message-factory.git",
+                "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1",
+                "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4",
+                "psr/http-message": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com"
+                }
+            ],
+            "description": "Factory interfaces for PSR-7 HTTP Message",
+            "homepage": "http://php-http.org",
+            "keywords": [
+                "factory",
+                "http",
+                "message",
+                "stream",
+                "uri"
+            ],
+            "support": {
+                "issues": "https://github.com/php-http/message-factory/issues",
+                "source": "https://github.com/php-http/message-factory/tree/master"
+            },
+            "time": "2015-12-19T14:08:53+00:00"
+        },
+        {
+            "name": "php-http/promise",
+            "version": "1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-http/promise.git",
+                "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88",
+                "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "friends-of-phpspec/phpspec-code-coverage": "^4.3.2",
+                "phpspec/phpspec": "^5.1.2 || ^6.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Http\\Promise\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Joel Wurtz",
+                    "email": "joel.wurtz@gmail.com"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com"
+                }
+            ],
+            "description": "Promise used for asynchronous HTTP requests",
+            "homepage": "http://httplug.io",
+            "keywords": [
+                "promise"
+            ],
+            "support": {
+                "issues": "https://github.com/php-http/promise/issues",
+                "source": "https://github.com/php-http/promise/tree/1.1.0"
+            },
+            "time": "2020-07-07T09:29:14+00:00"
+        },
         {
             "name": "pimple/pimple",
             "version": "v3.3.0",
@@ -931,21 +1458,117 @@
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2.5",
-                "psr/container": "^1.0"
-            },
-            "require-dev": {
-                "symfony/phpunit-bridge": "^3.4|^4.4|^5.0"
+                "php": "^7.2.5",
+                "psr/container": "^1.0"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^3.4|^4.4|^5.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Pimple": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Pimple, a simple Dependency Injection Container",
+            "homepage": "https://pimple.symfony.com",
+            "keywords": [
+                "container",
+                "dependency injection"
+            ],
+            "time": "2020-03-03T09:12:48+00:00"
+        },
+        {
+            "name": "psr/container",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "time": "2017-02-14T16:28:37+00:00"
+        },
+        {
+            "name": "psr/http-client",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-client.git",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0",
+                "psr/http-message": "^1.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.3.x-dev"
+                    "dev-master": "1.0.x-dev"
                 }
             },
             "autoload": {
-                "psr-0": {
-                    "Pimple": "src/"
+                "psr-4": {
+                    "Psr\\Http\\Client\\": "src/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -954,34 +1577,40 @@
             ],
             "authors": [
                 {
-                    "name": "Fabien Potencier",
-                    "email": "fabien@symfony.com"
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
                 }
             ],
-            "description": "Pimple, a simple Dependency Injection Container",
-            "homepage": "https://pimple.symfony.com",
+            "description": "Common interface for HTTP clients",
+            "homepage": "https://github.com/php-fig/http-client",
             "keywords": [
-                "container",
-                "dependency injection"
+                "http",
+                "http-client",
+                "psr",
+                "psr-18"
             ],
-            "time": "2020-03-03T09:12:48+00:00"
+            "support": {
+                "source": "https://github.com/php-fig/http-client/tree/master"
+            },
+            "time": "2020-06-29T06:28:15+00:00"
         },
         {
-            "name": "psr/container",
-            "version": "1.0.0",
+            "name": "psr/http-factory",
+            "version": "1.0.1",
             "source": {
                 "type": "git",
-                "url": "https://github.com/php-fig/container.git",
-                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+                "url": "https://github.com/php-fig/http-factory.git",
+                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
-                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": ">=7.0.0",
+                "psr/http-message": "^1.0"
             },
             "type": "library",
             "extra": {
@@ -991,7 +1620,7 @@
             },
             "autoload": {
                 "psr-4": {
-                    "Psr\\Container\\": "src/"
+                    "Psr\\Http\\Message\\": "src/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -1004,16 +1633,21 @@
                     "homepage": "http://www.php-fig.org/"
                 }
             ],
-            "description": "Common Container Interface (PHP FIG PSR-11)",
-            "homepage": "https://github.com/php-fig/container",
+            "description": "Common interfaces for PSR-7 HTTP message factories",
             "keywords": [
-                "PSR-11",
-                "container",
-                "container-interface",
-                "container-interop",
-                "psr"
+                "factory",
+                "http",
+                "message",
+                "psr",
+                "psr-17",
+                "psr-7",
+                "request",
+                "response"
             ],
-            "time": "2017-02-14T16:28:37+00:00"
+            "support": {
+                "source": "https://github.com/php-fig/http-factory/tree/master"
+            },
+            "time": "2019-04-30T12:38:16+00:00"
         },
         {
             "name": "psr/http-message",
@@ -1487,6 +2121,56 @@
             ],
             "time": "2021-01-28T22:06:19+00:00"
         },
+        {
+            "name": "symfony/deprecation-contracts",
+            "version": "v2.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/deprecation-contracts.git",
+                "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+                "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.2-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "function.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "A generic function and convention to trigger deprecation notices",
+            "homepage": "https://symfony.com",
+            "time": "2020-09-07T11:33:47+00:00"
+        },
         {
             "name": "symfony/finder",
             "version": "v5.0.8",
@@ -1536,6 +2220,58 @@
             "homepage": "https://symfony.com",
             "time": "2020-03-27T16:56:45+00:00"
         },
+        {
+            "name": "symfony/options-resolver",
+            "version": "v5.2.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/options-resolver.git",
+                "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/options-resolver/zipball/5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce",
+                "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/deprecation-contracts": "^2.1",
+                "symfony/polyfill-php73": "~1.0",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\OptionsResolver\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides an improved replacement for the array_replace PHP function",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "config",
+                "configuration",
+                "options"
+            ],
+            "time": "2021-01-27T12:56:27+00:00"
+        },
         {
             "name": "symfony/polyfill-ctype",
             "version": "v1.22.0",
@@ -6576,56 +7312,6 @@
             "homepage": "https://symfony.com",
             "time": "2021-01-27T12:56:27+00:00"
         },
-        {
-            "name": "symfony/deprecation-contracts",
-            "version": "v2.2.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/symfony/deprecation-contracts.git",
-                "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665",
-                "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=7.1"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.2-dev"
-                },
-                "thanks": {
-                    "name": "symfony/contracts",
-                    "url": "https://github.com/symfony/contracts"
-                }
-            },
-            "autoload": {
-                "files": [
-                    "function.php"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Nicolas Grekas",
-                    "email": "p@tchwork.com"
-                },
-                {
-                    "name": "Symfony Community",
-                    "homepage": "https://symfony.com/contributors"
-                }
-            ],
-            "description": "A generic function and convention to trigger deprecation notices",
-            "homepage": "https://symfony.com",
-            "time": "2020-09-07T11:33:47+00:00"
-        },
         {
             "name": "symfony/dotenv",
             "version": "v5.2.3",
@@ -6909,58 +7595,6 @@
             "homepage": "https://symfony.com",
             "time": "2021-01-27T10:01:46+00:00"
         },
-        {
-            "name": "symfony/options-resolver",
-            "version": "v5.2.3",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/symfony/options-resolver.git",
-                "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/symfony/options-resolver/zipball/5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce",
-                "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=7.2.5",
-                "symfony/deprecation-contracts": "^2.1",
-                "symfony/polyfill-php73": "~1.0",
-                "symfony/polyfill-php80": "^1.15"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "Symfony\\Component\\OptionsResolver\\": ""
-                },
-                "exclude-from-classmap": [
-                    "/Tests/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Fabien Potencier",
-                    "email": "fabien@symfony.com"
-                },
-                {
-                    "name": "Symfony Community",
-                    "homepage": "https://symfony.com/contributors"
-                }
-            ],
-            "description": "Provides an improved replacement for the array_replace PHP function",
-            "homepage": "https://symfony.com",
-            "keywords": [
-                "config",
-                "configuration",
-                "options"
-            ],
-            "time": "2021-01-27T12:56:27+00:00"
-        },
         {
             "name": "symfony/var-dumper",
             "version": "v5.0.8",
@@ -7139,5 +7773,5 @@
     "platform-overrides": {
         "php": "7.3.11"
     },
-    "plugin-api-version": "2.2.0"
+    "plugin-api-version": "2.0.0"
 }

programming_language: JavaScript
filename: FrontControllerTest.php
change_type: ModificationType.MODIFY
diff: @@ -11,6 +11,7 @@ use Alltube\Exception\ConfigException;
 use Alltube\Exception\DependencyException;
 use Alltube\Library\Exception\AlltubeLibraryException;
 use Exception;
+use Graby\HttpClient\Plugin\ServerSideRequestForgeryProtection\Exception\InvalidURLException;
 use Slim\Http\Environment;
 use Slim\Http\Request;
 use SmartyException;
@@ -113,7 +114,8 @@ class FrontControllerTest extends ControllerTest
      */
     public function testInfoWithoutUrl()
     {
-        $this->assertRequestIsRedirect('info');
+        $this->expectException(InvalidURLException::class);
+        $this->getRequestResult('info', []);
     }
 
     /**

programming_language: PHP
repo_url: https://github.com/Rudloff/alltube
commit_details: repo_url: https://github.com/Rudloff/alltube
author: Pierre Rudloff
author_date: 2022-02-20 13:28:57+01:00
msg: Don't redirect to REQUEST_URI when browsing to index.php
Instead, we can make sure everything works correctly on index.php
repository: repo_url: https://github.com/Rudloff/alltube
repo_name: Rudloff/alltube
description: Web GUI for youtube-dl
repo_language: PHP
owner: Rudloff
file_changes: filename: ViewFactory.php
change_type: ModificationType.MODIFY
diff: @@ -37,6 +37,25 @@ class ViewFactory
             ->withScheme('https');
     }
 
+    /**
+     * @param Uri $uri
+     * @return Uri
+     */
+    private static function cleanBasePath(Uri $uri): Uri
+    {
+        $basePath = $uri->getBasePath();
+        if (str_ends_with($basePath, 'index.php')) {
+            /*
+             * When the base path ends with index.php,
+             * routing works correctly, but it breaks the URL of static assets using {base_url}.
+             * So we alter the base path but only in the URI used by SmartyPlugins.
+             */
+            $uri = $uri->withBasePath(dirname($basePath));
+        }
+
+        return $uri;
+    }
+
     /**
      * Create Smarty view object.
      *
@@ -76,6 +95,8 @@ class ViewFactory
         /** @var LocaleManager $localeManager */
         $localeManager = $container->get('locale');
 
+        $uri = self::cleanBasePath($uri);
+
         $smartyPlugins = new SmartyPlugins($container->get('router'), $uri->withUserInfo(''));
         $view->registerPlugin('function', 'path_for', [$smartyPlugins, 'pathFor']);
         $view->registerPlugin('function', 'base_url', [$smartyPlugins, 'baseUrl']);

programming_language: PHP
filename: index.php
change_type: ModificationType.MODIFY
diff: @@ -5,11 +5,6 @@ require_once __DIR__ . '/vendor/autoload.php';
 use Alltube\App;
 use Alltube\ErrorHandler;
 
-if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.php') !== false) {
-    header('Location: ' . str_ireplace('/index.php', '/', $_SERVER['REQUEST_URI']));
-    die;
-}
-
 try {
     // Create app.
     $app = new App();

programming_language: PHP
filename: logo.tpl
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,5 @@
 <h1 class="logobis">
-    <a class="logocompatible" href="{base_url}">
+    <a class="logocompatible" href="{path_for name="index"}">
         <span class="logocompatiblemask"><img src="{base_url}/img/logocompatiblemask.png" width="447" height="107"
                                               alt="{$config->appName}"/></span>
     </a></h1>

programming_language: Markdown
filename: ViewFactory.php
change_type: ModificationType.MODIFY
diff: @@ -37,6 +37,25 @@ class ViewFactory
             ->withScheme('https');
     }
 
+    /**
+     * @param Uri $uri
+     * @return Uri
+     */
+    private static function cleanBasePath(Uri $uri): Uri
+    {
+        $basePath = $uri->getBasePath();
+        if (str_ends_with($basePath, 'index.php')) {
+            /*
+             * When the base path ends with index.php,
+             * routing works correctly, but it breaks the URL of static assets using {base_url}.
+             * So we alter the base path but only in the URI used by SmartyPlugins.
+             */
+            $uri = $uri->withBasePath(dirname($basePath));
+        }
+
+        return $uri;
+    }
+
     /**
      * Create Smarty view object.
      *
@@ -76,6 +95,8 @@ class ViewFactory
         /** @var LocaleManager $localeManager */
         $localeManager = $container->get('locale');
 
+        $uri = self::cleanBasePath($uri);
+
         $smartyPlugins = new SmartyPlugins($container->get('router'), $uri->withUserInfo(''));
         $view->registerPlugin('function', 'path_for', [$smartyPlugins, 'pathFor']);
         $view->registerPlugin('function', 'base_url', [$smartyPlugins, 'baseUrl']);

programming_language: PHP
filename: index.php
change_type: ModificationType.MODIFY
diff: @@ -5,11 +5,6 @@ require_once __DIR__ . '/vendor/autoload.php';
 use Alltube\App;
 use Alltube\ErrorHandler;
 
-if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/index.php') !== false) {
-    header('Location: ' . str_ireplace('/index.php', '/', $_SERVER['REQUEST_URI']));
-    die;
-}
-
 try {
     // Create app.
     $app = new App();

programming_language: PHP
filename: logo.tpl
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,5 @@
 <h1 class="logobis">
-    <a class="logocompatible" href="{base_url}">
+    <a class="logocompatible" href="{path_for name="index"}">
         <span class="logocompatiblemask"><img src="{base_url}/img/logocompatiblemask.png" width="447" height="107"
                                               alt="{$config->appName}"/></span>
     </a></h1>

programming_language: Markdown