Repository: Ocramius/PSR7Csrf
Branch: master
Commit: 130e2aeb7350
Files: 38
Total size: 57.0 KB
Directory structure:
gitextract_rj5xlyxv/
├── .gitattributes
├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composer.json
├── examples/
│ ├── .gitignore
│ ├── composer.json
│ └── index.php
├── humbug.json.dist
├── phpcs.xml.dist
├── phpunit.xml.dist
├── src/
│ └── PSR7Csrf/
│ ├── CSRFCheckerMiddleware.php
│ ├── Exception/
│ │ ├── ExceptionInterface.php
│ │ ├── InvalidExpirationTimeException.php
│ │ ├── InvalidRequestParameterNameException.php
│ │ └── SessionAttributeNotFoundException.php
│ ├── Factory.php
│ ├── HttpMethod/
│ │ ├── IsSafeHttpRequest.php
│ │ └── IsSafeHttpRequestInterface.php
│ ├── RequestParameter/
│ │ ├── ExtractCSRFParameter.php
│ │ └── ExtractCSRFParameterInterface.php
│ ├── Session/
│ │ ├── ExtractUniqueKeyFromSession.php
│ │ └── ExtractUniqueKeyFromSessionInterface.php
│ ├── TokenGenerator.php
│ └── TokenGeneratorInterface.php
└── test/
└── PSR7CsrfTest/
├── CSRFCheckerMiddlewareTest.php
├── Exception/
│ ├── InvalidExpirationTimeExceptionTest.php
│ ├── InvalidRequestParameterNameExceptionTest.php
│ └── SessionAttributeNotFoundExceptionTest.php
├── FactoryTest.php
├── HttpMethod/
│ └── IsSafeHttpRequestTest.php
├── RequestParameter/
│ └── ExtractCSRFParameterTest.php
├── Session/
│ └── ExtractUniqueKeyFromSessionTest.php
└── TokenGeneratorTest.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
/test export-ignore
/examples export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.scrutinizer.yml export-ignore
.travis.yml export-ignore
phpunit.xml.dist
================================================
FILE: .gitignore
================================================
vendor
composer.lock
composer.phar
clover.xml
humbug-log.json
humbug-log.txt
humbug
================================================
FILE: .scrutinizer.yml
================================================
before_commands:
- "composer update --prefer-source"
tools:
external_code_coverage:
timeout: 1800
php_code_coverage:
enabled: true
php_code_sniffer:
enabled: false
php_cpd:
enabled: true
excluded_dirs: ["test", "vendor"]
php_cs_fixer:
enabled: true
config:
level: all
filter:
paths: ["src/*", "test/*"]
php_loc:
enabled: true
excluded_dirs: ["test", "vendor"]
php_mess_detector:
enabled: true
filter:
paths: ["src/*"]
php_pdepend:
enabled: true
excluded_dirs: ["test", "vendor"]
php_analyzer:
enabled: true
filter:
paths: ["src/*", "test/*"]
php_hhvm:
enabled: true
filter:
paths: ["src/*", "test/*"]
sensiolabs_security_checker: true
================================================
FILE: .travis.yml
================================================
language: php
php:
- 7.1
- 7.2
- nightly
before_script:
- composer update
- if [ "$DEPENDENCIES" = 'low' ] ; then composer update --prefer-source --prefer-lowest --prefer-stable; fi
- git clone https://github.com/padraic/humbug.git && cd humbug && composer install && cd ..
script:
- if [[ $TRAVIS_PHP_VERSION = '7.1' ]]; then PHPUNIT_FLAGS="--coverage-clover ./clover.xml"; else PHPUNIT_FLAGS=""; fi
- ./vendor/bin/phpunit $PHPUNIT_FLAGS
- ./vendor/bin/phpcs
- if [[ $TRAVIS_PHP_VERSION = '7.1' ]]; then ./vendor/bin/humbug; fi
- cd examples
- composer install
- php index.php > /dev/null
env:
matrix:
- DEPENDENCIES="high"
- DEPENDENCIES="low"
matrix:
allow_failures:
- php: hhvm
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- if [ -f clover.xml ]; then php ocular.phar code-coverage:upload --format=php-clover ./clover.xml; fi
================================================
FILE: CHANGELOG.md
================================================
This is a list of changes/improvements that were introduced in PSR7Csrf
## 2.0.0
- This release aligns the `PSR7Csrf\CSRFCheckerMiddleware` implementation to
the [PSR-15 `php-fig/http-server-middleware`](https://github.com/php-fig/http-server-middleware/tree/1.0.0)
specification.
This means that the signature of `PSR7Csrf\CSRFCheckerMiddleware`
changed, and therefore you need to look for usages of this class and verify
if the new signature is compatible with your API
Specifically, `PSR7Csrf\CSRFCheckerMiddleware#__invoke()` was removed.
- The minimum supported PHP version has been raised to `7.1.0`
- the `PSR7Csrf\Factory::createDefaultCSRFCheckerMiddleware()` method now has
a mandatory argument, which is the response to be produced in case of failed
CSRF validation. This argument is mandatory, since PSR7Csrf won't couple you
to a specific PSR-7 implementation.
## 1.0.2
### Fixed
- Allow installation of [PSR7Session](https://github.com/Ocramius/PSR7Session)
[2.0.0](https://github.com/Ocramius/PSR7Session/releases/tag/2.0.0) [#2](https://github.com/Ocramius/PSR7Csrf/pull/1)
## 1.0.1
### Fixed
- Minor wording issues in [`README.md`](README.md] [#1](https://github.com/Ocramius/PSR7Csrf/pull/1)
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
* Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
* The project will follow [object calisthenics](http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php)
* Any contribution must provide tests for additional/corrected scenarios
* Any un-confirmed issue needs a failing test case before being accepted
* Pull requests must be sent from a new hotfix/feature branch, not from `master`.
## Installation
To install the project and run the tests, you need to clone it first:
```sh
$ git clone git://github.com/Ocramius/PSR7Csrf.git
```
You will then need to run a composer installation:
```sh
$ cd PSR7Csrf
$ curl -s https://getcomposer.org/installer | php
$ php composer.phar update
```
## Testing
The PHPUnit version to be used is the one installed as a dev- dependency via composer:
```sh
$ ./vendor/bin/phpunit
```
Accepted coverage for new contributions is 80%. Any contribution not satisfying this requirement
won't be merged.
================================================
FILE: LICENSE
================================================
Copyright (c) 2015 Marco Pivetta
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# PSR-7 Storage-less HTTP CSRF protection
[](https://travis-ci.org/Ocramius/PSR7Csrf)
[](https://scrutinizer-ci.com/g/Ocramius/PSR7Csrf/?branch=master)
[](https://scrutinizer-ci.com/g/Ocramius/PSR7Csrf/?branch=master)
[](https://packagist.org/packages/ocramius/psr7-csrf)
[](https://packagist.org/packages/ocramius/psr7-csrf)
**PSR7Csrf** is a [PSR-7](http://www.php-fig.org/psr/psr-7/)
[middleware](https://mwop.net/blog/2015-01-08-on-http-middleware-and-psr-7.html) that enables
[CSRF](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)) protection for PSR-7 based applications.
# DEPRECATED in favor of `psr7-sessions/storageless` 5.0.0+
Please note that this package is **DEPRECATED**.
Since [`psr7-sessions/storageless` 5.0.0](https://github.com/psr7-sessions/storageless/releases/tag/5.0.0),
the generated cookies are CSRF-resistant by default for unsafe HTTP methods (`POST`/`PUT`/`DELETE`/`PATCH`/etc.),
so the usage of this package is no longer needed.
You can still install `ocramius/psr7-csrf`, but since there is no practical need for it,
it is not necessary to do so.
### What is this about?
Instead of storing tokens in the session, PSR7Csrf simply uses JWT tokens,
which can be verified, signed and have a specific lifetime on their own.
This storage-less approach prevents having to load tokens from a session
or from a database, and simplifies the entire UI workflow: tokens are
valid as long as their signature and expiration date holds.
### Installation
```sh
composer require ocramius/psr7-csrf
```
### Usage
The simplest usage is based on defaults. It assumes that you have
a configured PSR-7 compatible application that supports piping
middlewares, and it also requires you to run [PSR7Session](https://github.com/Ocramius/PSR7Session).
In a [`zendframework/zend-expressive`](https://github.com/zendframework/zend-expressive)
application, the setup would look like the following:
```php
$app = \Zend\Expressive\AppFactory::create();
$app->pipe(\PSR7Session\Http\SessionMiddleware::fromSymmetricKeyDefaults(
'mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=', // replace this with a key of your own (see PSR7Session docs)
1200 // 20 minutes session duration
));
$app->pipe(\PSR7Csrf\Factory::createDefaultCSRFCheckerMiddleware());
```
This setup will require that any requests that are not `GET`, `HEAD` or
`OPTIONS` contain a `csrf_token` in the request body parameters (JSON
or URL-encoded).
You can generate the CSRF token for any form like following:
```php
$tokenGenerator = \PSR7Csrf\Factory::createDefaultTokenGenerator();
$app->get('/get', function ($request, $response) use ($tokenGenerator) {
$response
->getBody()
->write(
'<form method="post" action="/post">'
. '<input type="submit"/>'
. '<input type="hidden" name="csrf_token" value="'
. $tokenGenerator($request)
. '"/>'
. '</form>'
);
return $response;
});
$app->post('/post', function ($request, $response) {
$response
->getBody()
->write('It works!');
return $response;
});
```
### Examples
```sh
composer install # install at the root of this package first!
cd examples
composer install
php -S localhost:9999 index.php
```
Then try accessing `http://localhost:9999`: you should see a simple
submission form.
If you try modifying the submitted CSRF token (which is in a hidden
form field), then the `POST` request will fail.
### Known limitations
Please refer to the [known limitations of PSR7Session](https://github.com/Ocramius/PSR7Session/blob/master/docs/limitations.md).
Also, this component does *NOT* prevent double-form-submissions: it
merely prevents CSRF attacks from third parties. As long as the CSRF
token is valid, it can be reused over multiple requests.
### Contributing
Please refer to the [contributing notes](CONTRIBUTING.md).
### License
This project is made public under the [MIT LICENSE](LICENSE).
================================================
FILE: composer.json
================================================
{
"name": "ocramius/psr7-csrf",
"license": "MIT",
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "http://ocramius.github.io/",
"role": "Developer"
}
],
"require": {
"php": "^7.1.0",
"psr/http-message": "^1.0.1",
"lcobucci/jwt": "^3.2.2",
"psr/http-server-handler": "^1.0.0",
"psr/http-server-middleware": "^1.0.0",
"psr7-sessions/storageless": "^4.0.0"
},
"require-dev": {
"phpunit/phpunit": "^6.5.5",
"humbug/humbug": "^1.0.0-rc.0",
"squizlabs/php_codesniffer": "^2.6.0"
},
"autoload": {
"psr-4": {
"PSR7Csrf\\": "src/PSR7Csrf"
}
},
"autoload-dev": {
"psr-4": {
"PSR7CsrfTest\\": "test/PSR7CsrfTest"
}
},
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
}
}
================================================
FILE: examples/.gitignore
================================================
vendor
composer.lock
================================================
FILE: examples/composer.json
================================================
{
"require": {
"zendframework/zend-diactoros": "^1.7.0"
},
"autoload": {
"files": [
"../vendor/autoload.php"
]
}
}
================================================
FILE: examples/index.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\Example;
use Dflydev\FigCookies\SetCookie;
use Lcobucci\Clock\SystemClock;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use PSR7Csrf\Factory;
use PSR7Csrf\TokenGeneratorInterface;
use PSR7Sessions\Storageless\Http\SessionMiddleware;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Diactoros\Response\JsonResponse;
use Zend\Diactoros\Response\SapiEmitter;
use Zend\Diactoros\Response\TextResponse;
use Zend\Diactoros\ServerRequestFactory;
require_once __DIR__ . '/vendor/autoload.php';
// We use PSR7Sessions\Storageless here - if you don't like it, you need to provide
// an equivalent session middleware implementation.
$sessionMiddleware = new SessionMiddleware(
new Sha256(),
'c9UA8QKLSmDEn4DhNeJIad/4JugZd/HvrjyKrS0jOes=', // signature key (important: change this to your own)
'c9UA8QKLSmDEn4DhNeJIad/4JugZd/HvrjyKrS0jOes=', // verification key (important: change this to your own)
SetCookie::create('an-example-cookie-name')
->withSecure(false)// false on purpose, unless you have https locally - DO NOT DO THIS IN PRODUCTION!
->withHttpOnly(true),
new Parser(),
1200,
new SystemClock()
);
/**
* This is the actual component from this package. Setup assumes that
* a `SessionMiddleware` was previously piped through. If you don't do that,
* then all requests will fail CSRF validation!
*/
$csrfMiddleware = Factory::createDefaultCSRFCheckerMiddleware(
(new JsonResponse(['error' => 'CSRF validation failed']))->withStatus(401)
);
/**
* The token generator is needed to generate CSRF tokens to be added to your forms
*/
$tokenGenerator = Factory::createDefaultTokenGenerator();
/**
* This is an example of how you'd generate a form with a CSRF token from this package
*/
$action = new class ($tokenGenerator) implements RequestHandlerInterface
{
/**
* @var TokenGeneratorInterface
*/
private $tokenGenerator;
public function __construct(TokenGeneratorInterface $tokenGenerator)
{
$this->tokenGenerator = $tokenGenerator;
}
public function handle(ServerRequestInterface $request) : ResponseInterface
{
if ('GET' === \strtoupper($request->getMethod())) {
return new HtmlResponse(
'<form method="post" action="/post">'
. '<input type="submit" value="Submit with CSRF token"/>'
. '<input type="hidden" name="csrf_token" value="'
. $this->tokenGenerator->__invoke($request)
. '"/>'
. '</form>'
. '<form method="post" action="/post">'
. '<input type="submit" value="Submit without CSRF token"/>'
. '</form>'
);
}
return new TextResponse('It works!');
}
};
/**
* Don't panic! This just emulates what a typical middleware-based HTTP framework does internally
*/
$pipe = new class ($action, $sessionMiddleware, $csrfMiddleware) implements RequestHandlerInterface
{
/**
* @var RequestHandlerInterface
*/
private $action;
/**
* @var MiddlewareInterface[]
*/
private $pipedMiddleware;
public function __construct(RequestHandlerInterface $action, MiddlewareInterface ...$pipedMiddleware)
{
$this->action = $action;
$this->pipedMiddleware = $pipedMiddleware;
}
public function handle(ServerRequestInterface $request) : ResponseInterface
{
if (! $this->pipedMiddleware) {
return $this->action->handle($request);
}
return $this->pipedMiddleware[0]->process(
$request,
new self($this->action, ...\array_values(\array_slice($this->pipedMiddleware, 1)))
);
}
};
// produce the response
(new SapiEmitter())
->emit($pipe->handle(ServerRequestFactory::fromGlobals()));
================================================
FILE: humbug.json.dist
================================================
{
"timeout": 30,
"source": {
"directories": [
"src"
]
},
"logs": {
"text": "humbug-log.txt",
"json": "humbug-log.json"
}
}
================================================
FILE: phpcs.xml.dist
================================================
<?xml version="1.0"?>
<ruleset name="Custom">
<description>code-reviews.io code-style</description>
<file>./src</file>
<file>./test</file>
<exclude-pattern>./test/PSR7CsrfTest/RequestParameter/ExtractCSRFParameterTest.php</exclude-pattern>
<rule ref="PSR2">
<exclude name="Generic.Files.LineLength"/>
</rule>
</ruleset>
================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="./vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
verbose="true"
stopOnFailure="false"
processIsolation="false"
backupGlobals="false"
beStrictAboutChangesToGlobalState="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutResourceUsageDuringSmallTests="true"
beStrictAboutTodoAnnotatedTests="true"
beStrictAboutTestsThatDoNotTestAnything="true"
>
<testsuite name="PSR7Csrf tests">
<directory>./test/PSR7CsrfTest</directory>
</testsuite>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src</directory>
</whitelist>
</filter>
</phpunit>
================================================
FILE: src/PSR7Csrf/CSRFCheckerMiddleware.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf;
use BadMethodCallException;
use InvalidArgumentException;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\ValidationData;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use PSR7Csrf\Exception\SessionAttributeNotFoundException;
use PSR7Csrf\HttpMethod\IsSafeHttpRequestInterface;
use PSR7Csrf\RequestParameter\ExtractCSRFParameterInterface;
use PSR7Csrf\Session\ExtractUniqueKeyFromSessionInterface;
use PSR7Sessions\Storageless\Session\SessionInterface;
final class CSRFCheckerMiddleware implements \Psr\Http\Server\MiddlewareInterface
{
/**
* @var Signer
*/
private $signer;
/**
* @var Parser
*/
private $tokenParser;
/**
* @var IsSafeHttpRequestInterface
*/
private $isSafeHttpRequest;
/**
* @var ExtractUniqueKeyFromSessionInterface
*/
private $extractUniqueKeyFromSession;
/**
* @var ExtractCSRFParameterInterface
*/
private $extractCSRFParameter;
/**
* @var string
*/
private $sessionAttribute;
/**
* @var ResponseInterface
*/
private $faultyResponse;
public function __construct(
IsSafeHttpRequestInterface $isSafeHttpRequest,
ExtractUniqueKeyFromSessionInterface $extractUniqueKeyFromSession,
ExtractCSRFParameterInterface $extractCSRFParameter,
Parser $tokenParser,
Signer $signer,
string $sessionAttribute,
ResponseInterface $faultyResponse
) {
$this->isSafeHttpRequest = $isSafeHttpRequest;
$this->extractUniqueKeyFromSession = $extractUniqueKeyFromSession;
$this->extractCSRFParameter = $extractCSRFParameter;
$this->tokenParser = $tokenParser;
$this->signer = $signer;
$this->sessionAttribute = $sessionAttribute;
$this->faultyResponse = $faultyResponse;
}
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
) : ResponseInterface {
if ($this->isSafeHttpRequest->__invoke($request)) {
return $handler->handle($request);
}
try {
$token = $this->tokenParser->parse($this->extractCSRFParameter->__invoke($request));
if ($token->validate(new ValidationData())
&& $token->verify(
$this->signer,
$this->extractUniqueKeyFromSession->__invoke($this->getSession($request))
)
) {
return $handler->handle($request);
}
} catch (BadMethodCallException $invalidToken) {
return $this->faultyResponse;
} catch (InvalidArgumentException $invalidToken) {
return $this->faultyResponse;
}
return $this->faultyResponse;
}
private function getSession(ServerRequestInterface $request) : SessionInterface
{
$session = $request->getAttribute($this->sessionAttribute);
if (! $session instanceof SessionInterface) {
throw SessionAttributeNotFoundException::fromAttributeNameAndRequest($this->sessionAttribute, $request);
}
return $session;
}
}
================================================
FILE: src/PSR7Csrf/Exception/ExceptionInterface.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\Exception;
use Throwable;
interface ExceptionInterface extends Throwable
{
}
================================================
FILE: src/PSR7Csrf/Exception/InvalidExpirationTimeException.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\Exception;
use InvalidArgumentException;
class InvalidExpirationTimeException extends InvalidArgumentException implements ExceptionInterface
{
public static function fromInvalidExpirationTime(int $expirationTime) : self
{
return new self(sprintf('The provided expiration time %s is invalid: expected a >0 integer', $expirationTime));
}
}
================================================
FILE: src/PSR7Csrf/Exception/InvalidRequestParameterNameException.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\Exception;
use InvalidArgumentException;
class InvalidRequestParameterNameException extends InvalidArgumentException implements ExceptionInterface
{
public static function fromEmptyRequestParameterName() : self
{
return new self('The given request parameter must be a non-empty string');
}
}
================================================
FILE: src/PSR7Csrf/Exception/SessionAttributeNotFoundException.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\Exception;
use Psr\Http\Message\ServerRequestInterface;
use UnexpectedValueException;
class SessionAttributeNotFoundException extends UnexpectedValueException implements ExceptionInterface
{
public static function fromAttributeNameAndRequest(string $attributeName, ServerRequestInterface $request) : self
{
return new self(sprintf(
'Provided request contains no matching session attribute "%s", attributes %s exist',
$attributeName,
json_encode(array_keys($request->getAttributes()))
));
}
}
================================================
FILE: src/PSR7Csrf/Factory.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Psr\Http\Message\ResponseInterface;
use PSR7Csrf\HttpMethod\IsSafeHttpRequest;
use PSR7Csrf\RequestParameter\ExtractCSRFParameter;
use PSR7Csrf\Session\ExtractUniqueKeyFromSession;
use PSR7Sessions\Storageless\Http\SessionMiddleware;
final class Factory
{
public const DEFAULT_SIGNATURE_KEY_NAME = 'csrf_signature_key';
public const DEFAULT_CSRF_DATA_KEY = 'csrf_token';
public const DEFAULT_EXPIRATION_TIME = 60 * 24;
public static function createDefaultCSRFCheckerMiddleware(
ResponseInterface $failedCsrfValidationResponse
) : CSRFCheckerMiddleware {
return new CSRFCheckerMiddleware(
IsSafeHttpRequest::fromDefaultSafeMethods(),
new ExtractUniqueKeyFromSession(self::DEFAULT_SIGNATURE_KEY_NAME),
new ExtractCSRFParameter(self::DEFAULT_CSRF_DATA_KEY),
new Parser(),
new Sha256(),
SessionMiddleware::SESSION_ATTRIBUTE,
$failedCsrfValidationResponse
);
}
public static function createDefaultTokenGenerator() : TokenGeneratorInterface
{
return new TokenGenerator(
new Sha256(),
new ExtractUniqueKeyFromSession(self::DEFAULT_SIGNATURE_KEY_NAME),
self::DEFAULT_EXPIRATION_TIME,
SessionMiddleware::SESSION_ATTRIBUTE
);
}
}
================================================
FILE: src/PSR7Csrf/HttpMethod/IsSafeHttpRequest.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\HttpMethod;
use Lcobucci\JWT\Signer;
use Psr\Http\Message\RequestInterface;
final class IsSafeHttpRequest implements IsSafeHttpRequestInterface
{
const STRICT_CHECKING = true;
/**
* @var \string[]
*/
private $safeMethods;
public function __construct(string ...$safeMethods)
{
$this->safeMethods = array_map('strtoupper', $safeMethods);
}
public static function fromDefaultSafeMethods() : self
{
return new self('GET', 'HEAD', 'OPTIONS');
}
public function __invoke(RequestInterface $request) : bool
{
return in_array(strtoupper($request->getMethod()), $this->safeMethods, self::STRICT_CHECKING);
}
}
================================================
FILE: src/PSR7Csrf/HttpMethod/IsSafeHttpRequestInterface.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\HttpMethod;
use Lcobucci\JWT\Signer;
use Psr\Http\Message\RequestInterface;
interface IsSafeHttpRequestInterface
{
public function __invoke(RequestInterface $request) : bool;
}
================================================
FILE: src/PSR7Csrf/RequestParameter/ExtractCSRFParameter.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\RequestParameter;
use Psr\Http\Message\ServerRequestInterface;
use PSR7Csrf\Exception\InvalidRequestParameterNameException;
final class ExtractCSRFParameter implements ExtractCSRFParameterInterface
{
/**
* @var string
*/
private $csrfDataKey;
public function __construct(string $csrfDataKey)
{
if ('' === $csrfDataKey) {
throw InvalidRequestParameterNameException::fromEmptyRequestParameterName();
}
$this->csrfDataKey = $csrfDataKey;
}
public function __invoke(ServerRequestInterface $request) : string
{
/* @var $requestBody array */
$requestBody = $request->getParsedBody();
if (is_object($requestBody) && array_key_exists($this->csrfDataKey, (array) $requestBody)) {
$arrayBody = (array) $requestBody;
return $this->ensureThatTheValueIsAString($arrayBody[$this->csrfDataKey]);
}
if (is_array($requestBody) && array_key_exists($this->csrfDataKey, $requestBody)) {
return $this->ensureThatTheValueIsAString($requestBody[$this->csrfDataKey]);
}
return '';
}
private function ensureThatTheValueIsAString($value) : string
{
if (! is_string($value)) {
return '';
}
return $value;
}
}
================================================
FILE: src/PSR7Csrf/RequestParameter/ExtractCSRFParameterInterface.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\RequestParameter;
use Psr\Http\Message\ServerRequestInterface;
interface ExtractCSRFParameterInterface
{
public function __invoke(ServerRequestInterface $request) : string;
}
================================================
FILE: src/PSR7Csrf/Session/ExtractUniqueKeyFromSession.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\Session;
use PSR7Sessions\Storageless\Session\SessionInterface;
final class ExtractUniqueKeyFromSession implements ExtractUniqueKeyFromSessionInterface
{
const ENTROPY = 32;
/**
* @var string
*/
private $uniqueIdKey;
public function __construct(string $uniqueIdKey)
{
$this->uniqueIdKey = $uniqueIdKey;
}
public function __invoke(SessionInterface $session) : string
{
$uniqueKey = $session->get($this->uniqueIdKey, '');
if ('' === $uniqueKey || ! is_string($uniqueKey)) {
$generatedKey = bin2hex(random_bytes(self::ENTROPY));
$session->set($this->uniqueIdKey, $generatedKey);
return $generatedKey;
}
return $uniqueKey;
}
}
================================================
FILE: src/PSR7Csrf/Session/ExtractUniqueKeyFromSessionInterface.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf\Session;
use PSR7Sessions\Storageless\Session\SessionInterface;
interface ExtractUniqueKeyFromSessionInterface
{
public function __invoke(SessionInterface $session) : string;
}
================================================
FILE: src/PSR7Csrf/TokenGenerator.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Token;
use Psr\Http\Message\ServerRequestInterface;
use PSR7Csrf\Exception\InvalidExpirationTimeException;
use PSR7Csrf\Exception\SessionAttributeNotFoundException;
use PSR7Csrf\Session\ExtractUniqueKeyFromSessionInterface;
use PSR7Sessions\Storageless\Session\SessionInterface;
final class TokenGenerator implements TokenGeneratorInterface
{
/**
* @var Signer
*/
private $signer;
/**
* @var ExtractUniqueKeyFromSessionInterface
*/
private $extractUniqueKeyFromSession;
/**
* @var int
*/
private $expirationTime;
/**
* @var string
*/
private $sessionAttribute;
/**
* @param Signer $signer
* @param ExtractUniqueKeyFromSessionInterface $extractUniqueKeyFromSession
* @param int $expirationTime
* @param string $sessionAttribute
*
* @throws InvalidExpirationTimeException
*/
public function __construct(
Signer $signer,
ExtractUniqueKeyFromSessionInterface $extractUniqueKeyFromSession,
int $expirationTime,
string $sessionAttribute
) {
if ($expirationTime <= 0) {
throw InvalidExpirationTimeException::fromInvalidExpirationTime($expirationTime);
}
$this->signer = $signer;
$this->extractUniqueKeyFromSession = $extractUniqueKeyFromSession;
$this->expirationTime = $expirationTime;
$this->sessionAttribute = $sessionAttribute;
}
public function __invoke(ServerRequestInterface $request) : Token
{
$session = $request->getAttribute($this->sessionAttribute);
if (! $session instanceof SessionInterface) {
throw SessionAttributeNotFoundException::fromAttributeNameAndRequest($this->sessionAttribute, $request);
}
$timestamp = (new \DateTime())->getTimestamp();
return (new Builder())
->setIssuedAt($timestamp)
->setExpiration($timestamp + $this->expirationTime)
->sign($this->signer, $this->extractUniqueKeyFromSession->__invoke($session))
->getToken();
}
}
================================================
FILE: src/PSR7Csrf/TokenGeneratorInterface.php
================================================
<?php
declare(strict_types=1);
namespace PSR7Csrf;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Token;
use Psr\Http\Message\ServerRequestInterface;
interface TokenGeneratorInterface
{
public function __invoke(ServerRequestInterface $request) : Token;
}
================================================
FILE: test/PSR7CsrfTest/CSRFCheckerMiddlewareTest.php
================================================
<?php
declare(strict_types=1);
namespace PSR7CsrfTest;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use PSR7Csrf\CSRFCheckerMiddleware;
use PSR7Csrf\Exception\SessionAttributeNotFoundException;
use PSR7Csrf\HttpMethod\IsSafeHttpRequestInterface;
use PSR7Csrf\RequestParameter\ExtractCSRFParameterInterface;
use PSR7Csrf\Session\ExtractUniqueKeyFromSessionInterface;
use PSR7Sessions\Storageless\Session\SessionInterface;
use stdClass;
/**
* @covers \PSR7Csrf\CSRFCheckerMiddleware
*/
final class CSRFCheckerMiddlewareTest extends TestCase
{
/**
* @var Signer
*/
private $signer;
/**
* @var Parser
*/
private $tokenParser;
/**
* @var IsSafeHttpRequestInterface|\PHPUnit_Framework_MockObject_MockObject
*/
private $isSafeHttpRequest;
/**
* @var ExtractUniqueKeyFromSessionInterface|\PHPUnit_Framework_MockObject_MockObject
*/
private $extractUniqueKeyFromSession;
/**
* @var ExtractCSRFParameterInterface|\PHPUnit_Framework_MockObject_MockObject
*/
private $extractCSRFParameter;
/**
* @var ServerRequestInterface|\PHPUnit_Framework_MockObject_MockObject
*/
private $request;
/**
* @var ResponseInterface|\PHPUnit_Framework_MockObject_MockObject
*/
private $response;
/**
* @var SessionInterface|\PHPUnit_Framework_MockObject_MockObject
*/
private $session;
/**
* @var string
*/
private $sessionAttribute;
/**
* @var RequestHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
private $nextMiddleware;
/**
* @var CSRFCheckerMiddleware
*/
private $middleware;
/**
* @var ResponseInterface
*/
private $faultyResponse;
/**
* {@inheritDoc}
*/
protected function setUp()
{
parent::setUp();
$this->signer = new Signer\Hmac\Sha256();
$this->tokenParser = new Parser();
$this->isSafeHttpRequest = $this->createMock(IsSafeHttpRequestInterface::class);
$this->extractUniqueKeyFromSession = $this->createMock(ExtractUniqueKeyFromSessionInterface::class);
$this->extractCSRFParameter = $this->createMock(ExtractCSRFParameterInterface::class);
$this->request = $this->createMock(ServerRequestInterface::class);
$this->response = $this->createMock(ResponseInterface::class);
$this->session = $this->createMock(SessionInterface::class);
$this->sessionAttribute = uniqid('session', true);
$this->nextMiddleware = $this->createMock(RequestHandlerInterface::class);
$this->faultyResponse = $this->createMock(ResponseInterface::class);
$this->middleware = new CSRFCheckerMiddleware(
$this->isSafeHttpRequest,
$this->extractUniqueKeyFromSession,
$this->extractCSRFParameter,
$this->tokenParser,
$this->signer,
$this->sessionAttribute,
$this->faultyResponse
);
}
public function testWillIgnoreSafeRequestsWithNoNextMiddleware()
{
$this->isSafeHttpRequest->expects(self::any())->method('__invoke')->with($this->request)->willReturn(true);
$this
->nextMiddleware
->expects(self::once())
->method('handle')
->with($this->request)
->willReturn($this->response);
self::assertSame($this->response, $this->middleware->process($this->request, $this->nextMiddleware));
}
public function testWillSucceedIfANonSafeRequestIsProvidedWithAValidTokenWithNextMiddleware()
{
$secret = uniqid('secret', true);
$validToken = (new Builder())
->sign($this->signer, $secret)
->getToken();
$this
->nextMiddleware
->expects(self::once())
->method('handle')
->with($this->request)
->willReturn($this->response);
$this->isSafeHttpRequest->expects(self::any())->method('__invoke')->with($this->request)->willReturn(false);
$this
->extractUniqueKeyFromSession
->expects(self::any())
->method('__invoke')
->with($this->session)
->willReturn($secret);
$this
->extractCSRFParameter
->expects(self::any())
->method('__invoke')
->with($this->request)
->willReturn((string) $validToken);
$this
->request
->expects(self::any())
->method('getAttribute')
->with($this->sessionAttribute)
->willReturn($this->session);
self::assertSame(
$this->response,
$this->middleware->process($this->request, $this->nextMiddleware)
);
}
public function testNonMatchingSignedTokensAreRejected()
{
$secret = uniqid('secret', true);
$validToken = (new Builder())
->sign($this->signer, uniqid('wrongSecret', true))
->getToken();
$this->isSafeHttpRequest->expects(self::any())->method('__invoke')->with($this->request)->willReturn(false);
$this
->extractUniqueKeyFromSession
->expects(self::any())
->method('__invoke')
->with($this->session)
->willReturn($secret);
$this
->extractCSRFParameter
->expects(self::any())
->method('__invoke')
->with($this->request)
->willReturn((string) $validToken);
$this
->request
->expects(self::any())
->method('getAttribute')
->with($this->sessionAttribute)
->willReturn($this->session);
$this->assertFaultyResponse();
}
public function testUnsignedTokensAreRejected()
{
$secret = uniqid('secret', true);
$validToken = (new Builder())->getToken();
$this->isSafeHttpRequest->expects(self::any())->method('__invoke')->with($this->request)->willReturn(false);
$this
->extractUniqueKeyFromSession
->expects(self::any())
->method('__invoke')
->with($this->session)
->willReturn($secret);
$this
->extractCSRFParameter
->expects(self::any())
->method('__invoke')
->with($this->request)
->willReturn((string) $validToken);
$this
->request
->expects(self::any())
->method('getAttribute')
->with($this->sessionAttribute)
->willReturn($this->session);
$this->assertFaultyResponse();
}
public function testExpiredSignedTokensAreRejected()
{
$secret = uniqid('secret', true);
$validToken = (new Builder())
->setExpiration(time() - 3600)
->sign($this->signer, $secret)
->getToken();
$this->isSafeHttpRequest->expects(self::any())->method('__invoke')->with($this->request)->willReturn(false);
$this
->extractUniqueKeyFromSession
->expects(self::any())
->method('__invoke')
->with($this->session)
->willReturn($secret);
$this
->extractCSRFParameter
->expects(self::any())
->method('__invoke')
->with($this->request)
->willReturn((string) $validToken);
$this
->request
->expects(self::any())
->method('getAttribute')
->with($this->sessionAttribute)
->willReturn($this->session);
$this->assertFaultyResponse();
}
public function testMalformedTokensShouldBeRejected()
{
$this->isSafeHttpRequest->expects(self::any())->method('__invoke')->with($this->request)->willReturn(false);
$this
->extractUniqueKeyFromSession
->expects(self::any())
->method('__invoke')
->with($this->session)
->willReturn(uniqid('secret', true));
$this
->extractCSRFParameter
->expects(self::any())
->method('__invoke')
->with($this->request)
->willReturn('yadda yadda invalid bs');
$this
->request
->expects(self::any())
->method('getAttribute')
->with($this->sessionAttribute)
->willReturn($this->session);
$this->assertFaultyResponse();
}
public function testWillFailIfARequestDoesNotIncludeASession()
{
$this->isSafeHttpRequest->expects(self::any())->method('__invoke')->with($this->request)->willReturn(false);
$this
->extractCSRFParameter
->expects(self::any())
->method('__invoke')
->with($this->request)
->willReturn((new Builder())->getToken());
$this
->request
->expects(self::any())
->method('getAttribute')
->with($this->sessionAttribute)
->willReturn(new stdClass());
$this
->request
->expects(self::any())
->method('getAttributes')
->willReturn([]);
$this->expectException(SessionAttributeNotFoundException::class);
$this->middleware->process($this->request, $this->nextMiddleware);
}
private function assertFaultyResponse() : void
{
$this->nextMiddleware->expects(self::never())->method('handle');
self::assertSame($this->faultyResponse, $this->middleware->process($this->request, $this->nextMiddleware));
}
}
================================================
FILE: test/PSR7CsrfTest/Exception/InvalidExpirationTimeExceptionTest.php
================================================
<?php
declare(strict_types=1);
namespace PSR7CsrfTest\Exception;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use PSR7Csrf\Exception\ExceptionInterface;
use PSR7Csrf\Exception\InvalidExpirationTimeException;
/**
* @covers \PSR7Csrf\Exception\InvalidExpirationTimeException
*/
final class InvalidExpirationTimeExceptionTest extends TestCase
{
public function testFromInvalidExpirationTime()
{
$exception = InvalidExpirationTimeException::fromInvalidExpirationTime(-4);
self::assertInstanceOf(InvalidExpirationTimeException::class, $exception);
self::assertInstanceOf(InvalidArgumentException::class, $exception);
self::assertInstanceOf(ExceptionInterface::class, $exception);
self::assertSame('The provided expiration time -4 is invalid: expected a >0 integer', $exception->getMessage());
}
}
================================================
FILE: test/PSR7CsrfTest/Exception/InvalidRequestParameterNameExceptionTest.php
================================================
<?php
declare(strict_types=1);
namespace PSR7CsrfTest\Exception;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use PSR7Csrf\Exception\ExceptionInterface;
use PSR7Csrf\Exception\InvalidRequestParameterNameException;
/**
* @covers \PSR7Csrf\Exception\InvalidRequestParameterNameException
*/
final class InvalidRequestParameterNameExceptionTest extends TestCase
{
public function testFromEmptyRequestParameterName()
{
$exception = InvalidRequestParameterNameException::fromEmptyRequestParameterName();
self::assertInstanceOf(InvalidRequestParameterNameException::class, $exception);
self::assertInstanceOf(InvalidArgumentException::class, $exception);
self::assertInstanceOf(ExceptionInterface::class, $exception);
self::assertSame('The given request parameter must be a non-empty string', $exception->getMessage());
}
}
================================================
FILE: test/PSR7CsrfTest/Exception/SessionAttributeNotFoundExceptionTest.php
================================================
<?php
declare(strict_types=1);
namespace PSR7CsrfTest\Exception;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface;
use PSR7Csrf\Exception\ExceptionInterface;
use PSR7Csrf\Exception\SessionAttributeNotFoundException;
use UnexpectedValueException;
/**
* @covers \PSR7Csrf\Exception\SessionAttributeNotFoundException
*/
final class SessionAttributeNotFoundExceptionTest extends TestCase
{
public function testFromInvalidExpirationTime()
{
/* @var $request ServerRequestInterface|\PHPUnit_Framework_MockObject_MockObject */
$request = $this->createMock(ServerRequestInterface::class);
$request->expects(self::any())->method('getAttributes')->willReturn(['foo' => 'bar', 'baz' => 'tab']);
$exception = SessionAttributeNotFoundException::fromAttributeNameAndRequest('foo', $request);
self::assertInstanceOf(SessionAttributeNotFoundException::class, $exception);
self::assertInstanceOf(UnexpectedValueException::class, $exception);
self::assertInstanceOf(ExceptionInterface::class, $exception);
self::assertSame(
'Provided request contains no matching session attribute "foo", attributes ["foo","baz"] exist',
$exception->getMessage()
);
}
}
================================================
FILE: test/PSR7CsrfTest/FactoryTest.php
================================================
<?php
declare(strict_types=1);
namespace PSR7CsrfTest;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use PSR7Csrf\CSRFCheckerMiddleware;
use PSR7Csrf\Factory;
use PSR7Csrf\TokenGeneratorInterface;
/**
* @covers \PSR7Csrf\Factory
*/
final class FactoryTest extends TestCase
{
public function testCreateDefaultCSRFCheckerMiddleware()
{
$faultyResponse = $this->createMock(ResponseInterface::class);
$middleware = Factory::createDefaultCSRFCheckerMiddleware($faultyResponse);
self::assertInstanceOf(CSRFCheckerMiddleware::class, $middleware);
$request = $this->createMock(ServerRequestInterface::class);
$request
->expects(self::any())
->method('getMethod')
->willReturn('POST');
self::assertSame(
$faultyResponse,
$middleware->process(
$request,
$this->createMock(RequestHandlerInterface::class)
),
'Faulty http response passed to the factory is returned as part of a failed CSRF validation'
);
}
public function testCreateDefaultTokenGenerator()
{
self::assertInstanceOf(TokenGeneratorInterface::class, Factory::createDefaultTokenGenerator());
}
}
================================================
FILE: test/PSR7CsrfTest/HttpMethod/IsSafeHttpRequestTest.php
================================================
<?php
declare(strict_types=1);
namespace PSR7CsrfTest\HttpMethod;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
use PSR7Csrf\HttpMethod\IsSafeHttpRequest;
/**
* @covers \PSR7Csrf\HttpMethod\IsSafeHttpRequest
*/
final class IsSafeHttpRequestTest extends TestCase
{
/**
* @dataProvider httpMethodsProvider
*
* @param array $safeMethods
* @param string $httpMethod
* @param bool $expectedResult
*/
public function testSafeMethods(array $safeMethods, string $httpMethod, bool $expectedResult)
{
/* @var $request RequestInterface|\PHPUnit_Framework_MockObject_MockObject */
$request = $this->createMock(RequestInterface::class);
$request->expects(self::any())->method('getMethod')->willReturn($httpMethod);
self::assertSame($expectedResult, (new IsSafeHttpRequest(...$safeMethods))->__invoke($request));
}
public function httpMethodsProvider() : array
{
return [
'empty' => [
[],
'GET',
false,
],
'GET only' => [
['GET'],
'GET',
true,
],
'get only' => [
['get'],
'GET',
true,
],
'GET only, matching lowercase get' => [
['GET'],
'get',
true,
],
'GET only, non-matching method' => [
['GET'],
'PUT',
false,
],
'GET, PUT only, matching method' => [
['GET', 'PUT'],
'PUT',
true,
],
];
}
/**
* @dataProvider safeDefaultsMatchingProvider
*
* @param string $httpMethod
* @param bool $expectedResult
*/
public function testSafeMethodsWithDefaults(string $httpMethod, bool $expectedResult)
{
/* @var $request RequestInterface|\PHPUnit_Framework_MockObject_MockObject */
$request = $this->createMock(RequestInterface::class);
$request->expects(self::any())->method('getMethod')->willReturn($httpMethod);
self::assertSame($expectedResult, IsSafeHttpRequest::fromDefaultSafeMethods()->__invoke($request));
}
public function safeDefaultsMatchingProvider() : array
{
return [
'empty' => [
'',
false,
],
'GET' => [
'GET',
true,
],
'get' => [
'get',
true,
],
'HEAD' => [
'HEAD',
true,
],
'head' => [
'head',
true,
],
'OPTIONS' => [
'OPTIONS',
true,
],
'options' => [
'options',
true,
],
'DELETE' => [
'DELETE',
false,
],
'delete' => [
'delete',
false,
],
'POST' => [
'POST',
false,
],
'post' => [
'post',
false,
],
'PUT' => [
'PUT',
false,
],
'put' => [
'put',
false,
],
'UNKNOWN' => [
'UNKNOWN',
false,
],
'unknown' => [
'unknown',
false,
],
];
}
}
================================================
FILE: test/PSR7CsrfTest/RequestParameter/ExtractCSRFParameterTest.php
================================================
<?php
declare(strict_types=1);
namespace PSR7CsrfTest\RequestParameter;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface;
use PSR7Csrf\Exception\InvalidRequestParameterNameException;
use PSR7Csrf\RequestParameter\ExtractCSRFParameter;
/**
* @covers \PSR7Csrf\RequestParameter\ExtractCSRFParameter
*/
final class ExtractCSRFParameterTest extends TestCase
{
public function testRejectsEmptyRequestParameterName()
{
$this->expectException(InvalidRequestParameterNameException::class);
new ExtractCSRFParameter('');
}
/**
* @dataProvider requestBodyProvider
*
* @param string $requestParameter
* @param null|object|array $body
* @param string $expectedExtractedValue
*
* @return void
*/
public function testExtraction(string $requestParameter, $body, string $expectedExtractedValue)
{
/* @var $request ServerRequestInterface|\PHPUnit_Framework_MockObject_MockObject */
$request = $this->createMock(ServerRequestInterface::class);
$request->expects(self::any())->method('getParsedBody')->willReturn($body);
self::assertSame($expectedExtractedValue, (new ExtractCSRFParameter($requestParameter))->__invoke($request));
}
public function requestBodyProvider()
{
/** @noinspection PhpUnusedPrivateFieldInspection */
return [
'null body' => [
'request parameter name',
null,
'',
],
'empty array' => [
'request parameter name',
[],
'',
],
'empty object' => [
'request parameter name',
(object) [],
'',
],
'array with matching parameter' => [
'request parameter name',
['request parameter name' => 'foo'],
'foo',
],
'array with matching non-string parameter' => [
'request parameter name',
['request parameter name' => 123],
'',
],
'object with matching parameter' => [
'request parameter name',
(object) ['request parameter name' => 'foo'],
'foo',
],
'object with matching non-string parameter' => [
'request parameter name',
(object) ['request parameter name' => 123],
'',
],
'class with private matching property' => [
'field',
new class {
private $field = 'bar';
},
'',
],
'class with protected matching property' => [
'field',
new class {
protected $field = 'bar';
},
'',
],
'class with public matching property' => [
'field',
new class {
public $field = 'bar';
},
'bar',
],
'class with public matching non-string property' => [
'field',
new class {
public $field = 123;
},
'',
],
];
}
}
================================================
FILE: test/PSR7CsrfTest/Session/ExtractUniqueKeyFromSessionTest.php
================================================
<?php
declare(strict_types=1);
namespace PSR7CsrfTest\HttpMethod;
use PHPUnit\Framework\TestCase;
use PSR7Csrf\Session\ExtractUniqueKeyFromSession;
use PSR7Sessions\Storageless\Session\SessionInterface;
/**
* @covers \PSR7Csrf\Session\ExtractUniqueKeyFromSession
*/
final class ExtractUniqueKeyFromSessionTest extends TestCase
{
/**
* @dataProvider keysProvider
*
* @param string $key
*/
public function testExtractionWithExistingKey(string $key)
{
/* @var $session SessionInterface|\PHPUnit_Framework_MockObject_MockObject */
$session = $this->createMock(SessionInterface::class);
$superSecret = uniqid('', true);
$session->expects(self::any())->method('get')->with($key, '')->willReturn($superSecret);
$session->expects(self::never())->method('set');
self::assertSame($superSecret, (new ExtractUniqueKeyFromSession($key))->__invoke($session));
}
/**
* @dataProvider keysProvider
*
* @param string $key
*/
public function testExtractionWithEmptyExistingKey(string $key)
{
$extractKey = new ExtractUniqueKeyFromSession($key);
/* @var $session SessionInterface|\PHPUnit_Framework_MockObject_MockObject */
$session = $this->createMock(SessionInterface::class);
$session->expects(self::any())->method('get')->with($key, '')->willReturn('');
$session->expects(self::exactly(2))->method('set')->with(
$key,
self::callback(function (string $secret) {
self::assertNotEmpty($secret);
return true;
})
);
$secretUniqueKey = $extractKey->__invoke($session);
self::assertInternalType('string', $secretUniqueKey);
self::assertNotEmpty($secretUniqueKey);
$anotherSecretKey = $extractKey->__invoke($session);
self::assertInternalType('string', $anotherSecretKey);
self::assertNotEmpty($anotherSecretKey);
self::assertNotEquals($secretUniqueKey, $anotherSecretKey);
}
/**
* @dataProvider keysProvider
*
* @param string $key
*/
public function testExtractionWithNonStringExistingKey(string $key)
{
$extractKey = new ExtractUniqueKeyFromSession($key);
/* @var $session SessionInterface|\PHPUnit_Framework_MockObject_MockObject */
$session = $this->createMock(SessionInterface::class);
$session->expects(self::any())->method('get')->with($key, '')->willReturn(123);
$session->expects(self::exactly(2))->method('set')->with(
$key,
self::callback(function (string $secret) {
self::assertNotEmpty($secret);
return true;
})
);
$secretUniqueKey = $extractKey->__invoke($session);
self::assertInternalType('string', $secretUniqueKey);
self::assertNotEmpty($secretUniqueKey);
$anotherSecretKey = $extractKey->__invoke($session);
self::assertInternalType('string', $anotherSecretKey);
self::assertNotEmpty($anotherSecretKey);
self::assertNotEquals($secretUniqueKey, $anotherSecretKey);
}
public function keysProvider() : array
{
return [
[''],
['key'],
['123'],
['123 456'],
];
}
}
================================================
FILE: test/PSR7CsrfTest/TokenGeneratorTest.php
================================================
<?php
declare(strict_types=1);
namespace PSR7CsrfTest;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface;
use PSR7Csrf\Exception\InvalidExpirationTimeException;
use PSR7Csrf\Exception\SessionAttributeNotFoundException;
use PSR7Csrf\Session\ExtractUniqueKeyFromSessionInterface;
use PSR7Csrf\TokenGenerator;
use PSR7Sessions\Storageless\Session\SessionInterface;
use stdClass;
/**
* @covers \PSR7Csrf\TokenGenerator
*/
final class TokenGeneratorTest extends TestCase
{
/**
* @dataProvider invalidExpirationTimeProvider
*
* @param int $invalidExpirationTime
* @param bool $valid
*/
public function testWillRejectInvalidExpirationTime(int $invalidExpirationTime, bool $valid)
{
/* @var $signer Signer */
$signer = $this->createMock(Signer::class);
/* @var $extractUniqueKeyFromSession ExtractUniqueKeyFromSessionInterface */
$extractUniqueKeyFromSession = $this->createMock(ExtractUniqueKeyFromSessionInterface::class);
if (! $valid) {
$this->expectException(InvalidExpirationTimeException::class);
}
self::assertInstanceOf(
TokenGenerator::class,
new TokenGenerator($signer, $extractUniqueKeyFromSession, $invalidExpirationTime, 'session')
);
}
public function invalidExpirationTimeProvider() : array
{
return [
[100, true],
[1, true],
[0, false],
[-1, false],
[-200, false],
];
}
/**
* @dataProvider validExpirationTimeProvider
*
* @param int $validExpirationTime
*/
public function testWillGenerateAValidJWTToken(int $validExpirationTime)
{
$signer = new Sha256();
/* @var $extractUniqueKeyFromSession ExtractUniqueKeyFromSessionInterface|\PHPUnit_Framework_MockObject_MockObject */
$extractUniqueKeyFromSession = $this->createMock(ExtractUniqueKeyFromSessionInterface::class);
/* @var $session SessionInterface */
$session = $this->createMock(SessionInterface::class);
/* @var $request ServerRequestInterface|\PHPUnit_Framework_MockObject_MockObject */
$request = $this->createMock(ServerRequestInterface::class);
$sessionAttribute = uniqid('session', true);
$generator = new TokenGenerator($signer, $extractUniqueKeyFromSession, $validExpirationTime, $sessionAttribute);
$secretKey = uniqid('secretKey', true);
$request->expects(self::any())->method('getAttribute')->with($sessionAttribute)->willReturn($session);
$extractUniqueKeyFromSession->expects(self::any())->method('__invoke')->with($session)->willReturn($secretKey);
$token = $generator->__invoke($request);
self::assertTrue($token->verify($signer, $secretKey));
self::assertLessThanOrEqual(time(), $token->getClaim('iat'));
self::assertGreaterThan(time(), $token->getClaim('exp'));
}
public function validExpirationTimeProvider() : array
{
return [
[10],
[100],
];
}
public function testWillFailIfTheSessionAttributeIsNotASession()
{
/* @var $extractUniqueKeyFromSession ExtractUniqueKeyFromSessionInterface|\PHPUnit_Framework_MockObject_MockObject */
$extractUniqueKeyFromSession = $this->createMock(ExtractUniqueKeyFromSessionInterface::class);
/* @var $request ServerRequestInterface|\PHPUnit_Framework_MockObject_MockObject */
$request = $this->createMock(ServerRequestInterface::class);
$sessionAttribute = uniqid('session', true);
$generator = new TokenGenerator(new Sha256(), $extractUniqueKeyFromSession, 10, $sessionAttribute);
$request->expects(self::any())->method('getAttribute')->with($sessionAttribute)->willReturn(new stdClass());
$request->expects(self::any())->method('getAttributes')->willReturn([]);
$this->expectException(SessionAttributeNotFoundException::class);
$generator->__invoke($request);
}
}
gitextract_rj5xlyxv/
├── .gitattributes
├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composer.json
├── examples/
│ ├── .gitignore
│ ├── composer.json
│ └── index.php
├── humbug.json.dist
├── phpcs.xml.dist
├── phpunit.xml.dist
├── src/
│ └── PSR7Csrf/
│ ├── CSRFCheckerMiddleware.php
│ ├── Exception/
│ │ ├── ExceptionInterface.php
│ │ ├── InvalidExpirationTimeException.php
│ │ ├── InvalidRequestParameterNameException.php
│ │ └── SessionAttributeNotFoundException.php
│ ├── Factory.php
│ ├── HttpMethod/
│ │ ├── IsSafeHttpRequest.php
│ │ └── IsSafeHttpRequestInterface.php
│ ├── RequestParameter/
│ │ ├── ExtractCSRFParameter.php
│ │ └── ExtractCSRFParameterInterface.php
│ ├── Session/
│ │ ├── ExtractUniqueKeyFromSession.php
│ │ └── ExtractUniqueKeyFromSessionInterface.php
│ ├── TokenGenerator.php
│ └── TokenGeneratorInterface.php
└── test/
└── PSR7CsrfTest/
├── CSRFCheckerMiddlewareTest.php
├── Exception/
│ ├── InvalidExpirationTimeExceptionTest.php
│ ├── InvalidRequestParameterNameExceptionTest.php
│ └── SessionAttributeNotFoundExceptionTest.php
├── FactoryTest.php
├── HttpMethod/
│ └── IsSafeHttpRequestTest.php
├── RequestParameter/
│ └── ExtractCSRFParameterTest.php
├── Session/
│ └── ExtractUniqueKeyFromSessionTest.php
└── TokenGeneratorTest.php
SYMBOL INDEX (79 symbols across 24 files)
FILE: examples/index.php
method __construct (line 64) | public function __construct(TokenGeneratorInterface $tokenGenerator)
method handle (line 69) | public function handle(ServerRequestInterface $request) : ResponseInterface
method __construct (line 104) | public function __construct(RequestHandlerInterface $action, MiddlewareI...
method handle (line 110) | public function handle(ServerRequestInterface $request) : ResponseInterface
FILE: src/PSR7Csrf/CSRFCheckerMiddleware.php
class CSRFCheckerMiddleware (line 21) | final class CSRFCheckerMiddleware implements \Psr\Http\Server\Middleware...
method __construct (line 58) | public function __construct(
method process (line 76) | public function process(
method getSession (line 104) | private function getSession(ServerRequestInterface $request) : Session...
FILE: src/PSR7Csrf/Exception/ExceptionInterface.php
type ExceptionInterface (line 9) | interface ExceptionInterface extends Throwable
FILE: src/PSR7Csrf/Exception/InvalidExpirationTimeException.php
class InvalidExpirationTimeException (line 9) | class InvalidExpirationTimeException extends InvalidArgumentException im...
method fromInvalidExpirationTime (line 11) | public static function fromInvalidExpirationTime(int $expirationTime) ...
FILE: src/PSR7Csrf/Exception/InvalidRequestParameterNameException.php
class InvalidRequestParameterNameException (line 9) | class InvalidRequestParameterNameException extends InvalidArgumentExcept...
method fromEmptyRequestParameterName (line 11) | public static function fromEmptyRequestParameterName() : self
FILE: src/PSR7Csrf/Exception/SessionAttributeNotFoundException.php
class SessionAttributeNotFoundException (line 10) | class SessionAttributeNotFoundException extends UnexpectedValueException...
method fromAttributeNameAndRequest (line 12) | public static function fromAttributeNameAndRequest(string $attributeNa...
FILE: src/PSR7Csrf/Factory.php
class Factory (line 15) | final class Factory
method createDefaultCSRFCheckerMiddleware (line 23) | public static function createDefaultCSRFCheckerMiddleware(
method createDefaultTokenGenerator (line 37) | public static function createDefaultTokenGenerator() : TokenGeneratorI...
FILE: src/PSR7Csrf/HttpMethod/IsSafeHttpRequest.php
class IsSafeHttpRequest (line 10) | final class IsSafeHttpRequest implements IsSafeHttpRequestInterface
method __construct (line 19) | public function __construct(string ...$safeMethods)
method fromDefaultSafeMethods (line 24) | public static function fromDefaultSafeMethods() : self
method __invoke (line 29) | public function __invoke(RequestInterface $request) : bool
FILE: src/PSR7Csrf/HttpMethod/IsSafeHttpRequestInterface.php
type IsSafeHttpRequestInterface (line 10) | interface IsSafeHttpRequestInterface
method __invoke (line 12) | public function __invoke(RequestInterface $request) : bool;
FILE: src/PSR7Csrf/RequestParameter/ExtractCSRFParameter.php
class ExtractCSRFParameter (line 10) | final class ExtractCSRFParameter implements ExtractCSRFParameterInterface
method __construct (line 17) | public function __construct(string $csrfDataKey)
method __invoke (line 26) | public function __invoke(ServerRequestInterface $request) : string
method ensureThatTheValueIsAString (line 44) | private function ensureThatTheValueIsAString($value) : string
FILE: src/PSR7Csrf/RequestParameter/ExtractCSRFParameterInterface.php
type ExtractCSRFParameterInterface (line 9) | interface ExtractCSRFParameterInterface
method __invoke (line 11) | public function __invoke(ServerRequestInterface $request) : string;
FILE: src/PSR7Csrf/Session/ExtractUniqueKeyFromSession.php
class ExtractUniqueKeyFromSession (line 9) | final class ExtractUniqueKeyFromSession implements ExtractUniqueKeyFromS...
method __construct (line 18) | public function __construct(string $uniqueIdKey)
method __invoke (line 23) | public function __invoke(SessionInterface $session) : string
FILE: src/PSR7Csrf/Session/ExtractUniqueKeyFromSessionInterface.php
type ExtractUniqueKeyFromSessionInterface (line 9) | interface ExtractUniqueKeyFromSessionInterface
method __invoke (line 11) | public function __invoke(SessionInterface $session) : string;
FILE: src/PSR7Csrf/TokenGenerator.php
class TokenGenerator (line 16) | final class TokenGenerator implements TokenGeneratorInterface
method __construct (line 46) | public function __construct(
method __invoke (line 62) | public function __invoke(ServerRequestInterface $request) : Token
FILE: src/PSR7Csrf/TokenGeneratorInterface.php
type TokenGeneratorInterface (line 11) | interface TokenGeneratorInterface
method __invoke (line 13) | public function __invoke(ServerRequestInterface $request) : Token;
FILE: test/PSR7CsrfTest/CSRFCheckerMiddlewareTest.php
class CSRFCheckerMiddlewareTest (line 25) | final class CSRFCheckerMiddlewareTest extends TestCase
method setUp (line 90) | protected function setUp()
method testWillIgnoreSafeRequestsWithNoNextMiddleware (line 116) | public function testWillIgnoreSafeRequestsWithNoNextMiddleware()
method testWillSucceedIfANonSafeRequestIsProvidedWithAValidTokenWithNextMiddleware (line 130) | public function testWillSucceedIfANonSafeRequestIsProvidedWithAValidTo...
method testNonMatchingSignedTokensAreRejected (line 169) | public function testNonMatchingSignedTokensAreRejected()
method testUnsignedTokensAreRejected (line 199) | public function testUnsignedTokensAreRejected()
method testExpiredSignedTokensAreRejected (line 227) | public function testExpiredSignedTokensAreRejected()
method testMalformedTokensShouldBeRejected (line 258) | public function testMalformedTokensShouldBeRejected()
method testWillFailIfARequestDoesNotIncludeASession (line 283) | public function testWillFailIfARequestDoesNotIncludeASession()
method assertFaultyResponse (line 309) | private function assertFaultyResponse() : void
FILE: test/PSR7CsrfTest/Exception/InvalidExpirationTimeExceptionTest.php
class InvalidExpirationTimeExceptionTest (line 15) | final class InvalidExpirationTimeExceptionTest extends TestCase
method testFromInvalidExpirationTime (line 17) | public function testFromInvalidExpirationTime()
FILE: test/PSR7CsrfTest/Exception/InvalidRequestParameterNameExceptionTest.php
class InvalidRequestParameterNameExceptionTest (line 15) | final class InvalidRequestParameterNameExceptionTest extends TestCase
method testFromEmptyRequestParameterName (line 17) | public function testFromEmptyRequestParameterName()
FILE: test/PSR7CsrfTest/Exception/SessionAttributeNotFoundExceptionTest.php
class SessionAttributeNotFoundExceptionTest (line 16) | final class SessionAttributeNotFoundExceptionTest extends TestCase
method testFromInvalidExpirationTime (line 18) | public function testFromInvalidExpirationTime()
FILE: test/PSR7CsrfTest/FactoryTest.php
class FactoryTest (line 18) | final class FactoryTest extends TestCase
method testCreateDefaultCSRFCheckerMiddleware (line 20) | public function testCreateDefaultCSRFCheckerMiddleware()
method testCreateDefaultTokenGenerator (line 45) | public function testCreateDefaultTokenGenerator()
FILE: test/PSR7CsrfTest/HttpMethod/IsSafeHttpRequestTest.php
class IsSafeHttpRequestTest (line 14) | final class IsSafeHttpRequestTest extends TestCase
method testSafeMethods (line 23) | public function testSafeMethods(array $safeMethods, string $httpMethod...
method httpMethodsProvider (line 33) | public function httpMethodsProvider() : array
method testSafeMethodsWithDefaults (line 75) | public function testSafeMethodsWithDefaults(string $httpMethod, bool $...
method safeDefaultsMatchingProvider (line 85) | public function safeDefaultsMatchingProvider() : array
FILE: test/PSR7CsrfTest/RequestParameter/ExtractCSRFParameterTest.php
class ExtractCSRFParameterTest (line 15) | final class ExtractCSRFParameterTest extends TestCase
method testRejectsEmptyRequestParameterName (line 17) | public function testRejectsEmptyRequestParameterName()
method testExtraction (line 33) | public function testExtraction(string $requestParameter, $body, string...
method requestBodyProvider (line 43) | public function requestBodyProvider()
FILE: test/PSR7CsrfTest/Session/ExtractUniqueKeyFromSessionTest.php
class ExtractUniqueKeyFromSessionTest (line 14) | final class ExtractUniqueKeyFromSessionTest extends TestCase
method testExtractionWithExistingKey (line 21) | public function testExtractionWithExistingKey(string $key)
method testExtractionWithEmptyExistingKey (line 38) | public function testExtractionWithEmptyExistingKey(string $key)
method testExtractionWithNonStringExistingKey (line 72) | public function testExtractionWithNonStringExistingKey(string $key)
method keysProvider (line 101) | public function keysProvider() : array
FILE: test/PSR7CsrfTest/TokenGeneratorTest.php
class TokenGeneratorTest (line 21) | final class TokenGeneratorTest extends TestCase
method testWillRejectInvalidExpirationTime (line 29) | public function testWillRejectInvalidExpirationTime(int $invalidExpira...
method invalidExpirationTimeProvider (line 46) | public function invalidExpirationTimeProvider() : array
method testWillGenerateAValidJWTToken (line 62) | public function testWillGenerateAValidJWTToken(int $validExpirationTime)
method validExpirationTimeProvider (line 86) | public function validExpirationTimeProvider() : array
method testWillFailIfTheSessionAttributeIsNotASession (line 94) | public function testWillFailIfTheSessionAttributeIsNotASession()
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (63K chars).
[
{
"path": ".gitattributes",
"chars": 172,
"preview": "/test export-ignore\n/examples export-ignore\n.gitattributes export-ignore\n.gitignore export-ignore\n.scrutinizer.yml expor"
},
{
"path": ".gitignore",
"chars": 83,
"preview": "vendor\ncomposer.lock\ncomposer.phar\nclover.xml\nhumbug-log.json\nhumbug-log.txt\nhumbug"
},
{
"path": ".scrutinizer.yml",
"chars": 888,
"preview": "before_commands:\n - \"composer update --prefer-source\"\n\ntools:\n external_code_coverage:\n timeout: 1800\n p"
},
{
"path": ".travis.yml",
"chars": 897,
"preview": "language: php\n\nphp:\n - 7.1\n - 7.2\n - nightly\n\nbefore_script:\n - composer update\n - if [ \"$DEPENDENCIES\" = 'low' ] ;"
},
{
"path": "CHANGELOG.md",
"chars": 1247,
"preview": "This is a list of changes/improvements that were introduced in PSR7Csrf\n\n## 2.0.0\n\n- This release aligns the `PSR7Csrf\\C"
},
{
"path": "CONTRIBUTING.md",
"chars": 1065,
"preview": "# Contributing\n\n * Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accep"
},
{
"path": "LICENSE",
"chars": 1057,
"preview": "Copyright (c) 2015 Marco Pivetta\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis s"
},
{
"path": "README.md",
"chars": 4397,
"preview": "# PSR-7 Storage-less HTTP CSRF protection\n\n[](https://travis"
},
{
"path": "composer.json",
"chars": 1098,
"preview": "{\n \"name\": \"ocramius/psr7-csrf\",\n \"license\": \"MIT\",\n \"authors\": [\n {\n \"name\": \"Marco"
},
{
"path": "examples/.gitignore",
"chars": 20,
"preview": "vendor\ncomposer.lock"
},
{
"path": "examples/composer.json",
"chars": 167,
"preview": "{\n \"require\": {\n \"zendframework/zend-diactoros\": \"^1.7.0\"\n },\n \"autoload\": {\n \"files\": [\n "
},
{
"path": "examples/index.php",
"chars": 4067,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\Example;\n\nuse Dflydev\\FigCookies\\SetCookie;\nuse Lcobucci\\Clock\\Syste"
},
{
"path": "humbug.json.dist",
"chars": 187,
"preview": "{\n \"timeout\": 30,\n \"source\": {\n \"directories\": [\n \"src\"\n ]\n },\n \"logs\": {\n \""
},
{
"path": "phpcs.xml.dist",
"chars": 352,
"preview": "<?xml version=\"1.0\"?>\n<ruleset name=\"Custom\">\n <description>code-reviews.io code-style</description>\n <file>./src<"
},
{
"path": "phpunit.xml.dist",
"chars": 945,
"preview": "<?xml version=\"1.0\"?>\n<phpunit\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noNamespaceSchemaLocati"
},
{
"path": "src/PSR7Csrf/CSRFCheckerMiddleware.php",
"chars": 3379,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf;\n\nuse BadMethodCallException;\nuse InvalidArgumentException;\nuse Lcob"
},
{
"path": "src/PSR7Csrf/Exception/ExceptionInterface.php",
"chars": 131,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\Exception;\n\nuse Throwable;\n\ninterface ExceptionInterface extends Thr"
},
{
"path": "src/PSR7Csrf/Exception/InvalidExpirationTimeException.php",
"chars": 412,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\Exception;\n\nuse InvalidArgumentException;\n\nclass InvalidExpirationTi"
},
{
"path": "src/PSR7Csrf/Exception/InvalidRequestParameterNameException.php",
"chars": 366,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\Exception;\n\nuse InvalidArgumentException;\n\nclass InvalidRequestParam"
},
{
"path": "src/PSR7Csrf/Exception/SessionAttributeNotFoundException.php",
"chars": 610,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\Exception;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Unexpec"
},
{
"path": "src/PSR7Csrf/Factory.php",
"chars": 1453,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf;\n\nuse Lcobucci\\JWT\\Parser;\nuse Lcobucci\\JWT\\Signer\\Hmac\\Sha256;\nuse "
},
{
"path": "src/PSR7Csrf/HttpMethod/IsSafeHttpRequest.php",
"chars": 740,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\HttpMethod;\n\nuse Lcobucci\\JWT\\Signer;\nuse Psr\\Http\\Message\\RequestIn"
},
{
"path": "src/PSR7Csrf/HttpMethod/IsSafeHttpRequestInterface.php",
"chars": 235,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\HttpMethod;\n\nuse Lcobucci\\JWT\\Signer;\nuse Psr\\Http\\Message\\RequestIn"
},
{
"path": "src/PSR7Csrf/RequestParameter/ExtractCSRFParameter.php",
"chars": 1363,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\RequestParameter;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse "
},
{
"path": "src/PSR7Csrf/RequestParameter/ExtractCSRFParameterInterface.php",
"chars": 233,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\RequestParameter;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\n\nint"
},
{
"path": "src/PSR7Csrf/Session/ExtractUniqueKeyFromSession.php",
"chars": 806,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\Session;\n\nuse PSR7Sessions\\Storageless\\Session\\SessionInterface;\n\nfi"
},
{
"path": "src/PSR7Csrf/Session/ExtractUniqueKeyFromSessionInterface.php",
"chars": 235,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf\\Session;\n\nuse PSR7Sessions\\Storageless\\Session\\SessionInterface;\n\nin"
},
{
"path": "src/PSR7Csrf/TokenGenerator.php",
"chars": 2350,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf;\n\nuse Lcobucci\\JWT\\Builder;\nuse Lcobucci\\JWT\\Signer;\nuse Lcobucci\\JW"
},
{
"path": "src/PSR7Csrf/TokenGeneratorInterface.php",
"chars": 258,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7Csrf;\n\nuse Lcobucci\\JWT\\Signer;\nuse Lcobucci\\JWT\\Token;\nuse Psr\\Http\\Mess"
},
{
"path": "test/PSR7CsrfTest/CSRFCheckerMiddlewareTest.php",
"chars": 10088,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7CsrfTest;\n\nuse Lcobucci\\JWT\\Builder;\nuse Lcobucci\\JWT\\Parser;\nuse Lcobucc"
},
{
"path": "test/PSR7CsrfTest/Exception/InvalidExpirationTimeExceptionTest.php",
"chars": 868,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7CsrfTest\\Exception;\n\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\"
},
{
"path": "test/PSR7CsrfTest/Exception/InvalidRequestParameterNameExceptionTest.php",
"chars": 893,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7CsrfTest\\Exception;\n\nuse InvalidArgumentException;\nuse PHPUnit\\Framework\\"
},
{
"path": "test/PSR7CsrfTest/Exception/SessionAttributeNotFoundExceptionTest.php",
"chars": 1279,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7CsrfTest\\Exception;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Psr\\Http\\Message"
},
{
"path": "test/PSR7CsrfTest/FactoryTest.php",
"chars": 1379,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7CsrfTest;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Psr\\Http\\Message\\ResponseI"
},
{
"path": "test/PSR7CsrfTest/HttpMethod/IsSafeHttpRequestTest.php",
"chars": 3750,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7CsrfTest\\HttpMethod;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Psr\\Http\\Messag"
},
{
"path": "test/PSR7CsrfTest/RequestParameter/ExtractCSRFParameterTest.php",
"chars": 3429,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7CsrfTest\\RequestParameter;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Psr\\Http\\"
},
{
"path": "test/PSR7CsrfTest/Session/ExtractUniqueKeyFromSessionTest.php",
"chars": 3360,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7CsrfTest\\HttpMethod;\n\nuse PHPUnit\\Framework\\TestCase;\nuse PSR7Csrf\\Sessio"
},
{
"path": "test/PSR7CsrfTest/TokenGeneratorTest.php",
"chars": 4150,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace PSR7CsrfTest;\n\nuse Lcobucci\\JWT\\Signer;\nuse Lcobucci\\JWT\\Signer\\Hmac\\Sha256;\n"
}
]
About this extraction
This page contains the full source code of the Ocramius/PSR7Csrf GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (57.0 KB), approximately 14.8k tokens, and a symbol index with 79 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.