1
0
Fork 0

Sanitize URLs in getRepoName and centralize the Url sanitization process

pull/8558/head
Jordi Boggiano 2020-01-30 15:50:46 +01:00
parent c41df325d8
commit 1e68555e0a
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
13 changed files with 58 additions and 49 deletions

View File

@ -17,6 +17,7 @@ use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
use Composer\Util\Filesystem;
use Composer\Util\Git as GitUtil;
use Composer\Util\Url;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
use Composer\Cache;
@ -434,7 +435,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
$this->io->writeError(' <warning>'.$reference.' is gone (history was rewritten?)</warning>');
}
throw new \RuntimeException(GitUtil::sanitizeUrl('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()));
throw new \RuntimeException(Url::sanitize('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()));
}
protected function updateOriginUrl($path, $url)

View File

@ -45,7 +45,7 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
public function getRepoName()
{
return 'platform repo ('.$this->lookup.')';
return 'artifact repo ('.$this->lookup.')';
}
public function getRepoConfig()

View File

@ -34,6 +34,7 @@ use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\EmptyConstraint;
use Composer\Util\Http\Response;
use Composer\Util\MetadataMinifier;
use Composer\Util\Url;
use React\Promise\Util as PromiseUtil;
/**
@ -129,7 +130,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
public function getRepoName()
{
return 'composer repo ('.$this->url.')';
return 'composer repo ('.Url::sanitize($this->url).')';
}
public function getRepoConfig()

View File

@ -41,7 +41,7 @@ class CompositeRepository extends BaseRepository
public function getRepoName()
{
return 'composite repo ('.count($this->repositories).' repos)';
return 'composite repo ('.implode(', ', array_map(function ($repo) { return $repo->getRepoName(); }, $this->repositories)).')';
}
/**

View File

@ -20,6 +20,7 @@ use Composer\Package\Version\VersionGuesser;
use Composer\Package\Version\VersionParser;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
use Composer\Util\Url;
/**
* This repository allows installing local packages that are not necessarily under their own VCS.
@ -113,7 +114,7 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
public function getRepoName()
{
return 'path repo ('.$this->repoConfig['url'].')';
return 'path repo ('.Url::sanitize($this->repoConfig['url']).')';
}
public function getRepoConfig()

View File

@ -22,6 +22,7 @@ use Composer\Package\Loader\LoaderInterface;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Util\ProcessExecutor;
use Composer\Util\HttpDownloader;
use Composer\Util\Url;
use Composer\IO\IOInterface;
use Composer\Config;
@ -87,7 +88,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
$driverType = $driverClass;
}
return 'vcs repo ('.$driverType.' '.$this->url.')';
return 'vcs repo ('.$driverType.' '.Url::sanitize($this->url).')';
}
public function getRepoConfig()

View File

@ -255,15 +255,4 @@ class AuthHelper
return count($pathParts) >= 4 && $pathParts[3] == 'downloads';
}
/**
* @param string $url
* @return string
*/
public function stripCredentialsFromUrl($url)
{
// GitHub repository rename result in redirect locations containing the access_token as GET parameter
// e.g. https://api.github.com/repositories/9999999999?access_token=github_token
return preg_replace('{([&?]access_token=)[^&]+}', '$1***', $url);
}
}

View File

@ -362,27 +362,16 @@ class Git
return '(' . implode('|', array_map('preg_quote', $config->get('gitlab-domains'))) . ')';
}
public static function sanitizeUrl($message)
{
return preg_replace_callback('{://(?P<user>[^@]+?):(?P<password>.+?)@}', function ($m) {
if (preg_match('{^[a-f0-9]{12,}$}', $m[1])) {
return '://***:***@';
}
return '://' . $m[1] . ':***@';
}, $message);
}
private function throwException($message, $url)
{
// git might delete a directory when it fails and php will not know
clearstatcache();
if (0 !== $this->process->execute('git --version', $ignoredOutput)) {
throw new \RuntimeException(self::sanitizeUrl('Failed to clone ' . $url . ', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()));
throw new \RuntimeException(Url::sanitize('Failed to clone ' . $url . ', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()));
}
throw new \RuntimeException(self::sanitizeUrl($message));
throw new \RuntimeException(Url::sanitize($message));
}
/**

View File

@ -72,23 +72,12 @@ class Hg
$this->throwException('Failed to clone ' . $url . ', ' . "\n\n" . $error, $url);
}
public static function sanitizeUrl($message)
{
return preg_replace_callback('{://(?P<user>[^@]+?):(?P<password>.+?)@}', function ($m) {
if (preg_match('{^[a-f0-9]{12,}$}', $m[1])) {
return '://***:***@';
}
return '://' . $m[1] . ':***@';
}, $message);
}
private function throwException($message, $url)
{
if (0 !== $this->process->execute('hg --version', $ignoredOutput)) {
throw new \RuntimeException(self::sanitizeUrl('Failed to clone ' . $url . ', hg was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()));
throw new \RuntimeException(Url::sanitize('Failed to clone ' . $url . ', hg was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput()));
}
throw new \RuntimeException(self::sanitizeUrl($message));
throw new \RuntimeException(Url::sanitize($message));
}
}

View File

@ -195,7 +195,7 @@ class CurlDownloader
$usingProxy = !empty($options['http']['proxy']) ? ' using proxy ' . $options['http']['proxy'] : '';
$ifModified = false !== strpos(strtolower(implode(',', $options['http']['header'])), 'if-modified-since:') ? ' if modified' : '';
if ($attributes['redirects'] === 0) {
$this->io->writeError('Downloading ' . $this->authHelper->stripCredentialsFromUrl($url) . $usingProxy . $ifModified, true, IOInterface::DEBUG);
$this->io->writeError('Downloading ' . Url::sanitize($url) . $usingProxy . $ifModified, true, IOInterface::DEBUG);
}
$this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle));
@ -254,12 +254,12 @@ class CurlDownloader
$contents = stream_get_contents($job['bodyHandle']);
}
$response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents);
$this->io->writeError('['.$statusCode.'] '.$this->authHelper->stripCredentialsFromUrl($progress['url']), true, IOInterface::DEBUG);
$this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG);
} else {
rewind($job['bodyHandle']);
$contents = stream_get_contents($job['bodyHandle']);
$response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents);
$this->io->writeError('['.$statusCode.'] '.$this->authHelper->stripCredentialsFromUrl($progress['url']), true, IOInterface::DEBUG);
$this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG);
}
fclose($job['bodyHandle']);
@ -362,7 +362,7 @@ class CurlDownloader
}
if (!empty($targetUrl)) {
$this->io->writeError(sprintf('Following redirect (%u) %s', $job['attributes']['redirects'] + 1, $this->authHelper->stripCredentialsFromUrl($targetUrl)), true, IOInterface::DEBUG);
$this->io->writeError(sprintf('Following redirect (%u) %s', $job['attributes']['redirects'] + 1, Url::sanitize($targetUrl)), true, IOInterface::DEBUG);
return $targetUrl;
}

View File

@ -246,7 +246,7 @@ class RemoteFilesystem
$actualContextOptions = stream_context_get_options($ctx);
$usingProxy = !empty($actualContextOptions['http']['proxy']) ? ' using proxy ' . $actualContextOptions['http']['proxy'] : '';
$this->io->writeError((substr($origFileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $this->authHelper->stripCredentialsFromUrl($origFileUrl) . $usingProxy, true, IOInterface::DEBUG);
$this->io->writeError((substr($origFileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . Url::sanitize($origFileUrl) . $usingProxy, true, IOInterface::DEBUG);
unset($origFileUrl, $actualContextOptions);
// Check for secure HTTP, but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256
@ -704,7 +704,7 @@ class RemoteFilesystem
$this->redirects++;
$this->io->writeError('', true, IOInterface::DEBUG);
$this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, $this->authHelper->stripCredentialsFromUrl($targetUrl)), true, IOInterface::DEBUG);
$this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, Url::sanitize($targetUrl)), true, IOInterface::DEBUG);
$additionalOptions['redirects'] = $this->redirects;

View File

@ -102,4 +102,21 @@ class Url
return $origin;
}
public static function sanitize($url)
{
// GitHub repository rename result in redirect locations containing the access_token as GET parameter
// e.g. https://api.github.com/repositories/9999999999?access_token=github_token
$url = preg_replace('{([&?]access_token=)[^&]+}', '$1***', $url);
$url = preg_replace_callback('{://(?P<user>[^:/\s@]+):(?P<password>[^@\s/]+)@}i', function ($m) {
if (preg_match('{^[a-f0-9]{12,}$}', $m['user'])) {
return '://***:***@';
}
return '://'.$m['user'].':***@';
}, $url);
return $url;
}
}

View File

@ -58,4 +58,25 @@ class UrlTest extends TestCase
array('https://mygitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.bz2?sha=abcd', 'https://mygitlab.com/api/v3/projects/foo%2Fbar/repository/archive.tar.bz2?sha=65', array('gitlab-domains' => array('mygitlab.com')), '65'),
);
}
/**
* @dataProvider sanitizeProvider
*/
public function testSanitize($expected, $url)
{
$this->assertSame($expected, Url::sanitize($url));
}
public static function sanitizeProvider()
{
return array(
array('https://foo:***@example.org/', 'https://foo:bar@example.org/'),
array('https://foo@example.org/', 'https://foo@example.org/'),
array('https://example.org/', 'https://example.org/'),
array('http://***:***@example.org', 'http://10a8f08e8d7b7b9:foo@example.org'),
array('https://foo:***@example.org:123/', 'https://foo:bar@example.org:123/'),
array('https://example.org/foo/bar?access_token=***', 'https://example.org/foo/bar?access_token=abcdef'),
array('https://example.org/foo/bar?foo=bar&access_token=***', 'https://example.org/foo/bar?foo=bar&access_token=abcdef'),
);
}
}