Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use "symfony/process" for command argument escaping #146

Merged
merged 1 commit into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/SVNBuddy/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

/** @var Connector $repository_connector */
$repository_connector = $this->dic['repository_connector'];
$client_version = $repository_connector->getCommand('', '--version --quiet')->run();
$client_version = $repository_connector->getCommand('', array('--version', '--quiet'))->run();

Check warning on line 60 in src/SVNBuddy/Application.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Application.php#L60

Added line #L60 was not covered by tests

return $version . ' (SVN <comment>v' . trim($client_version) . '</comment>)';
}
Expand Down
2 changes: 1 addition & 1 deletion src/SVNBuddy/Command/CleanupCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
$wc_path = $this->getWorkingCopyPath();

$this->io->writeln('Cleaning up working copy ... ');
$command = $this->repositoryConnector->getCommand('cleanup', '{' . $wc_path . '}');
$command = $this->repositoryConnector->getCommand('cleanup', array($wc_path));

Check warning on line 49 in src/SVNBuddy/Command/CleanupCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/CleanupCommand.php#L49

Added line #L49 was not covered by tests
$command->runLive(array(
$wc_path => '.',
));
Expand Down
13 changes: 6 additions & 7 deletions src/SVNBuddy/Command/CommitCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,23 +175,22 @@
$tmp_file = tempnam(sys_get_temp_dir(), 'commit_message_');
file_put_contents($tmp_file, $edited_commit_message);

$arguments = array(
'-F {' . $tmp_file . '}',
);
$arguments = array('-F', $tmp_file);

Check warning on line 178 in src/SVNBuddy/Command/CommitCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/CommitCommand.php#L178

Added line #L178 was not covered by tests

if ( strlen($changelist) ) {
$arguments[] = '--depth empty';
$arguments[] = '--depth';
$arguments[] = 'empty';

Check warning on line 182 in src/SVNBuddy/Command/CommitCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/CommitCommand.php#L181-L182

Added lines #L181 - L182 were not covered by tests

// Relative path used to make command line shorter.
foreach ( array_keys($this->repositoryConnector->getWorkingCopyStatus($wc_path, $changelist)) as $path ) {
$arguments[] = '{' . $path . '}';
$arguments[] = $path;

Check warning on line 186 in src/SVNBuddy/Command/CommitCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/CommitCommand.php#L186

Added line #L186 was not covered by tests
}
}
else {
$arguments[] = '{' . $wc_path . '}';
$arguments[] = $wc_path;

Check warning on line 190 in src/SVNBuddy/Command/CommitCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/CommitCommand.php#L190

Added line #L190 was not covered by tests
}

$this->repositoryConnector->getCommand('commit', implode(' ', $arguments))->runLive();
$this->repositoryConnector->getCommand('commit', $arguments)->runLive();

Check warning on line 193 in src/SVNBuddy/Command/CommitCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/CommitCommand.php#L193

Added line #L193 was not covered by tests
$this->_workingCopyConflictTracker->erase($wc_path);
unlink($tmp_file);

Expand Down
50 changes: 32 additions & 18 deletions src/SVNBuddy/Command/MergeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -554,11 +554,11 @@
{
$command = $this->repositoryConnector->getCommand(
'mergeinfo',
sprintf(
'--show-revs %s {%s} {%s}',
array(
'--show-revs',

Check warning on line 558 in src/SVNBuddy/Command/MergeCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/MergeCommand.php#L557-L558

Added lines #L557 - L558 were not covered by tests
$this->isReverseMerge() ? 'merged' : 'eligible',
$source_url,
$wc_path
$wc_path,

Check warning on line 561 in src/SVNBuddy/Command/MergeCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/MergeCommand.php#L561

Added line #L561 was not covered by tests
)
);

Expand Down Expand Up @@ -657,17 +657,6 @@
$revision_count += $used_revision_count;
}

$param_string_beginning = '-c ';
$param_string_ending = '{' . $source_url . '} {' . $wc_path . '}';

if ( $this->isReverseMerge() ) {
$param_string_beginning .= '-';
}

if ( $this->io->getOption('record-only') ) {
$param_string_ending = '--record-only ' . $param_string_ending;
}

$revision_log = $this->getRevisionLog($source_url);
$revisions_data = $revision_log->getRevisionsData('summary', $revisions);

Expand All @@ -678,11 +667,11 @@
$revision_title_mask .= ' revision';
}

