diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index 8bf68cac6..023fb0843 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -18,6 +18,7 @@ use Composer\Package\CompletePackageInterface; use Composer\Package\Loader\InvalidPackageException; use Composer\Repository\CompositeRepository; use Composer\Repository\RepositoryInterface; +use Composer\Util\ProcessExecutor; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; @@ -120,7 +121,7 @@ EOT */ private function openBrowser($url) { - $url = escapeshellarg($url); + $url = ProcessExecutor::escape($url); if (defined('PHP_WINDOWS_VERSION_MAJOR')) { return passthru('start "web" explorer "' . $url . '"'); diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index d7ee60677..8aa734630 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -20,6 +20,7 @@ use Composer\Package\Version\VersionSelector; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Package\Version\VersionParser; +use Composer\Util\ProcessExecutor; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -438,7 +439,7 @@ EOT $finder = new ExecutableFinder(); $gitBin = $finder->find('git'); - $cmd = new Process(sprintf('%s config -l', escapeshellarg($gitBin))); + $cmd = new Process(sprintf('%s config -l', ProcessExecutor::escape($gitBin))); $cmd->run(); if ($cmd->isSuccessful()) { diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 695996b5c..cbd7b0ba6 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -46,9 +46,13 @@ class GitDownloader extends VcsDownloader $flag = defined('PHP_WINDOWS_VERSION_MAJOR') ? '/D ' : ''; $command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer'; $this->io->write(" Cloning ".$ref); - + $commandCallable = function ($url) use ($ref, $path, $command) { - return sprintf($command, escapeshellarg($url), escapeshellarg($path), escapeshellarg($ref)); + return sprintf( + $command, + ProcessExecutor::escape($url), + ProcessExecutor::escape($path), + ProcessExecutor::escape($ref)); }; $this->gitUtil->runCommand($commandCallable, $url, $path, true); @@ -78,7 +82,7 @@ class GitDownloader extends VcsDownloader $command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer'; $commandCallable = function ($url) use ($command) { - return sprintf($command, escapeshellarg($url)); + return sprintf($command, ProcessExecutor::escape ($url)); }; $this->gitUtil->runCommand($commandCallable, $url, $path); @@ -225,7 +229,7 @@ class GitDownloader extends VcsDownloader && $branches && preg_match('{^\s+composer/'.preg_quote($reference).'$}m', $branches) ) { - $command = sprintf('git checkout -B %s %s && git reset --hard %2$s', escapeshellarg($branch), escapeshellarg('composer/'.$reference)); + $command = sprintf('git checkout -B %s %s && git reset --hard %2$s', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$reference)); if (0 === $this->process->execute($command, $output, $path)) { return; } @@ -238,19 +242,19 @@ class GitDownloader extends VcsDownloader $branch = 'v' . $branch; } - $command = sprintf('git checkout %s', escapeshellarg($branch)); - $fallbackCommand = sprintf('git checkout -B %s %s', escapeshellarg($branch), escapeshellarg('composer/'.$branch)); + $command = sprintf('git checkout %s', ProcessExecutor::escape($branch)); + $fallbackCommand = sprintf('git checkout -B %s %s', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$branch)); if (0 === $this->process->execute($command, $output, $path) || 0 === $this->process->execute($fallbackCommand, $output, $path) ) { - $command = sprintf('git reset --hard %s', escapeshellarg($reference)); + $command = sprintf('git reset --hard %s', ProcessExecutor::escape($reference)); if (0 === $this->process->execute($command, $output, $path)) { return; } } } - $command = sprintf($template, escapeshellarg($gitRef)); + $command = sprintf($template, ProcessExecutor::escape($gitRef)); if (0 === $this->process->execute($command, $output, $path)) { return; } @@ -269,7 +273,7 @@ class GitDownloader extends VcsDownloader foreach ($this->process->splitLines($output) as $line) { if (preg_match('{^composer/'.preg_quote($branch).'(?:\.x)?$}i', trim($line))) { // find the previous commit by date in the given branch - if (0 === $this->process->execute(sprintf($guessTemplate, $date, escapeshellarg(trim($line))), $output, $path)) { + if (0 === $this->process->execute(sprintf($guessTemplate, $date, ProcessExecutor::escape(trim($line))), $output, $path)) { $newReference = trim($output); } @@ -286,7 +290,7 @@ class GitDownloader extends VcsDownloader } // checkout the new recovered ref - $command = sprintf($template, escapeshellarg($newReference)); + $command = sprintf($template, ProcessExecutor::escape($newReference)); if (0 === $this->process->execute($command, $output, $path)) { $this->io->write(' '.$reference.' is gone (history was rewritten?), recovered by checking out '.$newReference); @@ -306,7 +310,7 @@ class GitDownloader extends VcsDownloader if ($protocols[0] !== 'git') { $pushUrl = 'https://' . $match[1] . '/'.$match[2].'/'.$match[3].'.git'; } - $cmd = sprintf('git remote set-url --push origin %s', escapeshellarg($pushUrl)); + $cmd = sprintf('git remote set-url --push origin %s', ProcessExecutor::escape($pushUrl)); $this->process->execute($cmd, $ignoredOutput, $path); } } diff --git a/src/Composer/Downloader/GzipDownloader.php b/src/Composer/Downloader/GzipDownloader.php index 073b18c30..f8624ab24 100644 --- a/src/Composer/Downloader/GzipDownloader.php +++ b/src/Composer/Downloader/GzipDownloader.php @@ -40,7 +40,7 @@ class GzipDownloader extends ArchiveDownloader // Try to use gunzip on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $command = 'gzip -cd ' . escapeshellarg($file) . ' > ' . escapeshellarg($targetFilepath); + $command = 'gzip -cd ' . ProcessExecutor::escape($file) . ' > ' . ProcessExecutor::escape($targetFilepath); if (0 === $this->process->execute($command, $ignoredOutput)) { return; diff --git a/src/Composer/Downloader/HgDownloader.php b/src/Composer/Downloader/HgDownloader.php index 69e8ba886..3d5cc6209 100644 --- a/src/Composer/Downloader/HgDownloader.php +++ b/src/Composer/Downloader/HgDownloader.php @@ -13,6 +13,7 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; +use Composer\Util\ProcessExecutor; /** * @author Per Bernhardt @@ -24,10 +25,10 @@ class HgDownloader extends VcsDownloader */ public function doDownload(PackageInterface $package, $path, $url) { - $url = escapeshellarg($url); - $ref = escapeshellarg($package->getSourceReference()); + $url = ProcessExecutor::escape($url); + $ref = ProcessExecutor::escape($package->getSourceReference()); $this->io->write(" Cloning ".$package->getSourceReference()); - $command = sprintf('hg clone %s %s', $url, escapeshellarg($path)); + $command = sprintf('hg clone %s %s', $url, ProcessExecutor::escape($path)); if (0 !== $this->process->execute($command, $ignoredOutput)) { throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); } @@ -42,8 +43,8 @@ class HgDownloader extends VcsDownloader */ public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url) { - $url = escapeshellarg($url); - $ref = escapeshellarg($target->getSourceReference()); + $url = ProcessExecutor::escape($url); + $ref = ProcessExecutor::escape($target->getSourceReference()); $this->io->write(" Updating to ".$target->getSourceReference()); if (!is_dir($path.'/.hg')) { diff --git a/src/Composer/Downloader/RarDownloader.php b/src/Composer/Downloader/RarDownloader.php index bb62ee0a8..12823422d 100644 --- a/src/Composer/Downloader/RarDownloader.php +++ b/src/Composer/Downloader/RarDownloader.php @@ -42,7 +42,7 @@ class RarDownloader extends ArchiveDownloader // Try to use unrar on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $command = 'unrar x ' . escapeshellarg($file) . ' ' . escapeshellarg($path) . ' && chmod -R u+w ' . escapeshellarg($path); + $command = 'unrar x ' . ProcessExecutor::escape($file) . ' ' . ProcessExecutor::escape($path) . ' && chmod -R u+w ' . ProcessExecutor::escape($path); if (0 === $this->process->execute($command, $ignoredOutput)) { return; diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index b35d6a5af..1370d82af 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -38,7 +38,7 @@ class ZipDownloader extends ArchiveDownloader // try to use unzip on *nix if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $command = 'unzip '.escapeshellarg($file).' -d '.escapeshellarg($path) . ' && chmod -R u+w ' . escapeshellarg($path); + $command = 'unzip '.ProcessExecutor::escape($file).' -d '.ProcessExecutor::escape($path) . ' && chmod -R u+w ' . ProcessExecutor::escape($path); try { if (0 === $this->process->execute($command, $ignoredOutput)) { return; diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 7e882ddb3..620010494 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -152,7 +152,7 @@ class EventDispatcher throw $e; } } else { - $args = implode(' ', array_map('escapeshellarg', $event->getArguments())); + $args = implode(' ', array_map(array('Composer\Util\ProcessExecutor','escape'), $event->getArguments())); if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) { $event->getIO()->write(sprintf('Script %s handling the %s event returned with an error', $callable, $event->getName())); diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 4facfd494..05cd420d7 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -17,6 +17,7 @@ use Composer\IO\IOInterface; use Composer\Repository\InstalledRepositoryInterface; use Composer\Package\PackageInterface; use Composer\Util\Filesystem; +use Composer\Util\ProcessExecutor; /** * Package installation manager. @@ -296,7 +297,7 @@ class LibraryInstaller implements InstallerInterface } return "@ECHO OFF\r\n". - "SET BIN_TARGET=%~dp0/".trim(escapeshellarg($binPath), '"')."\r\n". + "SET BIN_TARGET=%~dp0/".trim(ProcessExecutor::escape($binPath), '"')."\r\n". "{$caller} \"%BIN_TARGET%\" %*\r\n"; } @@ -307,7 +308,7 @@ class LibraryInstaller implements InstallerInterface return "#!/usr/bin/env sh\n". 'SRC_DIR="`pwd`"'."\n". 'cd "`dirname "$0"`"'."\n". - 'cd '.escapeshellarg(dirname($binPath))."\n". + 'cd '.ProcessExecutor::escape(dirname($binPath))."\n". 'BIN_TARGET="`pwd`/'.basename($binPath)."\"\n". 'cd "$SRC_DIR"'."\n". '"$BIN_TARGET" "$@"'."\n"; diff --git a/src/Composer/Installer/PearInstaller.php b/src/Composer/Installer/PearInstaller.php index defadd9cf..fd3bbd976 100644 --- a/src/Composer/Installer/PearInstaller.php +++ b/src/Composer/Installer/PearInstaller.php @@ -17,6 +17,7 @@ use Composer\Composer; use Composer\Downloader\PearPackageExtractor; use Composer\Repository\InstalledRepositoryInterface; use Composer\Package\PackageInterface; +use Composer\Util\ProcessExecutor; /** * Package installation manager. @@ -124,7 +125,7 @@ class PearInstaller extends LibraryInstaller "pushd .\r\n". "cd %~dp0\r\n". "set PHP_PROXY=%CD%\\composer-php.bat\r\n". - "cd ".escapeshellarg(dirname($binPath))."\r\n". + "cd ".ProcessExecutor::escape(dirname($binPath))."\r\n". "set BIN_TARGET=%CD%\\".basename($binPath)."\r\n". "popd\r\n". "%PHP_PROXY% \"%BIN_TARGET%\" %*\r\n"; @@ -134,7 +135,7 @@ class PearInstaller extends LibraryInstaller return "@echo off\r\n". "pushd .\r\n". "cd %~dp0\r\n". - "cd ".escapeshellarg(dirname($binPath))."\r\n". + "cd ".ProcessExecutor::escape(dirname($binPath))."\r\n". "set BIN_TARGET=%CD%\\".basename($binPath)."\r\n". "popd\r\n". $caller." \"%BIN_TARGET%\" %*\r\n"; diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 2fa9b011a..4eb836706 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -341,13 +341,13 @@ class Locker case 'git': GitUtil::cleanEnv(); - if (0 === $this->process->execute('git log -n1 --pretty=%ct '.escapeshellarg($sourceRef), $output, $path) && preg_match('{^\s*\d+\s*$}', $output)) { + if (0 === $this->process->execute('git log -n1 --pretty=%ct '.ProcessExecutor::escape($sourceRef), $output, $path) && preg_match('{^\s*\d+\s*$}', $output)) { $datetime = new \DateTime('@'.trim($output), new \DateTimeZone('UTC')); } break; case 'hg': - if (0 === $this->process->execute('hg log --template "{date|hgdate}" -r '.escapeshellarg($sourceRef), $output, $path) && preg_match('{^\s*(\d+)\s*}', $output, $match)) { + if (0 === $this->process->execute('hg log --template "{date|hgdate}" -r '.ProcessExecutor::escape($sourceRef), $output, $path) && preg_match('{^\s*(\d+)\s*}', $output, $match)) { $datetime = new \DateTime('@'.$match[1], new \DateTimeZone('UTC')); } break; diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 180c441c7..a020b92c2 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -62,7 +62,7 @@ class GitDriver extends VcsDriver if (is_dir($this->repoDir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $this->repoDir) && trim($output) === '.') { try { $commandCallable = function ($url) { - return sprintf('git remote set-url origin %s && git remote update --prune origin', escapeshellarg($url)); + return sprintf('git remote set-url origin %s && git remote update --prune origin', ProcessExecutor::escape($url)); }; $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir); } catch (\Exception $e) { @@ -74,7 +74,7 @@ class GitDriver extends VcsDriver $repoDir = $this->repoDir; $commandCallable = function ($url) use ($repoDir) { - return sprintf('git clone --mirror %s %s', escapeshellarg($url), escapeshellarg($repoDir)); + return sprintf('git clone --mirror %s %s', ProcessExecutor::escape($url), ProcessExecutor::escape($repoDir)); }; $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir, true); @@ -147,7 +147,7 @@ class GitDriver extends VcsDriver } if (!isset($this->infoCache[$identifier])) { - $resource = sprintf('%s:composer.json', escapeshellarg($identifier)); + $resource = sprintf('%s:composer.json', ProcessExecutor::escape($identifier)); $this->process->execute(sprintf('git show %s', $resource), $composer, $this->repoDir); if (!trim($composer)) { @@ -157,7 +157,7 @@ class GitDriver extends VcsDriver $composer = JsonFile::parseJson($composer, $resource); if (!isset($composer['time'])) { - $this->process->execute(sprintf('git log -1 --format=%%at %s', escapeshellarg($identifier)), $output, $this->repoDir); + $this->process->execute(sprintf('git log -1 --format=%%at %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir); $date = new \DateTime('@'.trim($output), new \DateTimeZone('UTC')); $composer['time'] = $date->format('Y-m-d H:i:s'); } diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index b72058790..eb962f276 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -56,7 +56,7 @@ class HgDriver extends VcsDriver // clean up directory and do a fresh clone into it $fs->removeDirectory($this->repoDir); - if (0 !== $this->process->execute(sprintf('hg clone --noupdate %s %s', escapeshellarg($this->url), escapeshellarg($this->repoDir)), $output, $cacheDir)) { + if (0 !== $this->process->execute(sprintf('hg clone --noupdate %s %s', ProcessExecutor::escape($this->url), ProcessExecutor::escape($this->repoDir)), $output, $cacheDir)) { $output = $this->process->getErrorOutput(); if (0 !== $this->process->execute('hg --version', $ignoredOutput)) { @@ -116,7 +116,7 @@ class HgDriver extends VcsDriver public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $this->process->execute(sprintf('hg cat -r %s composer.json', escapeshellarg($identifier)), $composer, $this->repoDir); + $this->process->execute(sprintf('hg cat -r %s composer.json', ProcessExecutor::escape($identifier)), $composer, $this->repoDir); if (!trim($composer)) { return; @@ -125,7 +125,7 @@ class HgDriver extends VcsDriver $composer = JsonFile::parseJson($composer, $identifier); if (!isset($composer['time'])) { - $this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', escapeshellarg($identifier)), $output, $this->repoDir); + $this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir); $date = new \DateTime(trim($output), new \DateTimeZone('UTC')); $composer['time'] = $date->format('Y-m-d H:i:s'); } @@ -215,7 +215,7 @@ class HgDriver extends VcsDriver } $processExecutor = new ProcessExecutor(); - $exit = $processExecutor->execute(sprintf('hg identify %s', escapeshellarg($url)), $ignored); + $exit = $processExecutor->execute(sprintf('hg identify %s', ProcessExecutor::escape($url)), $ignored); return $exit === 0; } diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 984f91e62..00f4b36f8 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -112,9 +112,9 @@ class Filesystem } if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $cmd = sprintf('rmdir /S /Q %s', escapeshellarg(realpath($directory))); + $cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape(realpath($directory))); } else { - $cmd = sprintf('rm -rf %s', escapeshellarg($directory)); + $cmd = sprintf('rm -rf %s', ProcessExecutor::escape($directory)); } $result = $this->getProcess()->execute($cmd, $output) === 0; @@ -269,7 +269,7 @@ class Filesystem if (defined('PHP_WINDOWS_VERSION_BUILD')) { // Try to copy & delete - this is a workaround for random "Access denied" errors. - $command = sprintf('xcopy %s %s /E /I /Q', escapeshellarg($source), escapeshellarg($target)); + $command = sprintf('xcopy %s %s /E /I /Q', ProcessExecutor::escape($source), ProcessExecutor::escape($target)); $result = $this->processExecutor->execute($command, $output); // clear stat cache because external processes aren't tracked by the php stat cache @@ -283,7 +283,7 @@ class Filesystem } else { // We do not use PHP's "rename" function here since it does not support // the case where $source, and $target are located on different partitions. - $command = sprintf('mv %s %s', escapeshellarg($source), escapeshellarg($target)); + $command = sprintf('mv %s %s', ProcessExecutor::escape($source), ProcessExecutor::escape($target)); $result = $this->processExecutor->execute($command, $output); // clear stat cache because external processes aren't tracked by the php stat cache diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php index ab4e4b2db..d5a0549d3 100644 --- a/src/Composer/Util/ProcessExecutor.php +++ b/src/Composer/Util/ProcessExecutor.php @@ -13,6 +13,7 @@ namespace Composer\Util; use Symfony\Component\Process\Process; +use Symfony\Component\Process\ProcessUtils; use Composer\IO\IOInterface; /** @@ -104,4 +105,17 @@ class ProcessExecutor { static::$timeout = $timeout; } + + /** + * Escapes a string to be used as a shell argument. + * + * @param string $argument The argument that will be escaped + * + * @return string The escaped argument + */ + + public static function escape ($argument) + { + return ProcessUtils::escapeArgument($argument); + } } diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index 6d659a7f1..4b6d615fb 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -196,11 +196,11 @@ class Svn $cmd, '--non-interactive ', $this->getCredentialString(), - escapeshellarg($url) + ProcessExecutor::escape($url) ); if ($path) { - $cmd .= ' ' . escapeshellarg($path); + $cmd .= ' ' . ProcessExecutor::escape($path); } return $cmd; @@ -222,8 +222,8 @@ class Svn return sprintf( ' %s--username %s --password %s ', $this->getAuthCache(), - escapeshellarg($this->getUsername()), - escapeshellarg($this->getPassword()) + ProcessExecutor::escape($this->getUsername()), + ProcessExecutor::escape($this->getPassword()) ); }