Ensure we have integrity and crossorigin params on all our css and js.

This commit is contained in:
Jonathan H. Wage
2018-08-30 21:58:43 +01:00
parent 414403d0df
commit 2069190e1d
8 changed files with 188 additions and 20 deletions

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Assets;
use function assert;
use function base64_encode;
use function file_get_contents;
use function hash;
use function realpath;
class AssetIntegrityGenerator
{
/** @var string */
private $sourcePath;
/** @var string[] */
private $cache = [];
public function __construct(string $sourcePath)
{
$this->sourcePath = $sourcePath;
}
public function getAssetIntegrity(string $path) : string
{
if (! isset($this->cache[$path])) {
$contents = $this->getFileContents($path);
$this->cache[$path] = $this->buildAssetIntegrityString($contents);
}
return $this->cache[$path];
}
private function getFileContents(string $path) : string
{
$assetPath = realpath($this->sourcePath . '/' . $path);
assert($assetPath !== false);
$contents = file_get_contents($assetPath);
assert($contents !== false);
return $contents;
}
private function buildAssetIntegrityString(string $contents) : string
{
return 'sha384-' . base64_encode(hash('sha384', $contents, true));
}
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\Website\Twig;
use Doctrine\Website\Assets\AssetIntegrityGenerator;
use Doctrine\Website\Projects\Project;
use Parsedown;
use Twig_Extension;
@@ -21,9 +22,17 @@ class MainExtension extends Twig_Extension
/** @var Parsedown */
private $parsedown;
public function __construct(Parsedown $parsedown)
/** @var AssetIntegrityGenerator */
private $assetIntegrityGenerator;
/** @var string */
private $sourcePath;
public function __construct(Parsedown $parsedown, AssetIntegrityGenerator $assetIntegrityGenerator, string $sourcePath)
{
$this->parsedown = $parsedown;
$this->parsedown = $parsedown;
$this->assetIntegrityGenerator = $assetIntegrityGenerator;
$this->sourcePath = $sourcePath;
}
/**
@@ -34,6 +43,7 @@ class MainExtension extends Twig_Extension
return [
new Twig_SimpleFunction('get_search_box_placeholder', [$this, 'getSearchBoxPlaceholder']),
new Twig_SimpleFunction('get_asset_url', [$this, 'getAssetUrl']),
new Twig_SimpleFunction('get_asset_integrity', [$this->assetIntegrityGenerator, 'getAssetIntegrity']),
];
}
@@ -71,7 +81,7 @@ class MainExtension extends Twig_Extension
private function getAssetCacheBuster(string $path) : string
{
$assetPath = realpath(__DIR__ . '/../../source/' . $path);
$assetPath = realpath($this->sourcePath . $path);
assert(is_string($assetPath));
$contents = file_get_contents($assetPath);

View File

@@ -146,7 +146,11 @@
{% endblock %}
{% block scripts %}
<script src="{{ get_asset_url('/js/sidebar.js', site.url) }}"></script>
<script
src="{{ get_asset_url('/js/sidebar.js', site.url) }}"
integrity="{{ get_asset_integrity('/js/sidebar.js') }}"
crossorigin="anonymous">
</script>
<script type="text/javascript">
new Sidebar();

View File

@@ -20,16 +20,53 @@
{% block head_meta '' %}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link
rel="stylesheet"
type="text/css"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/all.css" integrity="sha384-3AB7yXWz4OeoZcPbieVW64vVXEwADiYyAEhwilzWsLw+9FgqpyjjStpPnpBO8o8S" crossorigin="anonymous">
<link
rel="stylesheet"
type="text/css"
href="https://use.fontawesome.com/releases/v5.0.8/css/all.css"
integrity="sha384-3AB7yXWz4OeoZcPbieVW64vVXEwADiYyAEhwilzWsLw+9FgqpyjjStpPnpBO8o8S"
crossorigin="anonymous"
/>
<link href="{{ get_asset_url('/css/style.css', site.url) }}" rel="stylesheet" type="text/css" />
<link
rel="stylesheet"
type="text/css"
href="{{ get_asset_url('/css/style.css', site.url) }}"
integrity="{{ get_asset_integrity('/css/style.css') }}"
crossorigin="anonymous"
/>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/instantsearch.js@2.6.0/dist/instantsearch.min.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/instantsearch.js@2.6.0/dist/instantsearch-theme-algolia.min.css">
<link
rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/npm/instantsearch.js@2.6.0/dist/instantsearch.min.css"
integrity="sha384-3pqpjew6zqJsw9mdIpd6FVJvuFuacqOpSXwAifQ9iHE4keUyghetuuJDGFoTNhfn"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="{{ get_asset_url('/css/railscasts.css', site.url) }}" />
<link
rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/npm/instantsearch.js@2.6.0/dist/instantsearch-theme-algolia.min.css"
integrity="sha384-VzUGdSDk+HLoSg+ptudZxGZyaFVN7q7Eqh7QaUU19cWL2fK0kyz1belH38z7MbcZ"
crossorigin="anonymous"
/>
<link
rel="stylesheet"
type="text/css"
href="{{ get_asset_url('/css/railscasts.css', site.url) }}"
integrity="sha384-K7nnw0wrUNTkBLTS8aN3AOx/e095lEJ1g60EJX7ash3cfsHTpQV6kWKoCPxPc9AJ"
crossorigin="anonymous"
/>
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
@@ -193,7 +230,7 @@
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"
crossorigin="anonymous"></script>
<script
@@ -204,22 +241,40 @@
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
crossorigin="anonymous">
</script>
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js@2.6.0"></script>
<script
src="https://cdn.jsdelivr.net/npm/instantsearch.js@2.6.0"
integrity="sha384-EtEQyqoKtrBSat8+V0eTyycfmdkEHHzKesnWrc2VPnKAcD8PJw8HKAWKsK/QnGT+"
crossorigin="anonymous">
</script>
<script id="instantsearch-template" type="text/template">
{% include "search-results.html.twig" %}
</script>
<script src="https://cdn.ravenjs.com/3.24.2/raven.min.js" crossorigin="anonymous"></script>
<script
src="https://cdn.ravenjs.com/3.24.2/raven.min.js"
integrity="sha384-HwE7dFvrH8iD9R5VYUqNwjIhxDycip+e4DjP5nEhXrJtACG8AdNXjvP+AfJWxhJh"
crossorigin="anonymous">
</script>
<script type="text/javascript">
Raven.config('https://09ce137590054cfd8f0b7e9324d6ec14@sentry.io/1197701').install()
</script>
<script src="{{ get_asset_url('/js/main.js', site.url) }}"></script>
<script src="{{ get_asset_url('/js/search.js', site.url) }}"></script>
<script
src="{{ get_asset_url('/js/main.js', site.url) }}"
integrity="{{ get_asset_integrity('/js/main.js') }}"
crossorigin="anonymous">
</script>
<script
src="{{ get_asset_url('/js/search.js', site.url) }}"
integrity="{{ get_asset_integrity('/js/search.js') }}"
crossorigin="anonymous">
</script>
{% set project = null %}
@@ -265,7 +320,11 @@
}
</script>
<script type="text/javascript" src="https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit" async></script>
<script
type="text/javascript"
src="https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"
async>
</script>
{% block scripts '' %}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\Assets;
use Doctrine\Website\Assets\AssetIntegrityGenerator;
use PHPUnit\Framework\TestCase;
class AssetIntegrityGeneratorTest extends TestCase
{
/** @var string */
private $sourcePath;
/** @var AssetIntegrityGenerator */
private $assetIntegrityGenerator;
public function testGetAssetIntegrity() : void
{
$integrity = $this->assetIntegrityGenerator->getAssetIntegrity('/css/style.css');
self::assertSame('sha384-ypIyGShu7WBNc4JDDLBwHLtFojHsqgcjDrbRH9rt5hizlv05qzZgJKBSkJ0X6czC', $integrity);
}
protected function setUp() : void
{
$this->sourcePath = __DIR__ . '/../source';
$this->assetIntegrityGenerator = new AssetIntegrityGenerator($this->sourcePath);
}
}

