diff options
-rwxr-xr-x | config/global.ini.php | 4 | ||||
-rw-r--r-- | core/Http.php | 27 | ||||
-rw-r--r-- | tests/PHPUnit/Integration/HttpTest.php | 200 | ||||
-rw-r--r-- | tests/UI/expected-screenshots/UIIntegrationTest_admin_diagnostics_configfile.png | 4 |
4 files changed, 141 insertions, 94 deletions
diff --git a/config/global.ini.php b/config/global.ini.php index 8912d29c0c..6cbfd855a9 100755 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -719,6 +719,10 @@ enable_auto_update = 1 ; If set to 0 it also disables the "sent plugin update emails" feature in general and the related setting in the UI. enable_update_communication = 1 +; This option defines the protocols Matomo's Http class is allowed to open. +; If you may need to download GeoIP updates or other stuff using other protocols like ftp you may need to extend this list. +allowed_outgoing_protocols = 'http,https' + ; Comma separated list of plugin names for which console commands should be loaded (applies when Matomo is not installed yet) always_load_commands_from_plugin= diff --git a/core/Http.php b/core/Http.php index 0347163ffe..b3706b2b82 100644 --- a/core/Http.php +++ b/core/Http.php @@ -161,6 +161,31 @@ class Http throw new Exception('Too many redirects (' . $followDepth . ')'); } + $aUrl = trim($aUrl); + $parsedUrl = @parse_url($aUrl); + + if (empty($parsedUrl['scheme'])) { + throw new Exception('Missing scheme in given url'); + } + + $allowedProtocols = Config::getInstance()->General['allowed_outgoing_protocols']; + $isAllowed = false; + + foreach (explode(',', $allowedProtocols) as $protocol) { + if (strtolower($parsedUrl['scheme']) === strtolower(trim($protocol))) { + $isAllowed = true; + break; + } + } + + if (!$isAllowed) { + throw new Exception(sprintf( + 'Protocol %s not in list of allowed protocols: %s', + $parsedUrl['scheme'], + $allowedProtocols + )); + } + $contentLength = 0; $fileLength = 0; @@ -194,8 +219,6 @@ class Http list($proxyHost, $proxyPort, $proxyUser, $proxyPassword) = self::getProxyConfiguration($aUrl); - $aUrl = trim($aUrl); - // other result data $status = null; $headers = array(); diff --git a/tests/PHPUnit/Integration/HttpTest.php b/tests/PHPUnit/Integration/HttpTest.php index 73bcd27b35..3791c350c1 100644 --- a/tests/PHPUnit/Integration/HttpTest.php +++ b/tests/PHPUnit/Integration/HttpTest.php @@ -322,98 +322,118 @@ class HttpTest extends \PHPUnit\Framework\TestCase $this->assertEquals(51, strlen($result)); } - public function test_http_postsEvent() - { - $params = null; - $params2 = null; - Piwik::addAction('Http.sendHttpRequest', function () use (&$params) { - $params = func_get_args(); - }); - Piwik::addAction('Http.sendHttpRequest.end', function () use (&$params2) { - $params2 = func_get_args(); - }); - $destinationPath = PIWIK_USER_PATH . '/tmp/latest/LATEST'; - $url = Fixture::getRootUrl() . 'tests/PHPUnit/Integration/Http/Post.php'; - Http::sendHttpRequestBy( - Http::getTransportMethod(), - $url, - 30, - $userAgent = null, - $destinationPath, - $file = null, - $followDepth = 0, - $acceptLanguage = false, - $acceptInvalidSslCertificate = false, - $byteRange = array(10, 20), - $getExtendedInfo = false, - $httpMethod = 'POST', - $httpUsername = '', - $httpPassword = '', - array('adf2' => '44', 'afc23' => 'ab12') - ); - - $this->assertEquals(array($url, array( - 'httpMethod' => 'POST', - 'body' => array('adf2' => '44','afc23' => 'ab12'), - 'userAgent' => 'Matomo/' . Version::VERSION, - 'timeout' => 30, - 'headers' => array( - 'Range: bytes=10-20', + public function test_http_postsEvent() + { + $params = null; + $params2 = null; + Piwik::addAction('Http.sendHttpRequest', function () use (&$params) { + $params = func_get_args(); + }); + Piwik::addAction('Http.sendHttpRequest.end', function () use (&$params2) { + $params2 = func_get_args(); + }); + $destinationPath = PIWIK_USER_PATH . '/tmp/latest/LATEST'; + $url = Fixture::getRootUrl() . 'tests/PHPUnit/Integration/Http/Post.php'; + Http::sendHttpRequestBy( + Http::getTransportMethod(), + $url, + 30, + $userAgent = null, + $destinationPath, + $file = null, + $followDepth = 0, + $acceptLanguage = false, + $acceptInvalidSslCertificate = false, + $byteRange = array(10, 20), + $getExtendedInfo = false, + $httpMethod = 'POST', + $httpUsername = '', + $httpPassword = '', + array('adf2' => '44', 'afc23' => 'ab12') + ); + + $this->assertEquals(array($url, array( + 'httpMethod' => 'POST', + 'body' => array('adf2' => '44','afc23' => 'ab12'), + 'userAgent' => 'Matomo/' . Version::VERSION, + 'timeout' => 30, + 'headers' => array( + 'Range: bytes=10-20', 'Via: ' . Version::VERSION . ' (Matomo/' . Version::VERSION . ')', - 'X-Forwarded-For: 127.0.0.1', - ), - 'verifySsl' => true, - 'destinationPath' => $destinationPath - ), null, null, array()), $params); - - $this->assertNotEmpty($params2[4]);// headers - unset($params2[4]); - $this->assertEquals(array($url, array( - 'httpMethod' => 'POST', - 'body' => array('adf2' => '44','afc23' => 'ab12'), + 'X-Forwarded-For: 127.0.0.1', + ), + 'verifySsl' => true, + 'destinationPath' => $destinationPath + ), null, null, array()), $params); + + $this->assertNotEmpty($params2[4]);// headers + unset($params2[4]); + $this->assertEquals(array($url, array( + 'httpMethod' => 'POST', + 'body' => array('adf2' => '44','afc23' => 'ab12'), 'userAgent' => 'Matomo/' . Version::VERSION, - 'timeout' => 30, - 'headers' => array( - 'Range: bytes=10-20', + 'timeout' => 30, + 'headers' => array( + 'Range: bytes=10-20', 'Via: ' . Version::VERSION . ' (Matomo/' . Version::VERSION . ')', - 'X-Forwarded-For: 127.0.0.1', - ), - 'verifySsl' => true, - 'destinationPath' => $destinationPath - ), '{"adf2":"44","afc23":"ab12","method":"post"}', 200), $params2); - } - - public function test_http_returnsResultOfPostedEvent() - { - Piwik::addAction('Http.sendHttpRequest', function ($url, $args, &$response, &$status, &$headers) { - $response = '{test: true}'; - $status = 204; - $headers = array('content-length' => 948); - }); - - $result = Http::sendHttpRequestBy( - Http::getTransportMethod(), - Fixture::getRootUrl() . 'tests/PHPUnit/Integration/Http/Post.php', - 30, - $userAgent = null, - $destinationPath = null, - $file = null, - $followDepth = 0, - $acceptLanguage = false, - $acceptInvalidSslCertificate = false, - $byteRange = array(10, 20), - $getExtendedInfo = true, - $httpMethod = 'POST', - $httpUsername = '', - $httpPassword = '', - array('adf2' => '44', 'afc23' => 'ab12') - ); - - $this->assertEquals(array( - 'data' => '{test: true}', - 'status' => 204, - 'headers' => array('content-length' => 948) - ), $result); - } + 'X-Forwarded-For: 127.0.0.1', + ), + 'verifySsl' => true, + 'destinationPath' => $destinationPath + ), '{"adf2":"44","afc23":"ab12","method":"post"}', 200), $params2); + } + + public function test_http_returnsResultOfPostedEvent() + { + Piwik::addAction('Http.sendHttpRequest', function ($url, $args, &$response, &$status, &$headers) { + $response = '{test: true}'; + $status = 204; + $headers = array('content-length' => 948); + }); + + $result = Http::sendHttpRequestBy( + Http::getTransportMethod(), + Fixture::getRootUrl() . 'tests/PHPUnit/Integration/Http/Post.php', + 30, + $userAgent = null, + $destinationPath = null, + $file = null, + $followDepth = 0, + $acceptLanguage = false, + $acceptInvalidSslCertificate = false, + $byteRange = array(10, 20), + $getExtendedInfo = true, + $httpMethod = 'POST', + $httpUsername = '', + $httpPassword = '', + array('adf2' => '44', 'afc23' => 'ab12') + ); + + $this->assertEquals(array( + 'data' => '{test: true}', + 'status' => 204, + 'headers' => array('content-length' => 948) + ), $result); + } + /** + * @dataProvider getProtocolUrls + */ + public function test_invalid_protocols($url, $message) + { + self::expectException(\Exception::class); + self::expectExceptionMessage($message); + + Http::sendHttpRequest($url, 5); + } + + public function getProtocolUrls() + { + return [ + ['phar://malformed.url', 'Protocol phar not in list of allowed protocols: http,https'], + ['ftp://usful.ftp/file.md', 'Protocol ftp not in list of allowed protocols: http,https'], + ['rtp://custom.url', 'Protocol rtp not in list of allowed protocols: http,https'], + ['/local/file', 'Missing scheme in given url'], + ]; + } } diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_admin_diagnostics_configfile.png b/tests/UI/expected-screenshots/UIIntegrationTest_admin_diagnostics_configfile.png index 1741dda716..f5d351ebd9 100644 --- a/tests/UI/expected-screenshots/UIIntegrationTest_admin_diagnostics_configfile.png +++ b/tests/UI/expected-screenshots/UIIntegrationTest_admin_diagnostics_configfile.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7830c47b5000e60962a26e9759c05ec75de0f12b10bc497ed79184f85f1a6dc4 -size 4547003 +oid sha256:4a8cd99b98901d36bae43b767d3060d0967519fb6f842cb88a35889cd01b800a +size 4564855 |