Skip to content

Commit 39e0864

Browse files
committed
Initial release
0 parents  commit 39e0864

17 files changed

+753
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
composer.lock
2+
tests/temp
3+
vendor

.styleci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
preset: laravel
2+
3+
linting: true
4+
5+
disabled:
6+
- single_class_element_per_statement

.travis.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
language: php
2+
3+
php:
4+
- 5.5.9
5+
- 5.6
6+
- 7.0
7+
- 7.1
8+
9+
env:
10+
matrix:
11+
- COMPOSER_FLAGS="--prefer-lowest"
12+
- COMPOSER_FLAGS=""
13+
14+
before_script:
15+
- travis_retry composer self-update
16+
- travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-source
17+
18+
script:
19+
- phpunit

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) [2016] [Sébastien Nikolaou]
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Laravel SRI
2+
3+
Subresource Integrity (SRI) package for Laravel
4+
5+
[![Latest Version on Packagist](https://img.shields.io/packagist/v/sebdesign/laravel-sri.svg)](https://packagist.org/packages/sebdesign/laravel-sri)
6+
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.md)
7+
[![Build Status](https://img.shields.io/travis/sebdesign/laravel-sri/master.svg)](https://travis-ci.org/sebdesign/laravel-sri)
8+
9+
Reference and generate [Subresource Integrity (SRI)](https://www.w3.org/TR/SRI/) hashes from your Laravel Elixir asset pipeline.
10+
11+
## Installation
12+
13+
You can install the package via composer:
14+
15+
```bash
16+
$ composer require sebdesign/laravel-sri
17+
```
18+
19+
Next, you must install the service provider:
20+
21+
```php
22+
// config/app.php
23+
'providers' => [
24+
...
25+
Sebdesign\SRI\SubresourceIntegrityServiceProvider::class,
26+
];
27+
```
28+
29+
## Usage
30+
31+
This package is aimed to reference SRI hashes for `css` and `js` files from a `sri.json` file in your `/public` folder. In order to generate this file, see the [laravel-elixir-sri]('https://github.com/sebdesign/laravel-elixir-sri') repository.
32+
33+
To reference the generated hashes from the `sri.json` in your views, you may use the `integrity` helper function with the name of the file you are using in your `elixir` or `asset` function.
34+
35+
As a fallback, if the given file is not found in the `sri.json`, **it will generate the appropriate hashes on the fly** for your convenience.
36+
37+
```php
38+
{{-- Use with elixir() function --}}
39+
<link rel="stylesheet" href="{{ elixir('css/app.css') }}" integrity="{{ integrity('css/app.css') }}" crossorigin="anonymous">
40+
41+
{{-- Use with asset() function --}}
42+
<script src="{{ asset('js/app.js') }}" integrity="{{ integrity('js/app.js') }}" crossorigin="anonymous"></script>
43+
```
44+
45+
If you have set the output folder for the `sri.json` in a different location in your Gulpfile, you can specify its `path` on the `config/sri.php`.
46+
47+
```php
48+
// config/sri.php
49+
'path' => '/public/assets',
50+
```
51+
52+
You can also override the config options by passing an array as a second argument on the `integrity` helper function:
53+
54+
```php
55+
{{-- Use different hash algorithm --}}
56+
<link rel="stylesheet" href="{{ elixir('css/app.css') }}" integrity="{{ integrity('css/app.css', ['algorithms' => ['sha384']]) }}" crossorigin="anonymous">
57+
```
58+
59+
## Testing
60+
61+
``` bash
62+
$ composer test
63+
```
64+
65+
## License
66+
67+
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

composer.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "sebdesign/laravel-sri",
3+
"description": "Subresource Integrity (SRI) package for Laravel",
4+
"keywords": [
5+
"laravel",
6+
"sri"
7+
],
8+
"homepage": "https://github.com/sebdesign/laravel-sri",
9+
"type": "library",
10+
"require": {
11+
"laravel/framework": "~5.1.16|~5.2.0|~5.3.0"
12+
},
13+
"require-dev": {
14+
"phpunit/phpunit": "~4.0",
15+
"mockery/mockery": "^0.9.5",
16+
"orchestra/testbench": "^3.3"
17+
},
18+
"autoload": {
19+
"files": [
20+
"src/helpers.php"
21+
],
22+
"psr-4": {
23+
"Sebdesign\\SRI\\": "src"
24+
}
25+
},
26+
"autoload-dev": {
27+
"psr-4": {
28+
"Sebdesign\\SRI\\Test\\": "tests"
29+
}
30+
},
31+
"scripts": {
32+
"test": "vendor/bin/phpunit"
33+
},
34+
"license": "MIT",
35+
"authors": [
36+
{
37+
"name": "Sébastien Nikolaou",
38+
"email": "info@sebdesign.eu",
39+
"homepage": "http://sebdesign.eu"
40+
}
41+
]
42+
}

config/sri.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
return [
4+
5+
/**
6+
* List of hashing algorithms to be used.
7+
*/
8+
'algorithms' => [
9+
'sha256',
10+
// 'sha384',
11+
// 'sha512',
12+
],
13+
14+
/**
15+
* Integrity attribute delimiter.
16+
*/
17+
'delimiter' => ' ',
18+
19+
/**
20+
* Output filename.
21+
*/
22+
'filename' => 'sri.json',
23+
24+
/**
25+
* Output path.
26+
*/
27+
'path' => '/public',
28+
];

phpunit.xml.dist

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit bootstrap="vendor/autoload.php"
3+
backupGlobals="false"
4+
backupStaticAttributes="false"
5+
colors="true"
6+
verbose="true"
7+
convertErrorsToExceptions="true"
8+
convertNoticesToExceptions="true"
9+
convertWarningsToExceptions="true"
10+
processIsolation="false"
11+
stopOnFailure="false">
12+
<testsuites>
13+
<testsuite name="Test Suite">
14+
<directory>tests</directory>
15+
</testsuite>
16+
</testsuites>
17+
<filter>
18+
<whitelist>
19+
<directory suffix=".php">src/</directory>
20+
</whitelist>
21+
</filter>
22+
</phpunit>

src/Hasher.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
namespace Sebdesign\SRI;
4+
5+
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
6+
7+
class Hasher implements HasherContract
8+
{
9+
10+
/**
11+
* @var array
12+
*/
13+
protected $options = [];
14+
15+
/**
16+
* @var array
17+
*/
18+
protected $supportedAlgorithms = [];
19+
20+
/**
21+
* Constructor.
22+
*
23+
* @param array $supportedAlgorithms
24+
* @param array $options
25+
*/
26+
public function __construct(array $supportedAlgorithms, array $options)
27+
{
28+
$this->supportedAlgorithms = $supportedAlgorithms;
29+
$this->options = $options;
30+
}
31+
32+
/**
33+
* Hash the given file.
34+
*
35+
* @param string $file
36+
* @param array $options
37+
* @return string
38+
*/
39+
public function make($file, array $options = [])
40+
{
41+
$options = $this->getOptions($options);
42+
43+
return collect($this->getAlgorithms($options))
44+
->map(function ($algorithm) use ($file) {
45+
$digest = base64_encode(hash_file($algorithm, $file, true));
46+
47+
return $algorithm.'-'.$digest;
48+
})
49+
->implode($options['delimiter']);
50+
}
51+
52+
/**
53+
* Check the given file against a hash.
54+
*
55+
* @param string $file
56+
* @param string $integrity
57+
* @param array $options
58+
* @return bool
59+
*/
60+
public function check($file, $integrity, array $options = [])
61+
{
62+
return $this->make($file, $options) === $integrity;
63+
}
64+
65+
/**
66+
* Check if the given hash has been hashed using the given options.
67+
*
68+
* @param string $integrity
69+
* @param array $options
70+
* @return bool
71+
*/
72+
public function needsRehash($integrity, array $options = [])
73+
{
74+
$options = $this->getOptions($options);
75+
76+
$algorithms = collect(explode($options['delimiter'], $integrity))
77+
->map(function ($hash) {
78+
return head(explode('-', $hash));
79+
});
80+
81+
return $this->getAlgorithms($options) != $algorithms->all();
82+
}
83+
84+
/**
85+
* Replace the given options with the defaults.
86+
*
87+
* @param array $options
88+
* @return array
89+
*/
90+
protected function getOptions(array $options)
91+
{
92+
return array_replace_recursive($this->options, $options);
93+
}
94+
95+
/**
96+
* Get the hashing algorithms from the options.
97+
*
98+
* @param array $options
99+
* @return array
100+
* @throws \InvalidArgumentException
101+
*/
102+
protected function getAlgorithms(array $options)
103+
{
104+
if (!isset($options['algorithms'])
105+
|| !is_array($options['algorithms'])
106+
|| empty($options['algorithms'])) {
107+
throw new \InvalidArgumentException('No hashing algorithms are set.');
108+
}
109+
110+
if ($notSupported = array_diff($options['algorithms'], $this->supportedAlgorithms)) {
111+
throw new \InvalidArgumentException(sprintf(
112+
"The hashing algorithms [%s] are not supported.",
113+
implode(', ', $notSupported)
114+
));
115+
}
116+
117+
return $options['algorithms'];
118+
}
119+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace Sebdesign\SRI;
4+
5+
use Illuminate\Support\ServiceProvider;
6+
7+
class SubresourceIntegrityServiceProvider extends ServiceProvider
8+
{
9+
10+
/**
11+
* Indicates if loading of the provider is deferred.
12+
*
13+
* @var bool
14+
*/
15+
protected $defer = false;
16+
17+
/**
18+
* Bootstrap the application services.
19+
*/
20+
public function boot()
21+
{
22+
if ($this->app->runningInConsole()) {
23+
$this->publishes([
24+
__DIR__.'/../config/sri.php' => config_path('sri.php'),
25+
], 'config');
26+
}
27+
}
28+
29+
/**
30+
* Register the application services.
31+
*/
32+
public function register()
33+
{
34+
$this->mergeConfigFrom(__DIR__.'/../config/sri.php', 'sri');
35+
36+
$this->app->singleton(Hasher::class, function ($app) {
37+
return new Hasher(hash_algos(), $app['config']->get('sri'));
38+
});
39+
}
40+
41+
/**
42+
* Get the services provided by the provider.
43+
*
44+
* @return array
45+
*/
46+
public function provides()
47+
{
48+
return [Hasher::class];
49+
}
50+
}

0 commit comments

Comments
 (0)