diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2a5103b..d3311e2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -30,6 +30,9 @@ jobs:
- uses: php-actions/composer@v1
- uses: nanasess/setup-php@master
- uses: php-actions/phpunit@v9
+ with:
+ configuration: ./phpunit.xml
+ args: --coverage-text
- name: Publish to codecov
run: bash <(curl -s https://codecov.io/bash)
@@ -54,4 +57,4 @@ jobs:
message_id: ${{ steps.slack.outputs.message_id }}
channel: github-actions
status: FAILED
- color: danger
+ color: danger
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8d7d1b4..a64778a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -26,6 +26,9 @@ jobs:
- uses: php-actions/composer@v1
- uses: nanasess/setup-php@master
- uses: php-actions/phpunit@v9
+ with:
+ configuration: ./phpunit.xml
+ args: --coverage-text
- name: Publish to codecov
run: bash <(curl -s https://codecov.io/bash)
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index edf036a..728c02e 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -3,7 +3,7 @@ name: Publish
on:
release:
types: [created]
-
+
jobs:
release:
runs-on: ubuntu-latest
@@ -22,10 +22,6 @@ jobs:
- uses: actions/checkout@v2
- uses: php-actions/composer@v1
- uses: nanasess/setup-php@master
- - uses: php-actions/phpunit@v9
-
- - name: Publish to codecov
- run: bash <(curl -s https://codecov.io/bash)
- name: Publish
uses: musps/action-deployer-php@master
@@ -33,6 +29,11 @@ jobs:
env:
SSH_PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
+ - name: Create changelog
+ uses: heinrichreimer/github-changelog-generator-action@v2.1.1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+
- name: Notify slack success
if: success()
env:
diff --git a/.phpunit.result.cache b/.phpunit.result.cache
new file mode 100644
index 0000000..c39587a
--- /dev/null
+++ b/.phpunit.result.cache
@@ -0,0 +1 @@
+C:37:"PHPUnit\Runner\DefaultTestResultCache":5287:{a:2:{s:7:"defects";a:36:{s:30:"AgentTest::testApiKeyException";i:4;s:18:"ApiTest::testTrack";i:4;s:19:"ApiTest::testVerify";i:4;s:44:"ConfigurationManagerTest::testReadConfigFile";i:3;s:50:"ConfigurationManagerTest::testEnvironmentVariables";i:4;s:43:"ConfigurationManagerTest::testDefaultParams";i:3;s:58:"ConfigurationManagerTest::testEnvironmentVariablesOverride";i:4;s:34:"EventManagerTest::testEventOptions";i:4;s:32:"EventManagerTest::testBuildEvent";i:4;s:30:"EventManagerTest::testSendSync";i:4;s:34:"EventManagerTest::testSendFailSync";i:4;s:38:"EventManagerTest::testAsyncShouldRetry";i:4;s:31:"EventManagerTest::testSendAsync";i:4;s:62:"RequestUtilsTest::testIpExtractionUsingXFORWARDEDFORHeaderIpv6";i:3;s:70:"RequestUtilsTest::testIpExtractionUsingXFORWARDEDFORHeaderMultipleIpv4";i:3;s:68:"RequestUtilsTest::testIpExtractionUsingHTTPXREALIPHeaderMultipleIpv4";i:3;s:59:"RequestUtilsTest::testIpExtractionUsingREMOTEADDRHeaderIpv6";i:3;s:67:"RequestUtilsTest::testIpExtractionUsingREMOTEADDRHeaderMultipleIpv4";i:3;s:58:"RequestUtilsTest::testIpExtractionUsingXClientIpHeaderIpv6";i:3;s:66:"RequestUtilsTest::testIpExtractionUsingXClientIpHeaderMultipleIpv4";i:3;s:56:"RequestUtilsTest::testIpExtractionUsingXRealIpHeaderIpv6";i:3;s:64:"RequestUtilsTest::testIpExtractionUsingXRealIpHeaderMultipleIpv4";i:3;s:61:"RequestUtilsTest::testIpExtractionUsingForwardedForHeaderIpv6";i:3;s:69:"RequestUtilsTest::testIpExtractionUsingForwardedForHeaderMultipleIpv4";i:3;s:65:"RequestUtilsTest::testIpExtractionUsingXClusterClientIpHeaderIpv6";i:3;s:73:"RequestUtilsTest::testIpExtractionUsingXClusterClientIpHeaderMultipleIpv4";i:3;s:59:"RequestUtilsTest::testIpExtractionUsingXForwardedHeaderIpv6";i:3;s:67:"RequestUtilsTest::testIpExtractionUsingXForwardedHeaderMultipleIpv4";i:3;s:58:"RequestUtilsTest::testIpExtractionUsingForwardedHeaderIpv6";i:3;s:66:"RequestUtilsTest::testIpExtractionUsingForwardedHeaderMultipleIpv4";i:3;s:52:"RequestUtilsTest::testIpExtractionUsingViaHeaderIpv6";i:3;s:60:"RequestUtilsTest::testIpExtractionUsingViaHeaderMultipleIpv4";i:3;s:62:"RequestUtilsTest::testIpExtractionUsingHTTPXCLIENTIPHeaderIpv6";i:3;s:70:"RequestUtilsTest::testIpExtractionUsingHTTPXCLIENTIPHeaderMultipleIpv4";i:3;s:60:"RequestUtilsTest::testIpExtractionUsingHTTPXREALIPHeaderIpv6";i:3;s:60:"RequestUtilsTest::testExtractionPriorityWithoutXForwardedFor";i:3;}s:5:"times";a:45:{s:30:"AgentTest::testApiKeyException";d:0.023;s:18:"ApiTest::testTrack";d:0.015;s:31:"ApiTest::testTrackCustomContext";d:0.001;s:19:"ApiTest::testVerify";d:0.001;s:44:"ConfigurationManagerTest::testReadConfigFile";d:0.001;s:41:"ConfigurationManagerTest::testUnknownKeys";d:0.001;s:41:"ConfigurationManagerTest::testInvalidFile";d:0.001;s:48:"ConfigurationManagerTest::testInvalidFileEntries";d:0.001;s:40:"ConfigurationManagerTest::testLoadConfig";d:0;s:50:"ConfigurationManagerTest::testEnvironmentVariables";d:0;s:43:"ConfigurationManagerTest::testDefaultParams";d:0;s:58:"ConfigurationManagerTest::testEnvironmentVariablesOverride";d:0;s:34:"EventManagerTest::testEventOptions";d:0;s:32:"EventManagerTest::testBuildEvent";d:0;s:30:"EventManagerTest::testSendSync";d:0.002;s:34:"EventManagerTest::testSendFailSync";d:0.002;s:38:"EventManagerTest::testAsyncShouldRetry";d:0.002;s:31:"EventManagerTest::testSendAsync";d:0.001;s:42:"RequestUtilsTest::testProxyHeadersWithIpv4";d:0;s:42:"RequestUtilsTest::testProxyHeadersWithIpv6";d:0;s:53:"RequestUtilsTest::testProxyHeadersWithMultipleHeaders";d:0;s:62:"RequestUtilsTest::testIpExtractionUsingXFORWARDEDFORHeaderIpv6";d:0;s:70:"RequestUtilsTest::testIpExtractionUsingXFORWARDEDFORHeaderMultipleIpv4";d:0;s:60:"RequestUtilsTest::testIpExtractionUsingHTTPXREALIPHeaderIpv6";d:0;s:68:"RequestUtilsTest::testIpExtractionUsingHTTPXREALIPHeaderMultipleIpv4";d:0;s:59:"RequestUtilsTest::testIpExtractionUsingREMOTEADDRHeaderIpv6";d:0;s:67:"RequestUtilsTest::testIpExtractionUsingREMOTEADDRHeaderMultipleIpv4";d:0;s:58:"RequestUtilsTest::testIpExtractionUsingXClientIpHeaderIpv6";d:0;s:66:"RequestUtilsTest::testIpExtractionUsingXClientIpHeaderMultipleIpv4";d:0;s:56:"RequestUtilsTest::testIpExtractionUsingXRealIpHeaderIpv6";d:0;s:64:"RequestUtilsTest::testIpExtractionUsingXRealIpHeaderMultipleIpv4";d:0;s:61:"RequestUtilsTest::testIpExtractionUsingForwardedForHeaderIpv6";d:0;s:69:"RequestUtilsTest::testIpExtractionUsingForwardedForHeaderMultipleIpv4";d:0;s:65:"RequestUtilsTest::testIpExtractionUsingXClusterClientIpHeaderIpv6";d:0;s:73:"RequestUtilsTest::testIpExtractionUsingXClusterClientIpHeaderMultipleIpv4";d:0;s:59:"RequestUtilsTest::testIpExtractionUsingXForwardedHeaderIpv6";d:0;s:67:"RequestUtilsTest::testIpExtractionUsingXForwardedHeaderMultipleIpv4";d:0;s:58:"RequestUtilsTest::testIpExtractionUsingForwardedHeaderIpv6";d:0;s:66:"RequestUtilsTest::testIpExtractionUsingForwardedHeaderMultipleIpv4";d:0;s:52:"RequestUtilsTest::testIpExtractionUsingViaHeaderIpv6";d:0;s:60:"RequestUtilsTest::testIpExtractionUsingViaHeaderMultipleIpv4";d:0;s:62:"RequestUtilsTest::testIpExtractionUsingHTTPXCLIENTIPHeaderIpv6";d:0;s:70:"RequestUtilsTest::testIpExtractionUsingHTTPXCLIENTIPHeaderMultipleIpv4";d:0;s:57:"RequestUtilsTest::testExtractionPriorityWithXForwardedFor";d:0;s:60:"RequestUtilsTest::testExtractionPriorityWithoutXForwardedFor";d:0;}}}
\ No newline at end of file
diff --git a/README.md b/README.md
index 4e27378..6f3f21c 100644
--- a/README.md
+++ b/README.md
@@ -68,9 +68,9 @@ Attach `securenative.json` file to your root folder:
```json
{
- "SECURENATIVE_API_KEY": "SOME_API_KEY",
- "SECURENATIVE_APP_NAME": "SOME_APP_NAME",
- "SECURENATIVE_API_URL": "SOME_API_URL",
+ "SECURENATIVE_API_KEY": "YOUR_API_KEY",
+ "SECURENATIVE_APP_NAME": "APP_NAME",
+ "SECURENATIVE_API_URL": "API_URL",
"SECURENATIVE_INTERVAL": 1000,
"SECURENATIVE_MAX_EVENTS": 100,
"SECURENATIVE_TIMEOUT": 1500,
@@ -161,10 +161,12 @@ SecureNative::track(array(
**Example**
```php
+$options = new SecureNativeOptions();
+
$ver = SecureNative::verify(array(
'event' => EventTypes::VERIFY,
'userId' => '1234',
- 'context' => SecureNativeContext::fromRequest(),
+ 'context' => SecureNative::fromRequest(),
'userTraits' => (object)[
'name' => 'Your Name',
'email' => 'name@gmail.com'
@@ -187,3 +189,27 @@ if ($verified) {
// Request is trusted (coming from SecureNative)
}
```
+
+## Extract proxy headers from cloud providers
+
+You can specify custom header keys to allow extraction of client ip from different providers.
+This example demonstrates the usage of proxy headers for ip extraction from Cloudflare.
+
+### Option 1: Using config file
+```json
+{
+ "SECURENATIVE_API_KEY": "YOUR_API_KEY",
+ "SECURENATIVE_PROXY_HEADERS": ["CF-Connecting-IP"]
+}
+```
+
+Initialize sdk as shown above.
+
+### Options 2: Using ConfigurationBuilder
+
+```php
+$options = new SecureNativeOptions();
+$options->setProxyHeaders(["CF-Connecting-IP"]);
+
+SecureNative::init();
+```
\ No newline at end of file
diff --git a/build/logs/clover.xml b/build/logs/clover.xml
new file mode 100644
index 0000000..10aa9d4
--- /dev/null
+++ b/build/logs/clover.xml
@@ -0,0 +1,660 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/composer.json b/composer.json
index 8377580..36bfc8c 100644
--- a/composer.json
+++ b/composer.json
@@ -27,7 +27,9 @@
"guzzlehttp/guzzle": "^6.0",
"antecedent/patchwork": "~2.0",
"monolog/monolog": "2.0.2",
- "phpunit/phpunit": "^9"
+ "phpunit/phpunit": "^9",
+ "ext-openssl": "*",
+ "ext-json": "*"
},
"require-dev": {
"php-coveralls/php-coveralls": "*"
diff --git a/phpunit.xml b/phpunit.xml
index cc955a1..31788df 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,5 +1,5 @@
-
+
./tests
diff --git a/src/ConfigurationManager.php b/src/ConfigurationManager.php
index 1510265..f3d9b55 100644
--- a/src/ConfigurationManager.php
+++ b/src/ConfigurationManager.php
@@ -15,6 +15,7 @@ function getConfigMap()
'SECURENATIVE_DISABLE' => (object)['name' => 'disable', 'type' => 'boolean'],
'SECURENATIVE_LOG_LEVEL' => (object)['name' => 'logLevel', 'type' => 'string'],
'SECURENATIVE_FAILOVER_STRATEGY' => (object)['name' => 'failoverStrategy', 'type' => 'string'],
+ 'SECURENATIVE_PROXY_HEADERS' => (object)['name' => 'proxyHeaders', 'type' => 'array'],
];
}
@@ -88,6 +89,8 @@ private static function loadConfig($configFilePath = null)
$envVal = intval($envVal);
} else if ($type == "boolean") {
$envVal = boolval($envVal);
+ } else if ($type == "array") {
+ $envVal = explode(',', $envVal);
}
}
@@ -104,6 +107,7 @@ private static function loadConfig($configFilePath = null)
$getConfigOrEnv('disable', 'SECURENATIVE_DISABLE'),
$getConfigOrEnv('logLevel', 'SECURENATIVE_LOG_LEVEL'),
$getConfigOrEnv('failoverStrategy', 'SECURENATIVE_FAILOVER_STRATEGY'),
+ $getConfigOrEnv('proxyHeaders', 'SECURENATIVE_PROXY_HEADERS'),
);
}
diff --git a/src/SecureNative.php b/src/SecureNative.php
index 3a195bd..fc9ddfb 100644
--- a/src/SecureNative.php
+++ b/src/SecureNative.php
@@ -95,20 +95,6 @@ public static function flow($flowId, Array $attributes)
return self::$eventManager->sendSync($event, $requestUrl);
}
- public static function getRequestContext()
- {
- return SecureNativeContext::fromRequest();
- }
-
- /**
- * @deprecated use `getRequestContext()` instead
- * @return SecureNativeContext
- */
- public static function contextFromContext()
- {
- return SecureNativeContext::fromRequest();
- }
-
public static function getMiddleware()
{
return self::$middleware;
@@ -122,6 +108,14 @@ public static function getApiKey()
return self::$apiKey;
}
+ /**
+ * @return SecureNativeContext
+ */
+ public static function fromRequest()
+ {
+ return SecureNativeContext::fromRequest(self::$options);
+ }
+
/**
* Destroy object after use
*/
diff --git a/src/SecureNativeContext.php b/src/SecureNativeContext.php
index fe7ea94..57db7e4 100644
--- a/src/SecureNativeContext.php
+++ b/src/SecureNativeContext.php
@@ -35,11 +35,11 @@ public function __construct($clientToken, $ip = null, $remoteIp = null, $headers
$this->body = $body;
}
- public static function fromRequest() : SecureNativeContext
+ public static function fromRequest($options) : SecureNativeContext
{
- $clientToken = Utils::cookieIdFromRequest() ? Utils::cookieIdFromRequest() : Utils::securHeaderFromRequest();
- $ip = Utils::clientIpFromRequest();
- $remoteIp = Utils::clientIpFromRequest();
+ $clientToken = Utils::cookieIdFromRequest() ? Utils::cookieIdFromRequest() : Utils::secureHeaderFromRequest();
+ $ip = Utils::clientIpFromRequest($options);
+ $remoteIp = Utils::clientIpFromRequest($options);
$headers = Utils::headersFromRequest();
$url = Utils::urlFromRequest();
$method = Utils::methodFromRequest();
diff --git a/src/SecureNativeOptions.php b/src/SecureNativeOptions.php
index 4e9da63..742fce7 100644
--- a/src/SecureNativeOptions.php
+++ b/src/SecureNativeOptions.php
@@ -14,6 +14,7 @@ class SecureNativeOptions
private $disable;
private $logLevel;
private $failoverStrategy;
+ private $proxyHeaders;
// Needed for option merging
private $default = array(
@@ -26,9 +27,10 @@ class SecureNativeOptions
"disable" => false,
"logLevel" => 'fatal',
"failoverStrategy" => 'fail-open',
+ "proxyHeaders" => array(),
);
- public function __construct($apiKey = null, $apiUrl = null, $interval = null, $maxEvents = null, $timeout = null, $autoSend = null, $disable = null, $logLevel = null, $failoverStrategy = null)
+ public function __construct($apiKey = null, $apiUrl = null, $interval = null, $maxEvents = null, $timeout = null, $autoSend = null, $disable = null, $logLevel = null, $failoverStrategy = null, $proxyHeaders = null)
{
$this->apiKey = isset($apiKey) ? $apiKey : $this->default["apiKey"];
$this->apiUrl = isset($apiUrl) ? $apiUrl : $this->default["apiUrl"];
@@ -39,6 +41,7 @@ public function __construct($apiKey = null, $apiUrl = null, $interval = null, $m
$this->disable = isset($disable) ? $disable : $this->default["disable"];
$this->logLevel = isset($logLevel) ? $logLevel : $this->default["logLevel"];
$this->failoverStrategy = isset($failoverStrategy) ? $failoverStrategy : $this->default["failoverStrategy"];
+ $this->proxyHeaders = isset($proxyHeaders) ? $proxyHeaders : $this->default["proxyHeaders"];
}
/**
@@ -203,6 +206,24 @@ public function setFailoverStrategy(string $failoverStrategy): SecureNativeOptio
return $this;
}
+ /**
+ * @return array
+ */
+ public function getProxyHeaders(): array
+ {
+ return $this->proxyHeaders;
+ }
+
+ /**
+ * @param array $proxyHeaders
+ * @return SecureNativeOptions
+ */
+ public function setProxyHeaders(array $proxyHeaders): SecureNativeOptions
+ {
+ $this->proxyHeaders = $proxyHeaders;
+ return $this;
+ }
+
/**
* Merge an existing `SecureNativeOptions` object into an existing one
* @param SecureNativeOptions $options
@@ -217,5 +238,6 @@ public function mergeOptions(SecureNativeOptions $options) {
$options->isDisable() != $this->default["disable"] ? $this->setDisable($options->isDisable()) : null;
$options->getLogLevel() != $this->default["logLevel"] ? $this->setLogLevel($options->getLogLevel()) : null;
$options->getFailoverStrategy() != $this->default["failoverStrategy"] ? $this->setFailoverStrategy($options->getFailoverStrategy()) : null;
+ $options->getProxyHeaders() != $this->default["proxyHeaders"] ? $this->setProxyHeaders($options->getProxyHeaders()) : null;
}
}
diff --git a/src/Utils.php b/src/Utils.php
index 18998f7..617a8de 100644
--- a/src/Utils.php
+++ b/src/Utils.php
@@ -5,22 +5,37 @@
const ALGORITHM = "AES-256-CBC";
const BLOCK_SIZE = 16;
const AES_KEY_SIZE = 32;
+const IP_HEADERS = ["HTTP_X_FORWARDED_FOR", "X_FORWARDED_FOR", "HTTP_X_CLIENT_IP", "HTTP_X_REAL_IP", "REMOTE_ADDR", "x-forwarded-for", "x-client-ip", "x-real-ip", "x-forwarded", "x-cluster-client-ip", "forwarded-for", "forwarded", "via"];
abstract class Utils
{
- public static function clientIpFromRequest()
+ public static function clientIpFromRequest($options)
{
- if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
- $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
- return $parts[0];
- }
- if (array_key_exists('HTTP_X_REAL_IP', $_SERVER)) {
- return $_SERVER['HTTP_X_REAL_IP'];
+ if (!empty($options) && count($options->getProxyHeaders()) > 0) {
+ foreach ($options->getProxyHeaders() as $header) {
+ if (array_key_exists($header, $_SERVER)) {
+ $parts = explode(',', $_SERVER[$header]);
+ foreach ($parts as $ip) {
+ if (filter_var($ip, FILTER_VALIDATE_IP)) {
+ return $ip;
+ }
+ }
+ }
+ }
}
- if (array_key_exists('REMOTE_ADDR', $_SERVER)) {
- return $_SERVER['REMOTE_ADDR'];
+
+ foreach (IP_HEADERS as $header) {
+ if (array_key_exists($header, $_SERVER)) {
+ $parts = explode(',', $_SERVER[$header]);
+ foreach ($parts as $ip) {
+ if (filter_var($ip, FILTER_VALIDATE_IP)) {
+ return $ip;
+ }
+ }
+ }
}
+
return null;
}
@@ -63,7 +78,7 @@ public static function methodFromRequest()
return '';
}
- public static function securHeaderFromRequest()
+ public static function secureHeaderFromRequest()
{
if (array_key_exists('HTTP_X_SECURENATIVE', $_SERVER)) {
return $_SERVER['HTTP_X_SECURENATIVE'];
@@ -122,4 +137,4 @@ public static function encrypt($plainText, $cipherKey) {
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/AgentTest.php b/tests/AgentTest.php
index ff38425..efdf717 100644
--- a/tests/AgentTest.php
+++ b/tests/AgentTest.php
@@ -1,8 +1,6 @@
EventTypes::LOG_IN,
diff --git a/tests/ApiTest.php b/tests/ApiTest.php
index 8191558..0dc38d7 100644
--- a/tests/ApiTest.php
+++ b/tests/ApiTest.php
@@ -1,15 +1,9 @@
assertArrayHasKey('autoSend', $arr);
$this->assertArrayHasKey('disable', $arr);
$this->assertArrayHasKey('logLevel', $arr);
+ $this->assertArrayHasKey('proxyHeaders', $arr);
}
public function testUnknownKeys()
@@ -91,7 +92,7 @@ public function testEnvironmentVariables()
{
$testKeys = $this->getConfigTestKeys();
- // Set env for each ovject item
+ // Set env for each object item
foreach ($testKeys as $key => $item) {
putenv("$key=" . $item->value);
}
@@ -125,7 +126,7 @@ public function testEnvironmentVariablesOverride()
{
$testKeys = $this->getConfigTestKeys();
- // Set env for each ovject item
+ // Set env for each object item
foreach ($testKeys as $key => $item) {
putenv("$key=" . $item->value);
}
@@ -142,6 +143,7 @@ public function testEnvironmentVariablesOverride()
$this->assertEquals(true, $options->isAutoSend());
$this->assertEquals(false, $options->isDisable());
$this->assertEquals("fatal", $options->getLogLevel());
+ $this->assertEquals(["CF-Connecting-IP", "Some-Random-IP"], $options->getProxyHeaders());
foreach ($testKeys as $key => $item) {
// Clear env variables after test
diff --git a/tests/EventManagerTest.php b/tests/EventManagerTest.php
index 0dafafd..1e78417 100644
--- a/tests/EventManagerTest.php
+++ b/tests/EventManagerTest.php
@@ -105,7 +105,6 @@ public function testAsyncShouldRetry() {
$this->assertNull($callbackRes, 'Track callback should not be called');
}
-
public function testSendAsync()
{
$options = new SecureNativeOptions(self::TEST_API_KEY, "http://testushim.com");
@@ -133,5 +132,4 @@ public function testSendAsync()
$this->assertObjectHasAttribute('prop1', $callbackRes->{'properties'});
$this->assertObjectHasAttribute('prop2', $callbackRes->{'properties'});
}
-
}
diff --git a/tests/MockUtils.php b/tests/MockUtils.php
index da25df9..25782f9 100644
--- a/tests/MockUtils.php
+++ b/tests/MockUtils.php
@@ -6,6 +6,7 @@
use GuzzleHttp\Psr7\Response;
use SecureNative\sdk\EventTypes;
use SecureNative\sdk\SecureNative;
+use SecureNative\sdk\SecureNativeOptions;
function getClient($fail = false)
{
@@ -27,9 +28,10 @@ function getClient($fail = false)
function mock_track_object($context = null)
{
+ $options = new SecureNativeOptions();
return array(
'event' => EventTypes::LOG_IN,
- 'context' => isset($context) ? $context : SecureNative::getRequestContext(),
+ 'context' => isset($context) ? $context : SecureNative::fromRequest($options),
'userId' => '556595',
'userTraits' => (object)[
'name' => 'Your name',
diff --git a/tests/RequestUtilsTest.php b/tests/RequestUtilsTest.php
new file mode 100644
index 0000000..a899e80
--- /dev/null
+++ b/tests/RequestUtilsTest.php
@@ -0,0 +1,290 @@
+setProxyHeaders(["CF-Connecting-IP"]);
+
+ $_SERVER["CF-Connecting-IP"] = "203.0.113.1";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["CF-Connecting-IP"]);
+ $this->assertEquals("203.0.113.1", $client_ip);
+ }
+
+ public function testProxyHeadersWithIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $options->setProxyHeaders(["CF-Connecting-IP"]);
+
+ $_SERVER["CF-Connecting-IP"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["CF-Connecting-IP"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testProxyHeadersWithMultipleHeaders()
+ {
+ $options = new SecureNativeOptions();
+ $options->setProxyHeaders(["CF-Connecting-IP"]);
+
+ $_SERVER["CF-Connecting-IP"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["CF-Connecting-IP"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingXFORWARDEDFORHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["X_FORWARDED_FOR"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["X_FORWARDED_FOR"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingXFORWARDEDFORHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["X_FORWARDED_FOR"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["X_FORWARDED_FOR"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingHTTPXREALIPHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["HTTP_X_REAL_IP"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["HTTP_X_REAL_IP"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingHTTPXREALIPHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["HTTP_X_REAL_IP"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["HTTP_X_REAL_IP"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingREMOTEADDRHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["REMOTE_ADDR"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["REMOTE_ADDR"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingREMOTEADDRHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["REMOTE_ADDR"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["REMOTE_ADDR"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingXClientIpHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["x-client-ip"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["x-client-ip"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingXClientIpHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["x-client-ip"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["x-client-ip"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingXRealIpHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["x-real-ip"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["x-real-ip"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingXRealIpHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["x-real-ip"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["x-real-ip"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingForwardedForHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["forwarded-for"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["forwarded-for"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingForwardedForHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["forwarded-for"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["forwarded-for"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingXClusterClientIpHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["x-cluster-client-ip"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["x-cluster-client-ip"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingXClusterClientIpHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["x-cluster-client-ip"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["x-cluster-client-ip"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingXForwardedHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["x-forwarded"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["x-forwarded"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingXForwardedHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["x-forwarded"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["x-forwarded"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingForwardedHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["forwarded"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["forwarded"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingForwardedHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["forwarded"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["forwarded"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingViaHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["via"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["via"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingViaHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["via"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["via"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testIpExtractionUsingHTTPXCLIENTIPHeaderIpv6()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["HTTP_X_CLIENT_IP"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["HTTP_X_CLIENT_IP"]);
+ $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip);
+ }
+
+ public function testIpExtractionUsingHTTPXCLIENTIPHeaderMultipleIpv4()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["HTTP_X_CLIENT_IP"] = "141.246.115.116, 203.0.113.1, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["HTTP_X_CLIENT_IP"]);
+ $this->assertEquals("141.246.115.116", $client_ip);
+ }
+
+ public function testExtractionPriorityWithXForwardedFor()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["HTTP_X_FORWARDED_FOR"] = "203.0.113.1";
+ $_SERVER["HTTP_X_REAL_IP"] = "198.51.100.101";
+ $_SERVER["HTTP_X_CLIENT_IP"] = "198.51.100.102";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["HTTP_X_FORWARDED_FOR"]);
+ unset($_SERVER["HTTP_X_REAL_IP"]);
+ unset($_SERVER["HTTP_X_CLIENT_IP"]);
+ $this->assertEquals("203.0.113.1", $client_ip);
+ }
+
+ public function testExtractionPriorityWithoutXForwardedFor()
+ {
+ $options = new SecureNativeOptions();
+ $_SERVER["HTTP_X_REAL_IP"] = "198.51.100.101";
+ $_SERVER["HTTP_X_CLIENT_IP"] = "203.0.113.1, 141.246.115.116, 12.34.56.3";
+
+ $client_ip = Utils::clientIpFromRequest($options);
+ unset($_SERVER["HTTP_X_REAL_IP"]);
+ unset($_SERVER["HTTP_X_CLIENT_IP"]);
+ $this->assertEquals("203.0.113.1", $client_ip);
+ }
+}
\ No newline at end of file
diff --git a/tests/SecureNativeTest.php b/tests/SecureNativeTest.php
index 5b95e18..dce2bc6 100644
--- a/tests/SecureNativeTest.php
+++ b/tests/SecureNativeTest.php
@@ -1,11 +1,9 @@
-
+
.