Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ npm install webpack-subresource-integrity --save-dev
import SriPlugin from 'webpack-subresource-integrity';

const compiler = webpack({
output: {
crossOriginLoading: 'anonymous',
},
plugins: [
new SriPlugin({
hashFuncNames: ['sha256', 'sha384'],
enabled: process.env.NODE_ENV === 'production',
crossorigin: 'anonymous',
}),
],
});
Expand All @@ -46,8 +48,10 @@ HTML pages.)
#### With HtmlWebpackPlugin

When html-webpack-plugin is injecting assets into the template (the
default), the `integrity` attribute will be set automatically. There
is nothing else to be done.
default), the `integrity` attribute will be set automatically. The
`crossorigin` attribute will be set as well, to the value of
`output.crossOriginLoading` webpack option. There is nothing else to
be done.

#### With HtmlWebpackPlugin({ inject: false })

Expand All @@ -60,15 +64,15 @@ template as follows:
<script
src="<%= htmlWebpackPlugin.files.js[index] %>"
integrity="<%= htmlWebpackPlugin.files.jsIntegrity[index] %>"
crossorigin="<%= htmlWebpackPlugin.options.sriCrossOrigin %>"
crossorigin="<%= webpackConfig.output.crossOriginLoading %>"
></script>
<% } %>

<% for (var index in htmlWebpackPlugin.files.css) { %>
<link
href="<%= htmlWebpackPlugin.files.css[index] %>"
integrity="<%= htmlWebpackPlugin.files.cssIntegrity[index] %>"
crossorigin="<%= htmlWebpackPlugin.options.sriCrossOrigin %>"
crossorigin="<%= webpackConfig.output.crossOriginLoading %>"
rel="stylesheet"
/>
<% } %>
Expand All @@ -88,6 +92,10 @@ compiler.plugin("done", stats => {
});
```

Note that you're also required to set the `crossorigin` attribute. It
is recommended to set this attribute to the same value as the webpack
`output.crossOriginLoading` configuration option.

### Options

#### hashFuncNames
Expand All @@ -110,19 +118,22 @@ development mode.

#### crossorigin

Default value: `"anonymous"`
**DEPRECATED**. Use webpack option `output.crossOriginLoading'
instead'.

~~Default value: `"anonymous"`~~

When using `HtmlWebpackPlugin({ inject: true })`, this option
~~When using `HtmlWebpackPlugin({ inject: true })`, this option
specifies the value to be used for the `crossorigin` attribute for
injected assets.
injected assets.~~

The value will also be available as
~~The value will also be available as
`htmlWebpackPlugin.options.sriCrossOrigin` in html-webpack-plugin
templates.
templates.~~

See
~~See
[SRI: Cross-origin data leakage](https://www.w3.org/TR/SRI/#cross-origin-data-leakage) and
[MDN: CORS settings attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes)
[MDN: CORS settings attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes)~~

## Caveats

Expand Down
73 changes: 56 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,24 @@ function findDepChunks(chunk, allDepChunkIds) {
});
}

function WebIntegrityJsonpMainTemplatePlugin() {}
function WebIntegrityJsonpMainTemplatePlugin(sriPlugin, compilation) {
this.sriPlugin = sriPlugin;
this.compilation = compilation;
}

