Kohana v3.3.5

This commit is contained in:
Deon George
2016-05-01 20:50:24 +10:00
parent 8888719653
commit 68c7f4f159
170 changed files with 4565 additions and 1176 deletions

37
system/.travis.yml Normal file
View File

@@ -0,0 +1,37 @@
sudo: false
language: php
# Only build the main develop/master branches - feature branches will be covered by PRs
branches:
only:
- /^[0-9\.]+\/(develop|master)$/
cache:
directories:
- $HOME/.composer/cache/files
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: 'COMPOSER_PHPUNIT="lowest"'
before_script:
- composer self-update
- COMPOSER_ROOT_VERSION=3.3.x-dev composer install --prefer-dist --no-interaction
- if [ "$COMPOSER_PHPUNIT" = "lowest" ]; then COMPOSER_ROOT_VERSION=3.3.x-dev composer update --prefer-lowest --with-dependencies phpunit/phpunit; fi;
- vendor/bin/koharness
script:
- cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php
notifications:
email: false

1
system/CONTRIBUTING.md Normal file
View File

@@ -0,0 +1 @@
Please refer to the CONTRIBUTING file located under the [kohana](https://github.com/kohana/kohana) repo.

33
system/README.md Normal file
View File

@@ -0,0 +1,33 @@
# Kohana PHP Framework - core
| ver | Stable | Develop |
|-------|------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
| 3.3.x | [![Build Status - 3.3/master](https://travis-ci.org/kohana/core.svg?branch=3.3%2Fmaster)](https://travis-ci.org/kohana/core) | [![Build Status - 3.3/develop](https://travis-ci.org/kohana/core.svg?branch=3.3%2Fdevelop)](https://travis-ci.org/kohana/core) |
| 3.4.x | [![Build Status - 3.4/master](https://travis-ci.org/kohana/core.svg?branch=3.4%2Fmaster)](https://travis-ci.org/kohana/core) | [![Build Status - 3.4/develop](https://travis-ci.org/kohana/core.svg?branch=3.4%2Fdevelop)](https://travis-ci.org/kohana/core) |
This is the core package for the [Kohana](http://kohanaframework.org/) object oriented HMVC framework built using PHP5.
It aims to be swift, secure, and small.
Released under a [BSD license](http://kohanaframework.org/license), Kohana can be used legally for any open source,
commercial, or personal project.
## Documentation and installation
See the [sample application repository](https://github.com/kohana/kohana) for full readme and contributing information.
You will usually add `kohana/core` as a dependency in your own project's composer.json to install and work with this
package.
## Installation for development
To work on this package, you'll want to install it with composer to get the required dependencies. Note that there are
currently circular dependencies between this module and kohana/unittest. These may cause you problems if you are working
on a feature branch, because composer may not be able to figure out which version of kohana core you have.
To work around this, run composer like: `COMPOSER_ROOT_VERSION=3.3.x-dev composer install`. This tells composer that the
current checkout is a 3.3.* development version. Obviously change the argument if your branch is based on a different
version.
After installing the dependencies, you'll need a skeleton Kohana application before you can run the unit tests etc. The
simplest way to do this is to use kohana/koharness to build a bare project in `/tmp/koharness`.
If in doubt, check the install and test steps in the [.travis.yml](.travis.yml) file.

View File

@@ -279,7 +279,13 @@ class Kohana_Arr {
*/
public static function get($array, $key, $default = NULL)
{
return isset($array[$key]) ? $array[$key] : $default;
if ($array instanceof ArrayObject) {
// This is a workaround for inconsistent implementation of isset between PHP and HHVM
// See https://github.com/facebook/hhvm/issues/3437
return $array->offsetExists($key) ? $array->offsetGet($key) : $default;
} else {
return isset($array[$key]) ? $array[$key] : $default;
}
}
/**
@@ -387,7 +393,7 @@ class Kohana_Arr {
{
if (is_array($val))
{
$array[$key] = Arr::map($callbacks, $array[$key]);
$array[$key] = Arr::map($callbacks, $array[$key], $keys);
}
elseif ( ! is_array($keys) OR in_array($key, $keys))
{

View File

@@ -71,14 +71,14 @@ class Kohana_Cookie {
// Separate the salt and the value
list ($hash, $value) = explode('~', $cookie, 2);
if (Cookie::salt($key, $value) === $hash)
if (Security::slow_equals(Cookie::salt($key, $value), $hash))
{
// Cookie signature is valid
return $value;
}
// The cookie signature is invalid, delete it
Cookie::delete($key);
static::delete($key);
}
return $default;
@@ -88,33 +88,38 @@ class Kohana_Cookie {
* Sets a signed cookie. Note that all cookie values must be strings and no
* automatic serialization will be performed!
*
* [!!] By default, Cookie::$expiration is 0 - if you skip/pass NULL for the optional
* lifetime argument your cookies will expire immediately unless you have separately
* configured Cookie::$expiration.
*
*
* // Set the "theme" cookie
* Cookie::set('theme', 'red');
*
* @param string $name name of cookie
* @param string $value value of cookie
* @param integer $expiration lifetime in seconds
* @param integer $lifetime lifetime in seconds
* @return boolean
* @uses Cookie::salt
*/
public static function set($name, $value, $expiration = NULL)
public static function set($name, $value, $lifetime = NULL)
{
if ($expiration === NULL)
if ($lifetime === NULL)
{
// Use the default expiration
$expiration = Cookie::$expiration;
$lifetime = Cookie::$expiration;
}
if ($expiration !== 0)
if ($lifetime !== 0)
{
// The expiration is expected to be a UNIX timestamp
$expiration += time();
$lifetime += static::_time();
}
// Add the salt to the cookie value
$value = Cookie::salt($name, $value).'~'.$value;
return setcookie($name, $value, $expiration, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly);
return static::_setcookie($name, $value, $lifetime, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly);
}
/**
@@ -131,7 +136,7 @@ class Kohana_Cookie {
unset($_COOKIE[$name]);
// Nullify the cookie and make it expire
return setcookie($name, NULL, -86400, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly);
return static::_setcookie($name, NULL, -86400, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly);
}
/**
@@ -139,8 +144,10 @@ class Kohana_Cookie {
*
* $salt = Cookie::salt('theme', 'red');
*
* @param string $name name of cookie
* @param string $value value of cookie
* @param string $name name of cookie
* @param string $value value of cookie
*
* @throws Kohana_Exception if Cookie::$salt is not configured
* @return string
*/
public static function salt($name, $value)
@@ -154,7 +161,38 @@ class Kohana_Cookie {
// Determine the user agent
$agent = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower($_SERVER['HTTP_USER_AGENT']) : 'unknown';
return sha1($agent.$name.$value.Cookie::$salt);
return hash_hmac('sha1', $agent.$name.$value.Cookie::$salt, Cookie::$salt);
}
/**
* Proxy for the native setcookie function - to allow mocking in unit tests so that they do not fail when headers
* have been sent.
*
* @param string $name
* @param string $value
* @param integer $expire
* @param string $path
* @param string $domain
* @param boolean $secure
* @param boolean $httponly
*
* @return bool
* @see setcookie
*/
protected static function _setcookie($name, $value, $expire, $path, $domain, $secure, $httponly)
{
return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
}
/**
* Proxy for the native time function - to allow mocking of time-related logic in unit tests
*
* @return int
* @see time
*/
protected static function _time()
{
return time();
}
}

View File

@@ -16,8 +16,8 @@
class Kohana_Core {
// Release version and codename
const VERSION = '3.3.1';
const CODENAME = 'peregrinus';
const VERSION = '3.3.5';
const CODENAME = 'pharrell';
// Common environment type constants for consistency and convenience
const PRODUCTION = 10;
@@ -322,7 +322,7 @@ class Kohana_Core {
}
// Determine if the extremely evil magic quotes are enabled
Kohana::$magic_quotes = (version_compare(PHP_VERSION, '5.4') < 0 AND get_magic_quotes_gpc());
Kohana::$magic_quotes = (bool) get_magic_quotes_gpc();
// Sanitize all request variables
$_GET = Kohana::sanitize($_GET);

View File

@@ -592,10 +592,10 @@ class Kohana_Date {
$tz = new DateTimeZone($timezone ? $timezone : date_default_timezone_get());
$time = new DateTime($datetime_str, $tz);
if ($time->getTimeZone()->getName() !== $tz->getName())
{
$time->setTimeZone($tz);
}
// Convert the time back to the expected timezone if required (in case the datetime_str provided a timezone,
// offset or unix timestamp. This also ensures that the timezone reported by the object is correct on HHVM
// (see https://github.com/facebook/hhvm/issues/2302).
$time->setTimeZone($tz);
return $time->format($timestamp_format);
}

View File

@@ -133,8 +133,8 @@ class Kohana_Debug {
if ($marker === NULL)
{
// Make a unique marker
$marker = uniqid("\x00");
// Make a unique marker - force it to be alphanumeric so that it is always treated as a string array key
$marker = uniqid("\x00")."x";
}
if (empty($var))

View File

@@ -36,10 +36,33 @@ class Kohana_Encrypt {
public static $instances = array();
/**
* @var string OS-dependent RAND type to use
* @var string RAND type to use
*
* Only MCRYPT_DEV_URANDOM and MCRYPT_DEV_RANDOM are considered safe.
* Using MCRYPT_RAND will silently revert to MCRYPT_DEV_URANDOM
*/
protected static $_rand;
protected static $_rand = MCRYPT_DEV_URANDOM;
/**
* @var string Encryption key
*/
protected $_key;
/**
* @var string mcrypt mode
*/
protected $_mode;
/**
* @var string mcrypt cipher
*/
protected $_cipher;
/**
* @var int the size of the Initialization Vector (IV) in bytes
*/
protected $_iv_size;
/**
* Returns a singleton instance of Encrypt. An encryption key must be
* provided in your "encrypt" configuration file.
@@ -105,6 +128,10 @@ class Kohana_Encrypt {
// Shorten the key to the maximum size
$key = substr($key, 0, $size);
}
else if (version_compare(PHP_VERSION, '5.6.0', '>='))
{
$key = $this->_normalize_key($key, $cipher, $mode);
}
// Store the key, mode, and cipher
$this->_key = $key;
@@ -129,43 +156,8 @@ class Kohana_Encrypt {
*/
public function encode($data)
{
// Set the rand type if it has not already been set
if (Encrypt::$_rand === NULL)
{
if (Kohana::$is_windows)
{
// Windows only supports the system random number generator
Encrypt::$_rand = MCRYPT_RAND;
}
else
{
if (defined('MCRYPT_DEV_URANDOM'))
{
// Use /dev/urandom
Encrypt::$_rand = MCRYPT_DEV_URANDOM;
}
elseif (defined('MCRYPT_DEV_RANDOM'))
{
// Use /dev/random
Encrypt::$_rand = MCRYPT_DEV_RANDOM;
}
else
{
// Use the system random number generator
Encrypt::$_rand = MCRYPT_RAND;
}
}
}
if (Encrypt::$_rand === MCRYPT_RAND)
{
// The system random number generator must always be seeded each
// time it is used, or it will not produce true random results
mt_srand();
}
// Create a random initialization vector of the proper size for the current cipher
$iv = mcrypt_create_iv($this->_iv_size, Encrypt::$_rand);
// Get an initialization vector
$iv = $this->_create_iv();
// Encrypt the data using the configured options and generated iv
$data = mcrypt_encrypt($this->_cipher, $this->_key, $data, $this->_mode, $iv);
@@ -210,4 +202,54 @@ class Kohana_Encrypt {
return rtrim(mcrypt_decrypt($this->_cipher, $this->_key, $data, $this->_mode, $iv), "\0");
}
/**
* Proxy for the mcrypt_create_iv function - to allow mocking and testing against KAT vectors
*
* @return string the initialization vector or FALSE on error
*/
protected function _create_iv()
{
/*
* Silently use MCRYPT_DEV_URANDOM when the chosen random number generator
* is not one of those that are considered secure.
*
* Also sets Encrypt::$_rand to MCRYPT_DEV_URANDOM when it's not already set
*/
if ((Encrypt::$_rand !== MCRYPT_DEV_URANDOM) AND ( Encrypt::$_rand !== MCRYPT_DEV_RANDOM))
{
Encrypt::$_rand = MCRYPT_DEV_URANDOM;
}
// Create a random initialization vector of the proper size for the current cipher
return mcrypt_create_iv($this->_iv_size, Encrypt::$_rand);
}
/**
* Normalize key for PHP 5.6 for backwards compatibility
*
* This method is a shim to make PHP 5.6 behave in a B/C way for
* legacy key padding when shorter-than-supported keys are used
*
* @param string $key encryption key
* @param string $cipher mcrypt cipher
* @param string $mode mcrypt mode
*/
protected function _normalize_key($key, $cipher, $mode)
{
// open the cipher
$td = mcrypt_module_open($cipher, '', $mode, '');
// loop through the supported key sizes
foreach (mcrypt_enc_get_supported_key_sizes($td) as $supported) {
// if key is short, needs padding
if (strlen($key) <= $supported)
{
return str_pad($key, $supported, "\0");
}
}
// at this point key must be greater than max supported size, shorten it
return substr($key, 0, mcrypt_get_key_size($cipher, $mode));
}
}

View File

@@ -28,7 +28,7 @@ class Kohana_Form {
* @param mixed $action form action, defaults to the current request URI, or [Request] class to use
* @param array $attributes html attributes
* @return string
* @uses Request::instance
* @uses Request
* @uses URL::site
* @uses HTML::attributes
*/

View File

@@ -126,9 +126,9 @@ class Kohana_HTML {
$attributes['target'] = '_blank';
}
}
elseif ($uri[0] !== '#')
elseif ($uri[0] !== '#' AND $uri[0] !== '?')
{
// Make the URI absolute for non-id anchors
// Make the URI absolute for non-fragment and non-query anchors
$uri = URL::site($uri, $protocol, $index);
}
}
@@ -206,7 +206,7 @@ class Kohana_HTML {
*/
public static function style($file, array $attributes = NULL, $protocol = NULL, $index = FALSE)
{
if (strpos($file, '://') === FALSE)
if (strpos($file, '://') === FALSE AND strpos($file, '//') !== 0)
{
// Add the base URL
$file = URL::site($file, $protocol, $index);
@@ -239,7 +239,7 @@ class Kohana_HTML {
*/
public static function script($file, array $attributes = NULL, $protocol = NULL, $index = FALSE)
{
if (strpos($file, '://') === FALSE)
if (strpos($file, '://') === FALSE AND strpos($file, '//') !== 0)
{
// Add the base URL
$file = URL::site($file, $protocol, $index);

View File

@@ -95,7 +95,10 @@ abstract class Kohana_HTTP {
if (extension_loaded('http'))
{
// Use the fast method to parse header string
return new HTTP_Header(http_parse_headers($header_string));
$headers = version_compare(phpversion('http'), '2.0.0', '>=') ?
\http\Header::parse($header_string) :
http_parse_headers($header_string);
return new HTTP_Header($headers);
}
// Otherwise we use the slower PHP parsing
@@ -160,7 +163,10 @@ abstract class Kohana_HTTP {
elseif (extension_loaded('http'))
{
// Return the much faster method
return new HTTP_Header(http_get_request_headers());
$headers = version_compare(phpversion('http'), '2.0.0', '>=') ?
\http\Env::getRequestHeader() :
http_get_request_headers();
return new HTTP_Header($headers);
}
// Setup the output
@@ -186,8 +192,8 @@ abstract class Kohana_HTTP {
continue;
}
// This is a dirty hack to ensure HTTP_X_FOO_BAR becomes x-foo-bar
$headers[str_replace(array('HTTP_', '_'), array('', '-'), $key)] = $value;
// This is a dirty hack to ensure HTTP_X_FOO_BAR becomes X-FOO-BAR
$headers[str_replace('_', '-', substr($key, 5))] = $value;
}
return new HTTP_Header($headers);

View File

@@ -217,6 +217,16 @@ class Kohana_Kohana_Exception extends Exception {
$frame['type'] = '??';
}
// Xdebug returns the words 'dynamic' and 'static' instead of using '->' and '::' symbols
if ('dynamic' === $frame['type'])
{
$frame['type'] = '->';
}
elseif ('static' === $frame['type'])
{
$frame['type'] = '::';
}
// XDebug also has a different name for the parameters array
if (isset($frame['params']) AND ! isset($frame['args']))
{
@@ -238,7 +248,13 @@ class Kohana_Kohana_Exception extends Exception {
* The error view ends up several GB in size, taking
* serveral minutes to render.
*/
if (defined('PHPUnit_MAIN_METHOD'))
if (
defined('PHPUnit_MAIN_METHOD')
OR
defined('PHPUNIT_COMPOSER_INSTALL')
OR
defined('__PHPUNIT_PHAR__')
)
{
$trace = array_slice($trace, 0, 2);
}

View File

@@ -38,7 +38,7 @@ class Kohana_Request implements HTTP_Request {
/**
* Creates a new request object for the given URI. New requests should be
* created using the [Request::instance] or [Request::factory] methods.
* Created using the [Request::factory] method.
*
* $request = Request::factory($uri);
*
@@ -462,6 +462,12 @@ class Kohana_Request implements HTTP_Request {
foreach ($routes as $name => $route)
{
// Use external routes for reverse routing only
if ($route->is_external())
{
continue;
}
// We found something suitable
if ($params = $route->matches($request))
{
@@ -631,7 +637,7 @@ class Kohana_Request implements HTTP_Request {
/**
* Creates a new request object for the given URI. New requests should be
* created using the [Request::instance] or [Request::factory] methods.
* Created using the [Request::factory] method.
*
* $request = new Request($uri);
*
@@ -662,7 +668,7 @@ class Kohana_Request implements HTTP_Request {
$uri = array_shift($split_uri);
// Initial request has global $_GET already applied
if (Request::$initial !== NULL)
if (Request::$initial === NULL)
{
if ($split_uri)
{
@@ -675,7 +681,7 @@ class Kohana_Request implements HTTP_Request {
// being able to proxy external pages.
if ( ! $allow_external OR strpos($uri, '://') === FALSE)
{
// Remove trailing slashes from the URI
// Remove leading and trailing slashes from the URI
$this->_uri = trim($uri, '/');
// Apply the client
@@ -726,7 +732,7 @@ class Kohana_Request implements HTTP_Request {
if ($uri === NULL)
{
// Act as a getter
return empty($this->_uri) ? '/' : $this->_uri;
return ($this->_uri === '') ? '/' : $this->_uri;
}
// Act as a setter
@@ -740,7 +746,6 @@ class Kohana_Request implements HTTP_Request {
*
* echo URL::site($this->request->uri(), $protocol);
*
* @param array $params URI parameters
* @param mixed $protocol protocol string or Request object
* @return string
* @since 3.0.7
@@ -748,7 +753,13 @@ class Kohana_Request implements HTTP_Request {
*/
public function url($protocol = NULL)
{
// Create a URI with the current route and convert it to a URL
if ($this->is_external())
{
// If it's an external request return the URI
return $this->uri();
}
// Create a URI with the current route, convert to a URL and returns
return URL::site($this->uri(), $protocol);
}
@@ -1219,9 +1230,9 @@ class Kohana_Request implements HTTP_Request {
}
else
{
$this->headers('content-type',
'application/x-www-form-urlencoded; charset='.Kohana::$charset);
$body = http_build_query($post, NULL, '&');
$this->body($body)
->headers('content-type', 'application/x-www-form-urlencoded; charset='.Kohana::$charset);
}
// Set the content length

View File

@@ -26,7 +26,7 @@ abstract class Kohana_Request_Client {
/**
* @var array Headers to preserve when following a redirect
*/
protected $_follow_headers = array('Authorization');
protected $_follow_headers = array('authorization');
/**
* @var bool Follow 302 redirect with original request method?
@@ -205,7 +205,7 @@ abstract class Kohana_Request_Client {
if ($follow_headers === NULL)
return $this->_follow_headers;
$this->_follow_headers = $follow_headers;
$this->_follow_headers = array_map('strtolower', $follow_headers);
return $this;
}
@@ -407,7 +407,8 @@ abstract class Kohana_Request_Client {
// Prepare the additional request, copying any follow_headers that were present on the original request
$orig_headers = $request->headers()->getArrayCopy();
$follow_headers = array_intersect_assoc($orig_headers, array_fill_keys($client->follow_headers(), TRUE));
$follow_header_keys = array_intersect(array_keys($orig_headers), $client->follow_headers());
$follow_headers = \Arr::extract($orig_headers, $follow_header_keys);
$follow_request = Request::factory($response->headers('Location'))
->method($follow_method)

View File

@@ -34,7 +34,10 @@ class Kohana_Request_Client_Curl extends Request_Client_External {
// if using a request other than POST. PUT does support this method
// and DOES NOT require writing data to disk before putting it, if
// reading the PHP docs you may have got that impression. SdF
$options[CURLOPT_POSTFIELDS] = $request->body();
// This will also add a Content-Type: application/x-www-form-urlencoded header unless you override it
if ($body = $request->body()) {
$options[CURLOPT_POSTFIELDS] = $body;
}
// Process headers
if ($headers = $request->headers())

View File

@@ -127,6 +127,8 @@ abstract class Kohana_Request_Client_External extends Request_Client {
->headers('content-type', 'application/x-www-form-urlencoded; charset='.Kohana::$charset);
}
$request->headers('content-length', (string) $request->content_length());
// If Kohana expose, set the user-agent
if (Kohana::$expose)
{

View File

@@ -604,7 +604,10 @@ class Kohana_Response implements HTTP_Response {
{
if (extension_loaded('http'))
{
$this->_header['set-cookie'] = http_build_cookie($this->_cookies);
$cookies = version_compare(phpversion('http'), '2.0.0', '>=') ?
(string) new \http\Cookie($this->_cookies) :
http_build_cookie($this->_cookies);
$this->_header['set-cookie'] = $cookies;
}
else
{

View File

@@ -509,6 +509,14 @@ class Kohana_Route {
*/
public function uri(array $params = NULL)
{
if ($params)
{
// @issue #4079 rawurlencode parameters
$params = array_map('rawurlencode', $params);
// decode slashes back, see Apache docs about AllowEncodedSlashes and AcceptPathInfo
$params = str_replace(array('%2F', '%5C'), array('/', '\\'), $params);
}
$defaults = $this->_defaults;
/**

View File

@@ -28,8 +28,8 @@ class Kohana_Security {
* And then check it when using [Validation]:
*
* $array->rules('csrf', array(
* 'not_empty' => NULL,
* 'Security::check' => NULL,
* array('not_empty'),
* array('Security::check'),
* ));
*
* This provides a basic, but effective, method of preventing CSRF attacks.
@@ -81,8 +81,29 @@ class Kohana_Security {
*/
public static function check($token)
{
return Security::token() === $token;
return Security::slow_equals(Security::token(), $token);
}
/**
* Compare two hashes in a time-invariant manner.
* Prevents cryptographic side-channel attacks (timing attacks, specifically)
*
* @param string $a cryptographic hash
* @param string $b cryptographic hash
* @return boolean
*/
public static function slow_equals($a, $b)
{
$diff = strlen($a) ^ strlen($b);
for($i = 0; $i < strlen($a) AND $i < strlen($b); $i++)
{
$diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
}
/**
* Remove image tags from a string.

View File

@@ -24,8 +24,31 @@ class Kohana_Session_Native extends Session {
*/
protected function _read($id = NULL)
{
/**
* session_set_cookie_params will override php ini settings
* If Cookie::$domain is NULL or empty and is passed, PHP
* will override ini and sent cookies with the host name
* of the server which generated the cookie
*
* see issue #3604
*
* see http://www.php.net/manual/en/function.session-set-cookie-params.php
* see http://www.php.net/manual/en/session.configuration.php#ini.session.cookie-domain
*
* set to Cookie::$domain if available, otherwise default to ini setting
*/
$session_cookie_domain = empty(Cookie::$domain)
? ini_get('session.cookie_domain')
: Cookie::$domain;
// Sync up the session cookie with Cookie parameters
session_set_cookie_params($this->_lifetime, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly);
session_set_cookie_params(
$this->_lifetime,
Cookie::$path,
$session_cookie_domain,
Cookie::$secure,
Cookie::$httponly
);
// Do not allow PHP to send Cache-Control headers
session_cache_limiter(FALSE);

View File

@@ -240,12 +240,13 @@ class Kohana_Text {
*
* @param string $string string to transform
* @param string $delimiter delimiter to use
* @uses UTF8::ucfirst
* @return string
*/
public static function ucfirst($string, $delimiter = '-')
{
// Put the keys back the Case-Convention expected
return implode($delimiter, array_map('ucfirst', explode($delimiter, $string)));
return implode($delimiter, array_map('UTF8::ucfirst', explode($delimiter, $string)));
}
/**
@@ -293,12 +294,15 @@ class Kohana_Text {
$regex = '!'.$regex.'!ui';
// if $replacement is a single character: replace each of the characters of the badword with $replacement
if (UTF8::strlen($replacement) == 1)
{
$regex .= 'e';
return preg_replace($regex, 'str_repeat($replacement, UTF8::strlen(\'$1\'))', $str);
return preg_replace_callback($regex, function($matches) use ($replacement) {
return str_repeat($replacement, UTF8::strlen($matches[1]));
}, $str);
}
// if $replacement is not a single character, fully replace the badword with $replacement
return preg_replace($regex, $replacement, $str);
}
@@ -587,35 +591,40 @@ class Kohana_Text {
*
* echo Text::widont($text);
*
* regex courtesy of the Typogrify project
* @link http://code.google.com/p/typogrify/
*
* @param string $str text to remove widows from
* @return string
*/
public static function widont($str)
{
$str = rtrim($str);
$space = strrpos($str, ' ');
if ($space !== FALSE)
{
$str = substr($str, 0, $space).'&nbsp;'.substr($str, $space + 1);
}
return $str;
// use '%' as delimiter and 'x' as modifier
$widont_regex = "%
((?:</?(?:a|em|span|strong|i|b)[^>]*>)|[^<>\s]) # must be proceeded by an approved inline opening or closing tag or a nontag/nonspace
\s+ # the space to replace
([^<>\s]+ # must be flollowed by non-tag non-space characters
\s* # optional white space!
(</(a|em|span|strong|i|b)>\s*)* # optional closing inline tags with optional white space after each
((</(p|h[1-6]|li|dt|dd)>)|$)) # end with a closing p, h1-6, li or the end of the string
%x";
return preg_replace($widont_regex, '$1&nbsp;$2', $str);
}
/**
* Returns information about the client user agent.
*
* // Returns "Chrome" when using Google Chrome
* $browser = Text::user_agent('browser');
* $browser = Text::user_agent($agent, 'browser');
*
* Multiple values can be returned at once by using an array:
*
* // Get the browser and platform with a single call
* $info = Text::user_agent(array('browser', 'platform'));
* $info = Text::user_agent($agent, array('browser', 'platform'));
*
* When using an array for the value, an associative array will be returned.
*
* @param string $agent user_agent
* @param mixed $value array or string to return: browser, version, robot, mobile, platform
* @return mixed requested information, FALSE if nothing is found
* @uses Kohana::$config
@@ -649,7 +658,7 @@ class Kohana_Text {
// Set the browser name
$info['browser'] = $name;
if (preg_match('#'.preg_quote($search).'[^0-9.]*+([0-9.][0-9.a-z]*)#i', Request::$user_agent, $matches))
if (preg_match('#'.preg_quote($search).'[^0-9.]*+([0-9.][0-9.a-z]*)#i', $agent, $matches))
{
// Set the version number
$info['version'] = $matches[1];

View File

@@ -2,6 +2,8 @@
/**
* URL helper class.
*
* [!!] You need to setup the list of trusted hosts in the `url.php` config file, before starting using this helper class.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
@@ -14,7 +16,9 @@ class Kohana_URL {
* Gets the base URL to the application.
* To specify a protocol, provide the protocol as a string or request object.
* If a protocol is used, a complete URL will be generated using the
* `$_SERVER['HTTP_HOST']` variable.
* `$_SERVER['HTTP_HOST']` variable, which will be validated against RFC 952
* and RFC 2181, as well as against the list of trusted hosts you have set
* in the `url.php` config file.
*
* // Absolute URL path with no host or protocol
* echo URL::base();
@@ -75,7 +79,7 @@ class Kohana_URL {
$port = ':'.$port;
}
if ($domain = parse_url($base_url, PHP_URL_HOST))
if ($host = parse_url($base_url, PHP_URL_HOST))
{
// Remove everything but the path from the URL
$base_url = parse_url($base_url, PHP_URL_PATH);
@@ -83,11 +87,32 @@ class Kohana_URL {
else
{
// Attempt to use HTTP_HOST and fallback to SERVER_NAME
$domain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
// make $host lowercase
$host = strtolower($host);
// check that host does not contain forbidden characters (see RFC 952 and RFC 2181)
// use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) {
throw new Kohana_Exception(
'Invalid host :host',
array(':host' => $host)
);
}
// Validate $host, see if it matches trusted hosts
if ( ! static::is_trusted_host($host))
{
throw new Kohana_Exception(
'Untrusted host :host. If you trust :host, add it to the trusted hosts in the `url` config file.',
array(':host' => $host)
);
}
}
// Add the protocol and domain to the base URL
$base_url = $protocol.'://'.$domain.$port.$base_url;
$base_url = $protocol.'://'.$host.$port.$base_url;
}
return $base_url;
@@ -210,4 +235,41 @@ class Kohana_URL {
return trim($title, $separator);
}
/**
* Test if given $host should be trusted.
*
* Tests against given $trusted_hosts
* or looks for key `trusted_hosts` in `url` config
*
* @param string $host
* @param array $trusted_hosts
* @return boolean TRUE if $host is trustworthy
*/
public static function is_trusted_host($host, array $trusted_hosts = NULL)
{
// If list of trusted hosts is not directly provided read from config
if (empty($trusted_hosts))
{
$trusted_hosts = (array) Kohana::$config->load('url')->get('trusted_hosts');
}
// loop through the $trusted_hosts array for a match
foreach ($trusted_hosts as $trusted_host)
{
// make sure we fully match the trusted hosts
$pattern = '#^'.$trusted_host.'$#uD';
// return TRUE if there is match
if (preg_match($pattern, $host)) {
return TRUE;
}
}
// return FALSE as nothing is matched
return FALSE;
}
}

View File

@@ -70,13 +70,17 @@ class Kohana_UTF8 {
if ( ! UTF8::is_ascii($var))
{
// Disable notices
$error_reporting = error_reporting(~E_NOTICE);
// Temporarily save the mb_substitute_character() value into a variable
$mb_substitute_character = mb_substitute_character();
// Disable substituting illegal characters with the default '?' character
mb_substitute_character('none');
// convert encoding, this is expensive, used when $var is not ASCII
$var = mb_convert_encoding($var, $charset, $charset);
// Turn notices back on
error_reporting($error_reporting);
// Reset mb_substitute_character() value back to the original setting
mb_substitute_character($mb_substitute_character);
}
}

View File

@@ -219,7 +219,7 @@ class Kohana_Validation implements ArrayAccess {
if ($field !== TRUE AND ! isset($this->_labels[$field]))
{
// Set the field label to the field name
$this->_labels[$field] = preg_replace('/[^\pL]+/u', ' ', $field);
$this->_labels[$field] = $field;
}
// Store the rule and params for this rule
@@ -430,6 +430,13 @@ class Kohana_Validation implements ArrayAccess {
}
}
// Unbind all the automatic bindings to avoid memory leaks.
unset($this->_bound[':validation']);
unset($this->_bound[':data']);
unset($this->_bound[':field']);
unset($this->_bound[':value']);
// Restore the data to its original form
$this->_data = $original;

View File

@@ -40,6 +40,7 @@ class Kohana_View {
* @param string $kohana_view_filename filename
* @param array $kohana_view_data variables
* @return string
* @throws Exception
*/
protected static function capture($kohana_view_filename, array $kohana_view_data)
{
@@ -79,17 +80,25 @@ class Kohana_View {
*
* View::set_global($name, $value);
*
* @param string $key variable name or an array of variables
* @param mixed $value value
* You can also use an array or Traversable object to set several values at once:
*
* // Create the values $food and $beverage in the view
* View::set_global(array('food' => 'bread', 'beverage' => 'water'));
*
* [!!] Note: When setting with using Traversable object we're not attaching the whole object to the view,
* i.e. the object's standard properties will not be available in the view context.
*
* @param string|array|Traversable $key variable name or an array of variables
* @param mixed $value value
* @return void
*/
public static function set_global($key, $value = NULL)
{
if (is_array($key))
if (is_array($key) OR $key instanceof Traversable)
{
foreach ($key as $key2 => $value)
foreach ($key as $name => $value)
{
View::$_global_data[$key2] = $value;
View::$_global_data[$name] = $value;
}
}
else
@@ -127,7 +136,6 @@ class Kohana_View {
*
* @param string $file view filename
* @param array $data array of values
* @return void
* @uses View::set_filename
*/
public function __construct($file = NULL, array $data = NULL)
@@ -272,18 +280,21 @@ class Kohana_View {
* // This value can be accessed as $foo within the view
* $view->set('foo', 'my value');
*
* You can also use an array to set several values at once:
* You can also use an array or Traversable object to set several values at once:
*
* // Create the values $food and $beverage in the view
* $view->set(array('food' => 'bread', 'beverage' => 'water'));
*
* @param string $key variable name or an array of variables
* @param mixed $value value
* [!!] Note: When setting with using Traversable object we're not attaching the whole object to the view,
* i.e. the object's standard properties will not be available in the view context.
*
* @param string|array|Traversable $key variable name or an array of variables
* @param mixed $value value
* @return $this
*/
public function set($key, $value = NULL)
{
if (is_array($key))
if (is_array($key) OR $key instanceof Traversable)
{
foreach ($key as $name => $value)
{

View File

@@ -1,4 +1,5 @@
{
"_readme": "NOTE: see readme for COMPOSER_ROOT_VERSION instructions if you have dependency issues",
"name": "kohana/core",
"description": "Core system classes for the Kohana application framework",
"homepage": "http://kohanaframework.org",
@@ -21,12 +22,19 @@
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"suggest": {
"ext-http": "*",
"ext-curl": "*",
"ext-mcrypt": "*"
},
"extra": {
"installer-paths": {
"vendor/{$vendor}/{$name}": ["type:kohana-module"]
},
"branch-alias": {
"dev-3.3/develop": "3.3.x-dev",
"dev-3.4/develop": "3.4.x-dev"

View File

@@ -192,6 +192,7 @@ return array(
'wav' => array('audio/x-wav'),
'wax' => array('audio/x-ms-wax'),
'wbxml' => array('application/wbxml'),
'webapp' => array('application/x-web-app-manifest+json'),
'webm' => array('video/webm'),
'wm' => array('video/x-ms-wm'),
'wma' => array('audio/x-ms-wma'),

18
system/config/url.php Normal file
View File

@@ -0,0 +1,18 @@
<?php defined('SYSPATH') OR die('No direct script access.');
return array(
'trusted_hosts' => array(
// Set up your hostnames here
//
// Example:
//
// 'example\.org',
// '.*\.example\.org',
//
// Do not forget to escape your dots (.) as these are regex patterns.
// These patterns should always fully match,
// as they are prepended with `^` and appended with `$`
),
);

View File

@@ -3,6 +3,8 @@
return array(
'platform' => array(
'windows nt 10.0'=> 'Windows 10',
'windows nt 6.3' => 'Windows 8.1',
'windows nt 6.2' => 'Windows 8',
'windows nt 6.1' => 'Windows 7',
'windows nt 6.0' => 'Windows Vista',
@@ -47,6 +49,7 @@ return array(
'browser' => array(
'Opera' => 'Opera',
'Edge/12' => 'Edge',
'MSIE' => 'Internet Explorer',
'Internet Explorer' => 'Internet Explorer',
'Shiira' => 'Shiira',

View File

@@ -52,7 +52,7 @@ You can add conditional statements to make the bootstrap have different values b
/**
* Set the environment status by the domain.
*/
if (strpos($_SERVER['HTTP_HOST'], 'kohanaphp.com') !== FALSE)
if (strpos($_SERVER['HTTP_HOST'], 'kohanaframework.org') !== FALSE)
{
// We are live!
Kohana::$environment = Kohana::PRODUCTION;
@@ -66,7 +66,7 @@ if (strpos($_SERVER['HTTP_HOST'], 'kohanaphp.com') !== FALSE)
... [trimmed]
*/
Kohana::init(array(
'base_url' => Kohana::$environment === Kohana::PRODUCTION ? '/' : '/kohanaphp.com/',
'base_url' => Kohana::$environment === Kohana::PRODUCTION ? '/' : '/kohanaframework.org/',
'caching' => Kohana::$environment === Kohana::PRODUCTION,
'profile' => Kohana::$environment !== Kohana::PRODUCTION,
'index_file' => FALSE,

View File

@@ -16,7 +16,7 @@ Every application follows the same flow:
* Includes each module's `init.php` file, if it exists.
* The `init.php` file can perform additional environment setup, including adding routes.
10. [Route::set] is called multiple times to define the [application routes](routing).
11. [Request::instance] is called to start processing the request.
11. [Request::factory] is called to start processing the request.
1. Checks each route that has been set until a match is found.
2. Creates the controller instance and passes the request to it.
3. Calls the [Controller::before] method.
@@ -24,4 +24,4 @@ Every application follows the same flow:
5. Calls the [Controller::after] method.
* The above 5 steps can be repeated multiple times when using [HMVC sub-requests](requests).
3. Application flow returns to index.php
12. The main [Request] response is displayed
12. The main [Request] response is displayed

View File

@@ -34,15 +34,27 @@ Kohana::init(array(
));
~~~
- Make sure the `application/cache` and `application/logs` directories are writable by the web server.
- List your trusted hosts. Open `application/config/url.php` and add regex patterns of the hosts you expect your application to be accessible from.
[!!] Do not forget to escape your dots (.) as these are regex patterns. These patterns should always fully match, as they are prepended with `^` and appended with `$`.
~~~
sudo chmod -R a+rwx application/cache
sudo chmod -R a+rwx application/logs
return array(
'trusted_hosts' => array(
'example\.org',
'.*\.example\.org',
),
);
~~~
- Define a salt for the `Cookie` class.
~~~
Cookie::$salt = [really-long-cookie-salt-here]
Cookie::$salt = 'some-really-long-cookie-salt-here';
~~~
- Make sure the `application/cache` and `application/logs` directories are writable by the web server.
~~~
sudo chmod -R a+rwx application/cache
sudo chmod -R a+rwx application/logs
~~~
[!!] Make sure to use a unique salt for your application and never to share it. Take a look at the [Cookies](cookies) page for more information on how cookies work in Kohana. If you do not define a `Cookie::$salt` value, Kohana will throw an exception when it encounters any cookie on your domain.

View File

@@ -20,6 +20,7 @@
- [Error Handling](errors)
- [Tips & Common Mistakes](tips)
- [Upgrading from v3.2](upgrading)
- [Upgrading from v3.3.3.1](upgrading-from-3-3-3-1)
- Basic Usage
- [Debugging](debugging)
- [Loading Classes](autoloading)

View File

@@ -55,7 +55,7 @@ You can also have a controller extend another controller to share common things,
Every controller has the `$this->request` property which is the [Request] object that called the controller. You can use this to get information about the current request, as well as set the response body via `$this->response->body($ouput)`.
Here is a partial list of the properties and methods available to `$this->request`. These can also be accessed via `Request::instance()`, but `$this->request` is provided as a shortcut. See the [Request] class for more information on any of these.
Here is a partial list of the properties and methods available to `$this->request`. See the [Request] class for more information on any of these.
Property/method | What it does
--- | ---

View File

@@ -182,6 +182,7 @@ First, we need a [View] that contains the HTML form, which will be placed in `ap
<?php foreach ($errors as $message): ?>
<li><?php echo $message ?></li>
<?php endforeach ?>
</ul>
<?php endif ?>
<dl>

View File

@@ -1,17 +0,0 @@
# Tutorials
## Tutorials in this guide
## Tutorials written elsewhere
### Ellisgl's KO3 tutorial on dealtaker.com:
1. [Install and Basic Usage](http://www.dealtaker.com/blog/2009/11/20/kohana-php-3-0-ko3-tutorial-part-1/)
2. [Views](http://www.dealtaker.com/blog/2009/12/07/kohana-php-3-0-ko3-tutorial-part-2/)
3. [Controllers](http://www.dealtaker.com/blog/2009/12/30/kohana-php-3-0-ko3-tutorial-part-3/)
4. [Models](http://www.dealtaker.com/blog/2010/02/01/kohana-php-3-0-ko3-tutorial-part-4/)
5. [Subrequests](http://www.dealtaker.com/blog/2010/02/25/kohana-php-3-0-ko3-tutorial-part-5/)
6. [Routes](http://www.dealtaker.com/blog/2010/03/03/kohana-php-3-0-ko3-tutorial-part-6/)
7. [Helpers](http://www.dealtaker.com/blog/2010/03/26/kohana-php-3-0-ko3-tutorial-part-7/)
8. [Modules](http://www.dealtaker.com/blog/2010/04/30/kohana-php-3-0-ko3-tutorial-part-8/)
9. [Vendor Libraries](http://www.dealtaker.com/blog/2010/06/02/kohana-php-3-0-ko3-tutorial-part-9/)

View File

@@ -1,7 +0,0 @@
Making a template driven site.
<http://kerkness.ca/wiki/doku.php?id=template-site:create_the_template>
<http://kerkness.ca/wiki/doku.php?id=template-site:extending_the_template_controller>
<http://kerkness.ca/wiki/doku.php?id=template-site:basic_page_controller>

View File

@@ -1,5 +0,0 @@
<http://kerkness.ca/wiki/doku.php?id=routing:static_pages>
<http://kerkness.ca/wiki/doku.php?id=routing:multi-language_with_a_route>

View File

@@ -0,0 +1,23 @@
# Upgrading from 3.3.3.1
Minor version upgrades are usually done in a drop-in fashion. Unfortunately, however, upgrading from 3.3.3.1 to 3.3.4 needs a little configuration. This is because a [security disclosure from HP Fortify](https://github.com/kohana/kohana/issues/74), that unveiled a serious [host header attack](https://github.com/kohana/core/issues/613) vulnerability.
[!!] You *might* still be able to have a drop-in upgrade, in case you have set the `base_url` in the [Kohana::init] call to an absolute URL. We advise you however that you follow the step below to make your application secure, in case some day you decide to change your `base_url` to a relative URL.
## Trusted Hosts
You need to setup a list of trusted hosts. Trusted hosts are hosts that you expect your application to be accessible from.
Open `application/config/url.php` and add regex patterns of these hosts. An example is given hereunder:
~~~
return array(
'trusted_hosts' => array(
'example\.org',
'.*\.example\.org',
),
);
~~~
[!!] Do not forget to escape your dots (.) as these are regex patterns. These patterns should always fully match, as they are prepended with `^` and appended with `$`.

8
system/koharness.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
// Configuration for koharness - builds a standalone skeleton Kohana app for running unit tests
return array(
'modules' => array(
'unittest' => __DIR__ . '/vendor/kohana/unittest'
),
'syspath' => __DIR__,
);

View File

@@ -655,6 +655,22 @@ class Kohana_ArrTest extends Unittest_TestCase
'bar' => 'foobar',
),
),
array(
'strip_tags',
array(
array(
'foo' => '<p>foobar</p>',
'bar' => '<p>foobar</p>',
),
),
array('foo'),
array(
array(
'foo' => 'foobar',
'bar' => '<p>foobar</p>',
),
),
),
);
}

View File

@@ -11,13 +11,15 @@
* @category Tests
* @author Kohana Team
* @author Jeremy Bush <contractfrombelow@gmail.com>
* @copyright (c) 2008-2012 Kohana Team
* @author Andrew Coulton <andrew@ingenerator.com>
* @copyright (c) 2008-2014 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_CookieTest extends Unittest_TestCase
{
const UNIX_TIMESTAMP = 1411040141;
const COOKIE_EXPIRATION = 60;
protected $_default_salt = 'AdaoidadnA£ASDNadnaoiwdnawd';
/**
* Sets up the environment
*/
@@ -26,152 +28,298 @@ class Kohana_CookieTest extends Unittest_TestCase
// @codingStandardsIgnoreEnd
{
parent::setUp();
Kohana_CookieTest_TestableCookie::$_mock_cookies_set = array();
Cookie::$salt = $this->_default_salt;
$this->setEnvironment(array(
'Cookie::$salt' => 'some-random-salt',
'HTTP_USER_AGENT' => 'cli'
));
}
/**
* Tears down the environment
* Tests that cookies are set with the global path, domain, etc options.
*
* @covers Cookie::set
*/
public function test_set_creates_cookie_with_configured_cookie_options()
{
$this->setEnvironment(array(
'Cookie::$path' => '/path',
'Cookie::$domain' => 'my.domain',
'Cookie::$secure' => TRUE,
'Cookie::$httponly' => FALSE,
));
Kohana_CookieTest_TestableCookie::set('cookie', 'value');
$this->assertSetCookieWith(array(
'path' => '/path',
'domain' => 'my.domain',
'secure' => TRUE,
'httponly' => FALSE
));
}
/**
* Provider for test_set_calculates_expiry_from_lifetime
*
* @return array of $lifetime, $expect_expiry
*/
public function provider_set_calculates_expiry_from_lifetime()
{
return array(
array(NULL, self::COOKIE_EXPIRATION + self::UNIX_TIMESTAMP),
array(0, 0),
array(10, 10 + self::UNIX_TIMESTAMP),
);
}
/**
* @param int $expiration
* @param int $expect_expiry
*
* @dataProvider provider_set_calculates_expiry_from_lifetime
* @covers Cookie::set
*/
public function test_set_calculates_expiry_from_lifetime($expiration, $expect_expiry)
{
$this->setEnvironment(array('Cookie::$expiration' => self::COOKIE_EXPIRATION));
Kohana_CookieTest_TestableCookie::set('foo', 'bar', $expiration);
$this->assertSetCookieWith(array('expire' => $expect_expiry));
}
/**
* @covers Cookie::get
*/
public function test_get_returns_default_if_cookie_missing()
{
unset($_COOKIE['missing_cookie']);
$this->assertEquals('default', Cookie::get('missing_cookie', 'default'));
}
/**
* @covers Cookie::get
*/
public function test_get_returns_value_if_cookie_present_and_signed()
{
Kohana_CookieTest_TestableCookie::set('cookie', 'value');
$cookie = Kohana_CookieTest_TestableCookie::$_mock_cookies_set[0];
$_COOKIE[$cookie['name']] = $cookie['value'];
$this->assertEquals('value', Cookie::get('cookie', 'default'));
}
/**
* Provider for test_get_returns_default_without_deleting_if_cookie_unsigned
*
* @return array
*/
public function provider_get_returns_default_without_deleting_if_cookie_unsigned()
{
return array(
array('unsalted'),
array('un~salted'),
);
}
/**
* Verifies that unsigned cookies are not available to the kohana application, but are not affected for other
* consumers.
*
* @param string $unsigned_value
*
* @dataProvider provider_get_returns_default_without_deleting_if_cookie_unsigned
* @covers Cookie::get
*/
public function test_get_returns_default_without_deleting_if_cookie_unsigned($unsigned_value)
{
$_COOKIE['cookie'] = $unsigned_value;
$this->assertEquals('default', Kohana_CookieTest_TestableCookie::get('cookie', 'default'));
$this->assertEquals($unsigned_value, $_COOKIE['cookie'], '$_COOKIE not affected');
$this->assertEmpty(Kohana_CookieTest_TestableCookie::$_mock_cookies_set, 'No cookies set or changed');
}
/**
* If a cookie looks like a signed cookie but the signature no longer matches, it should be deleted.
*
* @covers Cookie::get
*/
public function test_get_returns_default_and_deletes_tampered_signed_cookie()
{
$_COOKIE['cookie'] = Cookie::salt('cookie', 'value').'~tampered';
$this->assertEquals('default', Kohana_CookieTest_TestableCookie::get('cookie', 'default'));
$this->assertDeletedCookie('cookie');
}
/**
* @covers Cookie::delete
*/
public function test_delete_removes_cookie_from_globals_and_expires_cookie()
{
$_COOKIE['cookie'] = Cookie::salt('cookie', 'value').'~tampered';
$this->assertTrue(Kohana_CookieTest_TestableCookie::delete('cookie'));
$this->assertDeletedCookie('cookie');
}
/**
* @covers Cookie::delete
* @link http://dev.kohanaframework.org/issues/3501
* @link http://dev.kohanaframework.org/issues/3020
*/
public function test_delete_does_not_require_configured_salt()
{
Cookie::$salt = NULL;
$this->assertTrue(Kohana_CookieTest_TestableCookie::delete('cookie'));
$this->assertDeletedCookie('cookie');
}
/**
* @covers Cookie::salt
* @expectedException Kohana_Exception
*/
public function test_salt_throws_with_no_configured_salt()
{
Cookie::$salt = NULL;
Cookie::salt('key', 'value');
}
/**
* @covers Cookie::salt
*/
public function test_salt_creates_same_hash_for_same_values_and_state()
{
$name = 'cookie';
$value = 'value';
$this->assertEquals(Cookie::salt($name, $value), Cookie::salt($name, $value));
}
/**
* Provider for test_salt_creates_different_hash_for_different_data
*
* @return array
*/
public function provider_salt_creates_different_hash_for_different_data()
{
return array(
array(array('name' => 'foo', 'value' => 'bar', 'salt' => 'our-salt', 'user-agent' => 'Chrome'), array('name' => 'changed')),
array(array('name' => 'foo', 'value' => 'bar', 'salt' => 'our-salt', 'user-agent' => 'Chrome'), array('value' => 'changed')),
array(array('name' => 'foo', 'value' => 'bar', 'salt' => 'our-salt', 'user-agent' => 'Chrome'), array('salt' => 'changed-salt')),
array(array('name' => 'foo', 'value' => 'bar', 'salt' => 'our-salt', 'user-agent' => 'Chrome'), array('user-agent' => 'Firefox')),
array(array('name' => 'foo', 'value' => 'bar', 'salt' => 'our-salt', 'user-agent' => 'Chrome'), array('user-agent' => NULL)),
);
}
/**
* @param array $first_args
* @param array $changed_args
*
* @dataProvider provider_salt_creates_different_hash_for_different_data
* @covers Cookie::salt
*/
public function test_salt_creates_different_hash_for_different_data($first_args, $changed_args)
{
$second_args = array_merge($first_args, $changed_args);
$hashes = array();
foreach (array($first_args, $second_args) as $args)
{
Cookie::$salt = $args['salt'];
$this->set_or_remove_http_user_agent($args['user-agent']);
$hashes[] = Cookie::salt($args['name'], $args['value']);
}
$this->assertNotEquals($hashes[0], $hashes[1]);
}
/**
* Verify that a cookie was deleted from the global $_COOKIE array, and that a setcookie call was made to remove it
* from the client.
*
* @param string $name
*/
// @codingStandardsIgnoreStart
public function tearDown()
protected function assertDeletedCookie($name)
// @codingStandardsIgnoreEnd
{
parent::tearDown();
Cookie::$salt = NULL;
$this->assertArrayNotHasKey($name, $_COOKIE);
// To delete the client-side cookie, Cookie::delete should send a new cookie with value NULL and expiry in the past
$this->assertSetCookieWith(array(
'name' => $name,
'value' => NULL,
'expire' => -86400,
'path' => Cookie::$path,
'domain' => Cookie::$domain,
'secure' => Cookie::$secure,
'httponly' => Cookie::$httponly
));
}
/**
* Provides test data for test_set()
* Verify that there was a single call to setcookie including the provided named arguments
*
* @return array
* @param array $expected
*/
public function provider_set()
// @codingStandardsIgnoreStart
protected function assertSetCookieWith($expected)
// @codingStandardsIgnoreEnd
{
return array(
array('foo', 'bar', NULL, TRUE),
array('foo', 'bar', 10, TRUE),
);
$this->assertCount(1, Kohana_CookieTest_TestableCookie::$_mock_cookies_set);
$relevant_values = array_intersect_key(Kohana_CookieTest_TestableCookie::$_mock_cookies_set[0], $expected);
$this->assertEquals($expected, $relevant_values);
}
/**
* Tests cookie::set()
* Configure the $_SERVER[HTTP_USER_AGENT] environment variable for the test
*
* @test
* @dataProvider provider_set
* @covers cookie::set
* @param mixed $key key to use
* @param mixed $value value to set
* @param mixed $exp exp to set
* @param boolean $expected Output for cookie::set()
* @param string $user_agent
*/
public function test_set($key, $value, $exp, $expected)
protected function set_or_remove_http_user_agent($user_agent)
{
if (headers_sent()) {
$this->markTestSkipped('Cannot test setting cookies as headers have already been sent');
}
$this->assertSame($expected, cookie::set($key, $value, $exp));
}
/**
* Provides test data for test_get()
*
* @return array
*/
public function provider_get()
{
// setUp is called after the provider so we need to specify a
// salt here in order to use it in the provider
Cookie::$salt = $this->_default_salt;
return array(
array('foo', Cookie::salt('foo', 'bar').'~bar', 'bar'),
array('bar', Cookie::salt('foo', 'bar').'~bar', NULL),
array(NULL, Cookie::salt('foo', 'bar').'~bar', NULL),
);
}
/**
* Tests cookie::set()
*
* @test
* @dataProvider provider_get
* @covers cookie::get
* @param mixed $key key to use
* @param mixed $value value to set
* @param boolean $expected Output for cookie::get()
*/
public function test_get($key, $value, $expected)
{
if (headers_sent()) {
$this->markTestSkipped('Cannot test setting cookies as headers have already been sent');
}
// Force $_COOKIE
if ($key !== NULL)
if ($user_agent === NULL)
{
$_COOKIE[$key] = $value;
unset($_SERVER['HTTP_USER_AGENT']);
}
$this->assertSame($expected, cookie::get($key));
}
/**
* Provides test data for test_delete()
*
* @return array
*/
public function provider_delete()
{
return array(
array('foo', TRUE),
);
}
/**
* Tests cookie::delete()
*
* @test
* @dataProvider provider_delete
* @covers cookie::delete
* @param mixed $key key to use
* @param boolean $expected Output for cookie::delete()
*/
public function test_delete($key, $expected)
{
if (headers_sent()) {
$this->markTestSkipped('Cannot test setting cookies as headers have already been sent');
else
{
$_SERVER['HTTP_USER_AGENT'] = $user_agent;
}
$this->assertSame($expected, cookie::delete($key));
}
/**
* Provides test data for test_salt()
*
* @return array
*/
public function provider_salt()
{
return array(
array('foo', 'bar', 'b5773a6255d1deefc23f9f69bcc40fdc998e5802'),
);
}
/**
* Tests cookie::salt()
*
* @test
* @dataProvider provider_salt
* @covers cookie::salt
* @param mixed $key key to use
* @param mixed $value value to salt with
* @param boolean $expected Output for cookie::delete()
*/
public function test_salt($key, $value, $expected)
{
$this->assertSame($expected, cookie::salt($key, $value));
}
}
/**
* Class Kohana_CookieTest_TestableCookie wraps the cookie class to mock out the actual setcookie and time calls for
* unit testing.
*/
class Kohana_CookieTest_TestableCookie extends Cookie {
/**
* @var array setcookie calls that were made
*/
public static $_mock_cookies_set = array();
/**
* {@inheritdoc}
*/
protected static function _setcookie($name, $value, $expire, $path, $domain, $secure, $httponly)
{
self::$_mock_cookies_set[] = array(
'name' => $name,
'value' => $value,
'expire' => $expire,
'path' => $path,
'domain' => $domain,
'secure' => $secure,
'httponly' => $httponly
);
return TRUE;
}
/**
* @return int
*/
protected static function _time()
{
return Kohana_CookieTest::UNIX_TIMESTAMP;
}
}

View File

@@ -18,6 +18,32 @@
*/
class Kohana_CoreTest extends Unittest_TestCase
{
protected $old_modules = array();
/**
* Captures the module list as it was before this test
*
* @return null
*/
// @codingStandardsIgnoreStart
public function setUp()
// @codingStandardsIgnoreEnd
{
parent::setUp();
$this->old_modules = Kohana::modules();
}
/**
* Restores the module list
*
* @return null
*/
// @codingStandardsIgnoreStart
public function tearDown()
// @codingStandardsIgnoreEnd
{
Kohana::modules($this->old_modules);
}
/**
* Provides test data for test_sanitize()
@@ -107,33 +133,15 @@ class Kohana_CoreTest extends Unittest_TestCase
*/
public function test_globals_removes_user_def_globals()
{
// Store the globals
$temp_globals = array(
'cookie' => $_COOKIE,
'get' => $_GET,
'files' => $_FILES,
'post' => $_POST,
'request' => $_REQUEST,
'server' => $_SERVER,
'session' => $_SESSION,
'globals' => $GLOBALS,
);
$GLOBALS = array('hackers' => 'foobar','name' => array('','',''), '_POST' => array());
$GLOBALS['hackers'] = 'foobar';
$GLOBALS['name'] = array('','','');
$GLOBALS['_POST'] = array();
Kohana::globals();
$this->assertEquals(array('_POST' => array()), $GLOBALS);
// Reset the globals for other tests
$_COOKIE = $temp_globals['cookie'];
$_GET = $temp_globals['get'];
$_FILES = $temp_globals['files'];
$_POST = $temp_globals['post'];
$_REQUEST = $temp_globals['request'];
$_SERVER = $temp_globals['server'];
$_SESSION = $temp_globals['session'];
$GLOBALS = $temp_globals['globals'];
$this->assertFalse(isset($GLOBALS['hackers']));
$this->assertFalse(isset($GLOBALS['name']));
$this->assertTrue(isset($GLOBALS['_POST']));
}
/**
@@ -175,35 +183,18 @@ class Kohana_CoreTest extends Unittest_TestCase
public function provider_message()
{
return array(
// $value, $result
array(':field must not be empty', 'validation', 'not_empty'),
array(
array('no_message_file', 'anything', 'default', 'default'),
array('no_message_file', NULL, 'anything', array()),
array('kohana_core_message_tests', 'bottom_only', 'anything', 'inherited bottom message'),
array('kohana_core_message_tests', 'cfs_replaced', 'anything', 'overriding cfs_replaced message'),
array('kohana_core_message_tests', 'top_only', 'anything', 'top only message'),
array('kohana_core_message_tests', 'missing', 'default', 'default'),
array('kohana_core_message_tests', NULL, 'anything',
array(
'alpha' => ':field must contain only letters',
'alpha_dash' => ':field must contain only numbers, letters and dashes',
'alpha_numeric' => ':field must contain only letters and numbers',
'color' => ':field must be a color',
'credit_card' => ':field must be a credit card number',
'date' => ':field must be a date',
'decimal' => ':field must be a decimal with :param2 places',
'digit' => ':field must be a digit',
'email' => ':field must be a email address',
'email_domain' => ':field must contain a valid email domain',
'equals' => ':field must equal :param2',
'exact_length' => ':field must be exactly :param2 characters long',
'in_array' => ':field must be one of the available options',
'ip' => ':field must be an ip address',
'matches' => ':field must be the same as :param2',
'min_length' => ':field must be at least :param2 characters long',
'max_length' => ':field must not exceed :param2 characters long',
'not_empty' => ':field must not be empty',
'numeric' => ':field must be numeric',
'phone' => ':field must be a phone number',
'range' => ':field must be within the range of :param2 to :param3',
'regex' => ':field does not match the required format',
'url' => ':field must be a url',
),
'validation', NULL,
'bottom_only' => 'inherited bottom message',
'cfs_replaced' => 'overriding cfs_replaced message',
'top_only' => 'top only message'
)
),
);
}
@@ -213,15 +204,18 @@ class Kohana_CoreTest extends Unittest_TestCase
*
* @test
* @dataProvider provider_message
* @covers Kohana::message
* @param boolean $expected Output for Kohana::message
* @param boolean $file File to look in for Kohana::message
* @param boolean $key Key for Kohana::message
* @covers Kohana::message
* @param string $file to pass to Kohana::message
* @param string $key to pass to Kohana::message
* @param string $default to pass to Kohana::message
* @param string $expected Output for Kohana::message
*/
public function test_message($expected, $file, $key)
public function test_message($file, $key, $default, $expected)
{
$this->markTestSkipped('This test is incredibly fragile and needs to be re-done');
$this->assertEquals($expected, Kohana::message($file, $key));
$test_path = realpath(dirname(__FILE__).'/../test_data/message_tests');
Kohana::modules(array('top' => "$test_path/top_module", 'bottom' => "$test_path/bottom_module"));
$this->assertEquals($expected, Kohana::message($file, $key, $default, $expected));
}
/**
@@ -314,7 +308,7 @@ class Kohana_CoreTest extends Unittest_TestCase
{
return array(
array(array(), array()),
array(array('unittest' => MODPATH.'unittest'), array('unittest' => $this->dirSeparator(MODPATH.'unittest/'))),
array(array('module' => __DIR__), array('module' => $this->dirSeparator(__DIR__.'/'))),
);
}

View File

@@ -0,0 +1,747 @@
<?php defined('SYSPATH') OR die('Kohana bootstrap needs to be included before tests run');
/**
* Tests the encrypt class
*
* @group kohana
* @group kohana.core
* @group kohana.core.encrypt
*
* @package Kohana
* @category Tests
* @author Kohana Team
* @author Samuel Demirdjian <sam@enov.ws>
* @copyright (c) 2014 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_EncryptTest extends Unittest_TestCase
{
/**
* Provider for test_encode
* AES Multiblock Message Test (MMT) Sample Vectors - Known Answer Test (KAT)
* @link http://csrc.nist.gov/groups/STM/cavp/index.html NIST - Cryptographic Algorithm Validation Program
* @link http://csrc.nist.gov/groups/STM/cavp/documents/aes/aesmmt.zip file used CBCMMT128.rsp
*
* @return array of $mode, $cipher, $key, $iv, $txt_plain, $txt_encoded
*/
public function provider_encode()
{
return array(
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "1f8e4973953f3fb0bd6b16662e9a3c17"),
// IV
pack("H*", "2fe2b333ceda8f98f4a99b40d2cd34a8"),
// txt_plain
pack("H*", "45cf12964fc824ab76616ae2f4bf0822"),
// txt_encoded
pack("H*", "0f61c4d44c5147c03c195ad7e2cc12b2"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "1f8e4973953f3fb0bd6b16662e9a3c17"),
// IV
pack("H*", "2fe2b333ceda8f98f4a99b40d2cd34a8"),
// txt_plain
pack("H*", "45cf12964fc824ab76616ae2f4bf0822"),
// txt_encoded
pack("H*", "0f61c4d44c5147c03c195ad7e2cc12b2"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "0700d603a1c514e46b6191ba430a3a0c"),
// IV
pack("H*", "aad1583cd91365e3bb2f0c3430d065bb"),
// txt_plain
pack("H*", "068b25c7bfb1f8bdd4cfc908f69dffc5ddc726a197f0e5f720f730393279be91"),
// txt_encoded
pack("H*", "c4dc61d9725967a3020104a9738f23868527ce839aab1752fd8bdb95a82c4d00"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "3348aa51e9a45c2dbe33ccc47f96e8de"),
// IV
pack("H*", "19153c673160df2b1d38c28060e59b96"),
// txt_plain
pack("H*", "9b7cee827a26575afdbb7c7a329f887238052e3601a7917456ba61251c214763d5e1847a6ad5d54127a399ab07ee3599"),
// txt_encoded
pack("H*", "d5aed6c9622ec451a15db12819952b6752501cf05cdbf8cda34a457726ded97818e1f127a28d72db5652749f0c6afee5"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "b7f3c9576e12dd0db63e8f8fac2b9a39"),
// IV
pack("H*", "c80f095d8bb1a060699f7c19974a1aa0"),
// txt_plain
pack("H*", "9ac19954ce1319b354d3220460f71c1e373f1cd336240881160cfde46ebfed2e791e8d5a1a136ebd1dc469dec00c4187722b841cdabcb22c1be8a14657da200e"),
// txt_encoded
pack("H*", "19b9609772c63f338608bf6eb52ca10be65097f89c1e0905c42401fd47791ae2c5440b2d473116ca78bd9ff2fb6015cfd316524eae7dcb95ae738ebeae84a467"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "b6f9afbfe5a1562bba1368fc72ac9d9c"),
// IV
pack("H*", "3f9d5ebe250ee7ce384b0d00ee849322"),
// txt_plain
pack("H*", "db397ec22718dbffb9c9d13de0efcd4611bf792be4fce0dc5f25d4f577ed8cdbd4eb9208d593dda3d4653954ab64f05676caa3ce9bfa795b08b67ceebc923fdc89a8c431188e9e482d8553982cf304d1"),
// txt_encoded
pack("H*", "10ea27b19e16b93af169c4a88e06e35c99d8b420980b058e34b4b8f132b13766f72728202b089f428fecdb41c79f8aa0d0ef68f5786481cca29e2126f69bc14160f1ae2187878ba5c49cf3961e1b7ee9"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "bbe7b7ba07124ff1ae7c3416fe8b465e"),
// IV
pack("H*", "7f65b5ee3630bed6b84202d97fb97a1e"),
// txt_plain
pack("H*", "2aad0c2c4306568bad7447460fd3dac054346d26feddbc9abd9110914011b4794be2a9a00a519a51a5b5124014f4ed2735480db21b434e99a911bb0b60fe0253763725b628d5739a5117b7ee3aefafc5b4c1bf446467e7bf5f78f31ff7caf187"),
// txt_encoded
pack("H*", "3b8611bfc4973c5cd8e982b073b33184cd26110159172e44988eb5ff5661a1e16fad67258fcbfee55469267a12dc374893b4e3533d36f5634c3095583596f135aa8cd1138dc898bc5651ee35a92ebf89ab6aeb5366653bc60a70e0074fc11efe"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "89a553730433f7e6d67d16d373bd5360"),
// IV
pack("H*", "f724558db3433a523f4e51a5bea70497"),
// txt_plain
pack("H*", "807bc4ea684eedcfdcca30180680b0f1ae2814f35f36d053c5aea6595a386c1442770f4d7297d8b91825ee7237241da8925dd594ccf676aecd46ca2068e8d37a3a0ec8a7d5185a201e663b5ff36ae197110188a23503763b8218826d23ced74b31e9f6e2d7fbfa6cb43420c7807a8625"),
// txt_encoded
pack("H*", "406af1429a478c3d07e555c5287a60500d37fc39b68e5bbb9bafd6ddb223828561d6171a308d5b1a4551e8a5e7d572918d25c968d3871848d2f16635caa9847f38590b1df58ab5efb985f2c66cfaf86f61b3f9c0afad6c963c49cee9b8bc81a2ddb06c967f325515a4849eec37ce721a"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "c491ca31f91708458e29a925ec558d78"),
// IV
pack("H*", "9ef934946e5cd0ae97bd58532cb49381"),
// txt_plain
pack("H*", "cb6a787e0dec56f9a165957f81af336ca6b40785d9e94093c6190e5152649f882e874d79ac5e167bd2a74ce5ae088d2ee854f6539e0a94796b1e1bd4c9fcdbc79acbef4d01eeb89776d18af71ae2a4fc47dd66df6c4dbe1d1850e466549a47b636bcc7c2b3a62495b56bb67b6d455f1eebd9bfefecbca6c7f335cfce9b45cb9d"),
// txt_encoded
pack("H*", "7b2931f5855f717145e00f152a9f4794359b1ffcb3e55f594e33098b51c23a6c74a06c1d94fded7fd2ae42c7db7acaef5844cb33aeddc6852585ed0020a6699d2cb53809cefd169148ce42292afab063443978306c582c18b9ce0da3d084ce4d3c482cfd8fcf1a85084e89fb88b40a084d5e972466d07666126fb761f84078f2"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "f6e87d71b0104d6eb06a68dc6a71f498"),
// IV
pack("H*", "1c245f26195b76ebebc2edcac412a2f8"),
// txt_plain
pack("H*", "f82bef3c73a6f7f80db285726d691db6bf55eec25a859d3ba0e0445f26b9bb3b16a3161ed1866e4dd8f2e5f8ecb4e46d74a7a78c20cdfc7bcc9e479ba7a0caba9438238ad0c01651d5d98de37f03ddce6e6b4bd4ab03cf9e8ed818aedfa1cf963b932067b97d776dce1087196e7e913f7448e38244509f0caf36bd8217e15336d35c149fd4e41707893fdb84014f8729"),
// txt_encoded
pack("H*", "b09512f3eff9ed0d85890983a73dadbb7c3678d52581be64a8a8fc586f490f2521297a478a0598040ebd0f5509fafb0969f9d9e600eaef33b1b93eed99687b167f89a5065aac439ce46f3b8d22d30865e64e45ef8cd30b6984353a844a11c8cd60dba0e8866b3ee30d24b3fa8a643b328353e06010fa8273c8fd54ef0a2b6930e5520aae5cd5902f9b86a33592ca4365"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "2c14413751c31e2730570ba3361c786b"),
// IV
pack("H*", "1dbbeb2f19abb448af849796244a19d7"),
// txt_plain
pack("H*", "40d930f9a05334d9816fe204999c3f82a03f6a0457a8c475c94553d1d116693adc618049f0a769a2eed6a6cb14c0143ec5cccdbc8dec4ce560cfd206225709326d4de7948e54d603d01b12d7fed752fb23f1aa4494fbb00130e9ded4e77e37c079042d828040c325b1a5efd15fc842e44014ca4374bf38f3c3fc3ee327733b0c8aee1abcd055772f18dc04603f7b2c1ea69ff662361f2be0a171bbdcea1e5d3f"),
// txt_encoded
pack("H*", "6be8a12800455a320538853e0cba31bd2d80ea0c85164a4c5c261ae485417d93effe2ebc0d0a0b51d6ea18633d210cf63c0c4ddbc27607f2e81ed9113191ef86d56f3b99be6c415a4150299fb846ce7160b40b63baf1179d19275a2e83698376d28b92548c68e06e6d994e2c1501ed297014e702cdefee2f656447706009614d801de1caaf73f8b7fa56cf1ba94b631933bbe577624380850f117435a0355b2b"),
),
);
}
/**
* @param string $mode
* @param string $cipher
* @param string $key Encryption key
* @param string $iv Initialization vector
* @param string $txt_plain Plain text to be encrypted
* @param string $txt_encoded Known ecrypted text
*
* @dataProvider provider_encode
* @covers Encrypt::encode
*/
public function test_encode($mode, $cipher, $key, $iv, $txt_plain, $txt_encoded)
{
// initialize
$e = new Kohana_EncryptTest_IvStubbed($key, $iv, $mode, $cipher);
// prepare data
$expected = base64_encode($iv . $txt_encoded);
$actual = $e->encode($txt_plain);
// assert
$this->assertSame($expected, $actual);
}
/**
* Provider for test_decode
* AES Multiblock Message Test (MMT) Sample Vectors - Known Answer Test (KAT)
* @link http://csrc.nist.gov/groups/STM/cavp/index.html NIST - Cryptographic Algorithm Validation Program
* @link http://csrc.nist.gov/groups/STM/cavp/documents/aes/aesmmt.zip file used CBCMMT128.rsp
*
* @return array of $mode, $cipher, $key, $iv, $txt_encoded, $txt_plain
*/
public function provider_decode()
{
return array(
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "6a7082cf8cda13eff48c8158dda206ae"),
// IV
pack("H*", "bd4172934078c2011cb1f31cffaf486e"),
// txt_encoded
pack("H*", "f8eb31b31e374e960030cd1cadb0ef0c"),
// txt_plain
pack("H*", "940bc76d61e2c49dddd5df7f37fcf105"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "625eefa18a4756454e218d8bfed56e36"),
// IV
pack("H*", "73d9d0e27c2ec568fbc11f6a0998d7c8"),
// txt_encoded
pack("H*", "5d6fed86f0c4fe59a078d6361a142812514b295dc62ff5d608a42ea37614e6a1"),
// txt_plain
pack("H*", "360dc1896ce601dfb2a949250067aad96737847a4580ede2654a329b842fe81e"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "fd6e0b954ae2e3b723d6c9fcae6ab09b"),
// IV
pack("H*", "f08b65c9f4dd950039941da2e8058c4e"),
// txt_encoded
pack("H*", "e29e3114c8000eb484395b256b1b3267894f290d3999819ff35da03e6463c186c4d7ebb964941f1986a2d69572fcaba8"),
// txt_plain
pack("H*", "a206385945b21f812a9475f47fddbb7fbdda958a8d14c0dbcdaec36e8b28f1f6ececa1ceae4ce17721d162c1d42a66c1"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "7b1ab9144b0239315cd5eec6c75663bd"),
// IV
pack("H*", "0b1e74f45c17ff304d99c059ce5cde09"),
// txt_encoded
pack("H*", "d3f89b71e033070f9d7516a6cb4ea5ef51d6fb63d4f0fea089d0a60e47bbb3c2e10e9ba3b282c7cb79aefe3068ce228377c21a58fe5a0f8883d0dbd3d096beca"),
// txt_plain
pack("H*", "b968aeb199ad6b3c8e01f26c2edad444538c78bfa36ed68ca76123b8cdce615a01f6112bb80bfc3f17490578fb1f909a52e162637b062db04efee291a1f1af60"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "36466b6bd25ea3857ea42f0cac1919b1"),
// IV
pack("H*", "7186fb6bdfa98a16189544b228f3bcd3"),
// txt_encoded
pack("H*", "9ed957bd9bc52bba76f68cfbcde52157a8ca4f71ac050a3d92bdebbfd7c78316b4c9f0ba509fad0235fdafe90056ad115dfdbf08338b2acb1c807a88182dd2a882d1810d4302d598454e34ef2b23687d"),
// txt_plain
pack("H*", "999983467c47bb1d66d7327ab5c58f61ddb09b93bd2460cb78cbc12b5fa1ea0c5f759ccc5e478697687012ff4673f6e61eecaeda0ccad2d674d3098c7d17f887b62b56f56b03b4d055bf3a4460e83efa"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "89373ee6e28397640d5082eed4123239"),
// IV
pack("H*", "1a74d7c859672c804b82472f7e6d3c6b"),
// txt_encoded
pack("H*", "1bcba44ddff503db7c8c2ec4c4eea0e827957740cce125c1e11769842fa97e25f1b89269e6d77923a512a358312f4ba1cd33f2d111280cd83e1ef9e7cf7036d55048d5c273652afa611cc81b4e9dac7b5078b7c4716062e1032ead1e3329588a"),
// txt_plain
pack("H*", "45efd00daa4cdc8273ef785cae9e944a7664a2391e1e2c449f475acec0124bbc22944331678617408a1702917971f4654310ffb9229bec6173715ae512d37f93aaa6abf009f7e30d65669d1db0366b5bce4c7b00f871014f5753744a1878dc57"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "bab0cceddc0abd63e3f82e9fbff7b8aa"),
// IV
pack("H*", "68b9140f300490c5c942f66e777eb806"),
// txt_encoded
pack("H*", "c65b94b1f291fa9f0600f22c3c0432c895ad5d177bcccc9ea44e8ec339c9adf43855b326179d6d81aa36ef59462fd86127e9d81b0f286f93306bf74d4c79e47c1b3d4b74edd3a16290e3c63b742e41f20d66ceee794316bb63d3bd002712a1b136ba6185bd5c1dab81b07db90d2af5e5"),
// txt_plain
pack("H*", "c5585ff215bbb73ba5393440852fb199436de0d15e55c631f877670aa3eda9f672eb1f876f09544e63558436b8928000db2f02a5ad90f95b05ac4cf49e198e617e7678480fdf0efacc6aae691271e6cdd3541ebf719a1ccaedb24e2f80f92455dd5910cb5086b0960a3942ec182dcbd7"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "9c702898efa44557b29ed283f5bc0293"),
// IV
pack("H*", "cec6e1b82e8b2a591a9fa5ff1cf5cc51"),
// txt_encoded
pack("H*", "ba9f646755dacc22911f51d7de2f7e7cb0bc0b75257ea44fe883edb055c7c28ede04c3a0adcb10128ad4517d0093fa16bb0bcd2635e7a0ba92c7609bc8d8568002a7a983473724d256513aa7d51b477aabec1975ab5faf2872a6407e922180eff02f1ef86a4591c8bd3d143da6f0ef0e4806f94ace0d5b0151c99640fccbc843"),
// txt_plain
pack("H*", "1d1f8d81bdc3e2c7cb057f408e6450000c5aaed3260ff1e87fbb6f324df6887ffd8f78d7e2a04c9ed9deda9d64482d2b002f4a2b78d8b4f691875c8295d4a64b22257ceaf713ed2f4b92530d7ad7151d629acda882b4829577a43990b0948c1149c22fe4273656d1b08833930e8b06709a94579a78fc220f7057bbc1fa9f6563"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "5674636dbdb38f705f0b08c372ef4785"),
// IV
pack("H*", "3f20ce0509b57420d53b6be4d0b7f0a9"),
// txt_encoded
pack("H*", "198351f453103face6655666fe90bdbd9630e3733b2d66c013a634e91f2bf015bd2d975d71b26322e44defa32d4e9dce50363557046ece08ba38f258dae5fd3e5049c647476c81e73482e40c171d89f9fea29452caf995733589b0061464fbd5dabe27dc5ea463a3deeb7dcb43664ae6a65c498c143883ab8e83b51e5410b181647602443dc3cfffe86f0205398fa83c"),
// txt_plain
pack("H*", "6d40fd2f908f48ce19241b6b278b1b1676dffd4a97ce9f8a1574c33bc59237deb536bee376fd6c381e6987700e39283aa111cf1a59f26fae6fb6700bf012646a2ab80239bf5e1632329043aa87d7911978b36523a2bc0bed9a9737ccf7a00baa2f3822b4e9e742e168e7069290705fed2eb63aa044b78f97dd33a8d6b24741ec1fd8c8db79d93b884e762dba0f406961"),
),
array(
// mode
MCRYPT_MODE_CBC,
// cypher
MCRYPT_RIJNDAEL_128,
// key
pack("H*", "97a1025529b9925e25bbe78770ca2f99"),
// IV
pack("H*", "d4b4eab92aa9637e87d366384ed6915c"),
// txt_encoded
pack("H*", "22cdc3306fcd4d31ccd32720cbb61bad28d855670657c48c7b88c31f4fa1f93c01b57da90be63ead67d6a325525e6ed45083e6fb70a53529d1fa0f55653b942af59d78a2660361d63a7290155ac5c43312a25b235dacbbc863faf00940c99624076dfa44068e7c554c9038176953e571751dfc0954d41d113771b06466b1c8d13e0d4cb675ed58d1a619e1540970983781dc11d2dd8525ab5745958d615defda"),
// txt_plain
pack("H*", "e8b89150d8438bf5b17449d6ed26bd72127e10e4aa57cad85283e8359e089208e84921649f5b60ea21f7867cbc9620560c4c6238db021216db453c9943f1f1a60546173daef2557c3cdd855031b353d4bf176f28439e48785c37d38f270aa4a6faad2baabcb0c0b2d1dd5322937498ce803ba1148440a52e227ddba4872fe4d81d2d76a939d24755adb8a7b8452ceed2d179e1a5848f316f5c016300a390bfa7"),
),
);
}
/**
* @param string $mode
* @param string $cipher
* @param string $key Encryption key
* @param string $iv Initialization vector
* @param string $txt_encoded ecrypted text
* @param string $txt_plain Known plain text that is decripted
*
* @dataProvider provider_decode
* @covers Encrypt::decode
*/
public function test_decode($mode, $cipher, $key, $iv, $txt_encoded, $txt_plain)
{
// initialize
$e = new Encrypt($key, $mode, $cipher);
// prepare data
$expected = $txt_plain;
$actual = $e->decode(base64_encode($iv . $txt_encoded));
// assert
$this->assertSame($expected, $actual);
}
/**
* Provider for test_encode_decode, test_consecutive_encode_different_results
*
* @return array of $key, $mode, $cipher, $txt_plain
*/
public function provider_encode_decode()
{
return array(
array(
// key
"Some super secret key",
// mode
MCRYPT_MODE_NOFB,
// cypher
MCRYPT_RIJNDAEL_128,
// txt_plain
"The quick brown fox jumps over the lazy dog",
),
array(
// key
"De finibus bonorum et malorum",
// mode
MCRYPT_MODE_NOFB,
// cypher
MCRYPT_RIJNDAEL_128,
// txt_plain
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
),
);
}
/**
* @param type $key Encryption Key
* @param type $mode Encryption Mode
* @param type $cipher Encryption Cipher
* @param type $txt_plain Plain text to encode and then decode back
*
* @dataProvider provider_encode_decode
* @covers Encrypt::encode
* @covers Encrypt::decode
*/
public function test_encode_decode($key, $mode, $cipher, $txt_plain)
{
// initialize, encode
$e = new Encrypt($key, $mode, $cipher);
$txt_encoded = $e->encode($txt_plain);
// prepare data
$expected = $txt_plain;
$actual = $e->decode($txt_encoded);
// assert
$this->assertSame($expected, $actual);
}
/**
* Provider for test_decode_invalid_data
*
* @return array of $key, $mode, $cipher, $txt_invalid_encoded
*/
public function provider_decode_invalid_data()
{
return array(
array(
// key
"Some super secret key",
// mode
MCRYPT_MODE_NOFB,
// cypher
MCRYPT_RIJNDAEL_128,
// txt_invalid_encoded
".:This data is not a valid base 64 string:.",
),
array(
// key
"Some super secret key",
// mode
MCRYPT_MODE_NOFB,
// cypher
MCRYPT_RIJNDAEL_128,
// txt_invalid_encoded
base64_encode("too short"),
),
);
}
/**
* Tests for decode when the string is not valid base64,
* or is too short to contain a valid IV
*
* @param type $key
* @param type $mode
* @param type $cipher
* @param type $txt_encoded
*
* @dataProvider provider_decode_invalid_data
*/
public function test_decode_invalid_data($key, $mode, $cipher, $txt_invalid_encoded)
{
// initialize
$e = new Encrypt($key, $mode, $cipher);
// assert
$this->AssertFalse($e->decode($txt_invalid_encoded));
}
/**
* @param type $key Encryption Key
* @param type $mode Encryption Mode
* @param type $cipher Encryption Cipher
* @param type $txt_plain Plain text to encode and then decode back
*
* @dataProvider provider_encode_decode
* @covers Encrypt::encode
*/
public function test_consecutive_encode_produce_different_results($key, $mode, $cipher, $txt_plain)
{
// initialize, encode twice
$e = new Encrypt($key, $mode, $cipher);
$txt_encoded_first = $e->encode($txt_plain);
$txt_encoded_second = $e->encode($txt_plain);
// assert
$this->assertNotEquals($txt_encoded_first, $txt_encoded_second);
}
/**
* Provider for test_key_normalization
*
* @return array of $key, $iv, $mode, $cipher, $txt_plain
*/
public function provider_key_normalization()
{
return array(
array(
// key
"Some super secret key",
// IV
pack("H*", "2fe2b333ceda8f98f4a99b40d2cd34a8"),
// mode
MCRYPT_MODE_NOFB,
// cypher
MCRYPT_RIJNDAEL_128,
// txt_plain
"The quick brown fox jumps over the lazy dog",
),
array(
// key
"De finibus bonorum et malorum",
// IV
pack("H*", "2fe2b333ceda8f98f4a99b40d2cd34a8"),
// mode
MCRYPT_MODE_NOFB,
// cypher
MCRYPT_RIJNDAEL_128,
// txt_plain
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
),
);
}
/**
* Test if key normalization logic behaves well
* Encrypt::_normalize_key was ment for PHP > 5.6.0
*
* We are testing our key normalization only against lower versions of PHP
* (PHP < 5.6.0) to see if it matches the internal key padding those
* PHP versions already have
*
* @param type $key Encryption Key
* @param type $mode Encryption Mode
* @param type $cipher Encryption Cipher
* @param type $txt_plain Plain text to encode and then decode back
*
* @dataProvider provider_key_normalization
* @covers Encrypt::_normalize_key
*/
public function test_key_normalization($key, $iv, $mode, $cipher, $txt_plain)
{
if (version_compare(PHP_VERSION, '5.6.0', '>='))
{
$this->markTestSkipped('Starting from PHP 5.6.0, mcrypt does not pad encryption keys with null bytes.');
}
// initialize, encode twice
$e1 = new Kohana_EncryptTest_IvStubbed($key, $iv, $mode, $cipher);
$e2 = new Kohana_EncryptTest_KeyNormalized($key, $iv, $mode, $cipher);
$txt_encoded_1 = $e1->encode($txt_plain);
$txt_encoded_2 = $e2->encode($txt_plain);
// assert
$this->assertSame($txt_encoded_1, $txt_encoded_2);
}
/**
* @expectedException Kohana_Exception
* @expectedExceptionMessage No encryption key is defined in the encryption configuration group
*/
public function test_instance_throw_exception_when_no_key_provided()
{
Encrypt::instance();
}
/**
* Provider for test_instance_returns_singleton
*
* @return array of $instance_name, $missing_config
*/
public function provider_instance_returns_singleton()
{
return array(
array(
'default',
array(
'key' => 'trwQwVXX96TIJoKxyBHB9AJkwAOHixuV1ENZmIWyanI0j1zNgSVvqywy044Agaj',
)
),
array(
'blowfish',
array(
'key' => '7bZJJkmNrelj5NaKoY6h6rMSRSmeUlJuTeOd5HHka5XknyMX4uGSfeVolTz4IYy',
'cipher' => MCRYPT_BLOWFISH,
'mode' => MCRYPT_MODE_ECB,
)
),
array(
'tripledes',
array(
'key' => 'a9hcSLRvA3LkFc7EJgxXIKQuz1ec91J7P6WNq1IaxMZp4CTj5m31gZLARLxI1jD',
'cipher' => MCRYPT_3DES,
'mode' => MCRYPT_MODE_CBC,
)
),
);
}
/**
* Test to multiple calls to the instance() method returns same instance
* also test if the instances are appropriately configured.
*
* @param string $instance_name instance name
* @param array $config_array array of config variables missing from config
*
* @dataProvider provider_instance_returns_singleton
*/
public function test_instance_returns_singleton($instance_name, array $config_array)
{
// load config
$config = Kohana::$config->load('encrypt');
// if instance name is NULL the config group should be the default
$config_group = $instance_name ? : Encrypt::$default;
// if config group does not exists, create
if (!array_key_exists($config_group, $config))
{
$config[$config_group] = array();
}
// fill in the missing config variables
$config[$config_group] = $config[$config_group] + $config_array;
// call instance twice
$e = Encrypt::instance($instance_name);
$e2 = Encrypt::instance($instance_name);
// assert instances
$this->assertInstanceOf('Encrypt', $e);
$this->assertInstanceOf('Encrypt', $e2);
$this->assertSame($e, $e2);
// test if instances are well configured
// prepare expected variables
$expected_cipher = $config[$config_group]['cipher'];
$expected_mode = $config[$config_group]['mode'];
$expected_key_size = mcrypt_get_key_size($expected_cipher, $expected_mode);
$expected_key = substr($config[$config_group]['key'], 0, $expected_key_size);
// assert
$this->assertSameProtectedProperty($expected_key, $e, '_key');
$this->assertSameProtectedProperty($expected_cipher, $e, '_cipher');
$this->assertSameProtectedProperty($expected_mode, $e, '_mode');
}
/**
* Helper method to test for private/protected properties
*
* @param mixed $expect Expected value
* @param mixed $object object that holds the private/protected property
* @param string $name the name of the private/protected property
*/
protected function assertSameProtectedProperty($expect, $object, $name)
{
$refl = new ReflectionClass($object);
$property = $refl->getProperty($name);
$property->setAccessible(TRUE);
$this->assertSame($expect, $property->getValue($object));
}
}
/**
* Class Kohana_EncryptTest_IvStubbed wraps the Encrypt class to mock out
* the actual mcrypt_create_iv calls for unit testing.
*/
class Kohana_EncryptTest_IvStubbed extends Encrypt
{
/**
* override constructor to force class use known IVs
*
* @param string $key encryption key
* @param string $iv feed a known IV
* @param string $mode mcrypt mode
* @param string $cipher mcrypt cipher
*/
public function __construct($key, $iv, $mode, $cipher)
{
parent::__construct($key, $mode, $cipher);
$this->_iv = $iv;
}
/**
* Fake a random initialization vector by returning a known one
*
* @return string a known IV
*/
protected function _create_iv()
{
return isset($this->_iv) ? $this->_iv : FALSE;
}
}
/**
* Class Kohana_EncryptTest_KeyNormalized wraps the Encrypt class to mock out
* the actual mcrypt_create_iv calls for unit testing, as well as to always
* normalize keys
*/
class Kohana_EncryptTest_KeyNormalized extends Kohana_EncryptTest_IvStubbed
{
/**
* override constructor to force key normalization
*
* @param string $key encryption key
* @param string $mode mcrypt mode
* @param string $cipher mcrypt cipher
*/
public function __construct($key, $iv, $mode, $cipher)
{
parent::__construct($key, $iv, $mode, $cipher);
$this->_key = $this->_normalize_key($this->_key, $this->_cipher, $this->_mode);
}
}

View File

@@ -16,6 +16,18 @@
*/
class Kohana_FeedTest extends Unittest_TestCase
{
/**
* Sets up the environment
*/
// @codingStandardsIgnoreStart
public function setUp()
// @codingStandardsIgnoreEnd
{
parent::setUp();
Kohana::$config->load('url')->set('trusted_hosts', array('localhost'));
}
/**
* Provides test data for test_parse()
*
@@ -25,7 +37,8 @@ class Kohana_FeedTest extends Unittest_TestCase
{
return array(
// $source, $expected
array('http://dev.kohanaframework.org/projects/kohana3/activity.atom', 15),
array(realpath(__DIR__.'/../test_data/feeds/activity.atom'), array('Proposals (Political/Workflow) #4839 (New)', 'Proposals (Political/Workflow) #4782')),
array(realpath(__DIR__.'/../test_data/feeds/example.rss20'), array('Example entry')),
);
}
@@ -38,11 +51,15 @@ class Kohana_FeedTest extends Unittest_TestCase
* @param string $source URL to test
* @param integer $expected Count of items
*/
public function test_parse($source, $expected)
public function test_parse($source, $expected_titles)
{
$this->markTestSkipped('We don\'t go to the internet for tests.');
$titles = array();
foreach (Feed::parse($source) as $item)
{
$titles[] = $item['title'];
}
$this->assertEquals($expected, count(Feed::parse($source)));
$this->assertSame($expected_titles, $titles);
}
/**

View File

@@ -5,7 +5,7 @@
*
* @group kohana
* @group kohana.core
* @group kohana.core.url
* @group kohana.core.file
*
* @package Kohana
* @category Tests
@@ -25,8 +25,7 @@ class Kohana_FileTest extends Unittest_TestCase
{
return array(
// $value, $result
array(Kohana::find_file('classes', 'File')),
array(Kohana::find_file('tests', 'test_data/github', 'png')),
array(Kohana::find_file('tests', 'test_data/github', 'png'), 'image/png'),
);
}
@@ -38,12 +37,10 @@ class Kohana_FileTest extends Unittest_TestCase
* @param boolean $input Input for File::mime
* @param boolean $expected Output for File::mime
*/
public function test_mime($input)
public function test_mime($input, $expected)
{
$this->markTestSkipped(
'This test doesn\'t do anything useful!'
);
$this->assertSame(1, preg_match('/^(?:application|audio|image|message|multipart|text|video)\/[a-z.+0-9-]+$/i', File::mime($input)));
//@todo: File::mime coverage needs significant improvement or to be dropped for a composer package - it's a "horribly unreliable" method with very little testing
$this->assertSame($expected, File::mime($input));
}
/**

View File

@@ -16,6 +16,18 @@
*/
class Kohana_HTMLTest extends Unittest_TestCase
{
/**
* Sets up the environment
*/
// @codingStandardsIgnoreStart
public function setUp()
// @codingStandardsIgnoreEnd
{
parent::setUp();
Kohana::$config->load('url')->set('trusted_hosts', array('www\.kohanaframework\.org'));
}
/**
* Defaults for this test
* @var array
@@ -117,6 +129,10 @@ class Kohana_HTMLTest extends Unittest_TestCase
'https',
FALSE
),
array(
'<script type="text/javascript" src="//google.com/script.js"></script>',
'//google.com/script.js',
),
);
}
@@ -193,6 +209,13 @@ class Kohana_HTMLTest extends Unittest_TestCase
'https',
TRUE
),
array(
'<link type="text/css" href="//google.com/style.css" rel="stylesheet" />',
'//google.com/style.css',
array(),
NULL,
FALSE
),
);
}
@@ -223,6 +246,20 @@ class Kohana_HTMLTest extends Unittest_TestCase
public function provider_anchor()
{
return array(
// a fragment-only anchor
array(
'<a href="#go-to-section-kohana">Kohana</a>',
array(),
'#go-to-section-kohana',
'Kohana',
),
// a query-only anchor
array(
'<a href="?cat=a">Category A</a>',
array(),
'?cat=a',
'Category A',
),
array(
'<a href="http://kohanaframework.org">Kohana</a>',
array(),

View File

@@ -15,6 +15,33 @@
*/
class Kohana_HTTPTest extends Unittest_TestCase {
protected $_inital_request;
/**
* Sets up the environment
*/
// @codingStandardsIgnoreStart
public function setUp()
// @codingStandardsIgnoreEnd
{
parent::setUp();
Kohana::$config->load('url')->set('trusted_hosts', array('www\.example\.com'));
$this->_initial_request = Request::$initial;
Request::$initial = new Request('/');
}
/**
* Tears down whatever is setUp
*/
// @codingStandardsIgnoreStart
public function tearDown()
// @codingStandardsIgnoreEnd
{
Request::$initial = $this->_initial_request;
parent::tearDown();
}
// @codingStandardsIgnoreStart
/**
* Defaults for this test
* @var array
@@ -84,4 +111,70 @@ class Kohana_HTTPTest extends Unittest_TestCase {
$this->fail('HTTP_Exception_Redirect not thrown');
}
/**
* Provides test data for test_request_headers
*
* @return array
*/
public function provider_request_headers()
{
return array(
array(
array(
'CONTENT_TYPE' => 'text/html; charset=utf-8',
'CONTENT_LENGTH' => '3547',
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'HTTP_ACCEPT_ENCODING' => 'gzip, deflate, sdch',
'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8,fr;q=0.6,hy;q=0.4',
),
array(
'content-type' => 'text/html; charset=utf-8',
'content-length' => '3547',
'accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'accept-encoding'=>'gzip, deflate, sdch',
'accept-language'=>'en-US,en;q=0.8,fr;q=0.6,hy;q=0.4',
)
),
array(
array(
'HTTP_WEIRD_HTTP_HEADER' => 'A weird value for a weird header',
),
array(
'weird-http-header' => 'A weird value for a weird header',
)
),
);
}
/**
* Tests HTTP::request_headers()
*
* HTTP::request_headers relies on the $_SERVER superglobal if the function
* `apache_request_headers` or the PECL `http` extension are not available.
*
* The test feeds the $_SERVER superglobal with the test cases' datasets
* and then restores the $_SERVER superglobal so that it does not affect
* other tests.
*
* @test
* @dataProvider provider_request_headers
* @param array $server_globals globals to feed $_SERVER
* @param array $expected_headers Expected, cleaned HTTP headers
*/
public function test_request_headers(array $server_globals, array $expected_headers)
{
// save the $_SERVER super-global into temporary local var
$tmp_server = $_SERVER;
$_SERVER = array_replace_recursive($_SERVER, $server_globals);
$headers = HTTP::request_headers();
$actual_headers = array_intersect_key($headers->getArrayCopy(), $expected_headers);
$this->assertSame($expected_headers, $actual_headers);
// revert the super-global to its previous state
$_SERVER = $tmp_server;
}
}

View File

@@ -23,6 +23,7 @@ class Kohana_RequestTest extends Unittest_TestCase
// @codingStandardsIgnoreEnd
{
parent::setUp();
Kohana::$config->load('url')->set('trusted_hosts', array('localhost'));
$this->_initial_request = Request::$initial;
Request::$initial = new Request('/');
}
@@ -125,16 +126,15 @@ class Kohana_RequestTest extends Unittest_TestCase
{
$route = new Route('(<controller>(/<action>(/<id>)))');
$uri = 'foo/bar/id';
$uri = 'kohana_requesttest_dummy/foobar/some_id';
$request = Request::factory($uri, NULL, TRUE, array($route));
// We need to execute the request before it has matched a route
try
{
$request->execute();
}
catch (Exception $e) {}
$response = $request->execute();
$controller = new Controller_Kohana_RequestTest_Dummy($request, $response);
$this->assertSame(200, $response->status());
$this->assertSame($controller->get_expected_response(), $response->body());
$this->assertArrayHasKey('id', $request->param());
$this->assertArrayNotHasKey('foo', $request->param());
$this->assertEquals($request->uri(), $uri);
@@ -150,17 +150,16 @@ class Kohana_RequestTest extends Unittest_TestCase
$this->assertArrayNotHasKey('route', $params);
$route = new Route('(<uri>)', array('uri' => '.+'));
$route->defaults(array('controller' => 'foobar', 'action' => 'index'));
$request = Request::factory('foobar', NULL, TRUE, array($route));
$route->defaults(array('controller' => 'kohana_requesttest_dummy', 'action' => 'foobar'));
$request = Request::factory('kohana_requesttest_dummy', NULL, TRUE, array($route));
// We need to execute the request before it has matched a route
try
{
$request->execute();
}
catch (Exception $e) {}
$response = $request->execute();
$controller = new Controller_Kohana_RequestTest_Dummy($request, $response);
$this->assertSame('foobar', $request->param('uri'));
$this->assertSame(200, $response->status());
$this->assertSame($controller->get_expected_response(), $response->body());
$this->assertSame('kohana_requesttest_dummy', $request->param('uri'));
}
/**
@@ -270,6 +269,16 @@ class Kohana_RequestTest extends Unittest_TestCase
'http',
'http://localhost/kohana/foo'
),
array(
'http://www.google.com',
'http',
'http://www.google.com'
),
array(
'0',
'http',
'http://localhost/kohana/0'
)
);
}
@@ -296,7 +305,14 @@ class Kohana_RequestTest extends Unittest_TestCase
'Kohana::$index_file' => FALSE,
));
$this->assertEquals(Request::factory($uri)->url($protocol), $expected);
// issue #3967: inject the route so that we don't conflict with the application's default route
$route = new Route('(<controller>(/<action>))');
$route->defaults(array(
'controller' => 'welcome',
'action' => 'index',
));
$this->assertEquals(Request::factory($uri, array(), TRUE, array($route))->url($protocol), $expected);
}
/**
@@ -397,8 +413,15 @@ class Kohana_RequestTest extends Unittest_TestCase
*/
public function provider_uri_only_trimed_on_internal()
{
// issue #3967: inject the route so that we don't conflict with the application's default route
$route = new Route('(<controller>(/<action>))');
$route->defaults(array(
'controller' => 'welcome',
'action' => 'index',
));
$old_request = Request::$initial;
Request::$initial = new Request(TRUE);
Request::$initial = new Request(TRUE, array(), TRUE, array($route));
$result = array(
array(
@@ -417,6 +440,14 @@ class Kohana_RequestTest extends Unittest_TestCase
new Request('foo/bar'),
'foo/bar'
),
array(
new Request('/0'),
'0'
),
array(
new Request('0'),
'0'
),
array(
new Request('/'),
'/'
@@ -505,18 +536,18 @@ class Kohana_RequestTest extends Unittest_TestCase
{
$x_powered_by = 'Kohana Unit Test';
$content_type = 'application/x-www-form-urlencoded';
$request = new Request('foo/bar', array(), TRUE, array());
return array(
array(
$request = Request::factory('foo/bar')
->headers(array(
$request->headers(array(
'x-powered-by' => $x_powered_by,
'content-type' => $content_type
)
),
array(
'x-powered-by' => $x_powered_by,
'content-type' => $content_type
array(
'x-powered-by' => $x_powered_by,
'content-type' => $content_type
)
)
);
@@ -548,7 +579,6 @@ class Kohana_RequestTest extends Unittest_TestCase
{
return array(
array(
Request::factory(),
array(
'content-type' => 'application/x-www-form-urlencoded',
'x-test-header' => 'foo'
@@ -556,7 +586,6 @@ class Kohana_RequestTest extends Unittest_TestCase
"Content-Type: application/x-www-form-urlencoded\r\nX-Test-Header: foo\r\n\r\n"
),
array(
Request::factory(),
array(
'content-type' => 'application/json',
'x-powered-by' => 'kohana'
@@ -571,13 +600,13 @@ class Kohana_RequestTest extends Unittest_TestCase
*
* @dataProvider provider_headers_set
*
* @param Request request object
* @param array header(s) to set to the request object
* @param string expected http header
* @return void
*/
public function test_headers_set(Request $request, $headers, $expected)
public function test_headers_set($headers, $expected)
{
$request = new Request(TRUE, array(), TRUE, array());
$request->headers($headers);
$this->assertSame($expected, (string) $request->headers());
}
@@ -707,14 +736,83 @@ class Kohana_RequestTest extends Unittest_TestCase
$this->assertEquals($client->strict_redirect(), FALSE);
}
/**
* Tests correctness request content-length header after calling render
*/
public function test_content_length_after_render()
{
$request = Request::factory('https://example.org/post')
->client(new Kohana_RequestTest_Header_Spying_Request_Client_External)
->method(Request::POST)
->post(array('aaa' => 'bbb'));
$request->render();
$request->execute();
$headers = $request->client()->get_received_request_headers();
$this->assertEquals(strlen($request->body()), $headers['content-length']);
}
/**
* Tests correctness request content-length header after calling render
* and changing post
*/
public function test_content_length_after_changing_post()
{
$request = Request::factory('https://example.org/post')
->client(new Kohana_RequestTest_Header_Spying_Request_Client_External)
->method(Request::POST)
->post(array('aaa' => 'bbb'));
$request->render();
$request->post(array('one' => 'one', 'two' => 'two', 'three' => 'three'));
$request->execute();
$headers = $request->client()->get_received_request_headers();
$this->assertEquals(strlen($request->body()), $headers['content-length']);
}
} // End Kohana_RequestTest
/**
* A dummy Request_Client_External implementation, that spies on the headers
* of the request
*/
class Kohana_RequestTest_Header_Spying_Request_Client_External extends Request_Client_External
{
private $headers;
protected function _send_message(\Request $request, \Response $response)
{
$this->headers = $request->headers();
return $response;
}
public function get_received_request_headers()
{
return $this->headers;
}
}
class Controller_Kohana_RequestTest_Dummy extends Controller
{
public function action_index()
// hard coded dummy response
protected $dummy_response = "this is a dummy response";
public function action_foobar()
{
$this->response->body($this->dummy_response);
}
public function get_expected_response()
{
return $this->dummy_response;
}
} // End Kohana_RequestTest

View File

@@ -171,27 +171,6 @@ class Kohana_ResponseTest extends Unittest_TestCase
$this->assertSame(Cookie::$expiration, $cookie['expiration']);
}
/**
* Tests that the headers are not sent by PHP in CLI mode
*
* @return void
*/
public function test_send_headers_cli()
{
if (headers_sent())
{
$this->markTestSkipped('Cannot test this feature as headers have already been sent!');
}
$content_type = 'application/json';
$response = new Response;
$response->headers('content-type', $content_type)
->send_headers();
$this->assertFalse(headers_sent());
}
/**
* Test the content type is sent when set
*
@@ -205,4 +184,4 @@ class Kohana_ResponseTest extends Unittest_TestCase
$headers = $response->send_headers()->headers();
$this->assertSame($content_type, (string) $headers['content-type']);
}
}
}

View File

@@ -28,6 +28,8 @@ class Kohana_RouteTest extends Unittest_TestCase
{
parent::setUp();
Kohana::$config->load('url')->set('trusted_hosts', array('kohanaframework\.org'));
$this->cleanCacheDir();
}
@@ -268,11 +270,7 @@ class Kohana_RouteTest extends Unittest_TestCase
$route = new Route($uri);
// Mock a request class with the $match uri
$stub = $this->getMock('Request', array('uri'), array($match));
$stub->expects($this->any())
->method('uri')
// Request::uri() called by Route::matches() will return $match
->will($this->returnValue($match));
$stub = $this->get_request_mock($match);
$this->assertSame(FALSE, $route->matches($stub));
}
@@ -308,11 +306,7 @@ class Kohana_RouteTest extends Unittest_TestCase
$route = new Route($uri);
// Mock a request class with the $m uri
$request = $this->getMock('Request', array('uri'), array($m));
$request->expects($this->any())
->method('uri')
// Request::uri() called by Route::matches() will return $m
->will($this->returnValue($m));
$request = $this->get_request_mock($m);
$matches = $route->matches($request);
@@ -381,10 +375,7 @@ class Kohana_RouteTest extends Unittest_TestCase
$this->assertSame($defaults, $route->defaults());
// Mock a request class
$request = $this->getMock('Request', array('uri'), array($default_uri));
$request->expects($this->any())
->method('uri')
->will($this->returnValue($default_uri));
$request = $this->get_request_mock($default_uri);
$matches = $route->matches($request);
@@ -550,28 +541,19 @@ class Kohana_RouteTest extends Unittest_TestCase
$route = new Route($uri);
// Mock a request class that will return empty uri
$request = $this->getMock('Request', array('uri'), array(''));
$request->expects($this->any())
->method('uri')
->will($this->returnValue(''));
$request = $this->get_request_mock('');
$this->assertFalse($route->matches($request));
// Mock a request class that will return route1
$request = $this->getMock('Request', array('uri'), array($matches_route1));
$request->expects($this->any())
->method('uri')
->will($this->returnValue($matches_route1));
$request = $this->get_request_mock($matches_route1);
$matches = $route->matches($request);
$this->assertInternalType('array', $matches);
// Mock a request class that will return route2 uri
$request = $this->getMock('Request', array('uri'), array($matches_route2));
$request->expects($this->any())
->method('uri')
->will($this->returnValue($matches_route2));
$request = $this->get_request_mock($matches_route2);
$matches = $route->matches($request);
@@ -899,14 +881,78 @@ class Kohana_RouteTest extends Unittest_TestCase
$route = new Route($route);
// Mock a request class
$request = $this->getMock('Request', array('uri'), array($uri));
$request->expects($this->any())
->method('uri')
->will($this->returnValue($uri));
$request = $this->get_request_mock($uri);
$params = $route->defaults($defaults)->filter($filter)->matches($request);
$this->assertSame($expected_params, $params);
}
/**
* Provides test data for test_route_uri_encode_parameters
*
* @return array
*/
public function provider_route_uri_encode_parameters()
{
return array(
array(
'article',
'blog/article/<article_name>',
array(
'controller' => 'home',
'action' => 'index'
),
'article_name',
'Article name with special chars \\ ##',
'blog/article/Article%20name%20with%20special%20chars%20\\%20%23%23'
)
);
}
/**
* http://dev.kohanaframework.org/issues/4079
*
* @test
* @covers Route::get
* @ticket 4079
* @dataProvider provider_route_uri_encode_parameters
*/
public function test_route_uri_encode_parameters($name, $uri_callback, $defaults, $uri_key, $uri_value, $expected)
{
Route::set($name, $uri_callback)->defaults($defaults);
$get_route_uri = Route::get($name)->uri(array($uri_key => $uri_value));
$this->assertSame($expected, $get_route_uri);
}
/**
* Get a mock of the Request class with a mocked `uri` method
*
* We are also mocking `method` method as it conflicts with newer PHPUnit,
* in order to avoid the fatal errors
*
* @param string $uri
* @return type
*/
public function get_request_mock($uri)
{
// Mock a request class with the $uri uri
$request = $this->getMock('Request', array('uri', 'method'), array($uri));
// mock `uri` method
$request->expects($this->any())
->method('uri')
// Request::uri() called by Route::matches() in the tests will return $uri
->will($this->returnValue($uri));
// also mock `method` method
$request->expects($this->any())
->method('method')
->withAnyParameters();
return $request;
}
}

View File

@@ -67,17 +67,6 @@ class Kohana_SecurityTest extends Unittest_TestCase
*/
public function provider_csrf_token()
{
// Unfortunately this data provider has to use the session in order to
// generate its data. If headers have already been sent then this method
// throws an error, even if the test is does not run. If we return an
// empty array then this also causes an error, so the only way to get
// around it is to return an array of misc data and have the test skip
// if headers have been sent. It's annoying this hack has to be
// implemented, but the security code isn't exactly brilliantly
// implemented. Ideally we'd be able to inject a session instance
if (headers_sent())
return array(array('', '', 0));
$array = array();
for ($i = 0; $i <= 4; $i++)
{
@@ -96,10 +85,7 @@ class Kohana_SecurityTest extends Unittest_TestCase
*/
public function test_csrf_token($expected, $input, $iteration)
{
if (headers_sent()) {
$this->markTestSkipped('Headers have already been sent, session not available');
}
//@todo: the Security::token tests need to be reviewed to check how much of the logic they're actually covering
Security::$token_name = 'token_'.$iteration;
$this->assertSame(TRUE, $input);
$this->assertSame($expected, Security::token(FALSE));

View File

@@ -95,20 +95,14 @@ class Kohana_SessionTest extends Unittest_TestCase
*/
public function test_constructor_loads_session_with_session_id()
{
$this->markTestIncomplete(
'Need to work out why constructor is not being called'
);
$config = array();
$session_id = 'lolums';
// Don't auto-call constructor, we need to setup the mock first
$session = $this->getMockForAbstractClass(
'Session',
array(),
'',
FALSE
);
$session = $this->getMockBuilder('Session')
->disableOriginalConstructor()
->setMethods(array('read'))
->getMockForAbstractClass();
$session
->expects($this->once())

View File

@@ -196,6 +196,30 @@ class Kohana_TextTest extends Unittest_TestCase
$this->assertSame('yes', Text::alternate($val_a, $val_b, $val_c));
}
/**
* Provides test data for test_ucfirst
*
* @return array Test data
*/
public function provider_ucfirst()
{
return array(
array('Content-Type', 'content-type', '-'),
array('Բարեւ|Ձեզ', 'բարեւ|ձեզ', '|'),
);
}
/**
* Covers Text::ucfirst()
*
* @test
* @dataProvider provider_ucfirst
*/
public function test_ucfirst($expected, $string, $delimiter)
{
$this->assertSame($expected, Text::ucfirst($string, $delimiter));
}
/**
* Provides test data for test_reducde_slashes()
*
@@ -386,9 +410,90 @@ class Kohana_TextTest extends Unittest_TestCase
{
return array
(
array('No gain, no&nbsp;pain', 'No gain, no pain'),
array("spaces?what'rethey?", "spaces?what'rethey?"),
array('', ''),
// A very simple widont test
array(
'A very simple&nbsp;test',
'A very simple test',
),
// Single word items shouldn't be changed
array(
'Test',
'Test',
),
// Single word after single space shouldn't be changed either
array(
' Test',
' Test',
),
// Single word with HTML all around
array(
'<ul><li><p>Test</p></li><ul>',
'<ul><li><p>Test</p></li><ul>',
),
// Single word after single space with HTML all around
array(
'<ul><li><p> Test</p></li><ul>',
'<ul><li><p> Test</p></li><ul>',
),
// Widont with more than one paragraph
array(
'<p>In a couple of&nbsp;paragraphs</p><p>paragraph&nbsp;two</p>',
'<p>In a couple of paragraphs</p><p>paragraph two</p>',
),
// a link inside a heading
array(
'<h1><a href="#">In a link inside a&nbsp;heading </a></h1>',
'<h1><a href="#">In a link inside a heading </a></h1>',
),
// a link followed by text
array(
'<h1><a href="#">In a link</a> followed by other&nbsp;text</h1>',
'<h1><a href="#">In a link</a> followed by other text</h1>',
),
// empty html, with no text inside
array(
'<h1><a href="#"></a></h1>',
'<h1><a href="#"></a></h1>',
),
// apparently, we don't love DIVs
array(
'<div>Divs get no love!</div>',
'<div>Divs get no love!</div>',
),
// we don't love PREs, either
array(
'<pre>Neither do PREs</pre>',
'<pre>Neither do PREs</pre>',
),
// but we love DIVs with paragraphs
array(
'<div><p>But divs with paragraphs&nbsp;do!</p></div>',
'<div><p>But divs with paragraphs do!</p></div>',
),
array(
'No gain, no&nbsp;pain',
'No gain, no pain',
),
array(
"spaces?what'rethey?",
"spaces?what'rethey?",
),
/*
* // @issue 3499, with HTML at the end
* array(
* 'with HTML at the end &nbsp;<strong>Kohana</strong>',
* 'with HTML at the end <strong>Kohana</strong>',
* ),
* // @issue 3499, with HTML with attributes at the end
* array(
* 'with HTML at the end:&nbsp;<a href="#" title="Kohana">Kohana</a>',
* 'with HTML at the end: <a href="#" title="Kohana">Kohana</a>',
* ),
*/
array(
'',
'',
),
);
}
@@ -639,4 +744,111 @@ class Kohana_TextTest extends Unittest_TestCase
}
public function provider_user_agents()
{
return array(
array(
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36",
array(
'browser' => 'Chrome',
'version' => '37.0.2049.0',
'platform' => "Windows 8.1"
)
),
array(
"Mozilla/5.0 (Macintosh; U; Mac OS X 10_6_1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/ Safari/530.5",
array(
'browser' => 'Chrome',
'version' => '530.5',
'platform' => "Mac OS X"
)
),
array(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2",
array(
'browser' => 'Safari',
'version' => '534.57.2',
'platform' => 'Mac OS X'
)
),
array(
"Lynx/2.8.8dev.3 libwww-FM/2.14 SSL-MM/1.4.1",
array(
'browser' => 'Lynx',
'version' => '2.8.8dev.3',
'platform' => false
)
)
);
}
/**
* Tests Text::user_agent
*
* @dataProvider provider_user_agents
* @group current
*/
public function test_user_agent_returns_correct_browser($userAgent, $expectedData)
{
$browser = Text::user_agent($userAgent, 'browser');
$this->assertEquals($expectedData['browser'], $browser);
}
/**
* Tests Text::user_agent
*
* @dataProvider provider_user_agents
* @test
*/
public function test_user_agent_returns_correct_version($userAgent, $expectedData)
{
$version = Text::user_agent($userAgent, 'version');
$this->assertEquals($expectedData['version'], $version);
}
/**
* Tests Text::user_agent
* @test
*/
public function test_user_agent_recognizes_robots()
{
$bot = Text::user_agent('Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', 'robot');
$this->assertEquals('Googlebot', $bot);
}
/**
* Tests Text::user_agent
*
* @dataProvider provider_user_agents
* @test
*/
public function test_user_agent_returns_correct_platform($userAgent, $expectedData)
{
$platform = Text::user_agent($userAgent, 'platform');
$this->assertEquals($expectedData['platform'], $platform);
}
/**
* Tests Text::user_agent
* @test
*/
public function test_user_agent_accepts_array()
{
$agent_info = Text::user_agent(
'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 '.
'(KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36',
array('browser', 'version', 'platform'));
$this->assertArrayHasKey('browser', $agent_info);
$this->assertArrayHasKey('version', $agent_info);
$this->assertArrayHasKey('platform', $agent_info);
}
}

View File

@@ -16,6 +16,18 @@
*/
class Kohana_URLTest extends Unittest_TestCase
{
/**
* Sets up the environment
*/
// @codingStandardsIgnoreStart
public function setUp()
// @codingStandardsIgnoreEnd
{
parent::setUp();
Kohana::$config->load('url')->set('trusted_hosts', array('example\.com', 'example\.org'));
}
/**
* Default values for the environment, see setEnvironment
* @var array
@@ -276,4 +288,95 @@ class Kohana_URLTest extends Unittest_TestCase
URL::query($params, $use_get)
);
}
/**
* Provides test data for URL::is_trusted_host()
* @return array
*/
public function provider_is_trusted_host()
{
return array(
// data set #0
array(
'givenhost',
array(
'list-of-trusted-hosts',
),
FALSE
),
// data set #1
array(
'givenhost',
array(
'givenhost',
'example\.com',
),
TRUE
),
// data set #2
array(
'www.kohanaframework.org',
array(
'.*\.kohanaframework\.org',
),
TRUE
),
// data set #3
array(
'kohanaframework.org',
array(
'.*\.kohanaframework\.org',
),
FALSE // because we are requesting a subdomain
),
);
}
/**
* Tests URL::is_trusted_hosts()
*
* @test
* @dataProvider provider_is_trusted_host
* @param string $host the given host
* @param array $trusted_hosts list of trusted hosts
* @param boolean $expected TRUE if host is trusted, FALSE otherwise
*/
public function test_is_trusted_host($host, $trusted_hosts, $expected)
{
$this->assertSame(
$expected,
URL::is_trusted_host($host, $trusted_hosts)
);
}
/**
* Tests if invalid host throws "Invalid host" exception
*
* @test
* @expectedException Kohana_Exception
* @expectedExceptionMessage Invalid host <invalid>
*/
public function test_if_invalid_host_throws_exception()
{
// set the global HTTP_HOST to <invalid>
$_SERVER['HTTP_HOST'] = '<invalid>';
// trigger exception
URL::base('https');
}
/**
* Tests if untrusted host throws "Untrusted host" exception
*
* @test
* @expectedException Kohana_Exception
* @expectedExceptionMessage Untrusted host untrusted.com
*/
public function test_if_untrusted_host_throws_exception()
{
// set the global HTTP_HOST to a valid but untrusted host
$_SERVER['HTTP_HOST'] = 'untrusted.com';
// trigger exception
URL::base('https');
}
}

View File

@@ -673,4 +673,48 @@ class Kohana_ValidationTest extends Unittest_TestCase
$this->assertSame($errors, $validation->errors('validation'));
}
/**
* Provides test data for test_rule_label_regex
*
* @return array
*/
public function provider_rule_label_regex()
{
// $data, $field, $rules, $expected
return array(
array(
array(
'email1' => '',
),
'email1',
array(
array(
'not_empty'
)
),
array(
'email1' => 'email1 must not be empty'
),
)
);
}
/**
* http://dev.kohanaframework.org/issues/4201
*
* @test
* @ticket 4201
* @covers Validation::rule
* @dataProvider provider_rule_label_regex
*/
public function test_rule_label_regex($data, $field, $rules, $expected)
{
$validation = Validation::factory($data)->rules($field, $rules);
$validation->check();
$errors = $validation->errors('');
$this->assertSame($errors, $expected);
}
}

View File

@@ -60,9 +60,23 @@ class Kohana_ViewTest extends Unittest_TestCase
);
}
/**
* Provider to test_set
*
* @return array
*/
public function provider_set()
{
return array(
array('foo', 'bar', 'foo', 'bar'),
array(array('foo' => 'bar'), NULL, 'foo', 'bar'),
array(new ArrayIterator(array('foo' => 'bar')), NULL, 'foo', 'bar'),
);
}
/**
* Tests that we can instantiate a view file
*
*
* @test
* @dataProvider provider_instantiate
*
@@ -80,4 +94,33 @@ class Kohana_ViewTest extends Unittest_TestCase
$this->assertSame(TRUE, $expects_exception);
}
}
/**
* Tests that we can set using string, array or Traversable object
*
* @test
* @dataProvider provider_set
*
* @return null
*/
public function test_set($data_key, $value, $test_key, $expected)
{
$view = View::factory()->set($data_key, $value);
$this->assertSame($expected, $view->$test_key);
}
/**
* Tests that we can set global using string, array or Traversable object
*
* @test
* @dataProvider provider_set
*
* @return null
*/
public function test_set_global($data_key, $value, $test_key, $expected)
{
$view = View::factory();
$view::set_global($data_key, $value);
$this->assertSame($expected, $view->$test_key);
}
}

View File

@@ -17,6 +17,30 @@
*/
class Kohana_Request_Client_InternalTest extends Unittest_TestCase
{
protected $_log_object;
// @codingStandardsIgnoreStart
public function setUp()
// @codingStandardsIgnoreEnd
{
parent::setUp();
// temporarily save $log object
$this->_log_object = Kohana::$log;
Kohana::$log = NULL;
}
// @codingStandardsIgnoreStart
public function tearDown()
// @codingStandardsIgnoreEnd
{
// re-assign log object
Kohana::$log = $this->_log_object;
parent::tearDown();
}
public function provider_response_failure_status()
{
return array(
@@ -37,7 +61,7 @@ class Kohana_Request_Client_InternalTest extends Unittest_TestCase
public function test_response_failure_status($directory, $controller, $action, $uri, $expected)
{
// Mock for request object
$request = $this->getMock('Request', array('directory', 'controller', 'action', 'uri', 'response'), array($uri));
$request = $this->getMock('Request', array('directory', 'controller', 'action', 'uri', 'response', 'method'), array($uri));
$request->expects($this->any())
->method('directory')
@@ -59,10 +83,15 @@ class Kohana_Request_Client_InternalTest extends Unittest_TestCase
->method('response')
->will($this->returnValue($this->getMock('Response')));
// mock `method` method to avoid fatals in newer versions of PHPUnit
$request->expects($this->any())
->method('method')
->withAnyParameters();
$internal_client = new Request_Client_Internal;
$response = $internal_client->execute($request);
$this->assertSame($expected, $response->status());
}
}
}

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Kohana v3.x: Activity</title>
<link href="http://dev.kohanaframework.org/projects/kohana3/activity.atom" rel="self"/>
<link href="http://dev.kohanaframework.org/projects/kohana3/activity" rel="alternate"/>
<id>http://dev.kohanaframework.org/</id>
<icon>http://dev.kohanaframework.org/favicon.ico?1392677580</icon>
<updated>2014-08-28T01:52:12Z</updated>
<author>
<name>Kohana Development</name>
</author>
<generator uri="http://www.redmine.org/">
Redmine </generator>
<entry>
<title>Proposals (Political/Workflow) #4839 (New)</title>
<link href="http://dev.kohanaframework.org/issues/4839" rel="alternate"/>
<id>http://dev.kohanaframework.org/issues/4839</id>
<updated>2014-08-28T01:52:12Z</updated>
<author>
<name>Guillaume Poirier-Morency</name>
<email>guillaumepoiriermorency@gmail.com</email>
</author>
<content type="html">
&lt;p&gt;I have a prototype here &lt;a class="external" href="https://github.com/arteymix/kohana-makefile"&gt;https://github.com/arteymix/kohana-makefile&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The tool is very useful for settings permissions and running tests.&lt;/p&gt;
&lt;p&gt;I think we should consider having a good make tool in the sample application for the 3.4.*.&lt;/p&gt; </content>
</entry>
<entry>
<title>Proposals (Political/Workflow) #4782</title>
<link href="http://dev.kohanaframework.org/issues/4782#change-17279" rel="alternate"/>
<id>http://dev.kohanaframework.org/issues/4782#change-17279</id>
<updated>2014-08-28T01:44:26Z</updated>
<author>
<name>Guillaume Poirier-Morency</name>
<email>guillaumepoiriermorency@gmail.com</email>
</author>
<content type="html">
&lt;p&gt;Moving to composer is a nice idea. This will allow Kohana modules to define a wide range of dependencies.&lt;/p&gt;
&lt;p&gt;Although, I think that modules designed specifically for Kohana should end in modules and external libraries in application/vendor. This makes a clear dinsinction between what gets autoloaded by the CFS and what gets loaded by composer. Technically, we add "vendor-dir": "application/vendor" in "config" in composer.json.&lt;/p&gt;
&lt;p&gt;Then, only add a line after the modules loading in bootstrap.php&lt;/p&gt;
&lt;pre&gt;
// Autoloading composer packages
require Kohana::find_file('vendor', 'autoload');
&lt;/pre&gt;
&lt;p&gt;This is pretty much what I do right now. This doesn't break anything and allow a full access to composer.&lt;/p&gt; </content>
</entry>
</feed>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>RSS Title</title>
<description>This is an example of an RSS feed</description>
<link>http://www.example.com/main.html</link>
<lastBuildDate>Mon, 06 Sep 2010 00:01:00 +0000 </lastBuildDate>
<pubDate>Sun, 06 Sep 2009 16:20:00 +0000</pubDate>
<ttl>1800</ttl>
<item>
<title>Example entry</title>
<description>Here is some text containing an interesting description.</description>
<link>http://www.example.com/blog/post/1</link>
<guid>7bd204c6-1655-4c27-aeee-53f933c5395f</guid>
<pubDate>Sun, 06 Sep 2009 16:20:00 +0000</pubDate>
</item>
</channel>
</rss>

View File

@@ -0,0 +1,6 @@
<?php
// See the Kohana_CoreTest tests for Kohana::message
return array(
'bottom_only' => 'inherited bottom message',
'cfs_replaced' => 'inherited cfs_replaced message',
);

View File

@@ -0,0 +1,6 @@
<?php
// See the Kohana_CoreTest tests for Kohana::message
return array(
'top_only' => 'top only message',
'cfs_replaced' => 'overriding cfs_replaced message',
);

View File

@@ -15,9 +15,10 @@ function _ucwords($str)
// [\x0c\x09\x0b\x0a\x0d\x20] matches form feeds, horizontal tabs, vertical tabs, linefeeds and carriage returns.
// This corresponds to the definition of a 'word' defined at http://php.net/ucwords
return preg_replace(
'/(?<=^|[\x0c\x09\x0b\x0a\x0d\x20])[^\x0c\x09\x0b\x0a\x0d\x20]/ue',
'UTF8::strtoupper(\'$0\')',
$str
);
return preg_replace_callback(
'/(?<=^|[\x0c\x09\x0b\x0a\x0d\x20])[^\x0c\x09\x0b\x0a\x0d\x20]/u',
function($matches){
return UTF8::strtoupper($matches[0]);
},
$str);
}

View File

@@ -49,7 +49,7 @@ function koggle(elem)
}
</script>
<div id="kohana_error">
<h1><span class="type"><?php echo $class ?> [ <?php echo $code ?> ]:</span> <span class="message"><?php echo htmlspecialchars( (string) $message, ENT_QUOTES, Kohana::$charset, TRUE); ?></span></h1>
<h1><span class="type"><?php echo $class ?> [ <?php echo $code ?> ]:</span> <span class="message"><?php echo htmlspecialchars( (string) $message, ENT_QUOTES | ENT_IGNORE, Kohana::$charset, TRUE); ?></span></h1>
<div id="<?php echo $error_id ?>" class="content">
<p><span class="file"><?php echo Debug::path($file) ?> [ <?php echo $line ?> ]</span></p>
<?php echo Debug::source($file, $line) ?>

View File

@@ -35,7 +35,7 @@ $application_cols = array('min', 'max', 'average', 'current');
<div>
<div class="value"><?php echo number_format($stats[$key]['time'], 6) ?> <abbr title="seconds">s</abbr></div>
<?php if ($key === 'total'): ?>
<div class="graph" style="left: <?php echo max(0, 100 - $stats[$key]['time'] / $group_stats[$group]['max']['time'] * 100) ?>%"></div>
<div class="graph" style="left: <?php echo $group_stats[$group]['max']['time'] ? max(0, 100 - $stats[$key]['time'] / $group_stats[$group]['max']['time'] * 100) : '0' ?>%"></div>
<?php endif ?>
</div>
</td>
@@ -47,7 +47,7 @@ $application_cols = array('min', 'max', 'average', 'current');
<div>
<div class="value"><?php echo number_format($stats[$key]['memory'] / 1024, 4) ?> <abbr title="kilobyte">kB</abbr></div>
<?php if ($key === 'total'): ?>
<div class="graph" style="left: <?php echo max(0, 100 - $stats[$key]['memory'] / $group_stats[$group]['max']['memory'] * 100) ?>%"></div>
<div class="graph" style="left: <?php echo $group_stats[$group]['max']['memory'] ? max(0, 100 - $stats[$key]['memory'] / $group_stats[$group]['max']['memory'] * 100) : '0' ?>%"></div>
<?php endif ?>
</div>
</td>
@@ -71,4 +71,4 @@ $application_cols = array('min', 'max', 'average', 'current');
<?php endforeach ?>
</tr>
</table>
</div>
</div>