public function validateTransactions($object, array $xactions)
 {
     $viewer = $this->getActor();
     $errors = array();
     $ics_type = PhabricatorCalendarICSURIImportEngine::ENGINETYPE;
     $import_type = $object->getEngine()->getImportEngineType();
     if ($import_type != $ics_type) {
         if (!$xactions) {
             return $errors;
         }
         $errors[] = $this->newInvalidError(pht('You can not attach an ICS URI to an import type other than ' . 'an ICS URI import (type is "%s").', $import_type));
         return $errors;
     }
     $new_value = $object->getParameter(self::PARAMKEY_URI);
     foreach ($xactions as $xaction) {
         $new_value = $xaction->getNewValue();
         if (!strlen($new_value)) {
             continue;
         }
         try {
             PhabricatorEnv::requireValidRemoteURIForFetch($new_value, array('http', 'https'));
         } catch (Exception $ex) {
             $errors[] = $this->newInvalidError($ex->getMessage(), $xaction);
         }
     }
     if (!strlen($new_value)) {
         $errors[] = $this->newRequiredError(pht('You must select an ".ics" URI to import.'));
     }
     return $errors;
 }
コード例 #2
0
 /**
  * Download a remote resource over HTTP and save the response body as a file.
  *
  * This method respects `security.outbound-blacklist`, and protects against
  * HTTP redirection (by manually following "Location" headers and verifying
  * each destination). It does not protect against DNS rebinding. See
  * discussion in T6755.
  */
 public static function newFromFileDownload($uri, array $params = array())
 {
     $timeout = 5;
     $redirects = array();
     $current = $uri;
     while (true) {
         try {
             if (count($redirects) > 10) {
                 throw new Exception(pht('Too many redirects trying to fetch remote URI.'));
             }
             $resolved = PhabricatorEnv::requireValidRemoteURIForFetch($current, array('http', 'https'));
             list($resolved_uri, $resolved_domain) = $resolved;
             $current = new PhutilURI($current);
             if ($current->getProtocol() == 'http') {
                 // For HTTP, we can use a pre-resolved URI to defuse DNS rebinding.
                 $fetch_uri = $resolved_uri;
                 $fetch_host = $resolved_domain;
             } else {
                 // For HTTPS, we can't: cURL won't verify the SSL certificate if
                 // the domain has been replaced with an IP. But internal services
                 // presumably will not have valid certificates for rebindable
                 // domain names on attacker-controlled domains, so the DNS rebinding
                 // attack should generally not be possible anyway.
                 $fetch_uri = $current;
                 $fetch_host = null;
             }
             $future = id(new HTTPSFuture($fetch_uri))->setFollowLocation(false)->setTimeout($timeout);
             if ($fetch_host !== null) {
                 $future->addHeader('Host', $fetch_host);
             }
             list($status, $body, $headers) = $future->resolve();
             if ($status->isRedirect()) {
                 // This is an HTTP 3XX status, so look for a "Location" header.
                 $location = null;
                 foreach ($headers as $header) {
                     list($name, $value) = $header;
                     if (phutil_utf8_strtolower($name) == 'location') {
                         $location = $value;
                         break;
                     }
                 }
                 // HTTP 3XX status with no "Location" header, just treat this like
                 // a normal HTTP error.
                 if ($location === null) {
                     throw $status;
                 }
                 if (isset($redirects[$location])) {
                     throw new Exception(pht('Encountered loop while following redirects.'));
                 }
                 $redirects[$location] = $location;
                 $current = $location;
                 // We'll fall off the bottom and go try this URI now.
             } else {
                 if ($status->isError()) {
                     // This is something other than an HTTP 2XX or HTTP 3XX status, so
                     // just bail out.
                     throw $status;
                 } else {
                     // This is HTTP 2XX, so use the response body to save the
                     // file data.
                     $params = $params + array('name' => basename($uri));
                     return self::newFromFileData($body, $params);
                 }
             }
         } catch (Exception $ex) {
             if ($redirects) {
                 throw new PhutilProxyException(pht('Failed to fetch remote URI "%s" after following %s redirect(s) ' . '(%s): %s', $uri, new PhutilNumber(count($redirects)), implode(' > ', array_keys($redirects)), $ex->getMessage()), $ex);
             } else {
                 throw $ex;
             }
         }
     }
 }