Implemented many unit tests.

The following tests for classes have been implemented:
- Security
- Input
- Encryption Library
- Utf8

A mocking autoloader has been added (but is not yet configured correctly)
This commit is contained in:
Abel Hoogeveen 2016-06-02 23:57:23 +02:00
parent 96ca890656
commit 5df6b733d0
14 changed files with 1624 additions and 101 deletions

View File

@ -12,7 +12,7 @@ return array(
'default_function' => 'index',
'application_prefix' => 'MY_',
'charset' => 'utf-8',
'charset' => 'UTF-8',
'language' => 'english',
/*

View File

@ -476,7 +476,7 @@ class FW_Zip {
* Lets you clear current zip data. Useful if you need to create
* multiple zips with different data.
*
* @return CI_Zip
* @return FW_Zip
*/
public function clear_data()
{

View File

@ -115,6 +115,13 @@ class Input {
*/
protected $_input_stream;
/**
* Factory object, allows this class to communicate with everything in FuzeWorks
*
* @var Factory
*/
protected $factory;
// --------------------------------------------------------------------
/**
@ -127,6 +134,9 @@ class Input {
*/
public function __construct()
{
// First load the factory so contact can be made with everything in FuzeWorks
$this->factory = Factory::getInstance();
$this->_allow_get_array = (Config::get('routing')->allow_get_array === TRUE);
$this->_enable_xss = (Config::get('security')->global_xss_filtering === TRUE);
$this->_enable_csrf = (Config::get('security')->csrf_protection === TRUE);
@ -138,7 +148,7 @@ class Input {
// CSRF Protection check
if ($this->_enable_csrf === TRUE && ! $this->is_cli_request())
{
Security::csrf_verify();
$this->factory->security->csrf_verify();
}
}
@ -204,7 +214,7 @@ class Input {
}
return ($xss_clean === TRUE)
? Security::xss_clean($value)
? $this->factory->security->xss_clean($value)
: $value;
}
@ -801,7 +811,7 @@ class Input {
}
return ($xss_clean === TRUE)
? Security::xss_clean($headers[$index])
? $this->factory->security->xss_clean($headers[$index])
: $headers[$index];
}

View File

@ -46,7 +46,7 @@ class Security {
*
* @var array
*/
public static $filename_bad_chars = array(
public $filename_bad_chars = array(
'../', '<!--', '-->', '<', '>',
"'", '"', '&', '$', '#',
'{', '}', '[', ']', '=',
@ -72,7 +72,7 @@ class Security {
*
* @var string
*/
public static $charset = 'UTF-8';
public $charset = 'UTF-8';
/**
* XSS Hash
@ -81,7 +81,7 @@ class Security {
*
* @var string
*/
protected static $_xss_hash;
protected $_xss_hash;
/**
* CSRF Hash
@ -90,7 +90,7 @@ class Security {
*
* @var string
*/
protected static $_csrf_hash;
protected $_csrf_hash;
/**
* CSRF Expire time
@ -100,7 +100,7 @@ class Security {
*
* @var int
*/
protected static $_csrf_expire = 7200;
protected $_csrf_expire = 7200;
/**
* CSRF Token name
@ -109,7 +109,7 @@ class Security {
*
* @var string
*/
protected static $_csrf_token_name = 'ci_csrf_token';
protected $_csrf_token_name = 'fw_csrf_token';
/**
* CSRF Cookie name
@ -118,14 +118,14 @@ class Security {
*
* @var string
*/
protected static $_csrf_cookie_name = 'ci_csrf_token';
protected $_csrf_cookie_name = 'fw_csrf_token';
/**
* List of never allowed strings
*
* @var array
*/
protected static $_never_allowed_str = array(
protected $_never_allowed_str = array(
'document.cookie' => '[removed]',
'document.write' => '[removed]',
'.parentNode' => '[removed]',
@ -142,7 +142,7 @@ class Security {
*
* @var array
*/
protected static $_never_allowed_regex = array(
protected $_never_allowed_regex = array(
'javascript\s*:',
'(document|(document\.)?window)\.(location|on\w*)',
'expression\s*(\(|&\#40;)', // CSS and IE
@ -159,7 +159,7 @@ class Security {
*
* @var ConfigORM
*/
private static $config;
private $config;
/**
* Class constructor
@ -168,31 +168,31 @@ class Security {
*/
public function __construct()
{
self::$config = Config::get('security');
$this->config = Config::get('security');
// Is CSRF protection enabled?
if (self::$config->csrf_protection)
if ($this->config->csrf_protection)
{
// CSRF config
foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key)
{
if (NULL !== ($val = self::$config->$key))
if (NULL !== ($val = $this->config->$key))
{
self::${'_'.$key} = $val;
$this->{'_'.$key} = $val;
}
}
// Append application specific cookie prefix
if ($cookie_prefix = Config::get('main')->cookie_prefix)
{
self::$_csrf_cookie_name = $cookie_prefix.self::$_csrf_cookie_name;
$this->_csrf_cookie_name = $cookie_prefix.$this->_csrf_cookie_name;
}
// Set the CSRF hash
self::_csrf_set_hash();
$this->_csrf_set_hash();
}
self::$charset = strtoupper(Config::get('main')->charset);
$this->charset = strtoupper(Config::get('main')->charset);
}
// --------------------------------------------------------------------
@ -200,51 +200,51 @@ class Security {
/**
* CSRF Verify
*
* @return CI_Security
* @return Security
*/
public static function csrf_verify()
public function csrf_verify()
{
// If it's not a POST request we will set the CSRF cookie
if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
{
return self::csrf_set_cookie();
return $this->csrf_set_cookie();
}
// Check if URI has been whitelisted from CSRF checks
if ($exclude_uris = self::$config->csrf_exclude_uris)
if ($exclude_uris = $this->config->csrf_exclude_uris)
{
foreach ($exclude_uris as $excluded)
{
if (preg_match('#^'.$excluded.'$#i'.(UTF8_ENABLED ? 'u' : ''), URI::uri_string()))
{
return true;
return $this;
}
}
}
// Do the tokens exist in both the _POST and _COOKIE arrays?
if ( ! isset($_POST[self::$_csrf_token_name], $_COOKIE[self::$_csrf_cookie_name])
OR $_POST[self::$_csrf_token_name] !== $_COOKIE[self::$_csrf_cookie_name]) // Do the tokens match?
if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])
OR $_POST[$this->_csrf_token_name] !== $_COOKIE[$this->_csrf_cookie_name]) // Do the tokens match?
{
self::csrf_show_error();
$this->csrf_show_error();
}
// We kill this since we're done and we don't want to polute the _POST array
unset($_POST[self::$_csrf_token_name]);
unset($_POST[$this->_csrf_token_name]);
// Regenerate on every submission?
if (self::$config->csrf_regenerate)
if ($this->config->csrf_regenerate)
{
// Nothing should last forever
unset($_COOKIE[self::$_csrf_cookie_name]);
self::$_csrf_hash = NULL;
unset($_COOKIE[$this->_csrf_cookie_name]);
$this->_csrf_hash = NULL;
}
self::_csrf_set_hash();
self::csrf_set_cookie();
$this->_csrf_set_hash();
$this->csrf_set_cookie();
Logger::log('CSRF token verified');
return true;
return $this;
}
// --------------------------------------------------------------------
@ -253,22 +253,22 @@ class Security {
* CSRF Set Cookie
*
* @codeCoverageIgnore
* @return CI_Security
* @return Security
*/
public static function csrf_set_cookie()
public function csrf_set_cookie()
{
$expire = time() + self::$_csrf_expire;
$expire = time() + $this->_csrf_expire;
$cfg = Config::get('main');
$secure_cookie = (bool) $cfg->cookie_secure;
if ($secure_cookie && ! is_https())
{
return FALSE;
return $this;
}
setcookie(
self::$_csrf_cookie_name,
self::$_csrf_hash,
$this->_csrf_cookie_name,
$this->_csrf_hash,
$expire,
$cfg->cookie_path,
$cfg->cookie_domain,
@ -276,8 +276,7 @@ class Security {
$cfg->cookie_httponly
);
Logger::log('CSRF cookie sent');
return true;
return $this;
}
// --------------------------------------------------------------------
@ -287,7 +286,7 @@ class Security {
*
* @return void
*/
public static function csrf_show_error()
public function csrf_show_error()
{
throw new SecurityException('The action you have requested is not allowed.', 1);
}
@ -297,12 +296,12 @@ class Security {
/**
* Get CSRF Hash
*
* @see CI_Security::$_csrf_hash
* @see Security::$_csrf_hash
* @return string CSRF hash
*/
public static function get_csrf_hash()
public function get_csrf_hash()
{
return self::$_csrf_hash;
return $this->_csrf_hash;
}
// --------------------------------------------------------------------
@ -310,12 +309,12 @@ class Security {
/**
* Get CSRF Token Name
*
* @see CI_Security::$_csrf_token_name
* @see Security::$_csrf_token_name
* @return string CSRF token name
*/
public static function get_csrf_token_name()
public function get_csrf_token_name()
{
return self::$_csrf_token_name;
return $this->_csrf_token_name;
}
// --------------------------------------------------------------------
@ -346,14 +345,14 @@ class Security {
* @param bool $is_image Whether the input is an image
* @return string
*/
public static function xss_clean($str, $is_image = FALSE)
public function xss_clean($str, $is_image = FALSE)
{
// Is the string an array?
if (is_array($str))
{
while (list($key) = each($str))
{
$str[$key] = self::xss_clean($str[$key]);
$str[$key] = $this->xss_clean($str[$key]);
}
return $str;
@ -404,7 +403,7 @@ class Security {
$converted_string = $str;
// Remove Strings that are never allowed
$str = self::_do_never_allowed($str);
$str = $this->_do_never_allowed($str);
/*
* Makes PHP tags safe
@ -536,7 +535,7 @@ class Security {
// Final clean up
// This adds a bit of extra precaution in case
// something got through the above filters
$str = self::_do_never_allowed($str);
$str = $this->_do_never_allowed($str);
/*
* Images are Handled in a Special Way
@ -562,20 +561,20 @@ class Security {
*
* Generates the XSS hash if needed and returns it.
*
* @see CI_Security::$_xss_hash
* @see Security::$_xss_hash
* @return string XSS hash
*/
public static function xss_hash()
public function xss_hash()
{
if (self::$_xss_hash === NULL)
if ($this->_xss_hash === NULL)
{
$rand = self::get_random_bytes(16);
self::$_xss_hash = ($rand === FALSE)
$rand = $this->get_random_bytes(16);
$this->_xss_hash = ($rand === FALSE)
? md5(uniqid(mt_rand(), TRUE))
: bin2hex($rand);
}
return self::$_xss_hash;
return $this->_xss_hash;
}
// --------------------------------------------------------------------
@ -586,7 +585,7 @@ class Security {
* @param int $length Output length
* @return string
*/
public static function get_random_bytes($length)
public function get_random_bytes($length)
{
if (empty($length) OR ! ctype_digit((string) $length))
{
@ -655,7 +654,7 @@ class Security {
* @param string $charset Character set
* @return string
*/
public static function entity_decode($str, $charset = NULL)
public function entity_decode($str, $charset = NULL)
{
if (strpos($str, '&') === FALSE)
{
@ -664,7 +663,7 @@ class Security {
static $_entities;
isset($charset) OR $charset = self::$charset;
isset($charset) OR $charset = $this->charset;
$flag = Core::isPHP('5.4')
? ENT_COMPAT | ENT_HTML5
: ENT_COMPAT;
@ -730,9 +729,9 @@ class Security {
* @param bool $relative_path Whether to preserve paths
* @return string
*/
public static function sanitize_filename($str, $relative_path = FALSE)
public function sanitize_filename($str, $relative_path = FALSE)
{
$bad = self::$filename_bad_chars;
$bad = $this->filename_bad_chars;
if ( ! $relative_path)
{
@ -760,7 +759,7 @@ class Security {
* @param string $str
* @return string
*/
public static function strip_image_tags($str)
public function strip_image_tags($str)
{
return preg_replace(
array(
@ -780,11 +779,11 @@ class Security {
* Callback method for xss_clean() to remove whitespace from
* things like 'j a v a s c r i p t'.
*
* @used-by CI_Security::xss_clean()
* @used-by Security::xss_clean()
* @param array $matches
* @return string
*/
protected static function _compact_exploded_words($matches)
protected function _compact_exploded_words($matches)
{
return preg_replace('/\s+/s', '', $matches[1]).$matches[2];
}
@ -796,11 +795,11 @@ class Security {
*
* Callback method for xss_clean() to remove naughty HTML elements.
*
* @used-by CI_Security::xss_clean()
* @used-by Security::xss_clean()
* @param array $matches
* @return string
*/
protected static function _sanitize_naughty_html($matches)
protected function _sanitize_naughty_html($matches)
{
static $naughty_tags = array(
'alert', 'prompt', 'confirm', 'applet', 'audio', 'basefont', 'base', 'behavior', 'bgsound',
@ -891,18 +890,18 @@ class Security {
* and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
* PHP 5.2+ on link-heavy strings.
*
* @used-by CI_Security::xss_clean()
* @used-by Security::xss_clean()
* @param array $match
* @return string
*/
protected static function _js_link_removal($match)
protected function _js_link_removal($match)
{
return str_replace(
$match[1],
preg_replace(
'#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si',
'',
self::_filter_attributes($match[1])
$this->_filter_attributes($match[1])
),
$match[0]
);
@ -919,18 +918,18 @@ class Security {
* and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
* PHP 5.2+ on image tag heavy strings.
*
* @used-by CI_Security::xss_clean()
* @used-by Security::xss_clean()
* @param array $match
* @return string
*/
protected static function _js_img_removal($match)
protected function _js_img_removal($match)
{
return str_replace(
$match[1],
preg_replace(
'#src=.*?(?:(?:alert|prompt|confirm|eval)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si',
'',
self::_filter_attributes($match[1])
$this->_filter_attributes($match[1])
),
$match[0]
);
@ -941,11 +940,11 @@ class Security {
/**
* Attribute Conversion
*
* @used-by CI_Security::xss_clean()
* @used-by Security::xss_clean()
* @param array $match
* @return string
*/
protected static function _convert_attribute($match)
protected function _convert_attribute($match)
{
return str_replace(array('>', '<', '\\'), array('&gt;', '&lt;', '\\\\'), $match[0]);
}
@ -957,12 +956,12 @@ class Security {
*
* Filters tag attributes for consistency and safety.
*
* @used-by CI_Security::_js_img_removal()
* @used-by CI_Security::_js_link_removal()
* @used-by Security::_js_img_removal()
* @used-by Security::_js_link_removal()
* @param string $str
* @return string
*/
protected static function _filter_attributes($str)
protected function _filter_attributes($str)
{
$out = '';
if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches))
@ -981,21 +980,21 @@ class Security {
/**
* HTML Entity Decode Callback
*
* @used-by CI_Security::xss_clean()
* @used-by Security::xss_clean()
* @param array $match
* @return string
*/
protected static function _decode_entity($match)
protected function _decode_entity($match)
{
// Protect GET variables in URLs
// 901119URL5918AMP18930PROTECT8198
$match = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-/]+)|i', self::xss_hash().'\\1=\\2', $match[0]);
$match = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-/]+)|i', $this->xss_hash().'\\1=\\2', $match[0]);
// Decode, then un-protect URL GET vars
return str_replace(
self::xss_hash(),
$this->xss_hash(),
'&',
self::entity_decode($match, self::$charset)
$this->entity_decode($match, $this->charset)
);
}
@ -1004,15 +1003,15 @@ class Security {
/**
* Do Never Allowed
*
* @used-by CI_Security::xss_clean()
* @used-by Security::xss_clean()
* @param string
* @return string
*/
protected static function _do_never_allowed($str)
protected function _do_never_allowed($str)
{
$str = str_replace(array_keys(self::$_never_allowed_str), self::$_never_allowed_str, $str);
$str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str);
foreach (self::$_never_allowed_regex as $regex)
foreach ($this->_never_allowed_regex as $regex)
{
$str = preg_replace('#'.$regex.'#is', '[removed]', $str);
}
@ -1027,27 +1026,27 @@ class Security {
*
* @return string
*/
protected static function _csrf_set_hash()
protected function _csrf_set_hash()
{
if (self::$_csrf_hash === NULL)
if ($this->_csrf_hash === NULL)
{
// If the cookie exists we will use its value.
// We don't necessarily want to regenerate it with
// each page load since a page could contain embedded
// sub-pages causing this feature to fail
if (isset($_COOKIE[self::$_csrf_cookie_name]) && is_string($_COOKIE[self::$_csrf_cookie_name])
&& preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[self::$_csrf_cookie_name]) === 1)
if (isset($_COOKIE[$this->_csrf_cookie_name]) && is_string($_COOKIE[$this->_csrf_cookie_name])
&& preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1)
{
return self::$_csrf_hash = $_COOKIE[self::$_csrf_cookie_name];
return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name];
}
$rand = self::get_random_bytes(16);
self::$_csrf_hash = ($rand === FALSE)
$rand = $this->get_random_bytes(16);
$this->_csrf_hash = ($rand === FALSE)
? md5(uniqid(mt_rand(), TRUE))
: bin2hex($rand);
}
return self::$_csrf_hash;
return $this->_csrf_hash;
}
}

View File

@ -55,6 +55,9 @@ restore_exception_handler();
ini_set('display_errors', 1);
error_reporting(E_ALL | E_STRICT);
// Set localhost "remote" IP
isset($_SERVER['REMOTE_ADDR']) OR $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
// Load the vfsStream class either through PEAR installed library or through composer
if ( ! class_exists('vfsStream') && file_exists('vendor/autoload.php'))
{
@ -64,4 +67,7 @@ if ( ! class_exists('vfsStream') && file_exists('vendor/autoload.php'))
class_alias('org\bovigo\vfs\vfsStreamWrapper', 'vfsStreamWrapper');
}
Logger::setLoggerTemplate('logger_cli');
Logger::setLoggerTemplate('logger_cli');
require_once('mocks/autoloader.php');
spl_autoload_register('autoload');

297
tests/core_inputTest.php Normal file
View File

@ -0,0 +1,297 @@
<?php
/**
* FuzeWorks.
*
* The FuzeWorks MVC PHP FrameWork
*
* Copyright (C) 2015 TechFuze
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
* @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/)
* @license http://opensource.org/licenses/GPL-3.0 GPLv3 License
*
* @link http://fuzeworks.techfuze.net
* @since Version 0.0.1
*
* @version Version 0.0.1
*/
use FuzeWorks\Factory;
/**
* Class InputTest.
*
* Core testing suite, will test basic input class functionality
*/
class inputTest extends CoreTestAbstract
{
protected $factory;
public function setUp()
{
// Load the factory first
$this->factory = Factory::getInstance();
// Set server variable to GET as default, since this will leave unset in STDIN env
$_SERVER['REQUEST_METHOD'] = 'GET';
// Set config for Input class
$this->factory->config->getConfig('routing')->allow_get_array = TRUE;
$this->factory->config->getConfig('security')->global_xss_filtering = FALSE;
$this->factory->config->getConfig('security')->csrf_protection = FALSE;
$security = new Mock_Core_Security();
$this->factory->config->getConfig('main')->charset = 'UTF-8';
$utf8 = new Mock_Core_Utf8();
$this->input = new Mock_Core_Input($security, $utf8);
}
// --------------------------------------------------------------------
public function test_get_not_exists()
{
$this->assertTrue($this->input->get() === array());
$this->assertTrue($this->input->get('foo') === NULL);
}
// --------------------------------------------------------------------
public function test_get_exist()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_GET['foo'] = 'bar';
$this->assertArrayHasKey('foo', $this->input->get());
$this->assertEquals('bar', $this->input->get('foo'));
}
// --------------------------------------------------------------------
public function test_get_exist_with_xss_clean()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_GET['harm'] = "Hello, i try to <script>alert('Hack');</script> your site";
$this->assertArrayHasKey('harm', $this->input->get());
$this->assertEquals("Hello, i try to <script>alert('Hack');</script> your site", $this->input->get('harm'));
$this->assertEquals("Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site", $this->input->get('harm', TRUE));
}
// --------------------------------------------------------------------
public function test_post_not_exists()
{
$this->assertTrue($this->input->post() === array());
$this->assertTrue($this->input->post('foo') === NULL);
}
// --------------------------------------------------------------------
public function test_post_exist()
{
$_SERVER['REQUEST_METHOD'] = 'POST';
$_POST['foo'] = 'bar';
$this->assertArrayHasKey('foo', $this->input->post());
$this->assertEquals('bar', $this->input->post('foo'));
}
// --------------------------------------------------------------------
public function test_post_exist_with_xss_clean()
{
$_SERVER['REQUEST_METHOD'] = 'POST';
$_POST['harm'] = "Hello, i try to <script>alert('Hack');</script> your site";
$this->assertArrayHasKey('harm', $this->input->post());
$this->assertEquals("Hello, i try to <script>alert('Hack');</script> your site", $this->input->post('harm'));
$this->assertEquals("Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site", $this->input->post('harm', TRUE));
}
// --------------------------------------------------------------------
public function test_post_get()
{
$_SERVER['REQUEST_METHOD'] = 'POST';
$_POST['foo'] = 'bar';
$this->assertEquals('bar', $this->input->post_get('foo'));
}
// --------------------------------------------------------------------
public function test_get_post()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_GET['foo'] = 'bar';
$this->assertEquals('bar', $this->input->get_post('foo'));
}
// --------------------------------------------------------------------
public function test_cookie()
{
$_COOKIE['foo'] = 'bar';
$this->assertEquals('bar', $this->input->cookie('foo'));
$this->assertNull($this->input->cookie('bar'));
}
// --------------------------------------------------------------------
public function test_server()
{
$this->assertEquals('GET', $this->input->server('REQUEST_METHOD'));
}
// --------------------------------------------------------------------
public function test_fetch_from_array()
{
$data = array(
'foo' => 'bar',
'harm' => 'Hello, i try to <script>alert(\'Hack\');</script> your site',
);
$foo = $this->input->fetch_from_array($data, 'foo');
$harm = $this->input->fetch_from_array($data, 'harm');
$harmless = $this->input->fetch_from_array($data, 'harm', TRUE);
$this->assertEquals('bar', $foo);
$this->assertEquals("Hello, i try to <script>alert('Hack');</script> your site", $harm);
$this->assertEquals("Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site", $harmless);
$_SERVER['REQUEST_METHOD'] = 'POST';
$_POST['foo']['bar'] = 'baz';
$barArray = array('bar' => 'baz');
$this->assertEquals('baz', $this->input->post('foo[bar]'));
$this->assertEquals($barArray, $this->input->post('foo[]'));
$this->assertNull($this->input->post('foo[baz]'));
}
// --------------------------------------------------------------------
public function test_valid_ip()
{
$this->assertTrue($this->input->valid_ip('192.18.0.1'));
$this->assertTrue($this->input->valid_ip('192.18.0.1', 'ipv4'));
$this->assertFalse($this->input->valid_ip('555.0.0.0'));
$this->assertFalse($this->input->valid_ip('2001:db8:0:85a3::ac1f:8001', 'ipv4'));
// v6 tests
$this->assertFalse($this->input->valid_ip('192.18.0.1', 'ipv6'));
$ip_v6 = array(
'2001:0db8:0000:85a3:0000:0000:ac1f:8001',
'2001:db8:0:85a3:0:0:ac1f:8001',
'2001:db8:0:85a3::ac1f:8001'
);
foreach ($ip_v6 as $ip)
{
$this->assertTrue($this->input->valid_ip($ip));
$this->assertTrue($this->input->valid_ip($ip, 'ipv6'));
}
}
// --------------------------------------------------------------------
public function test_method()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$this->assertEquals('get', $this->input->method());
$this->assertEquals('GET', $this->input->method(TRUE));
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->assertEquals('post', $this->input->method());
$this->assertEquals('POST', $this->input->method(TRUE));
}
// --------------------------------------------------------------------
public function test_is_ajax_request()
{
$this->assertFalse($this->input->is_ajax_request());
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'test';
$this->assertFalse($this->input->is_ajax_request());
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
$this->assertTrue($this->input->is_ajax_request());
}
// --------------------------------------------------------------------
public function test_ip_address()
{
$this->input->ip_address = '127.0.0.1';
$this->assertEquals('127.0.0.1', $this->input->ip_address());
// 127.0.0.1 is set in our Bootstrap file
$this->input->ip_address = FALSE;
$this->assertEquals('127.0.0.1', $this->input->ip_address());
// Invalid
$_SERVER['REMOTE_ADDR'] = 'invalid_ip_address';
$this->input->ip_address = FALSE; // reset cached value
$this->assertEquals('0.0.0.0', $this->input->ip_address());
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
// Proxy_ips tests
$this->input->ip_address = FALSE;
$this->factory->config->getConfig('security')->proxy_ips = array('127.0.0.3', '127.0.0.4', '127.0.0.2');
$_SERVER['HTTP_CLIENT_IP'] = '127.0.0.2';
$this->assertEquals('127.0.0.1', $this->input->ip_address());
// Invalid spoof
$this->input->ip_address = FALSE;
$this->factory->config->getConfig('security')->proxy_ips = 'invalid_ip_address';
$_SERVER['HTTP_CLIENT_IP'] = 'invalid_ip_address';
$this->assertEquals('127.0.0.1', $this->input->ip_address());
$this->input->ip_address = FALSE;
$this->factory->config->getConfig('security')->proxy_ips = array('http://foo/bar/baz', '127.0.0.1/1');
$_SERVER['HTTP_CLIENT_IP'] = '127.0.0.1';
$this->assertEquals('127.0.0.1', $this->input->ip_address());
$this->input->ip_address = FALSE;
$this->factory->config->getConfig('security')->proxy_ips = array('http://foo/bar/baz', '127.0.0.2');
$_SERVER['HTTP_CLIENT_IP'] = '127.0.0.2';
$_SERVER['REMOTE_ADDR'] = '127.0.0.2';
$this->assertEquals('127.0.0.2', $this->input->ip_address());
//IPv6
$this->input->ip_address = FALSE;
$this->factory->config->getConfig('security')->proxy_ips = array('FE80:0000:0000:0000:0202:B3FF:FE1E:8329/1', 'FE80:0000:0000:0000:0202:B3FF:FE1E:8300/2');
$_SERVER['HTTP_CLIENT_IP'] = 'FE80:0000:0000:0000:0202:B3FF:FE1E:8300';
$_SERVER['REMOTE_ADDR'] = 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329';
$this->assertEquals('FE80:0000:0000:0000:0202:B3FF:FE1E:8300', $this->input->ip_address());
$this->input->ip_address = FALSE;
$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; // back to reality
}
// --------------------------------------------------------------------
public function test_user_agent()
{
$_SERVER['HTTP_USER_AGENT'] = 'test';
$this->assertEquals('test', $this->input->user_agent());
}
}

385
tests/core_securityTest.php Normal file
View File

@ -0,0 +1,385 @@
<?php
/**
* FuzeWorks.
*
* The FuzeWorks MVC PHP FrameWork
*
* Copyright (C) 2015 TechFuze
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
* @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/)
* @license http://opensource.org/licenses/GPL-3.0 GPLv3 License
*
* @link http://fuzeworks.techfuze.net
* @since Version 0.0.1
*
* @version Version 0.0.1
*/
use FuzeWorks\Factory;
/**
* Class SecurityTest.
*
* Core testing suite, will test security class functionality
*/
class securityTest extends CoreTestAbstract
{
public function setUp()
{
// Set cookie for security test
$_COOKIE['fw_csrf_cookie'] = md5(uniqid(mt_rand(), TRUE));
// Set config for Security class
$config = Factory::getInstance()->config->getConfig('security');
$config->csrf_protection = true;
$config->csrf_token_name = 'fw_csrf_token';
$config->csrf_cookie_name = 'fw_csrf_cookie';
$this->security = new Mock_Core_Security();
}
// --------------------------------------------------------------------
public function test_csrf_verify()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$this->assertInstanceOf('FuzeWorks\Security', $this->security->csrf_verify());
}
// --------------------------------------------------------------------
/**
* @expectedException FuzeWorks\SecurityException
*/
public function test_csrf_verify_invalid()
{
// Without issuing $_POST[csrf_token_name], this request will triggering CSRF error
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->security->csrf_verify();
}
// --------------------------------------------------------------------
public function test_csrf_verify_valid()
{
$_SERVER['REQUEST_METHOD'] = 'POST';
$_POST[$this->security->csrf_token_name] = $this->security->csrf_hash;
$this->assertInstanceOf('FuzeWorks\Security', $this->security->csrf_verify());
}
// --------------------------------------------------------------------
public function test_get_csrf_hash()
{
$this->assertEquals($this->security->csrf_hash, $this->security->get_csrf_hash());
}
// --------------------------------------------------------------------
public function test_get_csrf_token_name()
{
$this->assertEquals('fw_csrf_token', $this->security->get_csrf_token_name());
}
// --------------------------------------------------------------------
public function test_xss_clean()
{
$harm_string = "Hello, i try to <script>alert('Hack');</script> your site";
$harmless_string = $this->security->xss_clean($harm_string);
$this->assertEquals("Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site", $harmless_string);
}
// --------------------------------------------------------------------
public function test_xss_clean_string_array()
{
$harm_strings = array(
"Hello, i try to <script>alert('Hack');</script> your site",
"Simple clean string",
"Hello, i try to <script>alert('Hack');</script> your site"
);
$harmless_strings = $this->security->xss_clean($harm_strings);
$this->assertEquals("Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site", $harmless_strings[0]);
$this->assertEquals("Simple clean string", $harmless_strings[1]);
$this->assertEquals("Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site", $harmless_strings[2]);
}
// --------------------------------------------------------------------
public function test_xss_clean_image_valid()
{
$harm_string = '<img src="test.png">';
$xss_clean_return = $this->security->xss_clean($harm_string, TRUE);
// $this->assertTrue($xss_clean_return);
}
// --------------------------------------------------------------------
public function test_xss_clean_image_invalid()
{
$harm_string = '<img src=javascript:alert(String.fromCharCode(88,83,83))>';
$xss_clean_return = $this->security->xss_clean($harm_string, TRUE);
$this->assertFalse($xss_clean_return);
}
// --------------------------------------------------------------------
public function test_xss_clean_entity_double_encoded()
{
$input = '<a href="&#38&#35&#49&#48&#54&#38&#35&#57&#55&#38&#35&#49&#49&#56&#38&#35&#57&#55&#38&#35&#49&#49&#53&#38&#35&#57&#57&#38&#35&#49&#49&#52&#38&#35&#49&#48&#53&#38&#35&#49&#49&#50&#38&#35&#49&#49&#54&#38&#35&#53&#56&#38&#35&#57&#57&#38&#35&#49&#49&#49&#38&#35&#49&#49&#48&#38&#35&#49&#48&#50&#38&#35&#49&#48&#53&#38&#35&#49&#49&#52&#38&#35&#49&#48&#57&#38&#35&#52&#48&#38&#35&#52&#57&#38&#35&#52&#49">Clickhere</a>';
$this->assertEquals('<a>Clickhere</a>', $this->security->xss_clean($input));
}
// --------------------------------------------------------------------
public function text_xss_clean_js_link_removal()
{
// This one is to prevent a false positive
$this->assertEquals(
"<a href=\"javascrip\n<t\n:alert\n&#40;1&#41;\"\n>",
$this->security->xss_clean("<a href=\"javascrip\n<t\n:alert\n(1)\"\n>")
);
}
// --------------------------------------------------------------------
public function test_xss_clean_js_img_removal()
{
$input = '<img src="&#38&#35&#49&#48&#54&#38&#35&#57&#55&#38&#35&#49&#49&#56&#38&#35&#57&#55&#38&#35&#49&#49&#53&#38&#35&#57&#57&#38&#35&#49&#49&#52&#38&#35&#49&#48&#53&#38&#35&#49&#49&#50&#38&#35&#49&#49&#54&#38&#35&#53&#56&#38&#35&#57&#57&#38&#35&#49&#49&#49&#38&#35&#49&#49&#48&#38&#35&#49&#48&#50&#38&#35&#49&#48&#53&#38&#35&#49&#49&#52&#38&#35&#49&#48&#57&#38&#35&#52&#48&#38&#35&#52&#57&#38&#35&#52&#49">Clickhere';
$this->assertEquals('<img>', $this->security->xss_clean($input));
}
// --------------------------------------------------------------------
public function test_xss_clean_sanitize_naughty_html_tags()
{
$this->assertEquals('&lt;unclosedTag', $this->security->xss_clean('<unclosedTag'));
$this->assertEquals('&lt;blink&gt;', $this->security->xss_clean('<blink>'));
$this->assertEquals('<fubar>', $this->security->xss_clean('<fubar>'));
$this->assertEquals(
'<img svg=""> src="x">',
$this->security->xss_clean('<img <svg=""> src="x">')
);
$this->assertEquals(
'<img src="b on=">on=">"x onerror="alert&#40;1&#41;">',
$this->security->xss_clean('<img src="b on="<x">on=">"x onerror="alert(1)">')
);
}
// --------------------------------------------------------------------
public function test_xss_clean_sanitize_naughty_html_attributes()
{
$this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo onAttribute="bar">'));
$this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo onAttributeNoQuotes=bar>'));
$this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo onAttributeWithSpaces = bar>'));
$this->assertEquals('<foo prefixOnAttribute="bar">', $this->security->xss_clean('<foo prefixOnAttribute="bar">'));
$this->assertEquals('<foo>onOutsideOfTag=test</foo>', $this->security->xss_clean('<foo>onOutsideOfTag=test</foo>'));
$this->assertEquals('onNoTagAtAll = true', $this->security->xss_clean('onNoTagAtAll = true'));
$this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo fscommand=case-insensitive>'));
$this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo seekSegmentTime=whatever>'));
$this->assertEquals(
'<foo bar=">" baz=\'>\' xss=removed>',
$this->security->xss_clean('<foo bar=">" baz=\'>\' onAfterGreaterThan="quotes">')
);
$this->assertEquals(
'<foo bar=">" baz=\'>\' xss=removed>',
$this->security->xss_clean('<foo bar=">" baz=\'>\' onAfterGreaterThan=noQuotes>')
);
$this->assertEquals(
'<img src="x" on=""> on=&lt;svg&gt; onerror=alert&#40;1&#41;>',
$this->security->xss_clean('<img src="x" on=""> on=<svg> onerror=alert(1)>')
);
$this->assertEquals(
'<img src="on=\'">"&lt;svg&gt; onerror=alert&#40;1&#41; onmouseover=alert&#40;1&#41;>',
$this->security->xss_clean('<img src="on=\'">"<svg> onerror=alert(1) onmouseover=alert(1)>')
);
$this->assertEquals(
'<img src="x"> on=\'x\' onerror=``,alert&#40;1&#41;>',
$this->security->xss_clean('<img src="x"> on=\'x\' onerror=``,alert(1)>')
);
$this->assertEquals(
'<a xss=removed>',
$this->security->xss_clean('<a< onmouseover="alert(1)">')
);
$this->assertEquals(
'<img src="x"> on=\'x\' onerror=,xssm()>',
$this->security->xss_clean('<img src="x"> on=\'x\' onerror=,xssm()>')
);
$this->assertEquals(
'<image src="<>" xss=removed>',
$this->security->xss_clean('<image src="<>" onerror=\'alert(1)\'>')
);
$this->assertEquals(
'<b xss=removed>',
$this->security->xss_clean('<b "=<= onmouseover=alert(1)>')
);
$this->assertEquals(
'<b xss=removed xss=removed>1">',
$this->security->xss_clean('<b a=<=" onmouseover="alert(1),1>1">')
);
$this->assertEquals(
'<b x=" onmouseover=alert&#40;1&#41;//">',
$this->security->xss_clean('<b "="< x=" onmouseover=alert(1)//">')
);
}
// --------------------------------------------------------------------
/**
* @depends test_xss_clean_sanitize_naughty_html_tags
* @depends test_xss_clean_sanitize_naughty_html_attributes
*/
public function test_naughty_html_plus_evil_attributes()
{
$this->assertEquals(
'&lt;svg<img src="x" xss=removed>',
$this->security->xss_clean('<svg<img > src="x" onerror="location=/javascript/.source+/:alert/.source+/(1)/.source">')
);
}
// --------------------------------------------------------------------
public function test_xss_hash()
{
$this->assertEmpty($this->security->xss_hash);
// Perform hash
$this->security->xss_hash();
$this->assertTrue(preg_match('#^[0-9a-f]{32}$#iS', $this->security->xss_hash) === 1);
}
// --------------------------------------------------------------------
public function test_get_random_bytes()
{
$length = "invalid";
$this->assertFalse($this->security->get_random_bytes($length));
$length = 10;
$this->assertNotEmpty($this->security->get_random_bytes($length));
}
// --------------------------------------------------------------------
public function test_entity_decode()
{
$encoded = '&lt;div&gt;Hello &lt;b&gt;Booya&lt;/b&gt;&lt;/div&gt;';
$decoded = $this->security->entity_decode($encoded);
$this->assertEquals('<div>Hello <b>Booya</b></div>', $decoded);
// Issue #3057 (https://github.com/bcit-ci/CodeIgniter/issues/3057)
$this->assertEquals(
'&foo should not include a semicolon',
$this->security->entity_decode('&foo should not include a semicolon')
);
}
// --------------------------------------------------------------------
public function test_sanitize_filename()
{
$filename = './<!--foo-->';
$safe_filename = $this->security->sanitize_filename($filename);
$this->assertEquals('foo', $safe_filename);
}
// --------------------------------------------------------------------
public function test_strip_image_tags()
{
$imgtags = array(
'<img src="smiley.gif" alt="Smiley face" height="42" width="42">',
'<img alt="Smiley face" height="42" width="42" src="smiley.gif">',
'<img src="http://www.w3schools.com/images/w3schools_green.jpg">',
'<img src="/img/sunset.gif" height="100%" width="100%">',
'<img src="mdn-logo-sm.png" alt="MD Logo" srcset="mdn-logo-HD.png 2x, mdn-logo-small.png 15w, mdn-banner-HD.png 100w 2x" />',
'<img sqrc="/img/sunset.gif" height="100%" width="100%">',
'<img srqc="/img/sunset.gif" height="100%" width="100%">',
'<img srcq="/img/sunset.gif" height="100%" width="100%">',
'<img src=non-quoted.attribute foo="bar">'
);
$urls = array(
'smiley.gif',
'smiley.gif',
'http://www.w3schools.com/images/w3schools_green.jpg',
'/img/sunset.gif',
'mdn-logo-sm.png',
'<img sqrc="/img/sunset.gif" height="100%" width="100%">',
'<img srqc="/img/sunset.gif" height="100%" width="100%">',
'<img srcq="/img/sunset.gif" height="100%" width="100%">',
'non-quoted.attribute'
);
for ($i = 0; $i < count($imgtags); $i++)
{
$this->assertEquals($urls[$i], $this->security->strip_image_tags($imgtags[$i]));
}
}
// --------------------------------------------------------------------
public function test_csrf_set_hash()
{
// Set cookie for security test
$_COOKIE['fw_csrf_cookie'] = md5(uniqid(mt_rand(), TRUE));
// Set config for Security class
$config = Factory::getInstance()->config->getConfig('security');
$config->csrf_protection = true;
$config->csrf_token_name = 'fw_csrf_token';
// leave csrf_cookie_name as blank to test _csrf_set_hash function
$config->csrf_cookie_name = '';
$this->security = new Mock_Core_Security();
$this->assertNotEmpty($this->security->get_csrf_hash());
}
}

132
tests/core_utf8Test.php Normal file
View File

@ -0,0 +1,132 @@
<?php
/**
* FuzeWorks.
*
* The FuzeWorks MVC PHP FrameWork
*
* Copyright (C) 2015 TechFuze
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
* @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/)
* @license http://opensource.org/licenses/GPL-3.0 GPLv3 License
*
* @link http://fuzeworks.techfuze.net
* @since Version 0.0.1
*
* @version Version 0.0.1
*/
use FuzeWorks\Factory;
/**
* Class Utf8Test.
*
* Core testing suite, will test UTF8 class functionality
*/
class utf8Test extends CoreTestAbstract
{
protected $factory;
public function setUp()
{
$this->factory = Factory::getInstance();
$this->factory->config->getConfig('main')->charset = 'UTF-8';
$this->utf8 = new Mock_Core_Utf8();
}
// --------------------------------------------------------------------
/**
* __construct() test
*
* @covers Utf8::__construct
*/
public function test___construct()
{
if (defined('PREG_BAD_UTF8_ERROR') && (ICONV_ENABLED === TRUE OR MB_ENABLED === TRUE) && strtoupper($this->factory->config->getConfig('main')->charset) === 'UTF-8')
{
$this->assertTrue(UTF8_ENABLED);
}
else
{
$this->assertFalse(UTF8_ENABLED);
}
}
// --------------------------------------------------------------------
/**
* is_ascii() test
*
* Note: DO NOT move this below test_clean_string()
*/
public function test_is_ascii()
{
$this->assertTrue($this->utf8->is_ascii('foo bar'));
$this->assertFalse($this->utf8->is_ascii('тест'));
}
// --------------------------------------------------------------------
/**
* clean_string() test
*
* @depends test_is_ascii
* @covers Utf8::clean_string
*/
public function test_clean_string()
{
$this->assertEquals('foo bar', $this->utf8->clean_string('foo bar'));
$illegal_utf8 = "\xc0тест";
if (MB_ENABLED)
{
$this->assertEquals('тест', $this->utf8->clean_string($illegal_utf8));
}
elseif (ICONV_ENABLED)
{
// This is a known issue, iconv doesn't always work with //IGNORE
$this->assertTrue(in_array($this->utf8->clean_string($illegal_utf8), array('тест', ''), TRUE));
}
else
{
$this->assertEquals($illegal_utf8, $this->utf8->clean_string($illegal_utf8));
}
}
// --------------------------------------------------------------------
/**
* convert_to_utf8() test
*
* @covers Utf8::convert_to_utf8
*/
public function test_convert_to_utf8()
{
$this->markTestSkipped("Does not work properly yet. See issue #95");
if (MB_ENABLED OR ICONV_ENABLED)
{
$this->assertEquals('тест', $this->utf8->convert_to_utf8('<27><><EFBFBD><EFBFBD>', 'WINDOWS-1251'));
}
else
{
$this->assertFalse($this->utf8->convert_to_utf8('<27><><EFBFBD><EFBFBD>', 'WINDOWS-1251'));
}
}
}

View File

@ -0,0 +1,41 @@
<?php
use FuzeWorks\Library\FW_Encryption;
class Mock_Libraries_Encryption extends FW_Encryption {
/**
* __get_params()
*
* Allows public calls to the otherwise protected _get_params().
*/
public function __get_params($params)
{
return $this->_get_params($params);
}
// --------------------------------------------------------------------
/**
* get_key()
*
* Allows checking for key changes.
*/
public function get_key()
{
return $this->_key;
}
// --------------------------------------------------------------------
/**
* __driver_get_handle()
*
* Allows checking for _mcrypt_get_handle(), _openssl_get_handle()
*/
public function __driver_get_handle($driver, $cipher, $mode)
{
return $this->{'_'.$driver.'_get_handle'}($cipher, $mode);
}
}

View File

@ -0,0 +1,423 @@
<?php
/**
* FuzeWorks.
*
* The FuzeWorks MVC PHP FrameWork
*
* Copyright (C) 2015 TechFuze
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
* @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/)
* @license http://opensource.org/licenses/GPL-3.0 GPLv3 License
*
* @link http://fuzeworks.techfuze.net
* @since Version 0.0.1
*
* @version Version 0.0.1
*/
use FuzeWorks\Factory;
/**
* Class FactoryTest.
*
* Will test the FuzeWorks Factory.
*/
class encryptionTest extends CoreTestAbstract
{
protected $factory;
public function setUp()
{
$this->factory = Factory::getInstance();
$this->factory->libraries->get('encryption');
require_once('encryption/mock_encryption.php');
$this->encryption = new Mock_Libraries_Encryption();
}
// --------------------------------------------------------------------
/**
* __construct test
*
* Covers behavior with $config['encryption_key'] set or not
*/
public function test___construct()
{
// Assume no configuration from set_up()
$this->assertNull($this->encryption->get_key());
// Try with an empty value
$this->factory->config->getConfig('encryption')->encryption_key = '';
$this->encrypt = new Mock_Libraries_Encryption();
$this->assertNull($this->encrypt->get_key());
$this->factory->config->getConfig('encryption')->encryption_key = str_repeat("\x0", 16);
$this->encrypt = new Mock_Libraries_Encryption();
$this->assertEquals(str_repeat("\x0", 16), $this->encrypt->get_key());
}
// --------------------------------------------------------------------
/**
* hkdf() test
*
* Applies test vectors described in Appendix A(1-3) RFC5869.
* Described vectors 4-7 SHA-1, which we don't support and are
* therefore excluded.
*
* Because our implementation is a single method instead of being
* split into hkdf_extract() and hkdf_expand(), we cannot test for
* the PRK value. As long as the OKM is correct though, it's fine.
*
* @link https://tools.ietf.org/rfc/rfc5869.txt
*/
public function test_hkdf()
{
$vectors = array(
// A.1: Basic test case with SHA-256
array(
'digest' => 'sha256',
'ikm' => "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
'salt' => "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c",
'length' => 42,
'info' => "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9",
// 'prk' => "\x07\x77\x09\x36\x2c\x2e\x32\xdf\x0d\xdc\x3f\x0d\xc4\x7b\xba\x63\x90\xb6\xc7\x3b\xb5\x0f\x9c\x31\x22\xec\x84\x4a\xd7\xc2\xb3\xe5",
'okm' => "\x3c\xb2\x5f\x25\xfa\xac\xd5\x7a\x90\x43\x4f\x64\xd0\x36\x2f\x2a\x2d\x2d\x0a\x90\xcf\x1a\x5a\x4c\x5d\xb0\x2d\x56\xec\xc4\xc5\xbf\x34\x00\x72\x08\xd5\xb8\x87\x18\x58\x65"
),
// A.2: Test with SHA-256 and longer inputs/outputs
array(
'digest' => 'sha256',
'ikm' => "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
'salt' => "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
'length' => 82,
'info' => "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
// 'prk' => "\x06\xa6\xb8\x8c\x58\x53\x36\x1a\x06\x10\x4c\x9c\xeb\x35\xb4\x5c\xef\x76\x00\x14\x90\x46\x71\x01\x4a\x19\x3f\x40\xc1\x5f\xc2\x44",
'okm' => "\xb1\x1e\x39\x8d\xc8\x03\x27\xa1\xc8\xe7\xf7\x8c\x59\x6a\x49\x34\x4f\x01\x2e\xda\x2d\x4e\xfa\xd8\xa0\x50\xcc\x4c\x19\xaf\xa9\x7c\x59\x04\x5a\x99\xca\xc7\x82\x72\x71\xcb\x41\xc6\x5e\x59\x0e\x09\xda\x32\x75\x60\x0c\x2f\x09\xb8\x36\x77\x93\xa9\xac\xa3\xdb\x71\xcc\x30\xc5\x81\x79\xec\x3e\x87\xc1\x4c\x01\xd5\xc1\xf3\x43\x4f\x1d\x87",
),
// A.3: Test with SHA-256 and zero-length salt/info
array(
'digest' => 'sha256',
'ikm' => "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
'salt' => '',
'length' => 42,
'info' => '',
// 'prk' => "\x19\xef\x24\xa3\x2c\x71\x7b\x16\x7f\x33\xa9\x1d\x6f\x64\x8b\xdf\x96\x59\x67\x76\xaf\xdb\x63\x77\xac\x43\x4c\x1c\x29\x3c\xcb\x04",
'okm' => "\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f\x71\x5f\x80\x2a\x06\x3c\x5a\x31\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d\x9d\x20\x13\x95\xfa\xa4\xb6\x1a\x96\xc8",
)
);
foreach ($vectors as $test)
{
$this->assertEquals(
$test['okm'],
$this->encryption->hkdf(
$test['ikm'],
$test['digest'],
$test['salt'],
$test['length'],
$test['info']
)
);
}
// Test default length, it must match the digest size
$this->assertEquals(64, strlen($this->encryption->hkdf('foobar', 'sha512')));
// Test maximum length (RFC5869 says that it must be up to 255 times the digest size)
$this->assertEquals(12240, strlen($this->encryption->hkdf('foobar', 'sha384', NULL, 48 * 255)));
$this->assertFalse($this->encryption->hkdf('foobar', 'sha224', NULL, 28 * 255 + 1));
// CI-specific test for an invalid digest
$this->assertFalse($this->encryption->hkdf('fobar', 'sha1'));
}
// --------------------------------------------------------------------
/**
* _get_params() test
*/
public function test__get_params()
{
$key = str_repeat("\x0", 16);
// Invalid custom parameters
$params = array(
// No cipher, mode or key
array('cipher' => 'aes-128', 'mode' => 'cbc'),
array('cipher' => 'aes-128', 'key' => $key),
array('mode' => 'cbc', 'key' => $key),
// No HMAC key or not a valid digest
array('cipher' => 'aes-128', 'mode' => 'cbc', 'key' => $key),
array('cipher' => 'aes-128', 'mode' => 'cbc', 'key' => $key, 'hmac_digest' => 'sha1', 'hmac_key' => $key),
// Invalid mode
array('cipher' => 'aes-128', 'mode' => 'foo', 'key' => $key, 'hmac_digest' => 'sha256', 'hmac_key' => $key)
);
for ($i = 0, $c = count($params); $i < $c; $i++)
{
$this->assertFalse($this->encryption->__get_params($params[$i]));
}
// Valid parameters
$params = array(
'cipher' => 'aes-128',
'mode' => 'cbc',
'key' => str_repeat("\x0", 16),
'hmac_key' => str_repeat("\x0", 16)
);
$this->assertTrue(is_array($this->encryption->__get_params($params)));
$params['base64'] = TRUE;
$params['hmac_digest'] = 'sha512';
// Including all parameters
$params = array(
'cipher' => 'aes-128',
'mode' => 'cbc',
'key' => str_repeat("\x0", 16),
'raw_data' => TRUE,
'hmac_key' => str_repeat("\x0", 16),
'hmac_digest' => 'sha256'
);
$output = $this->encryption->__get_params($params);
unset($output['handle'], $output['cipher'], $params['raw_data'], $params['cipher']);
$params['base64'] = FALSE;
$this->assertEquals($params, $output);
// HMAC disabled
unset($params['hmac_key'], $params['hmac_digest']);
$params['hmac'] = $params['raw_data'] = FALSE;
$params['cipher'] = 'aes-128';
$output = $this->encryption->__get_params($params);
unset($output['handle'], $output['cipher'], $params['hmac'], $params['raw_data'], $params['cipher']);
$params['base64'] = TRUE;
$params['hmac_digest'] = $params['hmac_key'] = NULL;
$this->assertEquals($params, $output);
}
// --------------------------------------------------------------------
/**
* initialize(), encrypt(), decrypt() test
*
* Testing the three methods separately is not realistic as they are
* designed to work together. A more thorough test for initialize()
* though is the OpenSSL/MCrypt compatibility test.
*
* @depends test_hkdf
* @depends test__get_params
*/
public function test_initialize_encrypt_decrypt()
{
$message = 'This is a plain-text message.';
$key = "\xd0\xc9\x08\xc4\xde\x52\x12\x6e\xf8\xcc\xdb\x03\xea\xa0\x3a\x5c";
// Default state (AES-128/Rijndael-128 in CBC mode)
$this->encryption->initialize(array('key' => $key));
// Was the key properly set?
$this->assertEquals($key, $this->encryption->get_key());
$this->assertEquals($message, $this->encryption->decrypt($this->encryption->encrypt($message)));
// Try DES in ECB mode, just for the sake of changing stuff
$this->encryption->initialize(array('cipher' => 'des', 'mode' => 'ecb', 'key' => substr($key, 0, 8)));
$this->assertEquals($message, $this->encryption->decrypt($this->encryption->encrypt($message)));
}
// --------------------------------------------------------------------
/**
* encrypt(), decrypt test with custom parameters
*
* @depends test__get_params
*/
public function test_encrypt_decrypt_custom()
{
$message = 'Another plain-text message.';
// A random invalid parameter
$this->assertFalse($this->encryption->encrypt($message, array('foo')));
$this->assertFalse($this->encryption->decrypt($message, array('foo')));
// No HMAC, binary output
$params = array(
'cipher' => 'tripledes',
'mode' => 'cfb',
'key' => str_repeat("\x1", 16),
'base64' => FALSE,
'hmac' => FALSE
);
$ciphertext = $this->encryption->encrypt($message, $params);
$this->assertEquals($message, $this->encryption->decrypt($ciphertext, $params));
}
// --------------------------------------------------------------------
/**
* _mcrypt_get_handle() test
*/
public function test__mcrypt_get_handle()
{
if ($this->encryption->drivers['mcrypt'] === FALSE)
{
return $this->markTestSkipped('Cannot test MCrypt because it is not available.');
}
$this->assertTrue(is_resource($this->encryption->__driver_get_handle('mcrypt', 'rijndael-128', 'cbc')));
}
// --------------------------------------------------------------------
/**
* _openssl_get_handle() test
*/
public function test__openssl_mcrypt_get_handle()
{
if ($this->encryption->drivers['openssl'] === FALSE)
{
return $this->markTestSkipped('Cannot test OpenSSL because it is not available.');
}
$this->assertEquals('aes-128-cbc', $this->encryption->__driver_get_handle('openssl', 'aes-128', 'cbc'));
$this->assertEquals('rc4-40', $this->encryption->__driver_get_handle('openssl', 'rc4-40', 'stream'));
}
// --------------------------------------------------------------------
/**
* OpenSSL/MCrypt portability test
*
* Amongst the obvious stuff, _cipher_alias() is also tested here.
*/
public function test_portability()
{
if ( ! $this->encryption->drivers['mcrypt'] OR ! $this->encryption->drivers['openssl'])
{
$this->markTestSkipped('Both MCrypt and OpenSSL support are required for portability tests.');
return;
}
$message = 'This is a message encrypted via MCrypt and decrypted via OpenSSL, or vice-versa.';
// Format is: <Cipher name>, <Cipher mode>, <Key size>
$portable = array(
array('aes-128', 'cbc', 16),
array('aes-128', 'cfb', 16),
array('aes-128', 'cfb8', 16),
array('aes-128', 'ofb', 16),
array('aes-128', 'ecb', 16),
array('aes-128', 'ctr', 16),
array('aes-192', 'cbc', 24),
array('aes-192', 'cfb', 24),
array('aes-192', 'cfb8', 24),
array('aes-192', 'ofb', 24),
array('aes-192', 'ecb', 24),
array('aes-192', 'ctr', 24),
array('aes-256', 'cbc', 32),
array('aes-256', 'cfb', 32),
array('aes-256', 'cfb8', 32),
array('aes-256', 'ofb', 32),
array('aes-256', 'ecb', 32),
array('aes-256', 'ctr', 32),
array('des', 'cbc', 7),
array('des', 'cfb', 7),
array('des', 'cfb8', 7),
array('des', 'ofb', 7),
array('des', 'ecb', 7),
array('tripledes', 'cbc', 7),
array('tripledes', 'cfb', 7),
array('tripledes', 'cfb8', 7),
array('tripledes', 'ofb', 7),
array('tripledes', 'cbc', 14),
array('tripledes', 'cfb', 14),
array('tripledes', 'cfb8', 14),
array('tripledes', 'ofb', 14),
array('tripledes', 'cbc', 21),
array('tripledes', 'cfb', 21),
array('tripledes', 'cfb8', 21),
array('tripledes', 'ofb', 21),
array('blowfish', 'cbc', 16),
array('blowfish', 'cfb', 16),
array('blowfish', 'ofb', 16),
array('blowfish', 'ecb', 16),
array('blowfish', 'cbc', 56),
array('blowfish', 'cfb', 56),
array('blowfish', 'ofb', 56),
array('blowfish', 'ecb', 56),
array('cast5', 'cbc', 11),
array('cast5', 'cfb', 11),
array('cast5', 'ofb', 11),
array('cast5', 'ecb', 11),
array('cast5', 'cbc', 16),
array('cast5', 'cfb', 16),
array('cast5', 'ofb', 16),
array('cast5', 'ecb', 16),
array('rc4', 'stream', 5),
array('rc4', 'stream', 8),
array('rc4', 'stream', 16),
array('rc4', 'stream', 32),
array('rc4', 'stream', 64),
array('rc4', 'stream', 128),
array('rc4', 'stream', 256)
);
$driver_index = array('mcrypt', 'openssl');
foreach ($portable as &$test)
{
// Add some randomness to the selected driver
$driver = mt_rand(0,1);
$params = array(
'driver' => $driver_index[$driver],
'cipher' => $test[0],
'mode' => $test[1],
'key' => openssl_random_pseudo_bytes($test[2])
);
$this->encryption->initialize($params);
$ciphertext = $this->encryption->encrypt($message);
$driver = (int) ! $driver;
$params['driver'] = $driver_index[$driver];
$this->encryption->initialize($params);
$this->assertEquals($message, $this->encryption->decrypt($ciphertext));
}
}
// --------------------------------------------------------------------
/**
* __get() test
*/
public function test_magic_get()
{
$this->assertNull($this->encryption->foo);
$this->assertEquals(array('mcrypt', 'openssl'), array_keys($this->encryption->drivers));
// 'stream' mode is translated into an empty string for OpenSSL
$this->encryption->initialize(array('cipher' => 'rc4', 'mode' => 'stream'));
$this->assertEquals('stream', $this->encryption->mode);
}
}

119
tests/mocks/autoloader.php Normal file
View File

@ -0,0 +1,119 @@
<?php
// This autoloader provide convinient way to working with mock object
// make the test looks natural. This autoloader support cascade file loading as well
// within mocks directory.
//
// Prototype :
//
// $mock_table = new Mock_Libraries_Table(); // Will load ./mocks/libraries/table.php
// $mock_database_driver = new Mock_Database_Driver(); // Will load ./mocks/database/driver.php
// and so on...
function autoload($class)
{
$dir = realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR;
$fw_core = array(
'Benchmark',
'Config',
'Controller',
'Exceptions',
'Hooks',
'Input',
'Lang',
'Loader',
'Log',
'Model',
'Output',
'Router',
'Security',
'URI',
'Utf8'
);
$fw_libraries = array(
'Calendar',
'Cart',
'Driver_Library',
'Email',
'Encrypt',
'Encryption',
'Form_validation',
'Ftp',
'Image_lib',
'Javascript',
'Migration',
'Pagination',
'Parser',
'Profiler',
'Table',
'Trackback',
'Typography',
'Unit_test',
'Upload',
'User_agent',
'Xmlrpc',
'Zip'
);
$fw_drivers = array('Session', 'Cache');
if (strpos($class, 'Mock_') === 0)
{
$class = strtolower(str_replace(array('Mock_', '_'), array('', DIRECTORY_SEPARATOR), $class));
}
elseif (strpos($class, 'FW_') === 0)
{
$subclass = substr($class, 3);
if (in_array($subclass, $fw_core))
{
$dir = 'Core'.DIRECTORY_SEPARATOR.'System'.DIRECTORY_SEPARATOR;
$class = $subclass;
}
elseif (in_array($subclass, $fw_libraries))
{
$dir = 'Core'.DIRECTORY_SEPARATOR.'Libraries'.DIRECTORY_SEPARATOR;
$class = ($subclass === 'Driver_Library') ? 'Driver' : $subclass;
}
elseif (in_array($subclass, $fw_drivers))
{
$dir = 'Core'.DIRECTORY_SEPARATOR.'Libraries'.DIRECTORY_SEPARATOR.$subclass.DIRECTORY_SEPARATOR;
$class = $subclass;
}
elseif (in_array(($parent = strtok($subclass, '_')), $fw_drivers)) {
$dir = 'Core'.DIRECTORY_SEPARATOR.'Libraries'.DIRECTORY_SEPARATOR.$parent.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
$class = $subclass;
}
elseif (preg_match('/^FW_DB_(.+)_(.+)_(driver|forge|result|utility)$/', $class, $m) && count($m) === 4)
{
$driver_path = 'Core'.DIRECTORY_SEPARATOR.'Database'.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
$dir = $driver_path.$m[1].DIRECTORY_SEPARATOR.'subdrivers'.DIRECTORY_SEPARATOR;
$file = $dir.$m[1].'_'.$m[2].'_'.$m[3].'.php';
}
elseif (preg_match('/^FW_DB_(.+)_(driver|forge|result|utility)$/', $class, $m) && count($m) === 3)
{
$driver_path = 'Core'.DIRECTORY_SEPARATOR.'Database'.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
$dir = $driver_path.$m[1].DIRECTORY_SEPARATOR;
$file = $dir.$m[1].'_'.$m[2].'.php';
}
elseif (strpos($class, 'FW_DB') === 0)
{
$dir = 'Core'.DIRECTORY_SEPARATOR.'Database'.DIRECTORY_SEPARATOR;
$file = $dir.str_replace(array('FW_DB','active_record'), array('DB', 'active_rec'), $subclass).'.php';
}
else
{
$class = strtolower($class);
}
}
$file = isset($file) ? $file : $dir.$class.'.php';
if ( ! file_exists($file))
{
return FALSE;
}
include_once($file);
}

View File

@ -0,0 +1,53 @@
<?php
use FuzeWorks\Input;
use FuzeWorks\Factory;
class Mock_Core_Input extends Input {
/**
* Since we use GLOBAL to fetch Security and Utf8 classes,
* we need to use inversion of control to mock up
* the same process within CI_Input class constructor.
*
* @covers CI_Input::__construct()
*/
public function __construct($security, $utf8)
{
parent::__construct();
$this->_allow_get_array = ($this->factory->config->getConfig('routing')->allow_get_array === TRUE);
$this->_enable_xss = ($this->factory->config->getConfig('security')->global_xss_filtering === TRUE);
$this->_enable_csrf = ($this->factory->config->getConfig('security')->csrf_protection === TRUE);
// Assign Security and Utf8 classes
$this->security = $security;
$this->uni = $utf8;
// Sanitize global arrays
$this->_sanitize_globals();
}
public function fetch_from_array($array, $index = '', $xss_clean = FALSE)
{
return parent::_fetch_from_array($array, $index, $xss_clean);
}
/**
* Lie about being a CLI request
*
* We take advantage of this in libraries/Session_test
*/
public function is_cli_request()
{
return FALSE;
}
public function __set($name, $value)
{
if ($name === 'ip_address')
{
$this->ip_address = $value;
}
}
}

View File

@ -0,0 +1,37 @@
<?php
use FuzeWorks\Security;
class Mock_Core_Security extends Security {
public function csrf_set_cookie()
{
// We cannot set cookie in CLI mode, so for csrf test, who rely on $_COOKIE,
// we superseded set_cookie with directly set the cookie variable,
// @see : ./tests/codeigniter/core/Security_test.php, line 8
return $this;
}
// Override inaccessible protected properties
public function __get($property)
{
return isset($this->{'_'.$property}) ? $this->{'_'.$property} : NULL;
}
public function remove_evil_attributes($str, $is_image)
{
return $this->_remove_evil_attributes($str, $is_image);
}
// Override inaccessible protected method
public function __call($method, $params)
{
if (is_callable(array($this, '_'.$method)))
{
return call_user_func_array(array($this, '_'.$method), $params);
}
throw new BadMethodCallException('Method '.$method.' was not found');
}
}

21
tests/mocks/core/utf8.php Normal file
View File

@ -0,0 +1,21 @@
<?php
use FuzeWorks\Utf8;
class Mock_Core_Utf8 extends Utf8 {
/**
* We need to define UTF8_ENABLED the same way that
* CI_Utf8 constructor does.
*/
public function __construct()
{
if (defined('UTF8_ENABLED'))
{
return;
}
parent::__construct();
}
}