Skip to content
This repository was archived by the owner on May 10, 2021. It is now read-only.

Commit 53b25db

Browse files
author
epriestley
committed
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
1 parent cb957f8 commit 53b25db

13 files changed

+233
-8
lines changed

src/__phutil_library_map__.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,7 @@
860860
'DiffusionRepositoryEditDangerousController' => 'applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php',
861861
'DiffusionRepositoryEditDeleteController' => 'applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php',
862862
'DiffusionRepositoryEditEngine' => 'applications/diffusion/editor/DiffusionRepositoryEditEngine.php',
863+
'DiffusionRepositoryEditEnormousController' => 'applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php',
863864
'DiffusionRepositoryEditUpdateController' => 'applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php',
864865
'DiffusionRepositoryFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php',
865866
'DiffusionRepositoryHistoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php',
@@ -5923,6 +5924,7 @@
59235924
'DiffusionRepositoryEditDangerousController' => 'DiffusionRepositoryManageController',
59245925
'DiffusionRepositoryEditDeleteController' => 'DiffusionRepositoryManageController',
59255926
'DiffusionRepositoryEditEngine' => 'PhabricatorEditEngine',
5927+
'DiffusionRepositoryEditEnormousController' => 'DiffusionRepositoryManageController',
59265928
'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryManageController',
59275929
'DiffusionRepositoryFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
59285930
'DiffusionRepositoryHistoryManagementPanel' => 'DiffusionRepositoryManagementPanel',

src/applications/diffusion/application/PhabricatorDiffusionApplication.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public function getRoutes() {
8989
'edit/' => array(
9090
'activate/' => 'DiffusionRepositoryEditActivateController',
9191
'dangerous/' => 'DiffusionRepositoryEditDangerousController',
92+
'enormous/' => 'DiffusionRepositoryEditEnormousController',
9293
'delete/' => 'DiffusionRepositoryEditDeleteController',
9394
'update/' => 'DiffusionRepositoryEditUpdateController',
9495
'testautomation/' => 'DiffusionRepositoryTestAutomationController',

src/applications/diffusion/controller/DiffusionCommitController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,9 @@ public function handleRequest(AphrontRequest $request) {
277277
'This commit is empty and does not affect any paths.'));
278278
} else if ($was_limited) {
279279
$info_panel = $this->renderStatusMessage(
280-
pht('Enormous Commit'),
280+
pht('Very Large Commit'),
281281
pht(
282-
'This commit is enormous, and affects more than %d files. '.
282+
'This commit is very large, and affects more than %d files. '.
283283
'Changes are not shown.',
284284
$hard_limit));
285285
} else if (!$this->getCommitExists()) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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+
}

src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,19 @@ protected function buildCustomEditFields($object) {
309309
->setConduitDescription(pht('Allow or prevent dangerous changes.'))
310310
->setConduitTypeDescription(pht('New protection setting.'))
311311
->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()),
312325
id(new PhabricatorSelectEditField())
313326
->setKey('status')
314327
->setLabel(pht('Status'))

