mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-18 04:42:40 +01:00
Enforce sensible, unique clone/checkout names for repositories
Summary: Fixes T7938. - Primarily, users can currently shoot themselves in the foot by putting `../../etc/passwd` and other similar nonsense in these fields (this is not dangerous, but also does not work). Require sensible names. - Enforce uniqueness so these names can be used in URIs and as identifiers in the future. - (This doesn't start actually using them for anything fancy yet.) Test Plan: - Gave several repositories clone names: a valid name, two duplicate names, an invalid, name, some with no names. - Ran migrations. - Got clean conversion for valid names, appropriate errors for invalid/duplicate names. Reviewers: chad Reviewed By: chad Maniphest Tasks: T7938 Differential Revision: https://secure.phabricator.com/D14986
This commit is contained in:
parent
e84693f589
commit
0b3d10c3da
8 changed files with 299 additions and 15 deletions
5
resources/sql/autopatches/20160110.repo.01.slug.sql
Normal file
5
resources/sql/autopatches/20160110.repo.01.slug.sql
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_repository.repository
|
||||||
|
ADD repositorySlug VARCHAR(64) COLLATE {$COLLATE_SORT};
|
||||||
|
|
||||||
|
ALTER TABLE {$NAMESPACE}_repository.repository
|
||||||
|
ADD UNIQUE KEY `key_slug` (repositorySlug);
|
49
resources/sql/autopatches/20160110.repo.02.slug.php
Normal file
49
resources/sql/autopatches/20160110.repo.02.slug.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$table = new PhabricatorRepository();
|
||||||
|
$conn_w = $table->establishConnection('w');
|
||||||
|
|
||||||
|
foreach (new LiskMigrationIterator($table) as $repository) {
|
||||||
|
$slug = $repository->getRepositorySlug();
|
||||||
|
|
||||||
|
if ($slug !== null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$clone_name = $repository->getDetail('clone-name');
|
||||||
|
|
||||||
|
if (!strlen($clone_name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PhabricatorRepository::isValidRepositorySlug($clone_name)) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'Repository "%s" has a "Clone/Checkout As" name which is no longer '.
|
||||||
|
'valid ("%s"). You can edit the repository to give it a new, valid '.
|
||||||
|
'short name.',
|
||||||
|
$repository->getDisplayName(),
|
||||||
|
$clone_name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'UPDATE %T SET repositorySlug = %s WHERE id = %d',
|
||||||
|
$table->getTableName(),
|
||||||
|
$clone_name,
|
||||||
|
$repository->getID());
|
||||||
|
} catch (AphrontDuplicateKeyQueryException $ex) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'Repository "%s" has a duplicate "Clone/Checkout As" name ("%s"). '.
|
||||||
|
'Each name must now be unique. You can edit the repository to give '.
|
||||||
|
'it a new, unique short name.',
|
||||||
|
$repository->getDisplayName(),
|
||||||
|
$clone_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,13 +17,15 @@ final class DiffusionRepositoryEditBasicController
|
||||||
|
|
||||||
$v_name = $repository->getName();
|
$v_name = $repository->getName();
|
||||||
$v_desc = $repository->getDetail('description');
|
$v_desc = $repository->getDetail('description');
|
||||||
$v_clone_name = $repository->getDetail('clone-name');
|
$v_clone_name = $repository->getRepositorySlug();
|
||||||
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||||
$repository->getPHID(),
|
$repository->getPHID(),
|
||||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
||||||
$e_name = true;
|
$e_name = true;
|
||||||
|
$e_slug = null;
|
||||||
$errors = array();
|
$errors = array();
|
||||||
|
|
||||||
|
$validation_exception = null;
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
$v_name = $request->getStr('name');
|
$v_name = $request->getStr('name');
|
||||||
$v_desc = $request->getStr('description');
|
$v_desc = $request->getStr('description');
|
||||||
|
@ -71,13 +73,20 @@ final class DiffusionRepositoryEditBasicController
|
||||||
'=' => array_fuse($v_projects),
|
'=' => array_fuse($v_projects),
|
||||||
));
|
));
|
||||||
|
|
||||||
id(new PhabricatorRepositoryEditor())
|
$editor = id(new PhabricatorRepositoryEditor())
|
||||||
->setContinueOnNoEffect(true)
|
->setContinueOnNoEffect(true)
|
||||||
->setContentSourceFromRequest($request)
|
->setContentSourceFromRequest($request)
|
||||||
->setActor($viewer)
|
->setActor($viewer);
|
||||||
->applyTransactions($repository, $xactions);
|
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())->setURI($edit_uri);
|
try {
|
||||||
|
$editor->applyTransactions($repository, $xactions);
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($edit_uri);
|
||||||
|
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||||
|
$validation_exception = $ex;
|
||||||
|
|
||||||
|
$e_slug = $ex->getShortMessage($type_clone_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +111,7 @@ final class DiffusionRepositoryEditBasicController
|
||||||
->setName('cloneName')
|
->setName('cloneName')
|
||||||
->setLabel(pht('Clone/Checkout As'))
|
->setLabel(pht('Clone/Checkout As'))
|
||||||
->setValue($v_clone_name)
|
->setValue($v_clone_name)
|
||||||
|
->setError($e_slug)
|
||||||
->setCaption(
|
->setCaption(
|
||||||
pht(
|
pht(
|
||||||
'Optional directory name to use when cloning or checking out '.
|
'Optional directory name to use when cloning or checking out '.
|
||||||
|
@ -130,6 +140,7 @@ final class DiffusionRepositoryEditBasicController
|
||||||
|
|
||||||
$object_box = id(new PHUIObjectBoxView())
|
$object_box = id(new PHUIObjectBoxView())
|
||||||
->setHeaderText($title)
|
->setHeaderText($title)
|
||||||
|
->setValidationException($validation_exception)
|
||||||
->setForm($form)
|
->setForm($form)
|
||||||
->setFormErrors($errors);
|
->setFormErrors($errors);
|
||||||
|
|
||||||
|
|
|
@ -286,7 +286,7 @@ final class DiffusionRepositoryEditMainController
|
||||||
$view->addProperty(pht('Type'), $type);
|
$view->addProperty(pht('Type'), $type);
|
||||||
$view->addProperty(pht('Callsign'), $repository->getCallsign());
|
$view->addProperty(pht('Callsign'), $repository->getCallsign());
|
||||||
|
|
||||||
$clone_name = $repository->getDetail('clone-name');
|
$clone_name = $repository->getRepositorySlug();
|
||||||
|
|
||||||
if ($repository->isHosted()) {
|
if ($repository->isHosted()) {
|
||||||
$view->addProperty(
|
$view->addProperty(
|
||||||
|
|
|
@ -99,7 +99,7 @@ final class PhabricatorRepositoryEditor
|
||||||
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
|
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
|
||||||
return $object->shouldAllowDangerousChanges();
|
return $object->shouldAllowDangerousChanges();
|
||||||
case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME:
|
case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME:
|
||||||
return $object->getDetail('clone-name');
|
return $object->getRepositorySlug();
|
||||||
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
|
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
|
||||||
return $object->getAlmanacServicePHID();
|
return $object->getAlmanacServicePHID();
|
||||||
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
|
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
|
||||||
|
@ -141,13 +141,18 @@ final class PhabricatorRepositoryEditor
|
||||||
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
|
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
|
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
|
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME:
|
|
||||||
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
|
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
|
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
|
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
|
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
|
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
|
||||||
return $xaction->getNewValue();
|
return $xaction->getNewValue();
|
||||||
|
case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME:
|
||||||
|
$name = $xaction->getNewValue();
|
||||||
|
if (strlen($name)) {
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
|
case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
|
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
|
||||||
return (int)$xaction->getNewValue();
|
return (int)$xaction->getNewValue();
|
||||||
|
@ -216,7 +221,7 @@ final class PhabricatorRepositoryEditor
|
||||||
$object->setDetail('allow-dangerous-changes', $xaction->getNewValue());
|
$object->setDetail('allow-dangerous-changes', $xaction->getNewValue());
|
||||||
return;
|
return;
|
||||||
case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME:
|
case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME:
|
||||||
$object->setDetail('clone-name', $xaction->getNewValue());
|
$object->setRepositorySlug($xaction->getNewValue());
|
||||||
return;
|
return;
|
||||||
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
|
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
|
||||||
$object->setAlmanacServicePHID($xaction->getNewValue());
|
$object->setAlmanacServicePHID($xaction->getNewValue());
|
||||||
|
@ -448,9 +453,69 @@ final class PhabricatorRepositoryEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME:
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
$old = $xaction->getOldValue();
|
||||||
|
$new = $xaction->getNewValue();
|
||||||
|
|
||||||
|
if (!strlen($new)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($new === $old) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PhabricatorRepository::asssertValidRepositorySlug($new);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||||
|
$type,
|
||||||
|
pht('Invalid'),
|
||||||
|
$ex->getMessage(),
|
||||||
|
$xaction);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$other = id(new PhabricatorRepositoryQuery())
|
||||||
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
|
->withSlugs(array($new))
|
||||||
|
->executeOne();
|
||||||
|
if ($other && ($other->getID() !== $object->getID())) {
|
||||||
|
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||||
|
$type,
|
||||||
|
pht('Duplicate'),
|
||||||
|
pht(
|
||||||
|
'The selected repository short name is already in use by '.
|
||||||
|
'another repository. Choose a unique short name.'),
|
||||||
|
$xaction);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $errors;
|
return $errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function didCatchDuplicateKeyException(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
array $xactions,
|
||||||
|
Exception $ex) {
|
||||||
|
|
||||||
|
$errors = array();
|
||||||
|
|
||||||
|
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||||
|
null,
|
||||||
|
pht('Invalid'),
|
||||||
|
pht(
|
||||||
|
'The chosen callsign or repository short name is already in '.
|
||||||
|
'use by another repository.'),
|
||||||
|
null);
|
||||||
|
|
||||||
|
throw new PhabricatorApplicationTransactionValidationException($errors);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ final class PhabricatorRepositoryQuery
|
||||||
private $nameContains;
|
private $nameContains;
|
||||||
private $remoteURIs;
|
private $remoteURIs;
|
||||||
private $datasourceQuery;
|
private $datasourceQuery;
|
||||||
|
private $slugs;
|
||||||
|
|
||||||
private $numericIdentifiers;
|
private $numericIdentifiers;
|
||||||
private $callsignIdentifiers;
|
private $callsignIdentifiers;
|
||||||
|
@ -114,6 +115,11 @@ final class PhabricatorRepositoryQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withSlugs(array $slugs) {
|
||||||
|
$this->slugs = $slugs;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function needCommitCounts($need_counts) {
|
public function needCommitCounts($need_counts) {
|
||||||
$this->needCommitCounts = $need_counts;
|
$this->needCommitCounts = $need_counts;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -564,6 +570,13 @@ final class PhabricatorRepositoryQuery
|
||||||
$callsign);
|
$callsign);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->slugs !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'r.repositorySlug IN (%Ls)',
|
||||||
|
$this->slugs);
|
||||||
|
}
|
||||||
|
|
||||||
return $where;
|
return $where;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $callsign;
|
protected $callsign;
|
||||||
|
protected $repositorySlug;
|
||||||
protected $uuid;
|
protected $uuid;
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
|
@ -93,6 +94,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'name' => 'sort255',
|
'name' => 'sort255',
|
||||||
'callsign' => 'sort32',
|
'callsign' => 'sort32',
|
||||||
|
'repositorySlug' => 'sort64?',
|
||||||
'versionControlSystem' => 'text32',
|
'versionControlSystem' => 'text32',
|
||||||
'uuid' => 'text64?',
|
'uuid' => 'text64?',
|
||||||
'pushPolicy' => 'policy',
|
'pushPolicy' => 'policy',
|
||||||
|
@ -100,11 +102,6 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
'almanacServicePHID' => 'phid?',
|
'almanacServicePHID' => 'phid?',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'key_phid' => null,
|
|
||||||
'phid' => array(
|
|
||||||
'columns' => array('phid'),
|
|
||||||
'unique' => true,
|
|
||||||
),
|
|
||||||
'callsign' => array(
|
'callsign' => array(
|
||||||
'columns' => array('callsign'),
|
'columns' => array('callsign'),
|
||||||
'unique' => true,
|
'unique' => true,
|
||||||
|
@ -115,6 +112,10 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
'key_vcs' => array(
|
'key_vcs' => array(
|
||||||
'columns' => array('versionControlSystem'),
|
'columns' => array('versionControlSystem'),
|
||||||
),
|
),
|
||||||
|
'key_slug' => array(
|
||||||
|
'columns' => array('repositorySlug'),
|
||||||
|
'unique' => true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
@ -297,7 +298,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getCloneName() {
|
public function getCloneName() {
|
||||||
$name = $this->getDetail('clone-name');
|
$name = $this->getRepositorySlug();
|
||||||
|
|
||||||
// Make some reasonable effort to produce reasonable default directory
|
// Make some reasonable effort to produce reasonable default directory
|
||||||
// names from repository names.
|
// names from repository names.
|
||||||
|
@ -314,6 +315,82 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function isValidRepositorySlug($slug) {
|
||||||
|
try {
|
||||||
|
self::asssertValidRepositorySlug($slug);
|
||||||
|
return true;
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function asssertValidRepositorySlug($slug) {
|
||||||
|
if (!strlen($slug)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'The empty string is not a valid repository short name. '.
|
||||||
|
'Repository short names must be at least one character long.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($slug) > 64) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'The name "%s" is not a valid repository short name. Repository '.
|
||||||
|
'short names must not be longer than 64 characters.',
|
||||||
|
$slug));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/[^a-zA-Z0-9._-]/', $slug)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'The name "%s" is not a valid repository short name. Repository '.
|
||||||
|
'short names may only contain letters, numbers, periods, hyphens '.
|
||||||
|
'and underscores.',
|
||||||
|
$slug));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9]/', $slug)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'The name "%s" is not a valid repository short name. Repository '.
|
||||||
|
'short names must begin with a letter or number.',
|
||||||
|
$slug));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/[a-zA-Z0-9]\z/', $slug)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'The name "%s" is not a valid repository short name. Repository '.
|
||||||
|
'short names must end with a letter or number.',
|
||||||
|
$slug));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/__|--|\\.\\./', $slug)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'The name "%s" is not a valid repository short name. Repository '.
|
||||||
|
'short names must not contain multiple consecutive underscores, '.
|
||||||
|
'hyphens, or periods.',
|
||||||
|
$slug));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^[A-Z]+\z/', $slug)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'The name "%s" is not a valid repository short name. Repository '.
|
||||||
|
'short names may not contain only uppercase letters.',
|
||||||
|
$slug));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^\d+\z/', $slug)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'The name "%s" is not a valid repository short name. Repository '.
|
||||||
|
'short names may not contain only numbers.',
|
||||||
|
$slug));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Remote Command Execution )------------------------------------------- */
|
/* -( Remote Command Execution )------------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -152,4 +152,68 @@ final class PhabricatorRepositoryTestCase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRepositoryShortNameValidation() {
|
||||||
|
$good = array(
|
||||||
|
'sensible-repository',
|
||||||
|
'AReasonableName',
|
||||||
|
'ACRONYM-project',
|
||||||
|
'sol-123',
|
||||||
|
'46-helixes',
|
||||||
|
'node.io',
|
||||||
|
'internet.com',
|
||||||
|
'www.internet-site.com.repository',
|
||||||
|
'with_under-scores',
|
||||||
|
|
||||||
|
// Can't win them all.
|
||||||
|
'A-_._-_._-_._-_._-_._-_._-1',
|
||||||
|
|
||||||
|
// 64-character names are fine.
|
||||||
|
str_repeat('a', 64),
|
||||||
|
);
|
||||||
|
|
||||||
|
$poor = array(
|
||||||
|
'',
|
||||||
|
'1',
|
||||||
|
'.',
|
||||||
|
'-_-',
|
||||||
|
'AAAA',
|
||||||
|
'..',
|
||||||
|
'a/b',
|
||||||
|
'../../etc/passwd',
|
||||||
|
'/',
|
||||||
|
'!',
|
||||||
|
'@',
|
||||||
|
'ca$hmoney',
|
||||||
|
'repo with spaces',
|
||||||
|
'hyphen-',
|
||||||
|
'-ated',
|
||||||
|
'_underscores_',
|
||||||
|
'yes!',
|
||||||
|
|
||||||
|
// 65-character names are no good.
|
||||||
|
str_repeat('a', 65),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($good as $nice_name) {
|
||||||
|
$actual = PhabricatorRepository::isValidRepositorySlug($nice_name);
|
||||||
|
$this->assertEqual(
|
||||||
|
true,
|
||||||
|
$actual,
|
||||||
|
pht(
|
||||||
|
'Expected "%s" to be a valid repository short name.',
|
||||||
|
$nice_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($poor as $poor_name) {
|
||||||
|
$actual = PhabricatorRepository::isValidRepositorySlug($poor_name);
|
||||||
|
$this->assertEqual(
|
||||||
|
false,
|
||||||
|
$actual,
|
||||||
|
pht(
|
||||||
|
'Expected "%s" to be rejected as an invalid repository '.
|
||||||
|
'short name.',
|
||||||
|
$poor_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue