diff --git a/.travis.yml b/.travis.yml index 429f28631..aa6d577a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,33 +18,14 @@ env: matrix: include: - - php: 5.6 - env: - - DEPS=lowest - - php: 5.6 - env: - - DEPS=locked - - EXECUTE_HOSTNAME_CHECK=true - - TEST_COVERAGE=true - - php: 5.6 - env: - - DEPS=latest - - php: 7 - env: - - DEPS=lowest - - php: 7 - env: - - DEPS=locked - - CS_CHECK=true - - php: 7 - env: - - DEPS=latest - php: 7.1 env: - DEPS=lowest - php: 7.1 env: - DEPS=locked + - EXECUTE_HOSTNAME_CHECK=true + - TEST_COVERAGE=true - php: 7.1 env: - DEPS=latest @@ -66,6 +47,15 @@ matrix: - php: 7.3 env: - DEPS=latest + - php: 7.4 + env: + - DEPS=lowest + - php: 7.4 + env: + - DEPS=locked + - php: 7.4 + env: + - DEPS=latest before_install: - if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 035e0f413..fa92841ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,21 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. -## 2.12.3 - TBD +## 2.13.0 - 2019-12-27 ### Added -- Nothing. +- [#275](https://github.com/zendframework/zend-validator/pull/275) adds a new `strict` option to `Zend\Validator\Date`; when `true`, the value being validated must both be a date AND in the same format as provided via the `format` option. + +- [#264](https://github.com/zendframework/zend-validator/pull/264) adds `Zend\Validator\UndisclosedPassword`, which can be used to determine if a password has been exposed in a known data breach as reported on the [Have I Been Pwned?](https://www.haveibeenpwned.com) website. [Documentation](https://docs.zendframework.com/zend-validator/validators/undisclosed-password/) + +- [#266](https://github.com/zendframework/zend-validator/pull/266) adds a new option to the `File\Extension` and `File\ExcludeExtension` validators, `allowNonExistentFile`. When set to `true`, the validators will continue validating the extension of the filename given even if the file does not exist. The default is `false`, to preserve backwards compatibility with previous versions. ### Changed -- Nothing. +- [#264](https://github.com/zendframework/zend-validator/pull/264) bumps the minimum supported PHP version to 7.1.0. + +- [#279](https://github.com/zendframework/zend-validator/pull/279) updates the `magic.mime` file used for file validations. ### Deprecated @@ -18,7 +24,7 @@ All notable changes to this project will be documented in this file, in reverse ### Removed -- Nothing. +- [#264](https://github.com/zendframework/zend-validator/pull/264) removes support for PHP versions prior to 7.1.0. ### Fixed diff --git a/composer.json b/composer.json index 89fa4c7da..076b1c724 100644 --- a/composer.json +++ b/composer.json @@ -16,13 +16,15 @@ "forum": "https://discourse.zendframework.com/c/questions/components" }, "require": { - "php": "^5.6 || ^7.0", + "php": "^7.1", "zendframework/zend-stdlib": "^3.2.1", "container-interop/container-interop": "^1.1" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", "psr/http-message": "^1.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", "zendframework/zend-cache": "^2.6.1", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-config": "^2.6", @@ -61,8 +63,8 @@ }, "extra": { "branch-alias": { - "dev-master": "2.12.x-dev", - "dev-develop": "2.13.x-dev" + "dev-master": "2.13.x-dev", + "dev-develop": "2.14.x-dev" }, "zf": { "component": "Zend\\Validator", diff --git a/composer.lock b/composer.lock index aa75f44d4..512cece00 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1a7fd31aa89bfed8ec74f609189ec441", + "content-hash": "8e8923c7e2ce24bb8e5adf1c8fada887", "packages": [ { "name": "container-interop/container-interop", @@ -35,6 +35,7 @@ ], "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", "homepage": "https://github.com/container-interop/container-interop", + "abandoned": "psr/container", "time": "2017-02-14T19:40:03+00:00" }, { @@ -136,27 +137,29 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.1.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", "shasum": "" }, "require": { "php": "^7.1" }, "require-dev": { - "athletic/athletic": "~0.1.8", + "doctrine/coding-standard": "^6.0", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "squizlabs/php_codesniffer": "^3.0.2" + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { @@ -181,25 +184,25 @@ } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], - "time": "2017-07-22T11:58:36+00:00" + "time": "2019-10-21T16:45:58+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.8.1", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" + "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/579bb7356d91f9456ccd505f24ca8b667966a0a7", + "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7", "shasum": "" }, "require": { @@ -234,7 +237,7 @@ "object", "object graph" ], - "time": "2018-06-11T23:09:50+00:00" + "time": "2019-12-15T19:12:40+00:00" }, { "name": "phar-io/manifest", @@ -340,35 +343,33 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", + "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", "shasum": "" }, "require": { - "php": ">=5.5" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "phpunit/phpunit": "~6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -390,30 +391,30 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "time": "2018-08-07T13:53:10+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", + "version": "4.3.3", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + "reference": "2ecaa9fef01634c83bfa8dc1fe35fb5cef223a62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2ecaa9fef01634c83bfa8dc1fe35fb5cef223a62", + "reference": "2ecaa9fef01634c83bfa8dc1fe35fb5cef223a62", "shasum": "" }, "require": { "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", + "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", + "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", "webmozart/assert": "^1.0" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", + "doctrine/instantiator": "^1.0.5", "mockery/mockery": "^1.0", "phpunit/phpunit": "^6.4" }, @@ -441,41 +442,40 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" + "time": "2019-12-20T13:40:23+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", + "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": "^7.1", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "^7.1", + "mockery/mockery": "~1", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -488,42 +488,43 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2019-08-22T18:11:29+00:00" }, { "name": "phpspec/prophecy", - "version": "1.8.0", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/cbe1df668b3fe136bcc909126a0f529a78d4cbbc", + "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", + "phpspec/phpspec": "^2.5 || ^3.2", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.10.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -551,7 +552,7 @@ "spy", "stub" ], - "time": "2018-08-05T17:53:17+00:00" + "time": "2019-12-22T21:05:45+00:00" }, { "name": "phpunit/php-code-coverage", @@ -804,16 +805,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.13", + "version": "6.5.14", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0973426fb012359b2f18d3bd1e90ef1172839693" + "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0973426fb012359b2f18d3bd1e90ef1172839693", - "reference": "0973426fb012359b2f18d3bd1e90ef1172839693", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7", + "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7", "shasum": "" }, "require": { @@ -884,7 +885,7 @@ "testing", "xunit" ], - "time": "2018-09-08T15:10:43+00:00" + "time": "2019-02-01T05:22:47+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -992,6 +993,107 @@ ], "time": "2016-08-06T20:24:11+00:00" }, + { + "name": "psr/http-client", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "496a823ef742b632934724bf769560c2a5c7c44e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/496a823ef742b632934724bf769560c2a5c7c44e", + "reference": "496a823ef742b632934724bf769560c2a5c7c44e", + "shasum": "" + }, + "require": { + "php": "^7.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "time": "2018-10-30T23:29:13+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "time": "2019-04-30T12:38:16+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -1303,16 +1405,16 @@ }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", "shasum": "" }, "require": { @@ -1339,6 +1441,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1347,17 +1453,13 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", @@ -1366,7 +1468,7 @@ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "time": "2019-09-14T09:02:43+00:00" }, { "name": "sebastian/global-state", @@ -1727,18 +1829,76 @@ ], "time": "2018-11-07T22:31:41+00:00" }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.13.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.13-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2019-11-27T13:56:44+00:00" + }, { "name": "theseer/tokenizer", - "version": "1.1.0", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", "shasum": "" }, "require": { @@ -1765,35 +1925,33 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07T12:08:54+00:00" + "time": "2019-06-13T22:48:21+00:00" }, { "name": "webmozart/assert", - "version": "1.3.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925", + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "vimeo/psalm": "<3.6.0" }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -1815,20 +1973,20 @@ "check", "validate" ], - "time": "2018-01-29T19:49:41+00:00" + "time": "2019-11-24T13:36:37+00:00" }, { "name": "zendframework/zend-cache", - "version": "2.8.2", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-cache.git", - "reference": "4983dff629956490c78b88adcc8ece4711d7d8a3" + "reference": "cffd54a2dc4db094976d3b3f05e418a047cc9110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/4983dff629956490c78b88adcc8ece4711d7d8a3", - "reference": "4983dff629956490c78b88adcc8ece4711d7d8a3", + "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/cffd54a2dc4db094976d3b3f05e418a047cc9110", + "reference": "cffd54a2dc4db094976d3b3f05e418a047cc9110", "shasum": "" }, "require": { @@ -1837,7 +1995,7 @@ "psr/simple-cache": "^1.0", "zendframework/zend-eventmanager": "^2.6.3 || ^3.2", "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "zendframework/zend-stdlib": "^3.2.1" }, "provide": { "psr/cache-implementation": "1.0", @@ -1870,8 +2028,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" }, "zf": { "component": "Zend\\Cache", @@ -1898,7 +2056,7 @@ "psr-6", "zf" ], - "time": "2018-05-01T21:58:00+00:00" + "time": "2019-08-29T18:30:41+00:00" }, { "name": "zendframework/zend-coding-standard", @@ -1987,16 +2145,16 @@ }, { "name": "zendframework/zend-db", - "version": "2.9.3", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-db.git", - "reference": "5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9" + "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9", - "reference": "5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9", + "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", + "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "shasum": "" }, "require": { @@ -2007,7 +2165,7 @@ "phpunit/phpunit": "^5.7.25 || ^6.4.4", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1", + "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { @@ -2041,20 +2199,20 @@ "db", "zf" ], - "time": "2018-04-09T13:21:36+00:00" + "time": "2019-02-25T11:37:45+00:00" }, { "name": "zendframework/zend-escaper", - "version": "2.6.0", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-escaper.git", - "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074" + "reference": "3801caa21b0ca6aca57fa1c42b08d35c395ebd5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", - "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074", + "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/3801caa21b0ca6aca57fa1c42b08d35c395ebd5f", + "reference": "3801caa21b0ca6aca57fa1c42b08d35c395ebd5f", "shasum": "" }, "require": { @@ -2086,7 +2244,7 @@ "escaper", "zf" ], - "time": "2018-04-25T15:48:53+00:00" + "time": "2019-09-05T20:03:20+00:00" }, { "name": "zendframework/zend-eventmanager", @@ -2144,16 +2302,16 @@ }, { "name": "zendframework/zend-filter", - "version": "2.9.0", + "version": "2.9.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-filter.git", - "reference": "875da9790e5cb16b9a12f41453d5f7c441452daf" + "reference": "d78f2cdde1c31975e18b2a0753381ed7b61118ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/875da9790e5cb16b9a12f41453d5f7c441452daf", - "reference": "875da9790e5cb16b9a12f41453d5f7c441452daf", + "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/d78f2cdde1c31975e18b2a0753381ed7b61118ef", + "reference": "d78f2cdde1c31975e18b2a0753381ed7b61118ef", "shasum": "" }, "require": { @@ -2199,32 +2357,32 @@ "license": [ "BSD-3-Clause" ], - "description": "provides a set of commonly needed data filters", + "description": "Programmatically filter and normalize data and files", "keywords": [ "ZendFramework", "filter", "zf" ], - "time": "2018-12-12T23:14:25+00:00" + "time": "2019-08-19T07:08:04+00:00" }, { "name": "zendframework/zend-http", - "version": "2.8.2", + "version": "2.11.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-http.git", - "reference": "2c8aed3d25522618573194e7cc51351f8cd4a45b" + "reference": "76000da8490b8685d63ff6f6ff8eefa459f6e9e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/2c8aed3d25522618573194e7cc51351f8cd4a45b", - "reference": "2c8aed3d25522618573194e7cc51351f8cd4a45b", + "url": "https://api.github.com/repos/zendframework/zend-http/zipball/76000da8490b8685d63ff6f6ff8eefa459f6e9e7", + "reference": "76000da8490b8685d63ff6f6ff8eefa459f6e9e7", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "zendframework/zend-loader": "^2.5.1", - "zendframework/zend-stdlib": "^3.1 || ^2.7.7", + "zendframework/zend-stdlib": "^3.2.1", "zendframework/zend-uri": "^2.5.2", "zendframework/zend-validator": "^2.10.1" }, @@ -2239,8 +2397,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" } }, "autoload": { @@ -2260,28 +2418,32 @@ "zend", "zf" ], - "time": "2018-08-13T18:47:03+00:00" + "time": "2019-12-04T23:02:34+00:00" }, { "name": "zendframework/zend-i18n", - "version": "2.9.0", + "version": "2.10.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f" + "reference": "84038e6a1838b611dcc491b1c40321fa4c3a123c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", - "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f", + "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/84038e6a1838b611dcc491b1c40321fa4c3a123c", + "reference": "84038e6a1838b611dcc491b1c40321fa4c3a123c", "shasum": "" }, "require": { + "ext-intl": "*", "php": "^5.6 || ^7.0", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, + "conflict": { + "phpspec/prophecy": "<1.9.0" + }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", "zendframework/zend-cache": "^2.6.1", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-config": "^2.6", @@ -2292,7 +2454,6 @@ "zendframework/zend-view": "^2.6.3" }, "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", "zendframework/zend-cache": "Zend\\Cache component", "zendframework/zend-config": "Zend\\Config component", "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", @@ -2305,8 +2466,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" }, "zf": { "component": "Zend\\I18n", @@ -2328,20 +2489,20 @@ "i18n", "zf" ], - "time": "2018-05-16T16:39:13+00:00" + "time": "2019-12-12T14:08:22+00:00" }, { "name": "zendframework/zend-loader", - "version": "2.6.0", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-loader.git", - "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c" + "reference": "91da574d29b58547385b2298c020b257310898c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/78f11749ea340f6ca316bca5958eef80b38f9b6c", - "reference": "78f11749ea340f6ca316bca5958eef80b38f9b6c", + "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/91da574d29b58547385b2298c020b257310898c6", + "reference": "91da574d29b58547385b2298c020b257310898c6", "shasum": "" }, "require": { @@ -2373,7 +2534,7 @@ "loader", "zf" ], - "time": "2018-04-30T15:20:54+00:00" + "time": "2019-09-04T19:38:14+00:00" }, { "name": "zendframework/zend-math", @@ -2427,23 +2588,23 @@ }, { "name": "zendframework/zend-servicemanager", - "version": "3.3.2", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-servicemanager.git", - "reference": "9f35a104b8d4d3b32da5f4a3b6efc0dd62e5af42" + "reference": "a1ed6140d0d3ee803fec96582593ed024950067b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/9f35a104b8d4d3b32da5f4a3b6efc0dd62e5af42", - "reference": "9f35a104b8d4d3b32da5f4a3b6efc0dd62e5af42", + "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/a1ed6140d0d3ee803fec96582593ed024950067b", + "reference": "a1ed6140d0d3ee803fec96582593ed024950067b", "shasum": "" }, "require": { "container-interop/container-interop": "^1.2", "php": "^5.6 || ^7.0", "psr/container": "^1.0", - "zendframework/zend-stdlib": "^3.1" + "zendframework/zend-stdlib": "^3.2.1" }, "provide": { "container-interop/container-interop-implementation": "^1.2", @@ -2491,32 +2652,32 @@ "servicemanager", "zf" ], - "time": "2018-01-29T16:48:37+00:00" + "time": "2018-12-22T06:05:09+00:00" }, { "name": "zendframework/zend-session", - "version": "2.8.5", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-session.git", - "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b" + "reference": "c289c4d733ec23a389e25c7c451f4d062088511f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", - "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b", + "url": "https://api.github.com/repos/zendframework/zend-session/zipball/c289c4d733ec23a389e25c7c451f4d062088511f", + "reference": "c289c4d733ec23a389e25c7c451f4d062088511f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "zendframework/zend-stdlib": "^3.2.1" }, "require-dev": { "container-interop/container-interop": "^1.1", "mongodb/mongodb": "^1.0.1", "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", - "phpunit/phpunit": "^5.7.5 || >=6.0.13 <6.5.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", "zendframework/zend-cache": "^2.6.1", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-db": "^2.7", @@ -2535,8 +2696,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev", - "dev-develop": "2.9-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" }, "zf": { "component": "Zend\\Session", @@ -2552,26 +2713,26 @@ "license": [ "BSD-3-Clause" ], - "description": "manage and preserve session data, a logical complement of cookie data, across multiple page requests by the same client", + "description": "Object-oriented interface to PHP sessions and storage", "keywords": [ "ZendFramework", "session", "zf" ], - "time": "2018-02-22T16:33:54+00:00" + "time": "2019-10-28T19:40:43+00:00" }, { "name": "zendframework/zend-uri", - "version": "2.6.1", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-uri.git", - "reference": "3b6463645c6766f78ce537c70cb4fdabee1e725f" + "reference": "bfc4a5b9a309711e968d7c72afae4ac50c650083" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/3b6463645c6766f78ce537c70cb4fdabee1e725f", - "reference": "3b6463645c6766f78ce537c70cb4fdabee1e725f", + "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/bfc4a5b9a309711e968d7c72afae4ac50c650083", + "reference": "bfc4a5b9a309711e968d7c72afae4ac50c650083", "shasum": "" }, "require": { @@ -2586,8 +2747,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6.x-dev", - "dev-develop": "2.7.x-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { @@ -2605,7 +2766,7 @@ "uri", "zf" ], - "time": "2018-04-30T13:40:08+00:00" + "time": "2019-10-07T13:35:33+00:00" } ], "aliases": [], @@ -2614,7 +2775,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^5.6 || ^7.0" + "php": "^7.1" }, "platform-dev": [] } diff --git a/docs/book/validators/date.md b/docs/book/validators/date.md index 83de95a40..fe484722e 100644 --- a/docs/book/validators/date.md +++ b/docs/book/validators/date.md @@ -33,3 +33,24 @@ $validator = new Zend\Validator\Date(['format' => 'Y']); $validator->isValid('2010'); // returns true $validator->isValid('May'); // returns false ``` + +## Strict mode + +- **Since 2.13.0** + +By default, `Zend\Validator\Date` only validates that it can convert the +provided value to a valid `DateTime` value. + +If you want to require that the date is specified in a specific format, you can +provide both the [date format](#specifying-a-date-format) and the `strict` +options. In such a scenario, the value must both be covertable to a `DateTime` +value **and** be in the same format as provided to the validator. (Generally, +this will mean the value must be a string.) + +```php +$validator = new Zend\Validator\Date(['format' => 'Y-m-d', 'strict' => true]); + +$validator->isValid('2010-10-10'); // returns true +$validator->isValid(new DateTime('2010-10-10)); // returns false; value is not a string +$validator->isValid('2010.10.10'); // returns false; format differs +``` diff --git a/docs/book/validators/file/extension.md b/docs/book/validators/file/extension.md index e53e21162..bcf89de1b 100644 --- a/docs/book/validators/file/extension.md +++ b/docs/book/validators/file/extension.md @@ -14,6 +14,9 @@ The following set of options are supported: against which to test. - `case`: Boolean indicating whether or not extensions should match case sensitively; defaults to `false` (case-insensitive). +- `allowNonExistentFile`: (**Since 2.13.0**) Boolean indicating whether or not + to allow validating a filename for a non-existent file. Defaults to `false` + (will not validate non-existent files). ## Usage Examples diff --git a/docs/book/validators/undisclosed-password.md b/docs/book/validators/undisclosed-password.md new file mode 100644 index 000000000..badaad823 --- /dev/null +++ b/docs/book/validators/undisclosed-password.md @@ -0,0 +1,93 @@ +# Undisclosed Password Validator + +- **Since 2.13.0** + +`Zend\Validator\UndisclosedPassword` allows you to validate if a given password was found in data breaches using the service [Have I Been Pwned?](https://www.haveibeenpwned.com), in a secure, anonymous way using [K-Anonymity](https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2) to ensure passwords are not send in full over the wire. + +> ### Installation requirements +> +> This validator needs to make a request over HTTP; therefore it requires an HTTP client. The validator provides support only for HTTP clients implementing [PSR-18](https://www.php-fig.org/psr/psr-18/) and [PSR-17](https://www.php-fig.org/psr/psr-17/) request and response factories. +> +> To ensure you have these installed before using this validator, run the following: +> +> ```bash +> $ composer require psr/http-client +> $ composer require psr/http-factory +> ``` + +## Basic usage + +The validator has three required constructor arguments: + +- an HTTP Client that implements `Psr\Http\Client\ClientInterface` +- a `Psr\Http\Message\RequestFactoryInterface` instance +- a `Psr\Http\Message\ResponseFactoryInterface` instance + +Once you have an instance, you can then pass a password to its `isValid()` method to determine if it has been disclosed in a known data breach. + +If the password was found via the service, `isValid()` will return `false`. If the password was not found, `isValid()` will return `true`. + +```php +$validator = new Zend\Validator\UndisclosedPassword( + $httpClient, // a PSR-18 HttpClientInterface + $requestFactory, // a PSR-17 RequestFactoryInterface + $responseFactory // a PSR-17 ResponseFactoryInterface +); + +$result = $validator->isValid('password'); +// $result is FALSE because "password" was found in a data breach + +$result = $validator->isValid('8aDk=XiW2E.77tLfuAcB'); +// $result is TRUE because "8aDk=XiW2E.77tLfuAcB" was not found in a data breach +``` + +## A simple command line example + +In this example, I'm using `zendframework/zend-diactoros` to provide HTTP messages, and `php-http/curl-client` as the HTTP client. Let's begin with installation of all required packages: + +```bash +$ composer require \ + php-http/message \ + php-http/message-factory \ + php-http/discovery \ + php-http/curl-client \ + zendframework/zend-diactoros \ + zendframework/zend-validator +``` + +Next, I create a file, `undisclosed.php`, where I put my code: + +```php +isValid('password') ? 'not disclosed' : 'disclosed') . PHP_EOL; +echo 'Password "NVt3MpvQ" is ' . ($undisclosedPassword->isValid('NVt3MpvQ') ? 'not disclosed' : 'disclosed') . PHP_EOL; +``` + +To run it, I use the PHP command line interpreter: + +```bash +$ php undisclosed.php +``` + +And it gives me the following output: + +```bash +Password "password" is disclosed +Password "NVt3MpvQ" is not disclosed +``` diff --git a/mkdocs.yml b/mkdocs.yml index d7a0d4849..927f7da53 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -35,6 +35,7 @@ nav: - Step: validators/step.md - StringLength: validators/string-length.md - Timezone: validators/timezone.md + - UndisclosedPassword: validators/undisclosed-password.md - Uri: validators/uri.md - Uuid: validators/uuid.md - "File Validators": diff --git a/src/Date.php b/src/Date.php index bd2fd9070..c096c9653 100644 --- a/src/Date.php +++ b/src/Date.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2019 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ @@ -56,6 +56,11 @@ class Date extends AbstractValidator */ protected $format = self::FORMAT_DEFAULT; + /** + * @var bool + */ + protected $strict = false; + /** * Sets validator options * @@ -100,6 +105,17 @@ public function setFormat($format = self::FORMAT_DEFAULT) return $this; } + public function setStrict(bool $strict) : self + { + $this->strict = $strict; + return $this; + } + + public function isStrict() : bool + { + return $this->strict; + } + /** * Returns true if $value is a DateTime instance or can be converted into one. * @@ -110,11 +126,17 @@ public function isValid($value) { $this->setValue($value); - if (! $this->convertToDateTime($value)) { + $date = $this->convertToDateTime($value); + if (! $date) { $this->error(self::INVALID_DATE); return false; } + if ($this->isStrict() && $date->format($this->getFormat()) !== $value) { + $this->error(self::FALSEFORMAT); + return false; + } + return true; } diff --git a/src/File/ExcludeExtension.php b/src/File/ExcludeExtension.php index 9f8d3e034..60a88f7df 100644 --- a/src/File/ExcludeExtension.php +++ b/src/File/ExcludeExtension.php @@ -45,14 +45,18 @@ public function isValid($value, $file = null) { $fileInfo = $this->getFileInfo($value, $file); - $this->setValue($fileInfo['filename']); - // Is file readable ? - if (empty($fileInfo['file']) || false === is_readable($fileInfo['file'])) { + if (! $this->getAllowNonExistentFile() + && (empty($fileInfo['file']) || false === is_readable($fileInfo['file'])) + ) { + if (preg_match('/nofile\.mo$/', $fileInfo['file'])) { + } $this->error(self::NOT_FOUND); return false; } + $this->setValue($fileInfo['filename']); + $extension = substr($fileInfo['filename'], strrpos($fileInfo['filename'], '.') + 1); $extensions = $this->getExtension(); @@ -61,6 +65,8 @@ public function isValid($value, $file = null) } elseif (! $this->getCase()) { foreach ($extensions as $ext) { if (strtolower($ext) == strtolower($extension)) { + if (preg_match('/nofile\.mo$/', $fileInfo['file'])) { + } $this->error(self::FALSE_EXTENSION); return false; } diff --git a/src/File/Extension.php b/src/File/Extension.php index 0e1cd9ed0..875a48bbc 100644 --- a/src/File/Extension.php +++ b/src/File/Extension.php @@ -44,6 +44,7 @@ class Extension extends AbstractValidator protected $options = [ 'case' => false, // Validate case sensitive 'extension' => '', // List of extensions + 'allowNonExistentFile' => false, // Allow validation even if file does not exist ]; /** @@ -170,6 +171,28 @@ public function addExtension($extension) return $this; } + /** + * Returns whether or not to allow validation of non-existent files. + * + * @return bool + */ + public function getAllowNonExistentFile() + { + return $this->options['allowNonExistentFile']; + } + + /** + * Sets the flag indicating whether or not to allow validation of non-existent files. + * + * @param bool $flag Whether or not to allow validation of non-existent files. + * @return self Provides a fluent interface + */ + public function setAllowNonExistentFile($flag) + { + $this->options['allowNonExistentFile'] = (bool) $flag; + return $this; + } + /** * Returns true if and only if the file extension of $value is included in the * set extension list @@ -182,14 +205,16 @@ public function isValid($value, $file = null) { $fileInfo = $this->getFileInfo($value, $file); - $this->setValue($fileInfo['filename']); - // Is file readable ? - if (empty($fileInfo['file']) || false === is_readable($fileInfo['file'])) { + if (! $this->getAllowNonExistentFile() + && (empty($fileInfo['file']) || false === is_readable($fileInfo['file'])) + ) { $this->error(self::NOT_FOUND); return false; } + $this->setValue($fileInfo['filename']); + $extension = substr($fileInfo['filename'], strrpos($fileInfo['filename'], '.') + 1); $extensions = $this->getExtension(); diff --git a/src/Hostname.php b/src/Hostname.php index 6823eab12..5b4fdcdc8 100644 --- a/src/Hostname.php +++ b/src/Hostname.php @@ -69,7 +69,7 @@ class Hostname extends AbstractValidator /** * Array of valid top-level-domains - * IanaVersion 2019081200 + * IanaVersion 2019122700 * * @see ftp://data.iana.org/TLD/tlds-alpha-by-domain.txt List of all TLDs by domain * @see http://www.iana.org/domains/root/db/ Official list of supported TLDs @@ -287,7 +287,6 @@ class Hostname extends AbstractValidator 'career', 'careers', 'cars', - 'cartier', 'casa', 'case', 'caseih', @@ -320,7 +319,6 @@ class Hostname extends AbstractValidator 'chintai', 'christmas', 'chrome', - 'chrysler', 'church', 'ci', 'cipriani', @@ -372,6 +370,7 @@ class Hostname extends AbstractValidator 'coupon', 'coupons', 'courses', + 'cpa', 'cr', 'credit', 'creditcard', @@ -433,7 +432,6 @@ class Hostname extends AbstractValidator 'do', 'docs', 'doctor', - 'dodge', 'dog', 'domains', 'dot', @@ -443,7 +441,6 @@ class Hostname extends AbstractValidator 'dubai', 'duck', 'dunlop', - 'duns', 'dupont', 'durban', 'dvag', @@ -479,7 +476,6 @@ class Hostname extends AbstractValidator 'eurovision', 'eus', 'events', - 'everbank', 'exchange', 'expert', 'exposed', @@ -769,12 +765,10 @@ class Hostname extends AbstractValidator 'kz', 'la', 'lacaixa', - 'ladbrokes', 'lamborghini', 'lamer', 'lancaster', 'lancia', - 'lancome', 'land', 'landrover', 'lanxess', @@ -814,6 +808,7 @@ class Hostname extends AbstractValidator 'lixil', 'lk', 'llc', + 'llp', 'loan', 'loans', 'locker', @@ -889,7 +884,6 @@ class Hostname extends AbstractValidator 'mo', 'mobi', 'mobile', - 'mobily', 'moda', 'moe', 'moi', @@ -897,7 +891,6 @@ class Hostname extends AbstractValidator 'monash', 'money', 'monster', - 'mopar', 'mormon', 'mortgage', 'moscow', @@ -905,7 +898,6 @@ class Hostname extends AbstractValidator 'motorcycles', 'mov', 'movie', - 'movistar', 'mp', 'mq', 'mr', @@ -1025,7 +1017,6 @@ class Hostname extends AbstractValidator 'photography', 'photos', 'physio', - 'piaget', 'pics', 'pictet', 'pictures', @@ -1227,7 +1218,6 @@ class Hostname extends AbstractValidator 'spreadbetting', 'sr', 'srl', - 'srt', 'ss', 'st', 'stada', @@ -1280,7 +1270,6 @@ class Hostname extends AbstractValidator 'tech', 'technology', 'tel', - 'telefonica', 'temasek', 'tennis', 'teva', @@ -1340,7 +1329,6 @@ class Hostname extends AbstractValidator 'ua', 'ubank', 'ubs', - 'uconnect', 'ug', 'uk', 'unicom', @@ -1393,7 +1381,6 @@ class Hostname extends AbstractValidator 'walter', 'wang', 'wanggou', - 'warman', 'watch', 'watches', 'weather', @@ -1524,7 +1511,6 @@ class Hostname extends AbstractValidator 'موريتانيا', 'پاکستان', 'الاردن', - 'موبايلي', 'بارت', 'بھارت', 'المغرب', @@ -1556,6 +1542,7 @@ class Hostname extends AbstractValidator '大拿', 'みんな', 'グーグル', + 'ευ', 'ελ', '世界', '書籍', diff --git a/src/UndisclosedPassword.php b/src/UndisclosedPassword.php new file mode 100644 index 000000000..00c305bfe --- /dev/null +++ b/src/UndisclosedPassword.php @@ -0,0 +1,156 @@ + + 'The provided password was found in previous breaches, please create another password', + self::NOT_A_STRING => 'The provided password is not a string, please provide a correct password', + ]; + + /** + * @var ClientInterface + */ + private $httpClient; + + /** + * @var RequestFactoryInterface + */ + private $makeHttpRequest; + + /** + * @var ResponseFactoryInterface + */ + private $makeHttpResponse; + + /** + * PasswordBreach constructor. + */ + public function __construct( + ClientInterface $httpClient, + RequestFactoryInterface $makeHttpRequest, + ResponseFactoryInterface $makeHttpResponse + ) { + $this->httpClient = $httpClient; + $this->makeHttpRequest = $makeHttpRequest; + $this->makeHttpResponse = $makeHttpResponse; + } + + /** + * @inheritDoc + */ + public function isValid($value) + { + if (! is_string($value)) { + $this->error(self::NOT_A_STRING); + return false; + } + + if ($this->isPwnedPassword($value)) { + $this->error(self::PASSWORD_BREACHED); + return false; + } + + return true; + } + + private function isPwnedPassword(string $password) : bool + { + $sha1Hash = $this->hashPassword($password); + $rangeHash = $this->getRangeHash($sha1Hash); + $hashList = $this->retrieveHashList($rangeHash); + return $this->hashInResponse($sha1Hash, $hashList); + } + + /** + * We use a SHA1 hashed password for checking it against + * the breached data set of HIBP. + * + * @param string $password + * @return string + */ + private function hashPassword(string $password) : string + { + $hashedPassword = \sha1($password); + return strtoupper($hashedPassword); + } + + /** + * Creates a hash range that will be send to HIBP API + * applying K-Anonymity + * + * @param string $passwordHash + * @return string + * @see https://www.troyhunt.com/enhancing-pwned-passwords-privacy-by-exclusively-supporting-anonymity/ + */ + private function getRangeHash(string $passwordHash) : string + { + return substr($passwordHash, self::HIBP_K_ANONYMITY_HASH_RANGE_BASE, self::HIBP_K_ANONYMITY_HASH_RANGE_LENGTH); + } + + /** + * Making a connection to the HIBP API to retrieve a + * list of hashes that all have the same range as we + * provided. + * + * @param string $passwordRange + * @return string + * @throws ClientExceptionInterface + */ + private function retrieveHashList(string $passwordRange) : string + { + $request = $this->makeHttpRequest->createRequest( + 'GET', + self::HIBP_API_URI . '/range/' . $passwordRange + ); + + $response = $this->httpClient->sendRequest($request); + return (string) $response->getBody(); + } + + /** + * Checks if the password is in the response from HIBP + * + * @param string $sha1Hash + * @param string $resultStream + * @return bool + */ + private function hashInResponse(string $sha1Hash, string $resultStream) : bool + { + $data = explode("\r\n", $resultStream); + $hashes = array_filter($data, function ($value) use ($sha1Hash) { + list($hash, $count) = explode(':', $value); + if (0 === strcmp($hash, substr($sha1Hash, self::HIBP_K_ANONYMITY_HASH_RANGE_LENGTH))) { + return true; + } + return false; + }); + if ([] === $hashes) { + return false; + } + return true; + } +} diff --git a/test/DateTest.php b/test/DateTest.php index c4398d9ca..cd92484a2 100644 --- a/test/DateTest.php +++ b/test/DateTest.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2019 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ @@ -44,48 +44,55 @@ public function testSetFormatIgnoresNull() public function datesDataProvider() { return [ - // date format isValid - ['2007-01-01', null, true], - ['2007-02-28', null, true], - ['2007-02-29', null, false], - ['2008-02-29', null, true], - ['2007-02-30', null, false], - ['2007-02-99', null, false], - ['2007-02-99', 'Y-m-d', false], - ['9999-99-99', null, false], - ['9999-99-99', 'Y-m-d', false], - ['Jan 1 2007', null, false], - ['Jan 1 2007', 'M j Y', true], - ['asdasda', null, false], - ['sdgsdg', null, false], - ['2007-01-01something', null, false], - ['something2007-01-01', null, false], - ['10.01.2008', 'd.m.Y', true], - ['01 2010', 'm Y', true], - ['2008/10/22', 'd/m/Y', false], - ['22/10/08', 'd/m/y', true], - ['22/10', 'd/m/Y', false], + // date format isValid isValid Strict + ['2007-01-01', null, true, true], + ['2007-02-28', null, true, true], + ['2007-02-29', null, false, false], + ['2008-02-29', null, true, true], + ['2007-02-30', null, false, false], + ['2007-02-99', null, false, false], + ['2007-02-99', 'Y-m-d', false, false], + ['9999-99-99', null, false, false], + ['9999-99-99', 'Y-m-d', false, false], + ['Jan 1 2007', null, false, false], + ['Jan 1 2007', 'M j Y', true, true], + ['asdasda', null, false, false], + ['sdgsdg', null, false, false], + ['2007-01-01something', null, false, false], + ['something2007-01-01', null, false, false], + ['10.01.2008', 'd.m.Y', true, true], + ['01 2010', 'm Y', true, true], + ['2008/10/22', 'd/m/Y', false, false], + ['22/10/08', 'd/m/y', true, true], + ['22/10', 'd/m/Y', false, false], // time - ['2007-01-01T12:02:55Z', DateTime::ISO8601, true], - ['12:02:55', 'H:i:s', true], - ['25:02:55', 'H:i:s', false], + ['2007-01-01T12:02:55Z', DateTime::ISO8601, true, false], + ['2007-01-01T12:02:55+0000', DateTime::ISO8601, true, true], + ['12:02:55', 'H:i:s', true, true], + ['25:02:55', 'H:i:s', false, false], // int - [0, null, true], - [1340677235, null, true], + [0, null, true, false], + [6, 'd', true, false], + ['6', 'd', true, false], + ['06', 'd', true, true], + [123, null, true, false], + [1340677235, null, true, false], + [1340677235, 'U', true, false], + ['1340677235', 'U', true, true], // 32bit version of php will convert this to double - [999999999999, null, true], + [999999999999, null, true, false], // double - [12.12, null, false], + [12.12, null, false, false], // array - [['2012', '06', '25'], null, true], + [['2012', '06', '25'], null, true, false], // 0012-06-25 is a valid date, if you want 2012, use 'y' instead of 'Y' - [['12', '06', '25'], null, true], - [['2012', '06', '33'], null, false], - [[1 => 1], null, false], + [['12', '06', '25'], null, true, false], + [['2012', '06', '33'], null, false, false], + [[1 => 1], null, false, false], // DateTime - [new DateTime(), null, true], + [new DateTime(), null, true, false], // invalid obj - [new stdClass(), null, false], + [new stdClass(), null, false, false], ]; } @@ -100,6 +107,18 @@ public function testBasic($input, $format, $result) $this->assertEquals($result, $this->validator->isValid($input)); } + /** + * @dataProvider datesDataProvider + * + * @param mixed $input + */ + public function testBasicStrictMode($input, ?string $format, bool $result, bool $resultStrict) : void + { + $this->validator->setStrict(true); + $this->validator->setFormat($format); + $this->assertSame($resultStrict, $this->validator->isValid($input)); + } + public function testDateTimeImmutable() { $this->assertTrue($this->validator->isValid(new DateTimeImmutable())); diff --git a/test/File/ExcludeExtensionTest.php b/test/File/ExcludeExtensionTest.php index 5ac2a2243..b7eeb344e 100644 --- a/test/File/ExcludeExtensionTest.php +++ b/test/File/ExcludeExtensionTest.php @@ -38,6 +38,7 @@ public function basicBehaviorDataProvider() $noFileTests = [ // Options, isValid Param, Expected value, message ['mo', $testFile, false, 'fileExcludeExtensionNotFound'], + [['extension' => 'gif', 'allowNonExistentFile' => true], $testFile, true, ''], ]; // Dupe data in File Upload format diff --git a/test/File/ExtensionTest.php b/test/File/ExtensionTest.php index 3b6231f19..01cb69e38 100644 --- a/test/File/ExtensionTest.php +++ b/test/File/ExtensionTest.php @@ -38,6 +38,7 @@ public function basicBehaviorDataProvider() $noFileTests = [ // Options, isValid Param, Expected value, message ['mo', $testFile, false, 'fileExtensionNotFound'], + [['extension' => 'mo', 'allowNonExistentFile' => true], $testFile, true, ''], ]; // Dupe data in File Upload format @@ -49,6 +50,8 @@ public function basicBehaviorDataProvider() ]; $testData[] = [$data[0], $fileUpload, $data[2], $data[3]]; } + + return $testData; } diff --git a/test/File/_files/magic.mime b/test/File/_files/magic.mime index f1d242d72..40b1370e4 100644 --- a/test/File/_files/magic.mime +++ b/test/File/_files/magic.mime @@ -167,7 +167,7 @@ # depending on version magic continues with 0x93453E6139FA (V 4.0) # 0x9445376139FA (V 3.90) # 0x9445366139FA (V 3.80) -# this is from source (http://www.adrift.org.uk/) and I have some taf +# this is from source (http://www.adrift.co/) and I have some taf # files, and checked them. #0 belong 0x3C423FC9 #>4 belong 0x6A87C2CF Adrift game file @@ -1055,7 +1055,7 @@ >0 byte x GameCube movie, >0x34 ubeshort x %d x >0x36 ubeshort x %d, ->0x26 ubeshort x %dµs, +>0x26 ubeshort x %dµs, >0x42 ubeshort 0 no audio >0x42 ubeshort >0 %dHz audio @@ -1612,7 +1612,7 @@ # AMGC 0 string \xad6" AMGC archive data # NuLIB -0 string NõFélå NuLIB archive data +0 string NõFélÃ¥ NuLIB archive data # PakLeo 0 string LEOLZW PAKLeo archive data # ChArc @@ -1624,7 +1624,7 @@ # Freeze 0 string \x1f\x9f\x4a\x10\x0a Freeze archive data # KBoom -0 string ¨MP¨ KBoom archive data +0 string ¨MP¨ KBoom archive data # NSQ, must go after CDC Codec 0 string \x76\xff NSQ archive data # DPA @@ -1662,7 +1662,7 @@ # MP3 (archiver, not lossy audio compression) 0 string MP3\x1a MP3-Archiver archive data # ZET -0 string OZÝ ZET archive data +0 string OZÝ ZET archive data # TSComp 0 string \x65\x5d\x13\x8c\x08\x01\x03\x00 TSComp archive data # ARQ @@ -1693,7 +1693,7 @@ # Xtreme 0 string ULEB\0 Xtreme archive data # Pack Magic -0 string @â\1\0 Pack Magic archive data +0 string @â\1\0 Pack Magic archive data # BTS 0 belong&0xfeffffff 0x1a034465 BTS archive data # ELI 5750 @@ -1829,7 +1829,7 @@ # XPack Data 0 string xpa XPack archive data # XPack Single Data -0 string Í\ jm XPack single archive data +0 string Í\ jm XPack single archive data # TODO: missing due to unknown magic/magic at end of file: #DWC @@ -2814,7 +2814,7 @@ >>12 ulelong x \b, sample rate %d # adlib sound files -# From Gürkan Sengün , http://www.linuks.mine.nu +# From Gürkan Sengün , http://www.linuks.mine.nu 0 string RAWADATA RdosPlay RAW 1068 string RoR AMUSIC Adlib Tracker @@ -3972,7 +3972,7 @@ >113 string x (%s) #------------------------------------------------------------------------------ -# Microsoft Xbox executables .xbe (Esa Hyytiä ) +# Microsoft Xbox executables .xbe (Esa Hyytiä ) 0 string XBEH XBE, Microsoft Xbox executable # probabilistic checks whether signed or not >0x0004 ulelong =0x0 @@ -4008,7 +4008,7 @@ # From: Serge van den Boom 0 string \x01ZZZZZ\x01 3DO "Opera" file system -# From Gürkan Sengün , www.linuks.mine.nu +# From Gürkan Sengün , www.linuks.mine.nu 0 string GBS Nintendo Gameboy Music/Audio Data 12 string GameBoy\ Music\ Module Nintendo Gameboy Music Module @@ -6468,8 +6468,8 @@ # 10 SS, 8 SPT # 11 DS, 8 SPT # -# 11111001 Double density 3 floppy disk, high density 5 -# 11110000 High density 3 floppy disk +# 11111001 Double density 3½ floppy disk, high density 5¼ +# 11110000 High density 3½ floppy disk # 11111000 Hard disk any format # @@ -6887,7 +6887,7 @@ 0 string OTTO OpenType font data !:mime application/vnd.ms-opentype -# Gürkan Sengün , www.linuks.mine.nu +# Gürkan Sengün , www.linuks.mine.nu 0 string SplineFontDB: Spline Font Database !:mime application/vnd.font-fontforge-sfd >14 string x version %s @@ -8641,7 +8641,7 @@ >5 byte 0x00 (white background) >5 byte 0xFF (black background) -# Gürkan Sengün , www.linuks.mine.nu +# Gürkan Sengün , www.linuks.mine.nu # http://www.atarimax.com/jindroush.atari.org/afmtatr.html 0 leshort 0x0296 Atari ATR image @@ -9039,7 +9039,7 @@ # rom: file(1) magic for BIOS ROM Extensions found in intel machines # mapped into memory between 0xC0000 and 0xFFFFF -# From Gürkan Sengün , www.linuks.mine.nu +# From Gürkan Sengün , www.linuks.mine.nu 0 beshort 0x55AA BIOS (ia32) ROM Ext. >5 string USB USB >7 string LDR UNDI image @@ -9504,7 +9504,7 @@ # # Linux kernel boot images, from Albert Cahalan # and others such as Axel Kohlmeyer -# and Nicols Lichtmaier +# and Nicolás Lichtmaier # All known start with: b8 c0 07 8e d8 b8 00 90 8e c0 b9 00 01 29 f6 29 # Linux kernel boot images (i386 arch) (Wolfram Kleff) 514 string HdrS Linux kernel @@ -9527,10 +9527,10 @@ >0x1e3 string Loading version 1.3.79 or older >0x1e9 string Loading from prehistoric times -# System.map files - Nicols Lichtmaier +# System.map files - Nicolás Lichtmaier 8 search/1 \ A\ _text Linux kernel symbol map text -# LSM entries - Nicols Lichtmaier +# LSM entries - Nicolás Lichtmaier 0 search/1 Begin3 Linux Software Map entry text 0 search/1 Begin4 Linux Software Map entry text (new format) @@ -11718,7 +11718,7 @@ # natinst: file(1) magic for National Instruments Code Files # -# From Enrique Gmez-Flores +# From Enrique Gámez-Flores # version 1 # Many formats still missing, we use, for the moment LabVIEW # We guess VXI format file. VISA, LabWindowsCVI, BridgeVIEW, etc, are missing @@ -12933,7 +12933,7 @@ >4 belong =2 \b, version 2 # Type: Git index file -# From: Frédéric Brière +# From: Frédéric Brière 0 string DIRC Git index >4 belong >0 \b, version %d >>8 belong >0 \b, %d entries @@ -12962,7 +12962,7 @@ # # http://www.seanet.com/users/matts/riffmci/riffmci.htm # -# AVI section extended by Patrik Rdman +# AVI section extended by Patrik Rådman # 0 string RIFF RIFF (little-endian) data # RIFF Palette format @@ -15809,7 +15809,7 @@ 512 string R\0o\0o\0t\0 Hangul (Korean) Word Processor File 2000 !:mime application/x-hwp -# CosmicBook, from Benot Rouits +# CosmicBook, from Benoît Rouits 0 string CSBK Ted Neslson's CosmicBook hypertext file 2 string EYWR AmigaWriter file diff --git a/test/TestAsset/HttpClientException.php b/test/TestAsset/HttpClientException.php new file mode 100644 index 000000000..414c6c65b --- /dev/null +++ b/test/TestAsset/HttpClientException.php @@ -0,0 +1,11 @@ +httpClient = $this->getMockBuilder(ClientInterface::class) + ->getMockForAbstractClass(); + $this->httpRequest = $this->getMockBuilder(RequestFactoryInterface::class) + ->getMockForAbstractClass(); + $this->httpResponse = $this->getMockBuilder(ResponseInterface::class) + ->getMockForAbstractClass(); + $responseFactoryInterface = $this->getMockBuilder(ResponseFactoryInterface::class) + ->getMockForAbstractClass(); + + $this->validator = new UndisclosedPassword( + $this->httpClient, + $this->httpRequest, + $responseFactoryInterface + ); + } + + /** + * @inheritDoc + */ + protected function tearDown() + { + $this->httpClient = null; + } + + /** + * @param string|object $classOrInstance + * @return mixed + */ + public function getConstant(string $constant, $classOrInstance) + { + $r = new ReflectionClass($classOrInstance); + return $r->getConstant($constant); + } + + /** + * Data provider returning good, strong and unseen + * passwords to be used in the validator. + * + * @return array + */ + public function goodPasswordProvider() + { + return [ + ['ABi$B47es.Pfg3n9PjPi'], + ['potence tipple would frisk shoofly'], + ]; + } + + /** + * Data provider for most common used passwords + * + * @return array + * @see https://en.wikipedia.org/wiki/List_of_the_most_common_passwords + */ + public function seenPasswordProvider() + { + return [ + ['123456'], + ['password'], + ['123456789'], + ['12345678'], + ['12345'], + ]; + } + + /** + * Testing that we reject invalid password types + * + * @covers \Zend\Validator\UndisclosedPassword + * @covers \Zend\Validator\AbstractValidator + * @todo Can be replaced by a \TypeError being thrown in PHP 7.0 or up + */ + public function testValidationFailsForInvalidInput() + { + $this->assertFalse($this->validator->isValid(true)); + $this->assertFalse($this->validator->isValid((new \stdClass()))); + $this->assertFalse($this->validator->isValid(['foo'])); + } + + /** + * Test that a given password was not found in the HIBP + * API service. + * + * @param string $password + * + * @covers \Zend\Validator\UndisclosedPassword + * @dataProvider goodPasswordProvider + */ + public function testStrongUnseenPasswordsPassValidation($password) + { + $this->httpResponse->method('getBody') + ->will($this->returnCallback(function () use ($password) { + $hash = \sha1('zend-validator'); + return sprintf( + '%s:%d', + strtoupper(substr($hash, $this->getConstant( + 'HIBP_K_ANONYMITY_HASH_RANGE_LENGTH', + UndisclosedPassword::class + ))), + rand(0, 100000) + ); + })); + $this->httpClient->method('sendRequest') + ->will($this->returnValue($this->httpResponse)); + + $this->assertTrue($this->validator->isValid($password)); + } + + /** + * Test that a given password was already seen in the HIBP + * AP service. + * + * @param string $password + * @dataProvider seenPasswordProvider + * @covers \Zend\Validator\UndisclosedPassword + * @covers \Zend\Validator\AbstractValidator + */ + public function testBreachedPasswordsDoNotPassValidation($password) + { + $this->httpResponse->method('getBody') + ->will($this->returnCallback(function () use ($password) { + $hash = \sha1($password); + return sprintf( + '%s:%d', + strtoupper(substr($hash, $this->getConstant( + 'HIBP_K_ANONYMITY_HASH_RANGE_LENGTH', + UndisclosedPassword::class + ))), + rand(0, 100000) + ); + })); + $this->httpClient->method('sendRequest') + ->will($this->returnValue($this->httpResponse)); + + $this->assertFalse($this->validator->isValid($password)); + } + + /** + * Testing we are setting error messages when a password was found + * in the breach database. + * + * @param string $password + * @depends testBreachedPasswordsDoNotPassValidation + * @dataProvider seenPasswordProvider + * @covers \Zend\Validator\UndisclosedPassword + */ + public function testBreachedPasswordReturnErrorMessages($password) + { + $this->httpClient->method('sendRequest') + ->will($this->throwException(new \Exception('foo'))); + + $this->expectException(\Exception::class); + $this->validator->isValid($password); + $this->fail('Excpected exception was not thrown'); + } + + /** + * Testing that we capture any failures when trying to connect with + * the HIBP web service. + * + * @param string $password + * @depends testBreachedPasswordsDoNotPassValidation + * @dataProvider seenPasswordProvider + * @covers \Zend\Validator\UndisclosedPassword + */ + public function testValidationDegradesGracefullyWhenNoConnectionCanBeMade($password) + { + $clientException = $this->getMockBuilder(HttpClientException::class) + ->getMock(); + $this->httpClient->method('sendRequest') + ->will($this->throwException($clientException)); + + $this->expectException(ClientExceptionInterface::class); + + $this->validator->isValid($password); + $this->fail('Expected ClientException was not thrown'); + } +}