@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.

Prevent enormous changes from being pushed to repositoires by default

Summary:
Fixes T13031. "Enormous" changes are basically changes which are too large to hold in memory, although the actual definition we use today is "more than 1GB of change text or `git diff` runs for more than 15 minutes".

If an install configures a Herald content rule like "when content matches /XYZ/, do something" and then a user pushes a 30 GB source file, we can't put it into memory to `preg_match()` it. Currently, the way to handle this case is to write a separate Herald rule that rejects enormous changes. However, this isn't obvious and means the default behavior is unsafe.

Make the default behavior safe by rejecting these changes with a message, similar to how we reject "dangerous" changes (which permanently delete or overwrite history) by default.

Also, change a couple of UI strings from "Enormous" to "Very Large" to reduce ambiguity. See <https://discourse.phabricator-community.org/t/herald-enormous-check/822>.

Test Plan: Changed the definition of "enormous" from 1GB to 1 byte. Pushed a change; got rejected. Allowed enormous changes, pushed, got rejected by a Herald rule. Disabled the Herald rule, pushed, got a clean push. Prevented enormous changes again. Grepped for "enormous" elsewhere in the UI.

Reviewers: amckinley

Reviewed By: amckinley

Subscribers: joshuaspence

Maniphest Tasks: T13031

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

+233 -8
+2
src/__phutil_library_map__.php
··· 860 860 'DiffusionRepositoryEditDangerousController' => 'applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php', 861 861 'DiffusionRepositoryEditDeleteController' => 'applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php', 862 862 'DiffusionRepositoryEditEngine' => 'applications/diffusion/editor/DiffusionRepositoryEditEngine.php', 863 + 'DiffusionRepositoryEditEnormousController' => 'applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php', 863 864 'DiffusionRepositoryEditUpdateController' => 'applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php', 864 865 'DiffusionRepositoryFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php', 865 866 'DiffusionRepositoryHistoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php', ··· 5923 5924 'DiffusionRepositoryEditDangerousController' => 'DiffusionRepositoryManageController', 5924 5925 'DiffusionRepositoryEditDeleteController' => 'DiffusionRepositoryManageController', 5925 5926 'DiffusionRepositoryEditEngine' => 'PhabricatorEditEngine', 5927 + 'DiffusionRepositoryEditEnormousController' => 'DiffusionRepositoryManageController', 5926 5928 'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryManageController', 5927 5929 'DiffusionRepositoryFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 5928 5930 'DiffusionRepositoryHistoryManagementPanel' => 'DiffusionRepositoryManagementPanel',
+1
src/applications/diffusion/application/PhabricatorDiffusionApplication.php
··· 89 89 'edit/' => array( 90 90 'activate/' => 'DiffusionRepositoryEditActivateController', 91 91 'dangerous/' => 'DiffusionRepositoryEditDangerousController', 92 + 'enormous/' => 'DiffusionRepositoryEditEnormousController', 92 93 'delete/' => 'DiffusionRepositoryEditDeleteController', 93 94 'update/' => 'DiffusionRepositoryEditUpdateController', 94 95 'testautomation/' => 'DiffusionRepositoryTestAutomationController',
+2 -2
src/applications/diffusion/controller/DiffusionCommitController.php
··· 277 277 'This commit is empty and does not affect any paths.')); 278 278 } else if ($was_limited) { 279 279 $info_panel = $this->renderStatusMessage( 280 - pht('Enormous Commit'), 280 + pht('Very Large Commit'), 281 281 pht( 282 - 'This commit is enormous, and affects more than %d files. '. 282 + 'This commit is very large, and affects more than %d files. '. 283 283 'Changes are not shown.', 284 284 $hard_limit)); 285 285 } else if (!$this->getCommitExists()) {
+90
src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php
··· 1 + <?php 2 + 3 + final class DiffusionRepositoryEditEnormousController 4 + extends DiffusionRepositoryManageController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $response = $this->loadDiffusionContextForEdit(); 8 + if ($response) { 9 + return $response; 10 + } 11 + 12 + $viewer = $this->getViewer(); 13 + $drequest = $this->getDiffusionRequest(); 14 + $repository = $drequest->getRepository(); 15 + 16 + $panel_uri = id(new DiffusionRepositoryBasicsManagementPanel()) 17 + ->setRepository($repository) 18 + ->getPanelURI(); 19 + 20 + if (!$repository->canAllowEnormousChanges()) { 21 + return $this->newDialog() 22 + ->setTitle(pht('Unprotectable Repository')) 23 + ->appendParagraph( 24 + pht( 25 + 'This repository can not be protected from enormous changes '. 26 + 'because Phabricator does not control what users are allowed '. 27 + 'to push to it.')) 28 + ->addCancelButton($panel_uri); 29 + } 30 + 31 + if ($request->isFormPost()) { 32 + $xaction = id(new PhabricatorRepositoryTransaction()) 33 + ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ENORMOUS) 34 + ->setNewValue(!$repository->shouldAllowEnormousChanges()); 35 + 36 + $editor = id(new PhabricatorRepositoryEditor()) 37 + ->setContinueOnNoEffect(true) 38 + ->setContentSourceFromRequest($request) 39 + ->setActor($viewer) 40 + ->applyTransactions($repository, array($xaction)); 41 + 42 + return id(new AphrontReloadResponse())->setURI($panel_uri); 43 + } 44 + 45 + if ($repository->shouldAllowEnormousChanges()) { 46 + $title = pht('Prevent Enormous Changes'); 47 + 48 + $body = pht( 49 + 'It will no longer be possible to push enormous changes to this '. 50 + 'repository.'); 51 + 52 + $submit = pht('Prevent Enormous Changes'); 53 + } else { 54 + $title = pht('Allow Enormous Changes'); 55 + 56 + $body = array( 57 + pht( 58 + 'If you allow enormous changes, users can push commits which are '. 59 + 'too large for Herald to process content rules for. This can allow '. 60 + 'users to evade content rules implemented in Herald.'), 61 + pht( 62 + 'You can selectively configure Herald by adding rules to prevent a '. 63 + 'subset of enormous changes (for example, based on who is trying '. 64 + 'to push the change).'), 65 + ); 66 + 67 + $submit = pht('Allow Enormous Changes'); 68 + } 69 + 70 + $more_help = pht( 71 + 'Enormous changes are commits which are too large to process with '. 72 + 'content rules because: the diff text for the change is larger than '. 73 + '%s bytes; or the diff text takes more than %s seconds to extract.', 74 + new PhutilNumber(HeraldCommitAdapter::getEnormousByteLimit()), 75 + new PhutilNumber(HeraldCommitAdapter::getEnormousTimeLimit())); 76 + 77 + $response = $this->newDialog(); 78 + 79 + foreach ((array)$body as $paragraph) { 80 + $response->appendParagraph($paragraph); 81 + } 82 + 83 + return $response 84 + ->setTitle($title) 85 + ->appendParagraph($more_help) 86 + ->addSubmitButton($submit) 87 + ->addCancelButton($panel_uri); 88 + } 89 + 90 + }
+13
src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php
··· 309 309 ->setConduitDescription(pht('Allow or prevent dangerous changes.')) 310 310 ->setConduitTypeDescription(pht('New protection setting.')) 311 311 ->setValue($object->shouldAllowDangerousChanges()), 312 + id(new PhabricatorBoolEditField()) 313 + ->setKey('allowEnormousChanges') 314 + ->setLabel(pht('Allow Enormous Changes')) 315 + ->setIsCopyable(true) 316 + ->setIsConduitOnly(true) 317 + ->setOptions( 318 + pht('Prevent Enormous Changes'), 319 + pht('Allow Enormous Changes')) 320 + ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ENORMOUS) 321 + ->setDescription(pht('Permit enomrous changes to be made.')) 322 + ->setConduitDescription(pht('Allow or prevent enormous changes.')) 323 + ->setConduitTypeDescription(pht('New protection setting.')) 324 + ->setValue($object->shouldAllowEnormousChanges()), 312 325 id(new PhabricatorSelectEditField()) 313 326 ->setKey('status') 314 327 ->setLabel(pht('Status'))
+59 -4
src/applications/diffusion/engine/DiffusionCommitHookEngine.php
··· 34 34 private $rejectCode = PhabricatorRepositoryPushLog::REJECT_BROKEN; 35 35 private $rejectDetails; 36 36 private $emailPHIDs = array(); 37 + private $changesets = array(); 37 38 38 39 39 40 /* -( Config )------------------------------------------------------------- */ ··· 131 132 $this->applyHeraldRefRules($ref_updates, $all_updates); 132 133 133 134 $content_updates = $this->findContentUpdates($ref_updates); 135 + 136 + try { 137 + $this->rejectEnormousChanges($content_updates); 138 + } catch (DiffusionCommitHookRejectException $ex) { 139 + // If we're rejecting enormous changes, flag everything. 140 + $this->rejectCode = PhabricatorRepositoryPushLog::REJECT_ENORMOUS; 141 + throw $ex; 142 + } 143 + 134 144 $all_updates = array_merge($all_updates, $content_updates); 135 145 136 146 $this->applyHeraldContentRules($content_updates, $all_updates); ··· 1079 1089 ->setEpoch(time()); 1080 1090 } 1081 1091 1082 - public function loadChangesetsForCommit($identifier) { 1092 + private function rejectEnormousChanges(array $content_updates) { 1093 + $repository = $this->getRepository(); 1094 + if ($repository->shouldAllowEnormousChanges()) { 1095 + return; 1096 + } 1097 + 1098 + foreach ($content_updates as $update) { 1099 + $identifier = $update->getRefNew(); 1100 + try { 1101 + $changesets = $this->loadChangesetsForCommit($identifier); 1102 + $this->changesets[$identifier] = $changesets; 1103 + } catch (Exception $ex) { 1104 + $this->changesets[$identifier] = $ex; 1105 + 1106 + $message = pht( 1107 + 'ENORMOUS CHANGE'. 1108 + "\n". 1109 + 'Enormous change protection is enabled for this repository, but '. 1110 + 'you are pushing an enormous change ("%s"). Edit the repository '. 1111 + 'configuration before making enormous changes.'. 1112 + "\n\n". 1113 + "Content Exception: %s", 1114 + $identifier, 1115 + $ex->getMessage()); 1116 + 1117 + throw new DiffusionCommitHookRejectException($message); 1118 + } 1119 + } 1120 + } 1121 + 1122 + private function loadChangesetsForCommit($identifier) { 1083 1123 $byte_limit = HeraldCommitAdapter::getEnormousByteLimit(); 1084 1124 $time_limit = HeraldCommitAdapter::getEnormousTimeLimit(); 1085 1125 ··· 1126 1166 if (strlen($raw_diff) >= $byte_limit) { 1127 1167 throw new Exception( 1128 1168 pht( 1129 - 'The raw text of this change is enormous (larger than %d '. 1130 - 'bytes). Herald can not process it.', 1131 - $byte_limit)); 1169 + 'The raw text of this change ("%s") is enormous (larger than %s '. 1170 + 'bytes).', 1171 + $identifier, 1172 + new PhutilNumber($byte_limit))); 1132 1173 } 1133 1174 1134 1175 if (!strlen($raw_diff)) { ··· 1141 1182 $diff = DifferentialDiff::newEphemeralFromRawChanges( 1142 1183 $changes); 1143 1184 return $diff->getChangesets(); 1185 + } 1186 + 1187 + public function getChangesetsForCommit($identifier) { 1188 + if (isset($this->changesets[$identifier])) { 1189 + $cached = $this->changesets[$identifier]; 1190 + 1191 + if ($cached instanceof Exception) { 1192 + throw $cached; 1193 + } 1194 + 1195 + return $cached; 1196 + } 1197 + 1198 + return $this->loadChangesetsForCommit($identifier); 1144 1199 } 1145 1200 1146 1201 public function loadCommitRefForCommit($identifier) {
+1 -1
src/applications/diffusion/herald/HeraldPreCommitContentAdapter.php
··· 37 37 public function getDiffContent($type) { 38 38 if ($this->changesets === null) { 39 39 try { 40 - $this->changesets = $this->getHookEngine()->loadChangesetsForCommit( 40 + $this->changesets = $this->getHookEngine()->getChangesetsForCommit( 41 41 $this->getObject()->getRefNew()); 42 42 } catch (Exception $ex) { 43 43 $this->changesets = $ex;
+31
src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php
··· 43 43 $delete_uri = $repository->getPathURI('edit/delete/'); 44 44 $encoding_uri = $this->getEditPageURI('encoding'); 45 45 $dangerous_uri = $repository->getPathURI('edit/dangerous/'); 46 + $enormous_uri = $repository->getPathURI('edit/enormous/'); 46 47 47 48 if ($repository->isTracked()) { 48 49 $activate_label = pht('Deactivate Repository'); ··· 59 60 $can_dangerous = ($can_edit && $repository->canAllowDangerousChanges()); 60 61 } 61 62 63 + $should_enormous = $repository->shouldAllowEnormousChanges(); 64 + if ($should_enormous) { 65 + $enormous_name = pht('Prevent Enormous Changes'); 66 + $can_enormous = $can_edit; 67 + } else { 68 + $enormous_name = pht('Allow Enormous Changes'); 69 + $can_enormous = ($can_edit && $repository->canAllowEnormousChanges()); 70 + } 71 + 62 72 $action_list->addAction( 63 73 id(new PhabricatorActionView()) 64 74 ->setName(pht('Edit Basic Information')) ··· 78 88 ->setName($dangerous_name) 79 89 ->setHref($dangerous_uri) 80 90 ->setDisabled(!$can_dangerous) 91 + ->setWorkflow(true)); 92 + 93 + $action_list->addAction( 94 + id(new PhabricatorActionView()) 95 + ->setName($enormous_name) 96 + ->setHref($enormous_uri) 97 + ->setDisabled(!$can_enormous) 81 98 ->setWorkflow(true)); 82 99 83 100 $action_list->addAction( ··· 197 214 } 198 215 199 216 $view->addProperty(pht('Dangerous Changes'), $dangerous); 217 + 218 + $can_enormous = $repository->canAllowEnormousChanges(); 219 + if (!$can_enormous) { 220 + $enormous = phutil_tag('em', array(), pht('Not Preventable')); 221 + } else { 222 + $should_enormous = $repository->shouldAllowEnormousChanges(); 223 + if ($should_enormous) { 224 + $enormous = pht('Allowed'); 225 + } else { 226 + $enormous = pht('Not Allowed'); 227 + } 228 + } 229 + 230 + $view->addProperty(pht('Enormous Changes'), $enormous); 200 231 201 232 return $view; 202 233 }
+1 -1
src/applications/files/config/PhabricatorFilesConfigOptions.php
··· 134 134 "Configure which uploaded file types may be viewed directly ". 135 135 "in the browser. Other file types will be downloaded instead ". 136 136 "of displayed. This is mainly a usability consideration, since ". 137 - "browsers tend to freak out when viewing enormous binary files.". 137 + "browsers tend to freak out when viewing very large binary files.". 138 138 "\n\n". 139 139 "The keys in this map are viewable MIME types; the values are ". 140 140 "the MIME types they are delivered as when they are viewed in ".
+8
src/applications/repository/editor/PhabricatorRepositoryEditor.php
··· 28 28 $types[] = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE; 29 29 $types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY; 30 30 $types[] = PhabricatorRepositoryTransaction::TYPE_DANGEROUS; 31 + $types[] = PhabricatorRepositoryTransaction::TYPE_ENORMOUS; 31 32 $types[] = PhabricatorRepositoryTransaction::TYPE_SLUG; 32 33 $types[] = PhabricatorRepositoryTransaction::TYPE_SERVICE; 33 34 $types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE; ··· 76 77 return $object->getPushPolicy(); 77 78 case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: 78 79 return $object->shouldAllowDangerousChanges(); 80 + case PhabricatorRepositoryTransaction::TYPE_ENORMOUS: 81 + return $object->shouldAllowEnormousChanges(); 79 82 case PhabricatorRepositoryTransaction::TYPE_SLUG: 80 83 return $object->getRepositorySlug(); 81 84 case PhabricatorRepositoryTransaction::TYPE_SERVICE: ··· 110 113 case PhabricatorRepositoryTransaction::TYPE_VCS: 111 114 case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: 112 115 case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: 116 + case PhabricatorRepositoryTransaction::TYPE_ENORMOUS: 113 117 case PhabricatorRepositoryTransaction::TYPE_SERVICE: 114 118 case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE: 115 119 case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES: ··· 184 188 case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: 185 189 $object->setDetail('allow-dangerous-changes', $xaction->getNewValue()); 186 190 return; 191 + case PhabricatorRepositoryTransaction::TYPE_ENORMOUS: 192 + $object->setDetail('allow-enormous-changes', $xaction->getNewValue()); 193 + return; 187 194 case PhabricatorRepositoryTransaction::TYPE_SLUG: 188 195 $object->setRepositorySlug($xaction->getNewValue()); 189 196 return; ··· 248 255 case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: 249 256 case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: 250 257 case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: 258 + case PhabricatorRepositoryTransaction::TYPE_ENORMOUS: 251 259 case PhabricatorRepositoryTransaction::TYPE_SLUG: 252 260 case PhabricatorRepositoryTransaction::TYPE_SERVICE: 253 261 case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
+12
src/applications/repository/storage/PhabricatorRepository.php
··· 1672 1672 return (bool)$this->getDetail('allow-dangerous-changes'); 1673 1673 } 1674 1674 1675 + public function canAllowEnormousChanges() { 1676 + if (!$this->isHosted()) { 1677 + return false; 1678 + } 1679 + 1680 + return true; 1681 + } 1682 + 1683 + public function shouldAllowEnormousChanges() { 1684 + return (bool)$this->getDetail('allow-enormous-changes'); 1685 + } 1686 + 1675 1687 public function writeStatusMessage( 1676 1688 $status_type, 1677 1689 $status_code,
+2
src/applications/repository/storage/PhabricatorRepositoryPushLog.php
··· 23 23 const CHANGEFLAG_APPEND = 4; 24 24 const CHANGEFLAG_REWRITE = 8; 25 25 const CHANGEFLAG_DANGEROUS = 16; 26 + const CHANGEFLAG_ENORMOUS = 32; 26 27 27 28 const REJECT_ACCEPT = 0; 28 29 const REJECT_DANGEROUS = 1; 29 30 const REJECT_HERALD = 2; 30 31 const REJECT_EXTERNAL = 3; 31 32 const REJECT_BROKEN = 4; 33 + const REJECT_ENORMOUS = 5; 32 34 33 35 protected $repositoryPHID; 34 36 protected $epoch;
+11
src/applications/repository/storage/PhabricatorRepositoryTransaction.php
··· 16 16 const TYPE_AUTOCLOSE = 'repo:autoclose'; 17 17 const TYPE_PUSH_POLICY = 'repo:push-policy'; 18 18 const TYPE_DANGEROUS = 'repo:dangerous'; 19 + const TYPE_ENORMOUS = 'repo:enormous'; 19 20 const TYPE_SLUG = 'repo:slug'; 20 21 const TYPE_SERVICE = 'repo:service'; 21 22 const TYPE_SYMBOLS_SOURCES = 'repo:symbol-source'; ··· 374 375 } else { 375 376 return pht( 376 377 '%s enabled protection against dangerous changes.', 378 + $this->renderHandleLink($author_phid)); 379 + } 380 + case self::TYPE_ENORMOUS: 381 + if ($new) { 382 + return pht( 383 + '%s disabled protection against enormous changes.', 384 + $this->renderHandleLink($author_phid)); 385 + } else { 386 + return pht( 387 + '%s enabled protection against enormous changes.', 377 388 $this->renderHandleLink($author_phid)); 378 389 } 379 390 case self::TYPE_SLUG: