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

Whitelist allowed editor protocols

Summary:
This is the other half of D8548. Specifically, the attack here was to set your own editor link to `javascript\n:...` and then you could XSS yourself. This isn't a hugely damaging attack, but we can be more certain by adding a whitelist here.

We already whitelist linkable protocols in remarkup (`uri.allowed-protocols`) in general.

Test Plan:
Tried to set and use valid/invalid editor URIs.

{F130883}

{F130884}

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

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

+191 -14
+6
src/__phutil_library_map__.php
··· 1090 1090 'PhabricatorApplicationFiles' => 'applications/files/application/PhabricatorApplicationFiles.php', 1091 1091 'PhabricatorApplicationFlags' => 'applications/flag/application/PhabricatorApplicationFlags.php', 1092 1092 'PhabricatorApplicationHarbormaster' => 'applications/harbormaster/application/PhabricatorApplicationHarbormaster.php', 1093 + 'PhabricatorApplicationHelp' => 'applications/help/application/PhabricatorApplicationHelp.php', 1093 1094 'PhabricatorApplicationHerald' => 'applications/herald/application/PhabricatorApplicationHerald.php', 1094 1095 'PhabricatorApplicationHome' => 'applications/home/application/PhabricatorApplicationHome.php', 1095 1096 'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php', ··· 1129 1130 'PhabricatorApplicationSlowvote' => 'applications/slowvote/application/PhabricatorApplicationSlowvote.php', 1130 1131 'PhabricatorApplicationStatusView' => 'applications/meta/view/PhabricatorApplicationStatusView.php', 1131 1132 'PhabricatorApplicationSubscriptions' => 'applications/subscriptions/application/PhabricatorApplicationSubscriptions.php', 1133 + 'PhabricatorApplicationSupport' => 'applications/support/application/PhabricatorApplicationSupport.php', 1132 1134 'PhabricatorApplicationSystem' => 'applications/system/application/PhabricatorApplicationSystem.php', 1133 1135 'PhabricatorApplicationTest' => 'applications/base/controller/__tests__/PhabricatorApplicationTest.php', 1134 1136 'PhabricatorApplicationTokens' => 'applications/tokens/application/PhabricatorApplicationTokens.php', ··· 1569 1571 'PhabricatorHash' => 'infrastructure/util/PhabricatorHash.php', 1570 1572 'PhabricatorHashTestCase' => 'infrastructure/util/__tests__/PhabricatorHashTestCase.php', 1571 1573 'PhabricatorHelpController' => 'applications/help/controller/PhabricatorHelpController.php', 1574 + 'PhabricatorHelpEditorProtocolController' => 'applications/help/controller/PhabricatorHelpEditorProtocolController.php', 1572 1575 'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/PhabricatorHelpKeyboardShortcutController.php', 1573 1576 'PhabricatorHomeController' => 'applications/home/controller/PhabricatorHomeController.php', 1574 1577 'PhabricatorHomeMainController' => 'applications/home/controller/PhabricatorHomeMainController.php', ··· 3751 3754 'PhabricatorApplicationFiles' => 'PhabricatorApplication', 3752 3755 'PhabricatorApplicationFlags' => 'PhabricatorApplication', 3753 3756 'PhabricatorApplicationHarbormaster' => 'PhabricatorApplication', 3757 + 'PhabricatorApplicationHelp' => 'PhabricatorApplication', 3754 3758 'PhabricatorApplicationHerald' => 'PhabricatorApplication', 3755 3759 'PhabricatorApplicationHome' => 'PhabricatorApplication', 3756 3760 'PhabricatorApplicationLaunchView' => 'AphrontView', ··· 3788 3792 'PhabricatorApplicationSlowvote' => 'PhabricatorApplication', 3789 3793 'PhabricatorApplicationStatusView' => 'AphrontView', 3790 3794 'PhabricatorApplicationSubscriptions' => 'PhabricatorApplication', 3795 + 'PhabricatorApplicationSupport' => 'PhabricatorApplication', 3791 3796 'PhabricatorApplicationSystem' => 'PhabricatorApplication', 3792 3797 'PhabricatorApplicationTest' => 'PhabricatorApplication', 3793 3798 'PhabricatorApplicationTokens' => 'PhabricatorApplication', ··· 4315 4320 'PhabricatorHarbormasterConfigOptions' => 'PhabricatorApplicationConfigOptions', 4316 4321 'PhabricatorHashTestCase' => 'PhabricatorTestCase', 4317 4322 'PhabricatorHelpController' => 'PhabricatorController', 4323 + 'PhabricatorHelpEditorProtocolController' => 'PhabricatorHelpController', 4318 4324 'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController', 4319 4325 'PhabricatorHomeController' => 'PhabricatorController', 4320 4326 'PhabricatorHomeMainController' => 'PhabricatorHomeController',
-3
src/aphront/configuration/AphrontDefaultApplicationConfiguration.php
··· 23 23 '' => 'DarkConsoleController', 24 24 'data/(?P<key>[^/]+)/' => 'DarkConsoleDataController', 25 25 ), 26 - '/help/' => array( 27 - 'keyboardshortcut/' => 'PhabricatorHelpKeyboardShortcutController', 28 - ), 29 26 ); 30 27 } 31 28
+38
src/applications/config/option/PhabricatorSecurityConfigOptions.php
··· 12 12 } 13 13 14 14 public function getOptions() { 15 + $support_href = PhabricatorEnv::getDoclink( 16 + 'article/feedback.html'); 17 + 15 18 return array( 16 19 $this->newOption('security.alternate-file-domain', 'string', null) 17 20 ->setSummary(pht("Alternate domain to serve files from.")) ··· 125 128 "javascript:// URIs.")) 126 129 ->addExample( 127 130 '{"http": true, "https": true"}', pht('Valid Setting')) 131 + ->setLocked(true), 132 + $this->newOption( 133 + 'uri.allowed-editor-protocols', 134 + 'set', 135 + array( 136 + 'http' => true, 137 + 'https' => true, 138 + 139 + // This handler is installed by Textmate. 140 + 'txmt' => true, 141 + 142 + // This handler is for MacVim. 143 + 'mvim' => true, 144 + 145 + // Unofficial handler for Vim. 146 + 'vim' => true, 147 + 148 + // Unofficial handler for Sublime. 149 + 'subl' => true, 150 + 151 + // Unofficial handler for Emacs. 152 + 'emacs' => true, 153 + 154 + // This isn't a standard handler installed by an application, but 155 + // is a reasonable name for a user-installed handler. 156 + 'editor' => true, 157 + )) 158 + ->setSummary(pht('Whitelists editor protocols for "Open in Editor".')) 159 + ->setDescription( 160 + pht( 161 + "Users can configure a URI pattern to open files in a text ". 162 + "editor. The URI must use a protocol on this whitelist.\n\n". 163 + "(If you use an editor which defines a protocol not on this ". 164 + "list, [[ %s | let us know ]] and we'll update the defaults.)", 165 + $support_href)) 128 166 ->setLocked(true), 129 167 $this->newOption( 130 168 'celerity.resource-hash',
+22
src/applications/help/application/PhabricatorApplicationHelp.php
··· 1 + <?php 2 + 3 + final class PhabricatorApplicationHelp extends PhabricatorApplication { 4 + 5 + public function canUninstall() { 6 + return false; 7 + } 8 + 9 + public function isUnlisted() { 10 + return true; 11 + } 12 + 13 + public function getRoutes() { 14 + return array( 15 + '/help/' => array( 16 + 'keyboardshortcut/' => 'PhabricatorHelpKeyboardShortcutController', 17 + 'editorprotocol/' => 'PhabricatorHelpEditorProtocolController', 18 + ), 19 + ); 20 + } 21 + 22 + }
+52
src/applications/help/controller/PhabricatorHelpEditorProtocolController.php
··· 1 + <?php 2 + 3 + final class PhabricatorHelpEditorProtocolController 4 + extends PhabricatorHelpController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function processRequest() { 11 + $request = $this->getRequest(); 12 + $viewer = $request->getUser(); 13 + 14 + $dialog = id(new AphrontDialogView()) 15 + ->setUser($viewer) 16 + ->setMethod('GET') 17 + ->setSubmitURI('/settings/panel/display/') 18 + ->setTitle(pht('Unsupported Editor Protocol')) 19 + ->appendParagraph( 20 + pht( 21 + 'Your configured editor URI uses an unsupported protocol. Change '. 22 + 'your settings to use a supported protocol, or ask your '. 23 + 'administrator to add support for the chosen protocol by '. 24 + 'configuring: %s', 25 + phutil_tag('tt', array(), 'uri.allowed-editor-protocols'))) 26 + ->addSubmitButton(pht('Change Settings')) 27 + ->addCancelButton('/'); 28 + 29 + return id(new AphrontDialogResponse()) 30 + ->setDialog($dialog); 31 + } 32 + 33 + public static function hasAllowedProtocol($uri) { 34 + $uri = new PhutilURI($uri); 35 + $editor_protocol = $uri->getProtocol(); 36 + if (!$editor_protocol) { 37 + // The URI must have a protocol. 38 + return false; 39 + } 40 + 41 + $allowed_key = 'uri.allowed-editor-protocols'; 42 + $allowed_protocols = PhabricatorEnv::getEnvConfig($allowed_key); 43 + if (empty($allowed_protocols[$editor_protocol])) { 44 + // The protocol must be on the allowed protocol whitelist. 45 + return false; 46 + } 47 + 48 + return true; 49 + } 50 + 51 + 52 + }
+19 -7
src/applications/people/storage/PhabricatorUser.php
··· 441 441 } 442 442 } 443 443 444 - if ($editor) { 445 - return strtr($editor, array( 446 - '%%' => '%', 447 - '%f' => phutil_escape_uri($path), 448 - '%l' => phutil_escape_uri($line), 449 - '%r' => phutil_escape_uri($callsign), 450 - )); 444 + if (!strlen($editor)) { 445 + return null; 451 446 } 447 + 448 + $uri = strtr($editor, array( 449 + '%%' => '%', 450 + '%f' => phutil_escape_uri($path), 451 + '%l' => phutil_escape_uri($line), 452 + '%r' => phutil_escape_uri($callsign), 453 + )); 454 + 455 + // The resulting URI must have an allowed protocol. Otherwise, we'll return 456 + // a link to an error page explaining the misconfiguration. 457 + 458 + $ok = PhabricatorHelpEditorProtocolController::hasAllowedProtocol($uri); 459 + if (!$ok) { 460 + return '/help/editorprotocol/'; 461 + } 462 + 463 + return (string)$uri; 452 464 } 453 465 454 466 public function getAlternateCSRFString() {
+33 -4
src/applications/settings/panel/PhabricatorSettingsPanelDisplayPreferences.php
··· 26 26 $pref_monospaced_textareas = 27 27 PhabricatorUserPreferences::PREFERENCE_MONOSPACED_TEXTAREAS; 28 28 29 + $errors = array(); 30 + $e_editor = null; 29 31 if ($request->isFormPost()) { 30 32 $monospaced = $request->getStr($pref_monospaced); 31 33 ··· 42 44 $pref_monospaced_textareas, 43 45 $request->getStr($pref_monospaced_textareas)); 44 46 45 - $preferences->save(); 46 - return id(new AphrontRedirectResponse()) 47 - ->setURI($this->getPanelURI('?saved=true')); 47 + $editor_pattern = $preferences->getPreference($pref_editor); 48 + if (strlen($editor_pattern)) { 49 + $ok = PhabricatorHelpEditorProtocolController::hasAllowedProtocol( 50 + $editor_pattern); 51 + if (!$ok) { 52 + $allowed_key = 'uri.allowed-editor-protocols'; 53 + $allowed_protocols = PhabricatorEnv::getEnvConfig($allowed_key); 54 + 55 + $proto_names = array(); 56 + foreach (array_keys($allowed_protocols) as $protocol) { 57 + $proto_names[] = $protocol.'://'; 58 + } 59 + 60 + $errors[] = pht( 61 + 'Editor link has an invalid or missing protocol. You must '. 62 + 'use a whitelisted editor protocol from this list: %s. To '. 63 + 'add protocols, update %s.', 64 + implode(', ', $proto_names), 65 + phutil_tag('tt', array(), $allowed_key)); 66 + 67 + $e_editor = pht('Invalid'); 68 + } 69 + } 70 + 71 + if (!$errors) { 72 + $preferences->save(); 73 + return id(new AphrontRedirectResponse()) 74 + ->setURI($this->getPanelURI('?saved=true')); 75 + } 48 76 } 49 77 50 78 $example_string = <<<EXAMPLE ··· 95 123 id(new AphrontFormTextControl()) 96 124 ->setLabel(pht('Editor Link')) 97 125 ->setName($pref_editor) 98 - // How to pht() 99 126 ->setCaption($editor_instructions) 127 + ->setError($e_editor) 100 128 ->setValue($preferences->getPreference($pref_editor))) 101 129 ->appendChild( 102 130 id(new AphrontFormSelectControl()) ··· 139 167 140 168 $form_box = id(new PHUIObjectBoxView()) 141 169 ->setHeaderText(pht('Display Preferences')) 170 + ->setFormErrors($errors) 142 171 ->setFormSaved($request->getStr('saved') === 'true') 143 172 ->setForm($form); 144 173
+21
src/applications/support/application/PhabricatorApplicationSupport.php
··· 1 + <?php 2 + 3 + final class PhabricatorApplicationSupport extends PhabricatorApplication { 4 + 5 + public function canUninstall() { 6 + return false; 7 + } 8 + 9 + public function isUnlisted() { 10 + return true; 11 + } 12 + 13 + public function getRoutes() { 14 + return array( 15 + '/help/' => array( 16 + 'keyboardshortcut/' => 'PhabricatorHelpKeyboardShortcutController', 17 + ), 18 + ); 19 + } 20 + 21 + }