@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Filter and reject "--config" and "--debugger" flags to Mercurial in any position

Summary:
Ref T13012. These flags can be exploited by attackers to execute code remotely. See T13012 for discussion and context.

Additionally, harden some Mercurial commands where possible (by using additional quoting or embedding arguments in other constructs) so they resist these flags and behave properly when passed arguments with these values.

Test Plan:
- Added unit tests.
- Verified "--config" and "--debugger" commands are rejected.
- Verified more commands now work properly even with branches and files named `--debugger`, although not all of them do.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13012

Differential Revision: https://secure.phabricator.com/D18769

+97 -13
+2
src/__phutil_library_map__.php
··· 768 768 'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php', 769 769 'DiffusionMercurialCommandEngine' => 'applications/diffusion/protocol/DiffusionMercurialCommandEngine.php', 770 770 'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php', 771 + 'DiffusionMercurialFlagInjectionException' => 'applications/diffusion/exception/DiffusionMercurialFlagInjectionException.php', 771 772 'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php', 772 773 'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php', 773 774 'DiffusionMercurialResponse' => 'applications/diffusion/response/DiffusionMercurialResponse.php', ··· 5814 5815 'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery', 5815 5816 'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine', 5816 5817 'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery', 5818 + 'DiffusionMercurialFlagInjectionException' => 'Exception', 5817 5819 'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery', 5818 5820 'DiffusionMercurialRequest' => 'DiffusionRequest', 5819 5821 'DiffusionMercurialResponse' => 'AphrontResponse',
+1 -1
src/applications/diffusion/conduit/DiffusionExistsQueryConduitAPIMethod.php
··· 47 47 $commit = $request->getValue('commit'); 48 48 list($err, $stdout) = $repository->execLocalCommand( 49 49 'id --rev %s', 50 - $commit); 50 + hgsprintf('%s', $commit)); 51 51 return !$err; 52 52 } 53 53
+10 -8
src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php
··· 123 123 // branches). 124 124 125 125 if (strlen($path)) { 126 - $path_arg = csprintf('-- %s', $path); 127 - $branch_arg = ''; 126 + $path_arg = csprintf('%s', $path); 127 + $revset_arg = hgsprintf( 128 + 'reverse(ancestors(%s))', 129 + $commit_hash); 128 130 } else { 129 131 $path_arg = ''; 130 - // NOTE: --branch used to be called --only-branch; use -b for 131 - // compatibility. 132 - $branch_arg = csprintf('-b %s', $drequest->getBranch()); 132 + $revset_arg = hgsprintf( 133 + 'branch(%s) and reverse(ancestors(%s))', 134 + $drequest->getBranch(), 135 + $commit_hash); 133 136 } 134 137 135 138 list($stdout) = $repository->execxLocalCommand( 136 - 'log --debug --template %s --limit %d %C --rev %s %C', 139 + 'log --debug --template %s --limit %d --rev %s -- %C', 137 140 '{node};{parents}\\n', 138 141 ($offset + $limit), // No '--skip' in Mercurial. 139 - $branch_arg, 140 - hgsprintf('reverse(ancestors(%s))', $commit_hash), 142 + $revset_arg, 141 143 $path_arg); 142 144 143 145 $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput(
+1 -1
src/applications/diffusion/conduit/DiffusionMergedCommitsQueryConduitAPIMethod.php
··· 77 77 list($parents) = $repository->execxLocalCommand( 78 78 'parents --template=%s --rev %s', 79 79 '{node}\\n', 80 - $commit); 80 + hgsprintf('%s', $commit)); 81 81 $parents = explode("\n", trim($parents)); 82 82 83 83 if (count($parents) < 2) {
+1 -1
src/applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php
··· 97 97 98 98 $results = array(); 99 99 $future = $repository->getLocalCommandFuture( 100 - 'grep --rev %s --print0 --line-number %s %s', 100 + 'grep --rev %s --print0 --line-number -- %s %s', 101 101 hgsprintf('ancestors(%s)', $drequest->getStableCommit()), 102 102 $grep, 103 103 $path);
+3
src/applications/diffusion/exception/DiffusionMercurialFlagInjectionException.php
··· 1 + <?php 2 + 3 + final class DiffusionMercurialFlagInjectionException extends Exception {}
+21
src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php
··· 11 11 protected function newFormattedCommand($pattern, array $argv) { 12 12 $args = array(); 13 13 14 + // Crudely blacklist commands which look like they may contain command 15 + // injection via "--config" or "--debugger". See T13012. To do this, we 16 + // print the whole command, parse it using shell rules, then examine each 17 + // argument to see if it looks like "--config" or "--debugger". 18 + 19 + $test_command = call_user_func_array( 20 + 'csprintf', 21 + array_merge(array($pattern), $argv)); 22 + $test_args = id(new PhutilShellLexer()) 23 + ->splitArguments($test_command); 24 + 25 + foreach ($test_args as $test_arg) { 26 + if (preg_match('/^--(config|debugger)/i', $test_arg)) { 27 + throw new DiffusionMercurialFlagInjectionException( 28 + pht( 29 + 'Mercurial command appears to contain unsafe injected "--config" '. 30 + 'or "--debugger": %s', 31 + $test_command)); 32 + } 33 + } 34 + 14 35 // NOTE: Here, and in Git and Subversion, we override the SSH command even 15 36 // if the repository does not use an SSH remote, since our SSH wrapper 16 37 // defuses an attack against older versions of Mercurial, Git and
+56
src/applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php
··· 123 123 'argv' => 'xyz', 124 124 'protocol' => 'https', 125 125 )); 126 + 127 + // Test that filtering defenses for "--config" and "--debugger" flag 128 + // injections in Mercurial are functional. See T13012. 129 + 130 + $caught = null; 131 + try { 132 + $this->assertCommandEngineFormat( 133 + '', 134 + array(), 135 + array( 136 + 'vcs' => $type_hg, 137 + 'argv' => '--debugger', 138 + )); 139 + } catch (DiffusionMercurialFlagInjectionException $ex) { 140 + $caught = $ex; 141 + } 142 + 143 + $this->assertTrue( 144 + ($caught instanceof DiffusionMercurialFlagInjectionException), 145 + pht('Expected "--debugger" injection in Mercurial to throw.')); 146 + 147 + 148 + $caught = null; 149 + try { 150 + $this->assertCommandEngineFormat( 151 + '', 152 + array(), 153 + array( 154 + 'vcs' => $type_hg, 155 + 'argv' => '--config=x', 156 + )); 157 + } catch (DiffusionMercurialFlagInjectionException $ex) { 158 + $caught = $ex; 159 + } 160 + 161 + $this->assertTrue( 162 + ($caught instanceof DiffusionMercurialFlagInjectionException), 163 + pht('Expected "--config" injection in Mercurial to throw.')); 164 + 165 + $caught = null; 166 + try { 167 + $this->assertCommandEngineFormat( 168 + '', 169 + array(), 170 + array( 171 + 'vcs' => $type_hg, 172 + 'argv' => (string)csprintf('%s', '--config=x'), 173 + )); 174 + } catch (DiffusionMercurialFlagInjectionException $ex) { 175 + $caught = $ex; 176 + } 177 + 178 + $this->assertTrue( 179 + ($caught instanceof DiffusionMercurialFlagInjectionException), 180 + pht('Expected quoted "--config" injection in Mercurial to throw.')); 181 + 126 182 } 127 183 128 184 private function assertCommandEngineFormat(
+2 -2
src/applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php
··· 16 16 // If `$commit` has no parents (usually because it's the first commit 17 17 // in the repository), we want to diff against `null`. This revset will 18 18 // do that for us automatically. 19 - $against = '('.$commit.'^ or null)'; 19 + $against = hgsprintf('(%s^ or null)', $commit); 20 20 } 21 21 22 22 $future = $repository->getLocalCommandFuture( 23 23 'diff -U %d --git --rev %s --rev %s -- %s', 24 24 $this->getLinesOfContext(), 25 25 $against, 26 - $commit, 26 + hgsprintf('%s', $commit), 27 27 $path); 28 28 29 29 return $future;