$merge_command_arguments = $this->getMergeCommandArguments($source_url, $wc_path);

Check warning on line 670 in src/SVNBuddy/Command/MergeCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/MergeCommand.php#L670

Added line #L670 was not covered by tests

foreach ( $revisions as $index => $revision ) {
$command = $this->repositoryConnector->getCommand(
'merge',
$param_string_beginning . $revision . ' ' . $param_string_ending
);
$command_arguments = str_replace('{revision}', $revision, $merge_command_arguments);
$command = $this->repositoryConnector->getCommand('merge', $command_arguments);

Check warning on line 674 in src/SVNBuddy/Command/MergeCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/MergeCommand.php#L673-L674

Added lines #L673 - L674 were not covered by tests

$progress_bar = $this->createMergeProgressBar($used_revision_count + $index + 1, $revision_count);

Expand Down Expand Up @@ -710,6 +699,31 @@
$this->performCommit();
}

/**
* Returns merge command arguments.
*
* @param string $source_url Merge source: url.
* @param string $wc_path Merge target: working copy path.
*
* @return array
*/
protected function getMergeCommandArguments($source_url, $wc_path)

Check warning on line 710 in src/SVNBuddy/Command/MergeCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/MergeCommand.php#L710

Added line #L710 was not covered by tests
{
$ret = array(
'-c',
$this->isReverseMerge() ? '-{revision}' : '{revision}',
);

Check warning on line 715 in src/SVNBuddy/Command/MergeCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/MergeCommand.php#L712-L715

Added lines #L712 - L715 were not covered by tests

if ( $this->io->getOption('record-only') ) {
$ret[] = '--record-only';
}

Check warning on line 719 in src/SVNBuddy/Command/MergeCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/MergeCommand.php#L717-L719

Added lines #L717 - L719 were not covered by tests

$ret[] = $source_url;
$ret[] = $wc_path;

Check warning on line 722 in src/SVNBuddy/Command/MergeCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/MergeCommand.php#L721-L722

Added lines #L721 - L722 were not covered by tests

return $ret;

Check warning on line 724 in src/SVNBuddy/Command/MergeCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/MergeCommand.php#L724

Added line #L724 was not covered by tests
}

/**
* Create merge progress bar.
*
Expand Down
2 changes: 1 addition & 1 deletion src/SVNBuddy/Command/RevertCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
$this->io->writeln('Reverting local changes in working copy ... ');
$command = $this->repositoryConnector->getCommand(
'revert',
'--depth infinity {' . $wc_path . '}'
array('--depth', 'infinity', $wc_path)

Check warning on line 76 in src/SVNBuddy/Command/RevertCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/RevertCommand.php#L76

Added line #L76 was not covered by tests
);
$command->runLive(array(
$wc_path => '.',
Expand Down
9 changes: 5 additions & 4 deletions src/SVNBuddy/Command/UpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,18 @@
'Updating working copy to <info>' . $show_revision . '</info> revision ' . $show_externals . ' ... '
);

$param_string = '{' . $wc_path . '}';
$arguments = array($wc_path);

Check warning on line 90 in src/SVNBuddy/Command/UpdateCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/UpdateCommand.php#L90

Added line #L90 was not covered by tests

if ( $revision ) {
$param_string .= ' --revision ' . $revision;
$arguments[] = '--revision';
$arguments[] = $revision;

Check warning on line 94 in src/SVNBuddy/Command/UpdateCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/UpdateCommand.php#L93-L94

Added lines #L93 - L94 were not covered by tests
}

if ( $ignore_externals ) {
$param_string .= ' --ignore-externals';
$arguments[] = '--ignore-externals';

Check warning on line 98 in src/SVNBuddy/Command/UpdateCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/UpdateCommand.php#L98

Added line #L98 was not covered by tests
}

$command = $this->repositoryConnector->getCommand('update', $param_string);
$command = $this->repositoryConnector->getCommand('update', $arguments);

Check warning on line 101 in src/SVNBuddy/Command/UpdateCommand.php

View check run for this annotation

Codecov / codecov/patch

src/SVNBuddy/Command/UpdateCommand.php#L101

Added line #L101 was not covered by tests
$command->runLive(array(
$wc_path => '.',
));
Expand Down
7 changes: 2 additions & 5 deletions src/SVNBuddy/Process/IProcessFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,12 @@ interface IProcessFactory
/**
* Creates new Symfony process with given arguments.
*
* @param string $commandline The command line to run.
* @param array $command_line The command line to run.
* @param integer|null $idle_timeout Idle timeout.
*
* @return Process
*/
public function createProcess(
$commandline,
$idle_timeout = null
);
public function createProcess(array $command_line, $idle_timeout = null);

