├── .gitignore ├── .scrutinizer.yml ├── nbproject ├── project.xml └── project.properties ├── src └── Whitelist │ ├── Definition │ ├── IPv6CIDR.php │ ├── IPv4Address.php │ ├── IPv6Address.php │ ├── IDefinition.php │ ├── WildcardDomain.php │ ├── IPAddress.php │ ├── Definition.php │ ├── IPv4CIDR.php │ ├── Domain.php │ └── IPCIDR.php │ └── Check.php ├── composer.json ├── tests ├── Definition │ ├── DefinitionTest.php │ ├── IPv6CIDRTest.php │ ├── IPv4AddressTest.php │ ├── IPv4CIDRTest.php │ └── DomainTest.php └── CheckTest.php ├── phpunit.xml ├── LICENSE ├── .github └── workflows │ └── tests.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | build/ 3 | /nbproject/private/ 4 | .idea 5 | composer.phar 6 | composer.lock 7 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | build: 2 | nodes: 3 | analysis: 4 | tests: 5 | override: 6 | - php-scrutinizer-run 7 | environment: 8 | php: 7.4 9 | 10 | checks: 11 | php: true 12 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.php.project 4 | 5 | 6 | php-whitelist-check 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Whitelist/Definition/IPv6CIDR.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright Copyright © Sam Stenvall 2014- 10 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 11 | */ 12 | class IPv6CIDR extends IPCIDR 13 | { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/Whitelist/Definition/IPv4Address.php: -------------------------------------------------------------------------------- 1 | 11 | * @copyright Copyright © Sam Stenvall 2014- 12 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 13 | */ 14 | class IPv4Address extends IPAddress 15 | { 16 | 17 | public function validate() 18 | { 19 | return IPv4::isValid($this->_definition); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/Whitelist/Definition/IPv6Address.php: -------------------------------------------------------------------------------- 1 | 11 | * @copyright Copyright © Sam Stenvall 2014- 12 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 13 | */ 14 | class IPv6Address extends IPAddress 15 | { 16 | 17 | public function validate() 18 | { 19 | return IPv6::isValid($this->_definition); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jalle19/php-whitelist-check", 3 | "type": "library", 4 | "description": "Provides a simple way to check whether an address or domain is on a whitelist", 5 | "keywords": ["whitelist", "ipv4", "cidr", "ipv6"], 6 | "homepage": "https://github.com/Jalle19/php-whitelist-check", 7 | "license": "BSD-2-Clause", 8 | "require": { 9 | "php": ">=5.5.2", 10 | "jycr753/ip-utils": "^2.0.1 | ^3.0.0" 11 | }, 12 | "require-dev": { 13 | "phpunit/phpunit": "^4.8" 14 | }, 15 | "autoload": { 16 | "psr-0": { 17 | "Whitelist\\": "src/" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Whitelist/Definition/IDefinition.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright Copyright © Sam Stenvall 2014- 10 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 11 | */ 12 | interface IDefinition 13 | { 14 | /** 15 | * Validates the definition and returns false if the definition is 16 | * invalid 17 | * @return boolean 18 | */ 19 | public function validate(); 20 | 21 | /** 22 | * Returns true if the value matches the definition 23 | * 24 | * @param string $value 25 | * 26 | * @return boolean 27 | */ 28 | public function match($value); 29 | } 30 | -------------------------------------------------------------------------------- /src/Whitelist/Definition/WildcardDomain.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright Copyright © Sam Stenvall 2014- 10 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 11 | */ 12 | class WildcardDomain extends Definition 13 | { 14 | 15 | public function validate() 16 | { 17 | return true; 18 | } 19 | 20 | public function match($value) 21 | { 22 | // Remove the wildcard part and check if it matches the end of $value 23 | $domain = substr($this->_definition, 1); 24 | 25 | return substr($value, -strlen($domain)) === $domain; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/Whitelist/Definition/IPAddress.php: -------------------------------------------------------------------------------- 1 | 12 | * @copyright Copyright © Sam Stenvall 2014- 13 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 14 | */ 15 | abstract class IPAddress extends Definition 16 | { 17 | 18 | public function match($value) 19 | { 20 | try { 21 | $address = Factory::getAddress($this->_definition); 22 | $otherAddress = Factory::getExpression($value); 23 | 24 | return $address->matches($otherAddress); 25 | } catch (Exception $e) { 26 | unset($e); 27 | 28 | return false; 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /tests/Definition/DefinitionTest.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright Copyright © Sam Stenvall 2014- 11 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 12 | */ 13 | abstract class DefinitionTest extends \PHPUnit_Framework_TestCase 14 | { 15 | 16 | /** 17 | * @var IDefinition the definition class 18 | */ 19 | protected $_definition; 20 | 21 | /** 22 | * Should test for exception when the constructor is called with an 23 | * empty string 24 | */ 25 | abstract public function testEmptyDefinition(); 26 | 27 | /** 28 | * Should test for exception when constructor is called with an invalid 29 | * definition string 30 | */ 31 | abstract public function testValidate(); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /tests/Definition/IPv6CIDRTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright © Sam Stenvall 2016- 8 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 9 | */ 10 | class IPv6CIDRTest extends DefinitionTest 11 | { 12 | 13 | 14 | /** 15 | * @expectedException InvalidArgumentException 16 | */ 17 | public function testEmptyDefinition() 18 | { 19 | new \Whitelist\Definition\IPv6CIDR(''); 20 | } 21 | 22 | 23 | /** 24 | * @expectedException InvalidArgumentException 25 | */ 26 | public function testValidate() 27 | { 28 | $cidr = new \Whitelist\Definition\IPv6CIDR('2001::/129'); 29 | 30 | $this->assertFalse($cidr->validate()); 31 | } 32 | 33 | 34 | /** 35 | * 36 | */ 37 | public function testValidateTrue() { 38 | $cidr = new \Whitelist\Definition\IPv6CIDR('2001::/3'); 39 | 40 | $this->assertTrue($cidr->validate()); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | ./tests/ 19 | 20 | 21 | 22 | 23 | ./src 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Whitelist/Definition/Definition.php: -------------------------------------------------------------------------------- 1 | 11 | * @copyright Copyright © Sam Stenvall 2014- 12 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 13 | */ 14 | abstract class Definition implements IDefinition 15 | { 16 | 17 | /** 18 | * @var string the actual definition 19 | */ 20 | protected $_definition; 21 | 22 | /** 23 | * Class constructor. It stores the definition string and validates it. 24 | * 25 | * @param string $definition the definition 26 | * 27 | * @throws \InvalidArgumentException if the definition is invalid 28 | */ 29 | public function __construct($definition) 30 | { 31 | $this->_definition = $definition; 32 | 33 | if (! $this->validate()) { 34 | throw new InvalidArgumentException('The definition "'.$this->_definition.'" is invalid'); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/Whitelist/Definition/IPv4CIDR.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright Copyright © Marc Seiler 2015- 10 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 11 | */ 12 | class IPv4CIDR extends Definition 13 | { 14 | private $regexp = "/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/([0-9]{1,2})?$/"; 15 | 16 | public function validate() 17 | { 18 | return preg_match($this->regexp, $this->_definition) === 1; 19 | } 20 | 21 | public function match($value) 22 | { 23 | // Adapted from https://gist.github.com/tott/7684443 24 | 25 | list($range, $netmask) = explode('/', $this->_definition, 2); 26 | $rangeDecimal = ip2long($range); 27 | $ipDecimal = ip2long($value); 28 | $wildcardDecimal = pow(2, (32 - $netmask)) - 1; 29 | $netmaskDecimal = ~$wildcardDecimal; 30 | 31 | return (($ipDecimal & $netmaskDecimal) == ($rangeDecimal & $netmaskDecimal)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Whitelist/Definition/Domain.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright Copyright © Sam Stenvall 2014- 10 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 11 | */ 12 | class Domain extends Definition 13 | { 14 | 15 | public function validate() 16 | { 17 | // The domain name cannot be empty 18 | if (strlen($this->_definition) === 0) { 19 | return false; 20 | } 21 | 22 | // None of the parts in the domain name can contain invalid characters 23 | // or begin/end with a dash 24 | foreach (explode('.', $this->_definition) as $part) { 25 | if (! preg_match('/^[a-zA-Z0-9-\.]+$/', $part) || 26 | substr($part, 0, 1) === '-' || 27 | substr($part, -1) === '-') { 28 | return false; 29 | } 30 | } 31 | 32 | return true; 33 | } 34 | 35 | public function match($value) 36 | { 37 | return $this->_definition === $value; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tests/Definition/IPv4AddressTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright © Sam Stenvall 2014- 8 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 9 | */ 10 | class IPv4AddressTest extends DefinitionTest 11 | { 12 | 13 | /** 14 | * @expectedException InvalidArgumentException 15 | */ 16 | public function testEmptyDefinition() 17 | { 18 | $this->_definition = new \Whitelist\Definition\IPv4Address(''); 19 | } 20 | 21 | /** 22 | * @expectedException InvalidArgumentException 23 | */ 24 | public function testValidate() 25 | { 26 | $this->_definition = new \Whitelist\Definition\IPv4Address('not.an.ipv4.address'); 27 | } 28 | 29 | /** 30 | * @dataProvider provider 31 | */ 32 | public function testMatch($expected, $address) 33 | { 34 | $this->_definition = new \Whitelist\Definition\IPv4Address('192.168.1.1'); 35 | $this->assertEquals($expected, $this->_definition->match($address)); 36 | } 37 | 38 | public function provider() 39 | { 40 | return array( 41 | array(true, '192.168.1.1'), 42 | array(false, '192.168.1.2'), 43 | ); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Sam Stenvall 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/Whitelist/Definition/IPCIDR.php: -------------------------------------------------------------------------------- 1 | 14 | * @copyright Copyright © Sam Stenvall 2014- 15 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 16 | */ 17 | abstract class IPCIDR extends Definition 18 | { 19 | 20 | /** 21 | * @var ExpressionInterface 22 | */ 23 | protected $_subnet; 24 | 25 | public function validate() 26 | { 27 | try { 28 | $this->_subnet = Factory::getExpression($this->_definition); 29 | 30 | // Check that we got a subnet expression and not something else 31 | if (! $this->_subnet instanceof Subnet) { 32 | return false; 33 | } 34 | } catch (Exception $e) { 35 | unset($e); 36 | 37 | return false; 38 | } 39 | 40 | return true; 41 | } 42 | 43 | public function match($value) 44 | { 45 | try { 46 | $address = Factory::getAddress($value); 47 | 48 | return $this->_subnet->matches($address); 49 | } catch (Exception $e) { 50 | unset($e); 51 | 52 | return false; 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Run test suite 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | test: 13 | name: Run test suite 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | php-version: ['5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4'] 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Setup PHP ${{ matrix.php-version }} 21 | uses: shivammathur/setup-php@v2 22 | with: 23 | php-version: ${{ matrix.php-version }} 24 | tools: composer 25 | - name: Install dependencies 26 | run: composer install 27 | - name: Run tests 28 | run: | 29 | mkdir -p build/logs 30 | vendor/bin/phpunit 31 | 32 | coverage: 33 | name: Generate test coverage 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v2 37 | - name: Setup PHP 38 | uses: shivammathur/setup-php@v2 39 | with: 40 | php-version: 7.4 41 | tools: composer 42 | - name: Install dependencies 43 | run: composer install 44 | - name: Run tests 45 | run: | 46 | mkdir -p build/logs 47 | vendor/bin/phpunit 48 | - name: Upload coverage to Coveralls 49 | env: 50 | COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | run: | 52 | composer global require php-coveralls/php-coveralls 53 | php-coveralls --coverage_clover=build/logs/clover.xml -v 54 | -------------------------------------------------------------------------------- /tests/Definition/IPv4CIDRTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright © Sam Stenvall 2014- 8 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 9 | */ 10 | class IPv4CIDRTest extends DefinitionTest 11 | { 12 | 13 | /** 14 | * @expectedException InvalidArgumentException 15 | */ 16 | public function testEmptyDefinition() 17 | { 18 | $this->_definition = new \Whitelist\Definition\IPv4CIDR(''); 19 | } 20 | 21 | /** 22 | * Test various combinations of CIDR's (valid and invalid) 23 | * 24 | * @return null 25 | */ 26 | public function testValidate() 27 | { 28 | $pass = false; 29 | 30 | // Sorry have to do this instead of using @dataProvider to avoid breaking the contract. 31 | foreach ($this->cidrProvider() as $cidr) { 32 | list ($expected, $address) = $cidr; 33 | try { 34 | $this->_definition = new \Whitelist\Definition\IPv4CIDR($address); 35 | $pass = true; 36 | } catch (Exception $e) { 37 | $pass = false; 38 | } 39 | $this->assertEquals($expected, $pass); 40 | } 41 | 42 | } 43 | 44 | public function cidrProvider() 45 | { 46 | return array( 47 | array(false, '10.10.0.3'), 48 | array(false, '10.10.0.0/23445'), 49 | array(true, '10.10.0.0/16'), 50 | array(true, '0.0.0.0/0'), 51 | ); 52 | } 53 | 54 | /** 55 | * @dataProvider provider 56 | */ 57 | public function testMatch($expected, $address) 58 | { 59 | // testing if address matches CIDR 60 | $this->_definition = new \Whitelist\Definition\IPv4CIDR('10.10.0.0/16'); 61 | $this->assertEquals($expected, $this->_definition->match($address)); 62 | 63 | // testing that all of them pass zero CIDR 64 | $this->_definition = new \Whitelist\Definition\IPv4CIDR('0.0.0.0/0'); 65 | $this->assertEquals(true, $this->_definition->match($address)); 66 | } 67 | 68 | public function provider() 69 | { 70 | return array( 71 | array(true, '10.10.1.1'), 72 | array(true, '10.10.76.1'), 73 | array(false, '110.1.76.1'), 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/Definition/DomainTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright © Sam Stenvall 2014- 8 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 9 | */ 10 | class DomainTest extends DefinitionTest 11 | { 12 | 13 | /** 14 | * @expectedException InvalidArgumentException 15 | */ 16 | public function testEmptyDefinition() 17 | { 18 | $this->_definition = new Whitelist\Definition\Domain(''); 19 | } 20 | 21 | public function testValidate() 22 | { 23 | // do nothing here 24 | } 25 | 26 | /** 27 | * @expectedException InvalidArgumentException 28 | */ 29 | public function testValidateInvalidCharacters() 30 | { 31 | $this->_definition = new Whitelist\Definition\Domain('ag*'); 32 | } 33 | 34 | /** 35 | * @expectedException InvalidArgumentException 36 | */ 37 | public function testValidateInvalidBeginning() 38 | { 39 | $this->_definition = new Whitelist\Definition\Domain('-otherwise-valid.com'); 40 | } 41 | 42 | /** 43 | * @expectedException InvalidArgumentException 44 | */ 45 | public function testValidateInvalidEnd() 46 | { 47 | $this->_definition = new Whitelist\Definition\Domain('otherwise-valid-.com'); 48 | } 49 | 50 | /** 51 | * @dataProvider provider 52 | */ 53 | public function testMatch($expected, $definition, $value) 54 | { 55 | $this->assertEquals($expected, $definition->match($value)); 56 | } 57 | 58 | public function provider() 59 | { 60 | return array( 61 | array(true, new Whitelist\Definition\WildcardDomain('*.example.com'), 'sub.example.com'), 62 | array(true, new Whitelist\Definition\WildcardDomain('*.example.com'), 'anothersub.example.com'), 63 | array(false, new Whitelist\Definition\WildcardDomain('*.example.com'), 'sub.example.net'), 64 | array(false, new Whitelist\Definition\WildcardDomain('*.example.com'), 'sub.anotherexample.com'), 65 | array(false, new Whitelist\Definition\WildcardDomain('*.example.com'), 'localhost'), 66 | array(true, new Whitelist\Definition\Domain('localhost'), 'localhost'), 67 | array(true, new Whitelist\Definition\Domain('example.com'), 'example.com'), 68 | array(false, new Whitelist\Definition\Domain('example.com'), 'sub.example.com'), 69 | array(false, new Whitelist\Definition\Domain('example.com'), 'example2.com'), 70 | ); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Run test suite](https://github.com/Jalle19/php-whitelist-check/actions/workflows/tests.yml/badge.svg)](https://github.com/Jalle19/php-whitelist-check/actions/workflows/tests.yml) 2 | [![Coverage Status](https://coveralls.io/repos/github/Jalle19/php-whitelist-check/badge.svg?branch=master)](https://coveralls.io/github/Jalle19/php-whitelist-check?branch=master) 3 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Jalle19/php-whitelist-check/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Jalle19/php-whitelist-check/?branch=master) 4 | 5 | php-whitelist-check 6 | =================== 7 | 8 | A modern and simple approach to validating IP addresses and domains against a whitelist. It supports both IPv4 and IPv6 addresses and CIDR subnets in addition to domain names and wild-card domains. 9 | 10 | ## Requirements 11 | 12 | * PHP 5.3 or newer 13 | 14 | ## Usage 15 | 16 | The `Check::whitelist()` method takes an array of definitions which will constitute the whitelist. The definitions can either be strings (which will be parsed to their respective objects) or objects. 17 | 18 | The `Check::check($value)` method is used to check the specified value against the current whitelist. The method will return true if the value matches any of the definitions. 19 | 20 | To create your own definition classes just extended `Whitelist\Definition\Definition` and implement `Whitelist\Definition\IDefinition` 21 | 22 | Example usage: 23 | 24 | ```php 25 | require_once("vendor/autoload.php"); 26 | 27 | $checker = new Whitelist\Check(); 28 | 29 | try { 30 | $checker->whitelist(array( 31 | '10.0.3.1', 32 | '10.0.0.0/16', 33 | '2001:db8:100:934b::3:1', 34 | '2001:db8:100:934b::/64', 35 | '*.example.com', 36 | 'localhost', 37 | new Whitelist\Definition\Domain('vpn.work.com'), 38 | )); 39 | } 40 | catch (InvalidArgumentException $e) { 41 | // thrown when an invalid definition is encountered 42 | } 43 | 44 | $checker->check('10.0.1.1'); // true 45 | $checker->check('10.1.1.1'); // false 46 | $checker->check('2001:db8:100:934b::210:2'); // true 47 | $checker->check('another.example.com'); // true 48 | 49 | ``` 50 | 51 | ## License 52 | 53 | This library is licensed under the BSD 2-Clause License 54 | 55 | ## Credits 56 | 57 | This library depends on `xrstf/ip-utils` for the IP-related functionality. It also assumes that ip-utils's test cases are sufficient, which is why only trivial testing on these functions have been made for this library. 58 | -------------------------------------------------------------------------------- /tests/CheckTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright Copyright © Sam Stenvall 2014- 8 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 9 | */ 10 | class CheckTest extends PHPUnit_Framework_TestCase 11 | { 12 | 13 | /** 14 | * @var Whitelist\Check the check instance 15 | */ 16 | protected $_checker; 17 | 18 | /** 19 | * Initialize the checker 20 | */ 21 | protected function setUp() 22 | { 23 | $this->_checker = new \Whitelist\Check(); 24 | } 25 | 26 | /** 27 | * Test invalid objects passed to whitelist() 28 | * @expectedException InvalidArgumentException 29 | */ 30 | public function testInvalidDefinitionObject() 31 | { 32 | $this->_checker->whitelist(array( 33 | new stdClass(), 34 | )); 35 | } 36 | 37 | /** 38 | * Test unparsable definition passed to whitelist() 39 | * @expectedException InvalidArgumentException 40 | */ 41 | public function testUnknownDefinition() 42 | { 43 | $this->_checker->whitelist(array( 44 | 'ag?', // definition class should not be able to be determined 45 | )); 46 | } 47 | 48 | /** 49 | * This test also tests that the whitelist definitions are valid, ie. they 50 | * don't throw an exception 51 | * @dataProvider matchDataprovider 52 | */ 53 | public function testMatch($expected, $expression) 54 | { 55 | $this->_checker->whitelist(array( 56 | '10.2.3.1', 57 | '10.0.0.0/16', 58 | '2001:14d8:100:934b::3:1', 59 | '2001:14b8:100:934b::/64', 60 | 'test.com', 61 | 'example-domain.com', 62 | '*.another-example-domain.com', 63 | '*.example.com', 64 | new Whitelist\Definition\Domain('sub.example.com'), 65 | )); 66 | 67 | $this->assertEquals($expected, $this->_checker->check($expression)); 68 | } 69 | 70 | public function matchDataProvider() 71 | { 72 | return array( 73 | array(true, '10.2.3.1'), 74 | array(false, '10.2.3.2'), 75 | array(true, '10.0.1.1'), 76 | array(false, '10.1.1.1'), 77 | array(true, '2001:14d8:100:934b::3:1'), 78 | array(false, '2001:14d8:100:934b::3:2'), 79 | array(true, '2001:14b8:100:934b::12b1:1'), 80 | array(false, '2001:14c8:100:934b::12b1:1'), 81 | array(true, 'test.com'), 82 | array(true, 'anything.goes.example.com'), 83 | array(true, 'sub.example.com'), 84 | array(false, 'test.example2.com'), 85 | array(true, 'example-domain.com'), 86 | array(true, 'test.another-example-domain.com') 87 | ); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=false 2 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=4 3 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=4 4 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=4 5 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80 6 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap=none 7 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project 8 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.catchBracePlacement=NEW_LINE 9 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.classDeclBracePlacement=NEW_LINE 10 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.expand-tabs=false 11 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.forBracePlacement=NEW_LINE 12 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.ifBracePlacement=NEW_LINE 13 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.indent-shift-width=4 14 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.methodDeclBracePlacement=NEW_LINE 15 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.otherBracePlacement=NEW_LINE 16 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.placeCatchOnNewLine=true 17 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.placeElseOnNewLine=true 18 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.placeFinallyOnNewLine=true 19 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.placeWhileOnNewLine=true 20 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.spaceAroundStringConcatOps=false 21 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.spaces-per-tab=4 22 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.switchBracePlacement=NEW_LINE 23 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.tab-size=4 24 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.text-limit-width=80 25 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.text-line-wrap=none 26 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.useTraitBodyBracePlacement=NEW_LINE 27 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.whileBracePlacement=NEW_LINE 28 | code.analysis.excludes= 29 | ignore.path= 30 | include.path=\ 31 | ${php.global.include.path} 32 | php.version=PHP_53 33 | source.encoding=UTF-8 34 | src.dir=. 35 | tags.asp=false 36 | tags.short=false 37 | web.root=. 38 | -------------------------------------------------------------------------------- /src/Whitelist/Check.php: -------------------------------------------------------------------------------- 1 | 14 | * @copyright Copyright © Sam Stenvall 2014- 15 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 16 | */ 17 | class Check 18 | { 19 | 20 | /** 21 | * @var IDefinition[] the whitelist definitions 22 | */ 23 | private $_definitions = []; 24 | 25 | /** 26 | * Parses the whitelist definitions into respective objects 27 | * 28 | * @param array $whitelist list of definition strings 29 | * 30 | * @throws \InvalidArgumentException if the definition type couldn't be 31 | * determined 32 | */ 33 | public function whitelist(array $whitelist) 34 | { 35 | $this->_definitions = []; 36 | 37 | foreach ($whitelist as $definition) { 38 | // Pre-configured object 39 | if (is_object($definition)) { 40 | if ($definition instanceof Definition\IDefinition) { 41 | $definitionObject = $definition; 42 | } else { 43 | throw new InvalidArgumentException('Definition objects must implement IDefinition'); 44 | } 45 | } // IPv4 address 46 | elseif (preg_match('/[a-z:\/]/', $definition) === 0) { 47 | $definitionObject = new Definition\IPv4Address($definition); 48 | } // IPv4 CIDR notation 49 | elseif (preg_match('/[a-z:]/', $definition) === 0) { 50 | $definitionObject = new Definition\IPv4CIDR($definition); 51 | } // IPv6 address 52 | elseif (preg_match('/^[0-9a-f:]+$/', $definition)) { 53 | $definitionObject = new Definition\IPv6Address($definition); 54 | } // IPv6 CIDR notation 55 | elseif (preg_match('/^[0-9a-f:\/]+$/', $definition)) { 56 | $definitionObject = new Definition\IPv6CIDR($definition); 57 | } // Wildcard domain 58 | elseif (preg_match('/^\*\.[\w\.\-]+$/', $definition)) { 59 | $definitionObject = new Definition\WildcardDomain($definition); 60 | } // Domain 61 | elseif (preg_match('/^[\w\.\-]+$/', $definition)) { 62 | $definitionObject = new Definition\Domain($definition); 63 | } else { 64 | throw new InvalidArgumentException('Unable to parse definition "'.$definition.'"'); 65 | } 66 | 67 | $this->_definitions[] = $definitionObject; 68 | } 69 | } 70 | 71 | /** 72 | * Checks the specified value against all configured definitions and 73 | * returns true if at least one definition considers it a match 74 | * 75 | * @param string $value the value to be checked 76 | * 77 | * @return boolean 78 | */ 79 | public function check($value) 80 | { 81 | foreach ($this->_definitions as $definition) { 82 | if ($definition->match($value)) { 83 | return true; 84 | } 85 | } 86 | 87 | return false; 88 | } 89 | 90 | } 91 | --------------------------------------------------------------------------------