WebIntegrityJsonpMainTemplatePlugin.prototype.apply = function apply(mainTemplate) {
var self = this;

/*
* Patch jsonp-script code to add the integrity attribute.
*/
mainTemplate.plugin('jsonp-script', function jsonpScriptPlugin(source) {
if (!this.outputOptions.crossOriginLoading) {
self.sriPlugin.error(
self.compilation,
'webpack option output.crossOriginLoading not set, code splitting will not work!'
);
}
return this.asString([
source,
'script.integrity = sriHashes[chunkId];'
Expand Down Expand Up @@ -81,8 +92,7 @@ function SubresourceIntegrityPlugin(options) {
}

this.options = {
enabled: true,
crossorigin: 'anonymous'
enabled: true
};

for (var key in useOptions) {
Expand Down Expand Up @@ -110,6 +120,10 @@ SubresourceIntegrityPlugin.prototype.error = function error(compilation, message
};

SubresourceIntegrityPlugin.prototype.validateOptions = function validateOptions(compilation) {
if (this.optionsValidated) {
return;
}
this.optionsValidated = true;
if (this.options.deprecatedOptions) {
this.warnOnce(
compilation,
Expand All @@ -118,6 +132,11 @@ SubresourceIntegrityPlugin.prototype.validateOptions = function validateOptions(
'Please update your code. ' +
'See https://github.com/waysact/webpack-subresource-integrity/issues/18 for more information.');
}
if (this.options.enabled && !compilation.compiler.options.output.crossOriginLoading) {
this.warnOnce(
compilation,
'Set webpack option output.crossOriginLoading when using this plugin.');
}
if (!Array.isArray(this.options.hashFuncNames)) {
this.error(
compilation,
Expand Down Expand Up @@ -160,20 +179,31 @@ SubresourceIntegrityPlugin.prototype.validateOptions = function validateOptions(
'See http://www.w3.org/TR/SRI/#cryptographic-hash-functions for more information.');
}
}
if (typeof this.options.crossorigin !== 'string' &&
!(this.options.crossorigin instanceof String)) {
this.error(
compilation,
'options.crossorigin must be a string.');
this.options.enabled = false;
return;
}
if (standardCrossoriginOptions.indexOf(this.options.crossorigin) < 0) {
if (typeof this.options.crossorigin === 'undefined') {
this.options.crossorigin =
compilation.compiler.options.output.crossOriginLoading || 'anonymous';
} else {
this.warnOnce(
compilation,
'You\'ve specified a value for the crossorigin option that is not part of the set of standard values. ' +
'These are: ' + standardCrossoriginOptions.join(', ') + '. ' +
'See https://www.w3.org/TR/SRI/#cross-origin-data-leakage for more information.');
'Specifying options.crossorigin is deprecated. ' +
'Instead, set webpack option output.crossOriginLoading. ' +
'Support will be removed in webpack-subresource-integrity 1.0.0. ' +
'See https://github.com/waysact/webpack-subresource-integrity/issues/20 for more information.');
if (typeof this.options.crossorigin !== 'string' &&
!(this.options.crossorigin instanceof String)) {
this.error(
compilation,
'options.crossorigin must be a string.');
this.options.enabled = false;
return;
}
if (standardCrossoriginOptions.indexOf(this.options.crossorigin) < 0) {
this.warnOnce(
compilation,
'You\'ve specified a value for the crossorigin option that is not part of the set of standard values. ' +
'These are: ' + standardCrossoriginOptions.join(', ') + '. ' +
'See https://www.w3.org/TR/SRI/#cross-origin-data-leakage for more information.');
}
}
};

Expand Down Expand Up @@ -204,7 +234,7 @@ SubresourceIntegrityPlugin.prototype.apply = function apply(compiler) {
return;
}

compilation.mainTemplate.apply(new WebIntegrityJsonpMainTemplatePlugin());
compilation.mainTemplate.apply(new WebIntegrityJsonpMainTemplatePlugin(self, compilation));

/*
* Calculate SRI values for each chunk and replace the magic
Expand Down Expand Up @@ -328,7 +358,16 @@ SubresourceIntegrityPlugin.prototype.apply = function apply(compiler) {
return compilation.assets[src].integrity;
});
});
pluginArgs.plugin.options.sriCrossOrigin = self.options.crossorigin;
Object.defineProperty(
pluginArgs.plugin.options, 'sriCrossOrigin', {
get: function get() {
self.warnOnce(
compilation,
'htmlWebpackPlugin.options.sriCrossOrigin is deprecated, use webpackConfig.output.crossOriginLoading instead.'
);
return self.options.crossorigin;
}
});
callback(null, pluginArgs);
}

Expand Down
11 changes: 8 additions & 3 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ function nextCreate(filesPromise, serveStaticFile, serveFile, injector, basePath
response.write = function nextWrite(chunk, encoding) {
var nextChunk = chunk.replace(
'src="/base/test/test.js',
'integrity="' + toplevelScriptIntegrity + '" src="/base/test/test.js');
'integrity="' + toplevelScriptIntegrity + '" crossorigin="anonymous" src="/base/test/test.js');
nextChunk = nextChunk.replace(
'rel="stylesheet"',
'rel="stylesheet" integrity="' + stylesheetIntegrity + '"'
'rel="stylesheet" integrity="' + stylesheetIntegrity + '" crossorigin="anonymous"'
);
prevWrite.call(response, nextChunk, encoding);
};
Expand Down Expand Up @@ -82,8 +82,13 @@ module.exports = function karmaConfig(config) {
'karma-mocha'
],
webpack: {
output: {
crossOriginLoading: 'anonymous'
},
plugins: [
new SriPlugin(['sha256', 'sha384']),
new SriPlugin({
hashFuncNames: ['sha256', 'sha384']
}),
new GetIntegrityPlugin()
],
module: {
Expand Down
3 changes: 2 additions & 1 deletion test/chunk2.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ module.exports = function chunk2(callback) {
function forEachElement(el) {
var src = el.getAttribute('src') || el.getAttribute('href');
var integrity = el.getAttribute('integrity');
var crossorigin = el.getAttribute('crossOrigin');
if (src) {
var match = src.match(/[^\/]+\.(js|css)/);
if (match && integrity && integrity.match(/^sha\d+-/)) {
if (match && crossorigin && integrity && integrity.match(/^sha\d+-/)) {
resourcesWithIntegrity.push(match[0].toString());
}
}
Expand Down
4 changes: 2 additions & 2 deletions test/index.ejs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<% for (var index in htmlWebpackPlugin.files.js) { %>
<script src="<%= htmlWebpackPlugin.files.js[index] %>"
integrity="<%= htmlWebpackPlugin.files.jsIntegrity[index] %>"
crossorigin="<%= htmlWebpackPlugin.options.sriCrossOrigin %>"
crossorigin="<%= webpackConfig.output.crossOriginLoading %>"
></script>
<% } %>

<% for (var index in htmlWebpackPlugin.files.css) { %>
<link href="<%= htmlWebpackPlugin.files.css[index] %>"
integrity="<%= htmlWebpackPlugin.files.cssIntegrity[index] %>"
crossorigin="<%= htmlWebpackPlugin.options.sriCrossOrigin %>"
crossorigin="<%= webpackConfig.output.crossOriginLoading %>"
rel="stylesheet"
/>
<% } %>
Loading