Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2680430
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
5 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/support/startup/__tests__/PreambleUtilsTestCase.php b/support/startup/__tests__/PreambleUtilsTestCase.php
new file mode 100755
index 0000000000..25062e3701
--- /dev/null
+++ b/support/startup/__tests__/PreambleUtilsTestCase.php
@@ -0,0 +1,123 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * /startup/ is not a Phutil library, so it can't use the phutil test fixture.
+ * This script will just run the tests directly.
+ *
+ * NOTE: This test file will not run as part of `arc unit` run!
+ */
+
+
+final class PreambleUtilsTestCase {
+
+
+ public function testTrustXForwardValues() {
+ // For specific values of `$_SERVER['HTTP_X_FORWARDED_FOR']`,
+ // `$_SERVER['REMOTE_ADDR']` will be updated with the result.
+
+
+ $undefined = 'SPECIAL::UNDEFINED';
+ $null_value = 'SPECIAL::NULL';
+
+ $test_cases = array(
+ 'abc' => 'abc',
+ $null_value => $undefined,
+ '' => $undefined,
+
+ // Strange, unexpected cases:
+ 144 => '144',
+ );
+
+ foreach ($test_cases as $input => $expected) {
+ switch ($input) {
+ case $undefined:
+ unset($_SERVER['HTTP_X_FORWARDED_FOR']);
+ break;
+
+ case $null_value:
+ $_SERVER['HTTP_X_FORWARDED_FOR'] = null;
+ break;
+
+ default:
+ $_SERVER['HTTP_X_FORWARDED_FOR'] = $input;
+ break;
+ }
+
+ unset($_SERVER['REMOTE_ADDR']);
+
+ preamble_trust_x_forwarded_for_header();
+
+ if (!isset($_SERVER['REMOTE_ADDR'])) {
+ if ($expected === $undefined) {
+ // test pass
+ continue;
+ } else {
+ $this->failTest("Failed for input {$input} - result is not defined!");
+ }
+ }
+
+ $actual = $_SERVER['REMOTE_ADDR'];
+
+ if ($actual !== $expected) {
+ var_dump($actual);
+
+ $this->failTest(
+ "Failed for input {$input} - actual output is {$actual}");
+ }
+
+ }
+
+ }
+
+
+ private function failTest($message = null) {
+ echo $message;
+ echo "\n";
+ throw new Exception();
+ }
+
+ /**
+ * Run all tests in this class.
+ *
+ * Return: True if all tests passed; False if any test failed.
+ */
+ final public function run() {
+ $reflection = new ReflectionClass($this);
+ $methods = $reflection->getMethods();
+
+ $any_fail = false;
+
+ // Try to ensure that poorly-written tests which depend on execution order
+ // (and are thus not properly isolated) will fail.
+ shuffle($methods);
+
+ foreach ($methods as $method) {
+ $name = $method->getName();
+ if (!preg_match('/^test/', $name)) {
+ continue;
+ }
+
+ try {
+ call_user_func_array(
+ array($this, $name),
+ array());
+ echo "Test passed: {$name}\n";
+ } catch (Throwable $ex) {
+ $any_fail = true;
+ echo "Failed test: {$name}\n";
+ }
+ }
+ return !$any_fail;
+ }
+
+}
+
+require_once dirname(__DIR__).'/preamble-utils.php';
+
+$test_case = new PreambleUtilsTestCase();
+$good = $test_case->run();
+
+if (!$good) {
+ exit(3);
+}
diff --git a/support/startup/preamble-utils.php b/support/startup/preamble-utils.php
index dfb5619a43..8dd3b502d6 100644
--- a/support/startup/preamble-utils.php
+++ b/support/startup/preamble-utils.php
@@ -1,77 +1,77 @@
<?php
/**
* Parse the "X_FORWARDED_FOR" HTTP header to determine the original client
* address.
*
* @param int Number of devices to trust.
* @return void
*/
function preamble_trust_x_forwarded_for_header($layers = 1) {
if (!is_int($layers) || ($layers < 1)) {
echo
'preamble_trust_x_forwarded_for_header(<layers>): '.
'"layers" parameter must an integer larger than 0.'."\n";
echo "\n";
exit(1);
}
if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return;
}
$forwarded_for = $_SERVER['HTTP_X_FORWARDED_FOR'];
- if (!phutil_nonempty_string($forwarded_for)) {
+ if (!strlen($forwarded_for)) {
return;
}
$address = preamble_get_x_forwarded_for_address($forwarded_for, $layers);
$_SERVER['REMOTE_ADDR'] = $address;
}
function preamble_get_x_forwarded_for_address($raw_header, $layers) {
// The raw header may be a list of IPs, like "1.2.3.4, 4.5.6.7", if the
// request the load balancer received also had this header. In particular,
// this happens routinely with requests received through a CDN, but can also
// happen illegitimately if the client just makes up an "X-Forwarded-For"
// header full of lies.
// We can only trust the N elements at the end of the list which correspond
// to network-adjacent devices we control. Usually, we're behind a single
// load balancer and "N" is 1, so we want to take the last element in the
// list.
// In some cases, "N" may be more than 1, if the network is configured so
// that that requests are routed through multiple layers of load balancers
// and proxies. In this case, we want to take the Nth-to-last element of
// the list.
$addresses = explode(',', $raw_header);
// If we have more than one trustworthy device on the network path, discard
// corresponding elements from the list. For example, if we have 7 devices,
// we want to discard the last 6 elements of the list.
// The final device address does not appear in the list, since devices do
// not append their own addresses to "X-Forwarded-For".
$discard_addresses = ($layers - 1);
// However, we don't want to throw away all of the addresses. Some requests
// may originate from within the network, and may thus not have as many
// addresses as we expect. If we have fewer addresses than trustworthy
// devices, discard all but one address.
$max_discard = (count($addresses) - 1);
$discard_count = min($discard_addresses, $max_discard);
if ($discard_count) {
$addresses = array_slice($addresses, 0, -$discard_count);
}
$original_address = end($addresses);
$original_address = trim($original_address);
return $original_address;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Dec 19, 17:02 (22 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1014880
Default Alt Text
(5 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment