mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 14:00:56 +01:00
39b4d20ce5
Summary: Provide a catchall mechanism to find unprotected writes. - Depends on D758. - Similar to WriteOnHTTPGet stuff from Facebook's stack. - Since we have a small number of storage mechanisms and highly structured read/write pathways, we can explicitly answer the question "is this page performing a write?". - Never allow writes without CSRF checks. - This will probably break some things. That's fine: they're CSRF vulnerabilities or weird edge cases that we can fix. But don't push to Facebook for a few days unless you're prepared to deal with this. - **>>> MEGADERP: All Conduit write APIs are currently vulnerable to CSRF! <<<** Test Plan: - Ran some scripts that perform writes (scripts/search indexers), no issues. - Performed normal CSRF submits. - Added writes to an un-CSRF'd page, got an exception. - Executed conduit methods. - Did login/logout (this works because the logged-out user validates the logged-out csrf "token"). - Did OAuth login. - Did OAuth registration. Reviewers: pedram, andrewjcg, erling, jungejason, tuomaspelkonen, aran, codeblock Commenters: pedram CC: aran, epriestley, pedram Differential Revision: 777
285 lines
8.1 KiB
PHP
285 lines
8.1 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Copyright 2011 Facebook, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
$__start__ = microtime(true);
|
|
|
|
error_reporting(E_ALL | E_STRICT);
|
|
|
|
phabricator_detect_insane_memory_limit();
|
|
|
|
ini_set('memory_limit', -1);
|
|
|
|
$env = getenv('PHABRICATOR_ENV'); // Apache
|
|
if (!$env) {
|
|
if (isset($_ENV['PHABRICATOR_ENV'])) {
|
|
$env = $_ENV['PHABRICATOR_ENV'];
|
|
}
|
|
}
|
|
|
|
if (!$env) {
|
|
phabricator_fatal_config_error(
|
|
"The 'PHABRICATOR_ENV' environmental variable is not defined. Modify ".
|
|
"your httpd.conf to include 'SetEnv PHABRICATOR_ENV <env>', where '<env>' ".
|
|
"is one of 'development', 'production', or a custom environment.");
|
|
}
|
|
|
|
if (!function_exists('mysql_connect')) {
|
|
phabricator_fatal_config_error(
|
|
"The PHP MySQL extension is not installed. This extension is required.");
|
|
}
|
|
|
|
if (!isset($_REQUEST['__path__'])) {
|
|
phabricator_fatal_config_error(
|
|
"__path__ is not set. Your rewrite rules are not configured correctly.");
|
|
}
|
|
|
|
if (get_magic_quotes_gpc()) {
|
|
phabricator_fatal_config_error(
|
|
"Your server is configured with PHP 'magic_quotes_gpc' enabled. This ".
|
|
"feature is 'highly discouraged' by PHP's developers and you must ".
|
|
"disable it to run Phabricator. Consult the PHP manual for instructions.");
|
|
}
|
|
|
|
register_shutdown_function('phabricator_shutdown');
|
|
|
|
require_once dirname(dirname(__FILE__)).'/conf/__init_conf__.php';
|
|
|
|
try {
|
|
setup_aphront_basics();
|
|
|
|
$conf = phabricator_read_config_file($env);
|
|
$conf['phabricator.env'] = $env;
|
|
|
|
phutil_require_module('phabricator', 'infrastructure/env');
|
|
PhabricatorEnv::setEnvConfig($conf);
|
|
|
|
phutil_require_module('phabricator', 'aphront/console/plugin/xhprof/api');
|
|
DarkConsoleXHProfPluginAPI::hookProfiler();
|
|
|
|
phutil_require_module('phabricator', 'aphront/console/plugin/errorlog/api');
|
|
|
|
PhutilErrorHandler::initialize();
|
|
|
|
} catch (Exception $ex) {
|
|
phabricator_fatal("[Initialization Exception] ".$ex->getMessage());
|
|
}
|
|
|
|
$tz = PhabricatorEnv::getEnvConfig('phabricator.timezone');
|
|
if ($tz) {
|
|
date_default_timezone_set($tz);
|
|
}
|
|
phutil_require_module('phabricator', 'aphront/console/plugin/errorlog/api');
|
|
phutil_require_module('phutil', 'error');
|
|
|
|
PhutilErrorHandler::setErrorListener(
|
|
array('DarkConsoleErrorLogPluginAPI', 'handleErrors'));
|
|
|
|
foreach (PhabricatorEnv::getEnvConfig('load-libraries') as $library) {
|
|
phutil_load_library($library);
|
|
}
|
|
|
|
if (PhabricatorEnv::getEnvConfig('phabricator.setup')) {
|
|
PhabricatorSetup::runSetup();
|
|
return;
|
|
}
|
|
|
|
|
|
$host = $_SERVER['HTTP_HOST'];
|
|
$path = $_REQUEST['__path__'];
|
|
|
|
switch ($host) {
|
|
default:
|
|
$config_key = 'aphront.default-application-configuration-class';
|
|
$config_class = PhabricatorEnv::getEnvConfig($config_key);
|
|
PhutilSymbolLoader::loadClass($config_class);
|
|
$application = newv($config_class, array());
|
|
break;
|
|
}
|
|
|
|
|
|
$application->setHost($host);
|
|
$application->setPath($path);
|
|
$application->willBuildRequest();
|
|
$request = $application->buildRequest();
|
|
|
|
$write_guard = new AphrontWriteGuard($request);
|
|
|
|
$application->setRequest($request);
|
|
list($controller, $uri_data) = $application->buildController();
|
|
try {
|
|
$response = $controller->willBeginExecution();
|
|
if (!$response) {
|
|
$controller->willProcessRequest($uri_data);
|
|
$response = $controller->processRequest();
|
|
}
|
|
} catch (AphrontRedirectException $ex) {
|
|
$response = id(new AphrontRedirectResponse())
|
|
->setURI($ex->getURI());
|
|
} catch (Exception $ex) {
|
|
$response = $application->handleException($ex);
|
|
}
|
|
|
|
try {
|
|
$response = $application->willSendResponse($response);
|
|
$response->setRequest($request);
|
|
$response_string = $response->buildResponseString();
|
|
} catch (Exception $ex) {
|
|
$write_guard->dispose();
|
|
phabricator_fatal('[Rendering Exception] '.$ex->getMessage());
|
|
}
|
|
|
|
$write_guard->dispose();
|
|
|
|
|
|
$code = $response->getHTTPResponseCode();
|
|
if ($code != 200) {
|
|
header("HTTP/1.0 {$code}");
|
|
}
|
|
|
|
$headers = $response->getCacheHeaders();
|
|
$headers = array_merge($headers, $response->getHeaders());
|
|
foreach ($headers as $header) {
|
|
list($header, $value) = $header;
|
|
header("{$header}: {$value}");
|
|
}
|
|
|
|
|
|
// TODO: This shouldn't be possible in a production-configured environment.
|
|
if (isset($_REQUEST['__profile__']) &&
|
|
($_REQUEST['__profile__'] == 'all')) {
|
|
$profile = DarkConsoleXHProfPluginAPI::stopProfiler();
|
|
$profile =
|
|
'<div style="text-align: center; background: #ff00ff; padding: 1em;
|
|
font-size: 24px; font-weight: bold;">'.
|
|
'<a href="/xhprof/profile/'.$profile.'/">'.
|
|
'>>> View Profile <<<'.
|
|
'</a>'.
|
|
'</div>';
|
|
if (strpos($response_string, '<body>') !== false) {
|
|
$response_string = str_replace(
|
|
'<body>',
|
|
'<body>'.$profile,
|
|
$response_string);
|
|
} else {
|
|
echo $profile;
|
|
}
|
|
}
|
|
|
|
echo $response_string;
|
|
|
|
/**
|
|
* @group aphront
|
|
*/
|
|
function setup_aphront_basics() {
|
|
$aphront_root = dirname(dirname(__FILE__));
|
|
$libraries_root = dirname($aphront_root);
|
|
|
|
$root = null;
|
|
if (!empty($_SERVER['PHUTIL_LIBRARY_ROOT'])) {
|
|
$root = $_SERVER['PHUTIL_LIBRARY_ROOT'];
|
|
}
|
|
|
|
ini_set('include_path', $libraries_root.':'.ini_get('include_path'));
|
|
@include_once $root.'libphutil/src/__phutil_library_init__.php';
|
|
if (!@constant('__LIBPHUTIL__')) {
|
|
echo "ERROR: Unable to load libphutil. Update your PHP 'include_path' to ".
|
|
"include the parent directory of libphutil/.\n";
|
|
exit(1);
|
|
}
|
|
|
|
// Load Phabricator itself using the absolute path, so we never end up doing
|
|
// anything surprising (loading index.php and libraries from different
|
|
// directories).
|
|
phutil_load_library($aphront_root.'/src');
|
|
phutil_load_library('arcanist/src');
|
|
|
|
}
|
|
|
|
function phabricator_fatal_config_error($msg) {
|
|
phabricator_fatal("CONFIG ERROR: ".$msg."\n");
|
|
die();
|
|
}
|
|
|
|
function phabricator_detect_insane_memory_limit() {
|
|
$memory_limit = ini_get('memory_limit');
|
|
$char_limit = 12;
|
|
if (strlen($memory_limit) <= $char_limit) {
|
|
return;
|
|
}
|
|
|
|
// colmdoyle ran into an issue on an Ubuntu box with Suhosin where his
|
|
// 'memory_limit' was set to:
|
|
//
|
|
// 3232323232323232323232323232323232323232323232323232323232323232M
|
|
//
|
|
// Not a typo. A wizard did it.
|
|
//
|
|
// Anyway, with this 'memory_limit', the machine would immediately fatal
|
|
// when executing the ini_set() later. I wasn't able to reproduce this on my
|
|
// EC2 Ubuntu + Suhosin box, but verified that it caused the problem on his
|
|
// machine and that setting it to a more sensible value fixed it. Since I
|
|
// have no idea how to actually trigger the issue, we look for a coarse
|
|
// approximation of it (a memory_limit setting more than 12 characters in
|
|
// length).
|
|
|
|
phabricator_fatal_config_error(
|
|
"Your PHP 'memory_limit' is set to something ridiculous ".
|
|
"(\"{$memory_limit}\"). Set it to a more reasonable value (it must be no ".
|
|
"more than {$char_limit} characters long).");
|
|
}
|
|
|
|
function phabricator_shutdown() {
|
|
$event = error_get_last();
|
|
|
|
if (!$event) {
|
|
return;
|
|
}
|
|
|
|
if ($event['type'] != E_ERROR) {
|
|
return;
|
|
}
|
|
|
|
$msg = ">>> UNRECOVERABLE FATAL ERROR <<<\n\n";
|
|
if ($event) {
|
|
// Even though we should be emitting this as text-plain, escape things just
|
|
// to be sure since we can't really be sure what the program state is when
|
|
// we get here.
|
|
$msg .= phutil_escape_html($event['message'])."\n\n";
|
|
$msg .= phutil_escape_html($event['file'].':'.$event['line']);
|
|
}
|
|
|
|
// flip dem tables
|
|
$msg .= "\n\n\n";
|
|
$msg .= "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb\x20\xef\xb8\xb5\x20\xc2\xaf".
|
|
"\x5c\x5f\x28\xe3\x83\x84\x29\x5f\x2f\xc2\xaf\x20\xef\xb8\xb5\x20".
|
|
"\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb";
|
|
|
|
phabricator_fatal($msg);
|
|
}
|
|
|
|
function phabricator_fatal($msg) {
|
|
header(
|
|
'Content-Type: text/plain; charset=utf-8',
|
|
$replace = true,
|
|
$http_error = 500);
|
|
|
|
error_log($msg);
|
|
echo $msg;
|
|
|
|
exit(1);
|
|
}
|