mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-31 08:58:20 +01:00
Relax the same-origin check in the Git commit discovery daemon
Summary: Git accepts either "git@x:/path" or "ssh://git@x.com/path" URIs to mean the exact same thing, which is causing some false positives and confusion, particularly because we sometimes mutate URIs. Since this is just a sanity check, we don't really care about the username, domain or credentials -- matching the paths is good enough. We're just trying to make it hard to shoot yourself in the foot by copy-pasting the same local path into two repositories and forgetting to change one, like I did. :P Relax the check to only verify the paths are the same. Test Plan: - Ran unit tests, which should fully cover things. - Ran commit discovery daemon in debug mode on incorrectly and correctly configured repositories. Reviewers: ajtrichards, jungejason, btrahan Reviewed By: jungejason CC: aran, jungejason Maniphest Tasks: T710 Differential Revision: https://secure.phabricator.com/D1279
This commit is contained in:
parent
cacdfcc8ea
commit
e786c44b6f
8 changed files with 196 additions and 15 deletions
|
@ -623,6 +623,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryEditController' => 'applications/repository/controller/edit',
|
'PhabricatorRepositoryEditController' => 'applications/repository/controller/edit',
|
||||||
'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/git',
|
'PhabricatorRepositoryGitCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/git',
|
||||||
'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/git',
|
'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/git',
|
||||||
|
'PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase' => 'applications/repository/daemon/commitdiscovery/git/__tests__',
|
||||||
'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/git',
|
'PhabricatorRepositoryGitCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/git',
|
||||||
'PhabricatorRepositoryGitFetchDaemon' => 'applications/repository/daemon/gitfetch',
|
'PhabricatorRepositoryGitFetchDaemon' => 'applications/repository/daemon/gitfetch',
|
||||||
'PhabricatorRepositoryGitHubNotification' => 'applications/repository/storage/githubnotification',
|
'PhabricatorRepositoryGitHubNotification' => 'applications/repository/storage/githubnotification',
|
||||||
|
@ -638,6 +639,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn',
|
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn',
|
||||||
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn',
|
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn',
|
||||||
'PhabricatorRepositorySymbol' => 'applications/repository/storage/symbol',
|
'PhabricatorRepositorySymbol' => 'applications/repository/storage/symbol',
|
||||||
|
'PhabricatorRepositoryTestCase' => 'applications/repository/storage/repository/__tests__',
|
||||||
'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype',
|
'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype',
|
||||||
'PhabricatorS3FileStorageEngine' => 'applications/files/engine/s3',
|
'PhabricatorS3FileStorageEngine' => 'applications/files/engine/s3',
|
||||||
'PhabricatorSQLPatchList' => 'infrastructure/setup/sql',
|
'PhabricatorSQLPatchList' => 'infrastructure/setup/sql',
|
||||||
|
@ -1267,6 +1269,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController',
|
'PhabricatorRepositoryEditController' => 'PhabricatorRepositoryController',
|
||||||
'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
|
'PhabricatorRepositoryGitCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
|
||||||
'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
|
'PhabricatorRepositoryGitCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
|
||||||
|
'PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
'PhabricatorRepositoryGitCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
||||||
'PhabricatorRepositoryGitFetchDaemon' => 'PhabricatorRepositoryPullLocalDaemon',
|
'PhabricatorRepositoryGitFetchDaemon' => 'PhabricatorRepositoryPullLocalDaemon',
|
||||||
'PhabricatorRepositoryGitHubNotification' => 'PhabricatorRepositoryDAO',
|
'PhabricatorRepositoryGitHubNotification' => 'PhabricatorRepositoryDAO',
|
||||||
|
@ -1282,6 +1285,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
|
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
|
||||||
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
||||||
'PhabricatorRepositorySymbol' => 'PhabricatorRepositoryDAO',
|
'PhabricatorRepositorySymbol' => 'PhabricatorRepositoryDAO',
|
||||||
|
'PhabricatorRepositoryTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
|
'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
|
||||||
'PhabricatorSearchAttachController' => 'PhabricatorSearchController',
|
'PhabricatorSearchAttachController' => 'PhabricatorSearchController',
|
||||||
'PhabricatorSearchBaseController' => 'PhabricatorController',
|
'PhabricatorSearchBaseController' => 'PhabricatorController',
|
||||||
|
|
|
@ -39,14 +39,10 @@ class PhabricatorRepositoryGitCommitDiscoveryDaemon
|
||||||
"Expected 'Fetch URL' in 'git remote show -n origin'.");
|
"Expected 'Fetch URL' in 'git remote show -n origin'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$remote = $matches[1];
|
self::verifySameGitOrigin(
|
||||||
$expect = $repository->getRemoteURI();
|
$matches[1],
|
||||||
if ($remote != $expect) {
|
$repository->getRemoteURI(),
|
||||||
$local_path = $repository->getLocalPath();
|
$repository->getLocalPath());
|
||||||
throw new Exception(
|
|
||||||
"Working copy '{$local_path}' has origin URL '{$remote}', but the ".
|
|
||||||
"configured URL '{$expect}' is expected. Refusing to proceed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
list($stdout) = $repository->execxLocalCommand(
|
list($stdout) = $repository->execxLocalCommand(
|
||||||
'branch -r --verbose --no-abbrev');
|
'branch -r --verbose --no-abbrev');
|
||||||
|
@ -118,4 +114,21 @@ class PhabricatorRepositoryGitCommitDiscoveryDaemon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function verifySameGitOrigin($remote, $expect, $where) {
|
||||||
|
$remote_uri = PhabricatorRepository::newPhutilURIFromGitURI($remote);
|
||||||
|
$expect_uri = PhabricatorRepository::newPhutilURIFromGitURI($expect);
|
||||||
|
|
||||||
|
$remote_path = $remote_uri->getPath();
|
||||||
|
$expect_path = $expect_uri->getPath();
|
||||||
|
|
||||||
|
if ($remote_path != $expect_path) {
|
||||||
|
throw new Exception(
|
||||||
|
"Working copy at '{$where}' has a mismatched origin URL. It has ".
|
||||||
|
"origin URL '{$remote}' (with remote path '{$remote_path}'), but the ".
|
||||||
|
"configured URL '{$expect}' (with remote path '{$expect_path}') is ".
|
||||||
|
"expected. Refusing to proceed because this may indicate that the ".
|
||||||
|
"working copy is actually some other repository.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
phutil_require_module('phabricator', 'applications/diffusion/query/branch/git');
|
phutil_require_module('phabricator', 'applications/diffusion/query/branch/git');
|
||||||
phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
|
phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
|
||||||
phutil_require_module('phabricator', 'applications/repository/daemon/commitdiscovery/base');
|
phutil_require_module('phabricator', 'applications/repository/daemon/commitdiscovery/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/repository/storage/repository');
|
||||||
|
|
||||||
|
|
||||||
phutil_require_source('PhabricatorRepositoryGitCommitDiscoveryDaemon.php');
|
phutil_require_source('PhabricatorRepositoryGitCommitDiscoveryDaemon.php');
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase
|
||||||
|
extends PhabricatorTestCase {
|
||||||
|
|
||||||
|
public function testVerifySameGitOrigin() {
|
||||||
|
$cases = array(
|
||||||
|
array(
|
||||||
|
'ssh://user@domain.com/path.git',
|
||||||
|
'ssh://user@domain.com/path.git',
|
||||||
|
true,
|
||||||
|
'Identical paths should pass.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'ssh://user@domain.com/path.git',
|
||||||
|
'https://user@domain.com/path.git',
|
||||||
|
true,
|
||||||
|
'Protocol changes should pass.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'ssh://user@domain.com/path.git',
|
||||||
|
'git@domain.com:path.git',
|
||||||
|
true,
|
||||||
|
'Git implicit SSH should pass.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'ssh://user@gitserv001.com/path.git',
|
||||||
|
'ssh://user@gitserv002.com/path.git',
|
||||||
|
true,
|
||||||
|
'Domain changes should pass.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'ssh://alincoln@domain.com/path.git',
|
||||||
|
'ssh://htaft@domain.com/path.git',
|
||||||
|
true,
|
||||||
|
'User/auth changes should pass.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'ssh://user@domain.com/apples.git',
|
||||||
|
'ssh://user@domain.com/bananas.git',
|
||||||
|
false,
|
||||||
|
'Path changes should fail.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'ssh://user@domain.com/apples.git',
|
||||||
|
'git@domain.com:bananas.git',
|
||||||
|
false,
|
||||||
|
'Git implicit SSH path changes should fail.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($cases as $case) {
|
||||||
|
list($remote, $config, $expect, $message) = $case;
|
||||||
|
|
||||||
|
$ex = null;
|
||||||
|
try {
|
||||||
|
PhabricatorRepositoryGitCommitDiscoveryDaemon::verifySameGitOrigin(
|
||||||
|
$remote,
|
||||||
|
$config,
|
||||||
|
'(a test case)');
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
$ex = $exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
$expect,
|
||||||
|
!$ex,
|
||||||
|
"Verification that '{$remote}' and '{$config}' are the same origin ".
|
||||||
|
"had a different outcome than expected: {$message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/repository/daemon/commitdiscovery/git');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/testing/testcase');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase.php');
|
|
@ -57,19 +57,30 @@ class PhabricatorRepository extends PhabricatorRepositoryDAO {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function newPhutilURIFromGitURI($raw_uri) {
|
||||||
|
// If there's no protocol (git implicit SSH) reformat the URI to be a
|
||||||
|
// normal URI. These git URIs look like "user@domain.com:path" instead of
|
||||||
|
// "ssh://user@domain/path".
|
||||||
|
|
||||||
|
$uri = new PhutilURI($raw_uri);
|
||||||
|
if (!$uri->getProtocol()) {
|
||||||
|
list($domain, $path) = explode(':', $raw_uri, 2);
|
||||||
|
$uri = new PhutilURI('ssh://'.$domain.'/'.$path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
public function getRemoteURI() {
|
public function getRemoteURI() {
|
||||||
$raw_uri = $this->getDetail('remote-uri');
|
$raw_uri = $this->getDetail('remote-uri');
|
||||||
|
|
||||||
$vcs = $this->getVersionControlSystem();
|
$vcs = $this->getVersionControlSystem();
|
||||||
$is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
|
$is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
|
||||||
|
|
||||||
// If there's no protocol (git implicit SSH) reformat the URI to be a
|
if ($is_git) {
|
||||||
// normal URI. These git URIs look like "user@domain.com:path" instead of
|
$uri = self::newPhutilURIFromGitURI($raw_uri);
|
||||||
// "ssh://user@domain/path".
|
} else {
|
||||||
$uri = new PhutilURI($raw_uri);
|
$uri = new PhutilURI($raw_uri);
|
||||||
if ($is_git && !$uri->getProtocol()) {
|
|
||||||
list($domain, $path) = explode(':', $raw_uri, 2);
|
|
||||||
$uri = new PhutilURI('ssh://'.$domain.'/'.$path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isSSHProtocol($uri->getProtocol())) {
|
if ($this->isSSHProtocol($uri->getProtocol())) {
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class PhabricatorRepositoryTestCase
|
||||||
|
extends PhabricatorTestCase {
|
||||||
|
|
||||||
|
public function testParseGitURI() {
|
||||||
|
static $map = array(
|
||||||
|
'ssh://user@domain.com/path.git' => 'ssh://user@domain.com/path.git',
|
||||||
|
'user@domain.com:path.git' => 'ssh://user@domain.com/path.git',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($map as $raw => $expect) {
|
||||||
|
$uri = PhabricatorRepository::newPhutilURIFromGitURI($raw);
|
||||||
|
$this->assertEqual(
|
||||||
|
$expect,
|
||||||
|
(string)$uri,
|
||||||
|
"Normalized Git URI '{$raw}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/repository/storage/repository');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/testing/testcase');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorRepositoryTestCase.php');
|
Loading…
Add table
Reference in a new issue