/**
* Creates new Symfony PHP process with given arguments.
Expand Down
15 changes: 7 additions & 8 deletions src/SVNBuddy/Process/ProcessFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,21 @@

use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessBuilder;

class ProcessFactory implements IProcessFactory
{

/**
* Creates new Symfony process with given arguments.
*
* @param string $commandline The command line to run.
* @param array $command_line The command line to run.
* @param integer|null $idle_timeout Idle timeout.
*
* @return Process
*/
public function createProcess(
$commandline,
$idle_timeout = null
) {
$process = new Process($commandline);
public function createProcess(array $command_line, $idle_timeout = null)
{
$process = new Process($command_line);
$process->setTimeout(null);
$process->setIdleTimeout($idle_timeout);

Expand All @@ -51,13 +48,15 @@ public function createCommandProcess($command, array $arguments = array())
$php_executable_finder = new PhpExecutableFinder();
$php_executable = $php_executable_finder->find();

// @codeCoverageIgnoreStart
if ( !$php_executable ) {
throw new \RuntimeException('The PHP executable cannot be found.');
}
// @codeCoverageIgnoreEnd

array_unshift($arguments, $php_executable, $_SERVER['argv'][0], $command);

return ProcessBuilder::create($arguments)->getProcess();
return new Process($arguments);
}

}
31 changes: 22 additions & 9 deletions src/SVNBuddy/Repository/Connector/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Command
/**
* Command line.
*
* @var string
* @var array
*/
private $_commandLine;

Expand Down Expand Up @@ -74,13 +74,13 @@ class Command
/**
* Creates a command instance.
*
* @param string $command_line Command line.
* @param array $command_line Command line.
* @param ConsoleIO $io Console IO.
* @param CacheManager $cache_manager Cache manager.
* @param IProcessFactory $process_factory Process factory.
*/
public function __construct(
$command_line,
array $command_line,
ConsoleIO $io,
CacheManager $cache_manager,
IProcessFactory $process_factory
Expand Down Expand Up @@ -166,7 +166,7 @@ public function run($callback = null)
}
}

if ( strpos($this->_commandLine, '--xml') !== false ) {
if ( in_array('--xml', $this->_commandLine) ) {
return simplexml_load_string($output);
}

Expand All @@ -181,11 +181,13 @@ public function run($callback = null)
private function _getCacheKey()
{
if ( $this->_cacheInvalidator || $this->_cacheDuration ) {
if ( preg_match(Connector::URL_REGEXP, $this->_commandLine, $regs) ) {
return $regs[2] . $regs[3] . $regs[4] . '/command:' . $this->_commandLine;
$command_string = (string)$this;

if ( preg_match(Connector::URL_REGEXP, $command_string, $regs) ) {
return $regs[2] . $regs[3] . $regs[4] . '/command:' . $command_string;
}

return 'misc/command:' . $this->_commandLine;
return 'misc/command:' . $command_string;
}

return '';
Expand All @@ -202,6 +204,7 @@ private function _getCacheKey()
private function _doRun($callback = null)
{
$process = $this->_processFactory->createProcess($this->_commandLine, 1200);
$command_string = (string)$this;

try {
$start = microtime(true);
Expand All @@ -210,7 +213,7 @@ private function _doRun($callback = null)
if ( $this->_io->isVerbose() ) {
$runtime = sprintf('%01.2f', microtime(true) - $start);
$this->_io->writeln(
array('', '<debug>[svn, ' . round($runtime, 2) . 's]: ' . $this->_commandLine . '</debug>')
array('', '<debug>[svn, ' . round($runtime, 2) . 's]: ' . $command_string . '</debug>')
);
}

Expand All @@ -224,7 +227,7 @@ private function _doRun($callback = null)
}
catch ( ProcessFailedException $e ) {
throw new RepositoryCommandException(
$this->_commandLine,
$command_string,
$process->getErrorOutput()
);
}
Expand Down Expand Up @@ -276,4 +279,14 @@ private function _createLiveOutputCallback(array $replacements = array())
};
}

/**
* Returns a string representation of a command.
*
* @return string
*/
public function __toString()
{
return implode(' ', $this->_commandLine);
}

}
Loading
Loading