Skip to content
This repository has been archived by the owner on Nov 8, 2020. It is now read-only.

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
klapuch committed May 8, 2017
0 parents commit afd4a83
Show file tree
Hide file tree
Showing 28 changed files with 798 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
vendor/
composer.lock
27 changes: 27 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
sudo: required

language: php

php:
- 7.1

before_install:
- composer self-update

install:
- composer install --no-interaction --prefer-dist --no-scripts --no-progress --no-suggest --optimize-autoloader --classmap-authoritative

script:
- ./vendor/bin/phing ci

after_script:
- >
wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar
&& php coveralls.phar --verbose --config Tests/.coveralls.yml;
after_failure:
- for i in $(find Tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done

cache:
directories:
- $HOME/.composer/cache
16 changes: 16 additions & 0 deletions Core/CombinedExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
declare(strict_types = 1);
namespace Klapuch\Internal;

final class CombinedExtension implements Extension {
private $extensions;

public function __construct(Extension ...$extensions) {
$this->extensions = $extensions;
}

public function improve(): void {
foreach ($this->extensions as $extension)
$extension->improve();
}
}
43 changes: 43 additions & 0 deletions Core/CspHeader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
declare(strict_types = 1);
namespace Klapuch\Internal;

final class CspHeader {
private $directives;
private $nonce;

public function __construct(array $directives) {
$this->directives = $directives;
}

public function nonce(): string {
if ($this->nonce === null)
$this->nonce = base64_encode(random_bytes(16));
return $this->nonce;
}

public function __toString(): string {
if (!$this->directives)
return '';
return 'Content-Security-Policy: ' . $this->withNonce(
$this->format($this->directives)
);
}

private function format(array $directives): string {
return implode(
'; ',
array_map(
function(string $directive, string $constraint): string {
return $directive . ' ' . $constraint;
},
array_keys($directives),
$directives
)
);
}

private function withNonce(string $header): string {
return str_replace('nonce', 'nonce-' . $this->nonce(), $header);
}
}
11 changes: 11 additions & 0 deletions Core/Extension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
declare(strict_types = 1);
namespace Klapuch\Internal;

interface Extension {
/**
* Improve the current setting
* @return void
*/
public function improve(): void;
}
23 changes: 23 additions & 0 deletions Core/HeaderExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
declare(strict_types = 1);
namespace Klapuch\Internal;

final class HeaderExtension implements Extension {
private $headers;

public function __construct(array $headers) {
$this->headers = $headers;
}

public function improve(): void {
(new RawHeaderExtension(
array_map(
function(string $field, string $value): string {
return sprintf('%s:%s', $field, $value);
},
array_keys($this->headers),
$this->headers
)
))->improve();
}
}
16 changes: 16 additions & 0 deletions Core/IniSetExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
declare(strict_types = 1);
namespace Klapuch\Internal;

final class IniSetExtension implements Extension {
private $settings;

public function __construct(array $settings) {
$this->settings = $settings;
}

public function improve(): void {
foreach ($this->settings as $name => $value)
ini_set($name, (string) $value);
}
}
20 changes: 20 additions & 0 deletions Core/InternationalExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
declare(strict_types = 1);
namespace Klapuch\Internal;

final class InternationalExtension implements Extension {
private $timezone;

public function __construct(string $timezone) {
$this->timezone = $timezone;
}

public function improve(): void {
mb_internal_encoding('UTF-8');
if (@date_default_timezone_set($this->timezone) === false) {
throw new \InvalidArgumentException(
sprintf('Timezone "%s" is invalid', $this->timezone)
);
}
}
}
16 changes: 16 additions & 0 deletions Core/RawHeaderExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
declare(strict_types = 1);
namespace Klapuch\Internal;

final class RawHeaderExtension implements Extension {
private $headers;

public function __construct(array $headers) {
$this->headers = $headers;
}

public function improve(): void {
foreach ($this->headers as $header)
header((string) $header);
}
}
74 changes: 74 additions & 0 deletions Core/SessionExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
declare(strict_types = 1);
namespace Klapuch\Internal;

final class SessionExtension implements Extension {
private const TIMER = '_timer',
DEFAULT_BREAK = 20;
private const PROPRIETARIES = ['SameSite'];
private $settings;
private $break;

public function __construct(array $settings, int $break = self::DEFAULT_BREAK) {
$this->settings = $settings;
$this->break = $break;
}

public function improve(): void {
if (session_status() === PHP_SESSION_NONE)
session_start($this->native($this->settings));
if ($this->elapsed($this->break))
session_regenerate_id(true);
header(static::raw($this->settings));
$_SESSION[self::TIMER] = time();
}

private function elapsed(int $break): bool {
return isset($_SESSION[self::TIMER])
&& (time() - $_SESSION[self::TIMER]) > $break;
}

/**
* Just the native and supported php setting
* @param array $settings
* @return array
*/
private function native(array $settings): array {
return array_diff_ukey(
$settings,
array_flip(self::PROPRIETARIES),
'strcasecmp'
);
}

/**
* The raw cookie header
* @param array $settings
* @return string
*/
private static function raw(array $settings): string {
$matches = array_intersect_ukey(
array_flip(self::PROPRIETARIES),
$settings,
'strcasecmp'
);
$headers = array_combine(
array_flip($matches),
array_intersect_ukey($settings, $matches, 'strcasecmp')
);
return sprintf(
'%s; %s',
current(preg_grep('~^Set-Cookie: ~', headers_list())),
implode(
';',
array_map(
function(string $field, string $value): string {
return sprintf('%s=%s', $field, $value);
},
array_keys($headers),
$headers
)
)
);
}
}
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Internal
[![Build Status](https://travis-ci.org/klapuch/Internal.svg?branch=master)](https://travis-ci.org/klapuch/Internal) [![Build status](https://ci.appveyor.com/api/projects/status/80sjyunbbtmdneyp?svg=true)](https://ci.appveyor.com/project/facedown/project) [![Coverage Status](https://coveralls.io/repos/github/klapuch/Internal/badge.svg?branch=master)](https://coveralls.io/github/klapuch/Internal?branch=master)

3 changes: 3 additions & 0 deletions Tests/.coveralls.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
service_name: travis-ci
coverage_clover: coverage.xml
json_path: coverage.json
2 changes: 2 additions & 0 deletions Tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.actual
*.expected
2 changes: 2 additions & 0 deletions Tests/Temporary/.gitginore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
40 changes: 40 additions & 0 deletions Tests/Unit/CombinedExtension.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
declare(strict_types = 1);
/**
* @testCase
* @phpVersion > 7.1
*/
namespace Klapuch\Internal\Unit;

use Klapuch\Internal;
use Tester;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';

final class CombinedExtension extends Tester\TestCase {
public function testConvertingIntegerToString() {
ob_start();
(new Internal\CombinedExtension(
new class implements Internal\Extension {
function improve(): void {
echo 'a';
}
},
new class implements Internal\Extension {
function improve(): void {
echo 'b';
}
},
new class implements Internal\Extension {
function improve(): void {
echo 'c';
}
}
))->improve();
Assert::same('abc', ob_get_clean());
}
}


(new CombinedExtension())->run();
52 changes: 52 additions & 0 deletions Tests/Unit/CspHeader.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php
declare(strict_types = 1);
/**
* @testCase
* @phpVersion > 7.1
*/
namespace Klapuch\Internal\Unit;

use Klapuch\Internal;
use Tester;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';

final class CspHeader extends Tester\TestCase {
public function testNoHeaderOnNoPassedDirectives() {
Assert::same('', (string) new Internal\CspHeader([]));
}

/**
* @dataProvider directives
*/
public function testFormattedDirectives(string $header, array $directives) {
Assert::same(
'Content-Security-Policy: ' . $header,
(string) new Internal\CspHeader($directives)
);
}

protected function directives(): array {
// expected => given
return [
["default-src 'self'", ['default-src' => "'self'"]],
["script-src 'self' 'unsafe-inline'", ['script-src' => "'self' 'unsafe-inline'"]],
["default-src 'self'; child-src 'none'", ['default-src' => "'self'", 'child-src' => "'none'"]],
];
}

public function testGeneratingNonceJustOnce() {
$csp = new Internal\CspHeader([]);
Assert::true(strlen($csp->nonce()) === 24);
Assert::same($csp->nonce(), $csp->nonce());
}

public function testPassingNonce() {
$csp = new Internal\CspHeader(['sctipt-src' => 'nonce']);
Assert::contains('nonce-' . $csp->nonce(), (string) $csp);
}
}


(new CspHeader())->run();
Loading

0 comments on commit afd4a83

Please sign in to comment.