src/applications/diffusion/engine/DiffusionCommitHookEngine.php

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ final class DiffusionCommitHookEngine extends Phobject {
3434
private $rejectCode = PhabricatorRepositoryPushLog::REJECT_BROKEN;
3535
private $rejectDetails;
3636
private $emailPHIDs = array();
37+
private $changesets = array();
3738

3839

3940
/* -( Config )------------------------------------------------------------- */
@@ -131,6 +132,15 @@ public function execute() {
131132
$this->applyHeraldRefRules($ref_updates, $all_updates);
132133

133134
$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+
134144
$all_updates = array_merge($all_updates, $content_updates);
135145

136146
$this->applyHeraldContentRules($content_updates, $all_updates);
@@ -1079,7 +1089,37 @@ private function newPushEvent() {
10791089
->setEpoch(time());
10801090
}
10811091

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) {
10831123
$byte_limit = HeraldCommitAdapter::getEnormousByteLimit();
10841124
$time_limit = HeraldCommitAdapter::getEnormousTimeLimit();
10851125

@@ -1126,9 +1166,10 @@ public function loadChangesetsForCommit($identifier) {
11261166
if (strlen($raw_diff) >= $byte_limit) {
11271167
throw new Exception(
11281168
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)));
11321173
}
11331174

11341175
if (!strlen($raw_diff)) {
@@ -1143,6 +1184,20 @@ public function loadChangesetsForCommit($identifier) {
11431184
return $diff->getChangesets();
11441185
}
11451186

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);
1199+
}
1200+
11461201
public function loadCommitRefForCommit($identifier) {
11471202
$repository = $this->getRepository();
11481203
$vcs = $repository->getVersionControlSystem();

src/applications/diffusion/herald/HeraldPreCommitContentAdapter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function isDiffEnormous() {
3737
public function getDiffContent($type) {
3838
if ($this->changesets === null) {
3939
try {
40-
$this->changesets = $this->getHookEngine()->loadChangesetsForCommit(
40+
$this->changesets = $this->getHookEngine()->getChangesetsForCommit(
4141
$this->getObject()->getRefNew());
4242
} catch (Exception $ex) {
4343
$this->changesets = $ex;

src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ private function buildActionMenu() {
4343
$delete_uri = $repository->getPathURI('edit/delete/');
4444
$encoding_uri = $this->getEditPageURI('encoding');
4545
$dangerous_uri = $repository->getPathURI('edit/dangerous/');
46+
$enormous_uri = $repository->getPathURI('edit/enormous/');
4647

4748
if ($repository->isTracked()) {
4849
$activate_label = pht('Deactivate Repository');
@@ -59,6 +60,15 @@ private function buildActionMenu() {
5960
$can_dangerous = ($can_edit && $repository->canAllowDangerousChanges());
6061
}
6162

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+
6272
$action_list->addAction(
6373
id(new PhabricatorActionView())
6474
->setName(pht('Edit Basic Information'))
@@ -80,6 +90,13 @@ private function buildActionMenu() {
8090
->setDisabled(!$can_dangerous)
8191
->setWorkflow(true));
8292

93+
$action_list->addAction(
94+
id(new PhabricatorActionView())
95+
->setName($enormous_name)
96+
->setHref($enormous_uri)
97+
->setDisabled(!$can_enormous)
98+
->setWorkflow(true));
99+
83100
$action_list->addAction(
84101
id(new PhabricatorActionView())
85102
->setHref($activate_uri)
@@ -198,6 +215,20 @@ private function buildBasics() {
198215

199216
$view->addProperty(pht('Dangerous Changes'), $dangerous);
200217

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);
231+
201232
return $view;
202233
}
203234

src/applications/files/config/PhabricatorFilesConfigOptions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public function getOptions() {
134134
"Configure which uploaded file types may be viewed directly ".
135135
"in the browser. Other file types will be downloaded instead ".
136136
"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.".
138138
"\n\n".
139139
"The keys in this map are viewable MIME types; the values are ".
140140
"the MIME types they are delivered as when they are viewed in ".

src/applications/repository/editor/PhabricatorRepositoryEditor.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public function getTransactionTypes() {
2828
$types[] = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE;
2929
$types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY;
3030
$types[] = PhabricatorRepositoryTransaction::TYPE_DANGEROUS;
31+
$types[] = PhabricatorRepositoryTransaction::TYPE_ENORMOUS;
3132
$types[] = PhabricatorRepositoryTransaction::TYPE_SLUG;
3233
$types[] = PhabricatorRepositoryTransaction::TYPE_SERVICE;
3334
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE;
@@ -76,6 +77,8 @@ protected function getCustomTransactionOldValue(
7677
return $object->getPushPolicy();
7778
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
7879
return $object->shouldAllowDangerousChanges();
80+
case PhabricatorRepositoryTransaction::TYPE_ENORMOUS:
81+
return $object->shouldAllowEnormousChanges();
7982
case PhabricatorRepositoryTransaction::TYPE_SLUG:
8083
return $object->getRepositorySlug();
8184
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
@@ -110,6 +113,7 @@ protected function getCustomTransactionNewValue(
110113
case PhabricatorRepositoryTransaction::TYPE_VCS:
111114
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
112115
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
116+
case PhabricatorRepositoryTransaction::TYPE_ENORMOUS:
113117
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
114118
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
115119
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
@@ -184,6 +188,9 @@ protected function applyCustomInternalTransaction(
184188
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
185189
$object->setDetail('allow-dangerous-changes', $xaction->getNewValue());
186190
return;
191+
case PhabricatorRepositoryTransaction::TYPE_ENORMOUS:
192+
$object->setDetail('allow-enormous-changes', $xaction->getNewValue());
193+
return;
187194
case PhabricatorRepositoryTransaction::TYPE_SLUG:
188195
$object->setRepositorySlug($xaction->getNewValue());
189196
return;
@@ -248,6 +255,7 @@ protected function requireCapabilities(
248255
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
249256
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
250257
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
258+
case PhabricatorRepositoryTransaction::TYPE_ENORMOUS:
251259
case PhabricatorRepositoryTransaction::TYPE_SLUG:
252260
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
253261
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:

0 commit comments

Comments
 (0)