View File

@@ -22,7 +22,7 @@ class SourceFileRepositoryTest extends TestCase
{
$files = $this->sortFiles($this->sourceFileRepository->getFiles(''));
self::assertCount(6, $files);
self::assertCount(7, $files);
self::assertSame('html', $files[0]->getExtension());
self::assertSame('/api/inflector.html', $files[0]->getWritePath());

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\Twig;
use Doctrine\Website\Assets\AssetIntegrityGenerator;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Tests\TestCase;
use Doctrine\Website\Twig\MainExtension;
@@ -14,15 +15,25 @@ class MainExtensionTest extends TestCase
/** @var Parsedown */
private $parsedown;
/** @var AssetIntegrityGenerator */
private $assetIntegrityGenerator;
/** @var string */
private $sourcePath;
/** @var MainExtension */
private $mainExtension;
protected function setUp() : void
{
$this->parsedown = $this->createMock(Parsedown::class);
$this->parsedown = $this->createMock(Parsedown::class);
$this->assetIntegrityGenerator = $this->createMock(AssetIntegrityGenerator::class);
$this->sourcePath = __DIR__ . '/../../source';
$this->mainExtension = new MainExtension(
$this->parsedown
$this->parsedown,
$this->assetIntegrityGenerator,
$this->sourcePath
);
}

View File

@@ -0,0 +1 @@
.test { color: black; }