Socket
Socket
Sign inDemoInstall

webpack-subresource-integrity

Package Overview
Dependencies
279
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.6.0 to 0.7.0

413

index.js

@@ -5,2 +5,8 @@ var crypto = require('crypto');

// https://www.w3.org/TR/2016/REC-SRI-20160623/#cryptographic-hash-functions
var standardHashFuncNames = ['sha256', 'sha384', 'sha512'];
// https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes
var standardCrossoriginOptions = ['anonymous', 'use-credentials'];
function makePlaceholder(id) {

@@ -12,4 +18,4 @@ return '*-*-*-CHUNK-SRI-HASH-' + id + '-*-*-*';

chunk.chunks.forEach(function forEachChunk(depChunk) {
if (!allDepChunkIds.includes(depChunk.id)) {
allDepChunkIds.push(depChunk.id);
if (!allDepChunkIds[depChunk.id]) {
allDepChunkIds[depChunk.id] = true;
}

@@ -20,32 +26,2 @@ findDepChunks(depChunk, allDepChunkIds);

/* Given a public URL path to an asset, as generated by
* HtmlWebpackPlugin for use as a `<script src>` or `<link href`> URL
* in `index.html`, return the path in the filesystem relative to the
* webpack output directory, suitable as a key into
* `compilation.assets`.
*/
function hwpSrcRelativeOutputPath(compiler, htmlWebpackPlugin, src) {
var webpackOutputPath = compiler.options.output.path;
// For publicPath we need to fall back to the empty string like
// webpack does (see e.g. webpack/lib/MainTemplate.js)
var webpackPublicPath = compiler.options.output.publicPath || '';
// Strip cache-busting hash query possibly added by HtmlWebpackPlugin
var srcWithoutHash = src.replace(/\?[a-zA-Z0-9]+$/, '');
// Determine source path relative to webpack public path
var srcPathRelativeToPublic = path.relative(webpackPublicPath, srcWithoutHash);
// Using this source path, determine full output path name in
// filesystem
var hwpOutputPath = path.dirname(htmlWebpackPlugin.options.filename);
var outputAssetPath = path.resolve(webpackOutputPath,
hwpOutputPath,
srcPathRelativeToPublic);
// Return the output path name relative to webpack output directory
return path.relative(webpackOutputPath, outputAssetPath);
}
function WebIntegrityJsonpMainTemplatePlugin() {}

@@ -72,3 +48,3 @@

if (chunk.chunks.length > 0) {
var allDepChunkIds = [];
var allDepChunkIds = {};
findDepChunks(chunk, allDepChunkIds);

@@ -80,3 +56,3 @@

this.indent(
allDepChunkIds.map(function mapChunkId(chunkId) {
Object.keys(allDepChunkIds).map(function mapChunkId(chunkId) {
return chunkId + ':"' + makePlaceholder(chunkId) + '"';

@@ -92,158 +68,281 @@ }).join(',\n')

function SubresourceIntegrityPlugin(algorithms) {
if (typeof algorithms === 'string') {
this.algorithms = [ algorithms ];
} else if (!Array.isArray(algorithms)) {
throw new Error('Expected an array of strings or a string');
} else if (algorithms.length === 0) {
throw new Error('Algorithms array must not be empty');
function SubresourceIntegrityPlugin(options) {
var useOptions;
if (typeof options === 'string') {
useOptions = {
hashFuncNames: [options],
deprecatedOptions: true
};
} else if (Array.isArray(options)) {
useOptions = {
hashFuncNames: options,
deprecatedOptions: true
};
} else if (options === null || typeof options === 'undefined') {
useOptions = {};
} else if (typeof options === 'object') {
useOptions = options;
} else {
this.algorithms = algorithms;
throw new Error('webpack-subresource-integrity: argument must be an object');
}
this.options = {
enabled: true,
crossorigin: 'anonymous'
};
for (var key in useOptions) {
if (useOptions.hasOwnProperty(key)) {
this.options[key] = useOptions[key];
}
}
this.emittedWarnings = {};
}
SubresourceIntegrityPlugin.prototype.emitMessage = function emitMessage(messages, message) {
messages.push(new Error('webpack-subresource-integrity: ' + message));
};
SubresourceIntegrityPlugin.prototype.warnOnce = function warn(compilation, message) {
if (!this.emittedWarnings[message]) {
this.emittedWarnings[message] = true;
this.emitMessage(compilation.warnings, message);
}
};
SubresourceIntegrityPlugin.prototype.error = function error(compilation, message) {
this.emitMessage(compilation.errors, message);
};
SubresourceIntegrityPlugin.prototype.validateOptions = function validateOptions(compilation) {
if (this.options.deprecatedOptions) {
this.warnOnce(
compilation,
'Passing a string or array to the plugin constructor is deprecated. ' +
'Support will be removed in webpack-subresource-integrity 1.0.0. ' +
'Please update your code. ' +
'See https://github.com/waysact/webpack-subresource-integrity/issues/18 for more information.');
}
if (!Array.isArray(this.options.hashFuncNames)) {
this.error(
compilation,
'options.hashFuncNames must be an array of hash function names, ' +
'instead got \'' + this.options.hashFuncNames + '\'.');
this.options.enabled = false;
} else {
var foundStandardHashFunc = false;
for (var i = 0; i < this.options.hashFuncNames.length; i++) {
var hashFuncName = this.options.hashFuncNames[i];
if (typeof hashFuncName !== 'string' &&
!(hashFuncName instanceof String)) {
this.error(
compilation,
'options.hashFuncNames must be an array of hash function names, ' +
'but contained ' + hashFuncName + '.');
this.options.enabled = false;
return;
}
try {
crypto.createHash(hashFuncName);
} catch (error) {
this.error(
compilation,
'Cannot use hash function \'' + hashFuncName + '\': ' +
error.message);
this.options.enabled = false;
return;
}
if (standardHashFuncNames.indexOf(hashFuncName) >= 0) {
foundStandardHashFunc = true;
}
}
if (!foundStandardHashFunc) {
this.warnOnce(
compilation,
'It is recommended that at least one hash function is part of the set ' +
'for which support is mandated by the specification. ' +
'These are: ' + standardHashFuncNames.join(', ') + '. ' +
'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) {
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.');
}
};
/* Given a public URL path to an asset, as generated by
* HtmlWebpackPlugin for use as a `<script src>` or `<link href`> URL
* in `index.html`, return the path to the asset, suitable as a key
* into `compilation.assets`.
*/
SubresourceIntegrityPlugin.prototype.hwpAssetPath = function hwpAssetPath(src) {
return path.relative(this.hwpPublicPath, src.replace(/\?[a-zA-Z0-9]+$/, ''));
};
SubresourceIntegrityPlugin.prototype.apply = function apply(compiler) {
var algorithms = this.algorithms;
var self = this;
function computeIntegrity(source) {
return algorithms.map(function mapAlgo(algo) {
var hash = crypto.createHash(algo).update(source, 'utf8').digest('base64');
return algo + '-' + hash;
return self.options.hashFuncNames.map(function mapHashFuncName(hashFuncName) {
var hash = crypto.createHash(hashFuncName).update(source, 'utf8').digest('base64');
return hashFuncName + '-' + hash;
}).join(' ');
}
compiler.plugin('compilation', function compilationPlugin(compilation) {
/*
* Double plug-in registration in order to push our
* plugin to the end of the plugin stack.
*/
compiler.plugin('this-compilation', function thisCompilationPlugin(thisCompilation) {
thisCompilation.mainTemplate.apply(new WebIntegrityJsonpMainTemplatePlugin());
});
compiler.plugin('after-plugins', function afterPlugins() {
compiler.plugin('this-compilation', function thisCompilation(compilation) {
self.validateOptions(compilation);
/*
* Calculate SRI values for each chunk and replace the magic
* placeholders by the actual values.
*/
compilation.plugin('optimize-assets', function optimizeAssetsPlugin(assets, callback) {
var hashByChunkId = {};
var visitedByChunkId = {};
function processChunkRecursive(chunk) {
var depChunkIds = [];
if (!self.options.enabled) {
return;
}
if (visitedByChunkId[chunk.id]) {
return [];
}
visitedByChunkId[chunk.id] = true;
compilation.mainTemplate.apply(new WebIntegrityJsonpMainTemplatePlugin());
chunk.chunks.forEach(function mapChunk(depChunk) {
depChunkIds = depChunkIds.concat(processChunkRecursive(depChunk));
});
/*
* Calculate SRI values for each chunk and replace the magic
* placeholders by the actual values.
*/
compilation.plugin('after-optimize-assets', function optimizeAssetsPlugin(assets) {
var hashByChunkId = {};
var visitedByChunkId = {};
function processChunkRecursive(chunk) {
var depChunkIds = [];
if (chunk.files.length > 0) {
var chunkFile = chunk.files[0];
if (visitedByChunkId[chunk.id]) {
return [];
}
visitedByChunkId[chunk.id] = true;
var oldSource = assets[chunkFile].source();
var newAsset = new ReplaceSource(assets[chunkFile]);
chunk.chunks.forEach(function mapChunk(depChunk) {
depChunkIds = depChunkIds.concat(processChunkRecursive(depChunk));
});
depChunkIds.forEach(function forEachChunk(depChunkId) {
var magicMarker = makePlaceholder(depChunkId);
var magicMarkerPos = oldSource.indexOf(magicMarker);
if (magicMarkerPos >= 0) {
newAsset.replace(
magicMarkerPos,
magicMarkerPos + magicMarker.length - 1,
hashByChunkId[depChunkId]);
if (chunk.files.length > 0) {
var chunkFile = chunk.files[0];
var oldSource = assets[chunkFile].source();
if (oldSource.indexOf('webpackHotUpdate') >= 0) {
self.warnOnce(
compilation,
'Chunks loaded by HMR are unprotected. ' +
'Consider disabling webpack-subresource-integrity in development mode.'
);
}
});
assets[chunkFile] = newAsset;
var newAsset = new ReplaceSource(assets[chunkFile]);
var newSource = newAsset.source();
hashByChunkId[chunk.id] = newAsset.integrity = computeIntegrity(newSource);
depChunkIds.forEach(function forEachChunk(depChunkId) {
var magicMarker = makePlaceholder(depChunkId);
var magicMarkerPos = oldSource.indexOf(magicMarker);
if (magicMarkerPos >= 0) {
newAsset.replace(
magicMarkerPos,
magicMarkerPos + magicMarker.length - 1,
hashByChunkId[depChunkId]);
}
});
assets[chunkFile] = newAsset;
var newSource = newAsset.source();
hashByChunkId[chunk.id] = newAsset.integrity = computeIntegrity(newSource);
}
return [ chunk.id ].concat(depChunkIds);
}
return [ chunk.id ].concat(depChunkIds);
}
compilation.chunks.forEach(function forEachChunk(chunk) {
// chunk.entry was removed in Webpack 2. Use hasRuntime() for this check instead (if it exists)
if (('hasRuntime' in chunk) ? chunk.hasRuntime() : chunk.entry) {
processChunkRecursive(chunk);
compilation.chunks.forEach(function forEachChunk(chunk) {
// chunk.entry was removed in Webpack 2. Use hasRuntime() for this check instead (if it exists)
if (('hasRuntime' in chunk) ? chunk.hasRuntime() : chunk.entry) {
processChunkRecursive(chunk);
}
});
for (var key in assets) {
if (assets.hasOwnProperty(key)) {
var asset = assets[key];
if (!asset.integrity) {
asset.integrity = computeIntegrity(asset.source());
}
}
}
});
for (var key in assets) {
if (assets.hasOwnProperty(key)) {
var asset = assets[key];
if (!asset.integrity) {
asset.integrity = computeIntegrity(asset.source());
}
}
function getTagSrc(tag) {
// Get asset path - src from scripts and href from links
return tag.attributes.href || tag.attributes.src;
}
callback();
});
function filterTag(tag) {
// Process only script and link tags with a url
return (tag.tagName === 'script' || tag.tagName === 'link') && getTagSrc(tag);
}
function getTagSrc(tag) {
// Get asset path - src from scripts and href from links
return tag.attributes.href || tag.attributes.src;
}
function getIntegrityChecksumForAsset(src) {
var asset = compilation.assets[src];
return asset && asset.integrity;
}
function filterTag(tag) {
// Process only script and link tags with a url
return (tag.tagName === 'script' || tag.tagName === 'link') && getTagSrc(tag);
}
function alterAssetTags(pluginArgs, callback) {
/* html-webpack-plugin has added an event so we can pre-process the html tags before they
inject them. This does the work.
*/
function processTag(tag) {
var src = self.hwpAssetPath(getTagSrc(tag));
var checksum = getIntegrityChecksumForAsset(src);
if (!checksum) {
self.warnOnce(
compilation,
'Cannot determine hash for asset \'' +
src + '\', the resource will be unprotected.');
return;
}
// Add integrity check sums
tag.attributes.integrity = checksum;
tag.attributes.crossorigin = self.options.crossorigin;
}
function getIntegrityChecksumForAsset(src) {
var asset = compilation.assets[src];
return asset && asset.integrity;
}
pluginArgs.head.filter(filterTag).forEach(processTag);
pluginArgs.body.filter(filterTag).forEach(processTag);
callback(null, pluginArgs);
}
function alterAssetTags(pluginArgs, callback) {
/* html-webpack-plugin has added an event so we can pre-process the html tags before they
inject them. This does the work.
*/
function processTag(tag) {
var src = hwpSrcRelativeOutputPath(compiler, pluginArgs.plugin, getTagSrc(tag));
var checksum = getIntegrityChecksumForAsset(src);
if (!checksum) {
compilation.warnings.push(new Error(
"webpack-subresource-integrity: cannot determine hash for asset '" +
src + "', the resource will be unprotected."));
return;
}
// Add integrity check sums
tag.attributes.integrity = checksum;
tag.attributes.crossorigin = 'anonymous';
/* Add jsIntegrity and cssIntegrity properties to pluginArgs, to
* go along with js and css properties. These are later
* accessible on `htmlWebpackPlugin.files`.
*/
function beforeHtmlGeneration(pluginArgs, callback) {
self.hwpPublicPath = pluginArgs.assets.publicPath;
['js', 'css'].forEach(function addIntegrity(fileType) {
pluginArgs.assets[fileType + 'Integrity'] =
pluginArgs.assets[fileType].map(function assetIntegrity(filePath) {
var src = self.hwpAssetPath(filePath);
return compilation.assets[src].integrity;
});
});
pluginArgs.plugin.options.sriCrossOrigin = self.options.crossorigin;
callback(null, pluginArgs);
}
pluginArgs.head.filter(filterTag).forEach(processTag);
pluginArgs.body.filter(filterTag).forEach(processTag);
callback(null, pluginArgs);
}
/* Add jsIntegrity and cssIntegrity properties to pluginArgs, to
* go along with js and css properties. These are later
* accessible on `htmlWebpackPlugin.files`.
*/
function beforeHtmlGeneration(pluginArgs, callback) {
['js', 'css'].forEach(function addIntegrity(fileType) {
pluginArgs.assets[fileType + 'Integrity'] =
pluginArgs.assets[fileType].map(function assetIntegrity(filePath) {
var src = hwpSrcRelativeOutputPath(compilation.compiler,
pluginArgs.plugin,
filePath);
return compilation.assets[src].integrity;
});
});
callback(null, pluginArgs);
}
/*
* html-webpack support:
* Modify the asset tags before webpack injects them for anything with an integrity value.
*/
compilation.plugin('html-webpack-plugin-alter-asset-tags', alterAssetTags);
compilation.plugin('html-webpack-plugin-before-html-generation', beforeHtmlGeneration);
/*
* html-webpack support:
* Modify the asset tags before webpack injects them for anything with an integrity value.
*/
compilation.plugin('html-webpack-plugin-alter-asset-tags', alterAssetTags);
compilation.plugin('html-webpack-plugin-before-html-generation', beforeHtmlGeneration);
});
});

@@ -250,0 +349,0 @@ };

{
"name": "webpack-subresource-integrity",
"version": "0.6.0",
"version": "0.7.0",
"description": "Webpack plugin for ensuring subresource integrity (SRI)",

@@ -5,0 +5,0 @@ "main": "index",

# webpack-subresource-integrity
[![Build Status](https://travis-ci.org/waysact/webpack-subresource-integrity.svg?branch=master)](https://travis-ci.org/waysact/webpack-subresource-integrity)
[![npm version](https://badge.fury.io/js/webpack-subresource-integrity.svg)](https://badge.fury.io/js/webpack-subresource-integrity) [![Build Status](https://travis-ci.org/waysact/webpack-subresource-integrity.svg?branch=master)](https://travis-ci.org/waysact/webpack-subresource-integrity) [![Dependency Status](https://david-dm.org/waysact/webpack-subresource-integrity.svg)](https://david-dm.org/waysact/webpack-subresource-integrity) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/waysact/webpack-subresource-integrity/master/LICENSE)
A Webpack plugin for ensuring
[subresource integrity](http://www.w3.org/TR/SRI/) on
[supported browsers](http://caniuse.com/#feat=subresource-integrity).
Webpack plugin for enabling Subresource Integrity.
Integrity is ensured automatically for lazy-loaded chunks (loaded via
`require.ensure`).
[Subresource Integrity](http://www.w3.org/TR/SRI/) (SRI) is a security
feature that enables browsers to verify that files they fetch (for
example, from a CDN) are delivered without unexpected
manipulation.
It's your responsibility to include the `integrity` attribute in the
HTML for top-level chunks. Obviously, SRI for lazy-loaded chunks is
pointless unless integrity of the top-level chunks is ensured as well.
## Features
[html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin)
users can get the `integrity` attribute set automatically, see below.
- Integration with [html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin)
- Support for code-splitting (integrity for lazy-loaded chunks)
## Usage
## Installation
### Installing the Plugin
```shell
npm install webpack-subresource-integrity --save-dev
```
$ npm install webpack-subresource-integrity --save-dev
### Webpack Configuration Example
Pass an array of
[hash algorithms](http://www.w3.org/TR/SRI/#cryptographic-hash-functions)
to the plugin constructor:
```javascript
import SriPlugin from 'webpack-subresource-integrity';
import SriPlugin from 'webpack-subresource-integrity';
const compiler = webpack({
plugins: [
new SriPlugin({
hashFuncNames: ['sha256', 'sha384'],
enabled: process.env.NODE_ENV === 'production',
crossorigin: 'anonymous',
}),
],
});
```
const compiler = webpack({
plugins: [
// ...
new SriPlugin(['sha256', 'sha384']),
],
});
### Setting the `integrity` attribute for top-level assets
### Accessing the `integrity` Value for Top-level Assets
For the plugin to take effect it is **essential** that you set the
`integrity` attribute for top-level assets (i.e. assets loaded by your
HTML pages.)
The correct value for the `integrity` attribute can be retrieved from
the `integrity` property of webpack assets. However, that property is
not copied over by webpack's `stats` module so you'll have to access
the "original" asset on the `compilation` object. Something like
this:
#### With HtmlWebpackPlugin
compiler.plugin("done", stats => {
var integrity = stats.compilation.assets[stats.toJson().assetsByChunkName.main].integrity;
});
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.
Use that value to generate the `integrity` attribute for tags such as
`<script>` and `<link>`. Note that you are also
[required to set the `crossorigin` attribute](https://www.w3.org/TR/SRI/#cross-origin-data-leakage).
#### With HtmlWebpackPlugin({ inject: false })
#### `html-webpack-plugin` Integration
When you use html-webpack-plugin with `inject: false`, you are
required to set the `integrity` and `crossorigin` attributes in your
template as follows:
The plugin installs a hook for `html-webpack-plugin` that adds the
`integrity` attribute automatically when `inject: true` (the default).
This requires `html-webpack-plugin` version `2.21.0` or later. The
`crossorigin` attribute will be set to `anonymous` in this case.
If you're using a template with `html-webpack-plugin` and with
`inject: false`, you can generate the attributes as follows:
```ejs
<% for (var index in htmlWebpackPlugin.files.js) { %>
<script src="<%= htmlWebpackPlugin.files.js[index] %>"
integrity="<%= htmlWebpackPlugin.files.jsIntegrity[index] %>"
crossorigin="anonymous"
<script
src="<%= htmlWebpackPlugin.files.js[index] %>"
integrity="<%= htmlWebpackPlugin.files.jsIntegrity[index] %>"
crossorigin="<%= htmlWebpackPlugin.options.sriCrossOrigin %>"
></script>

@@ -73,27 +67,91 @@ <% } %>

<% for (var index in htmlWebpackPlugin.files.css) { %>
<link href="<%= htmlWebpackPlugin.files.css[index] %>"
integrity="<%= htmlWebpackPlugin.files.cssIntegrity[index] %>"
rel="stylesheet"
crossorigin="anonymous"
>
<link
href="<%= htmlWebpackPlugin.files.css[index] %>"
integrity="<%= htmlWebpackPlugin.files.cssIntegrity[index] %>"
crossorigin="<%= htmlWebpackPlugin.options.sriCrossOrigin %>"
rel="stylesheet"
/>
<% } %>
```
#### Without HtmlWebpackPlugin
The correct value for the `integrity` attribute can be retrieved from
the `integrity` property of Webpack assets. However, that property is
not copied over by Webpack's `stats` module so you'll have to access
the "original" asset on the `compilation` object. For example:
```javascript
compiler.plugin("done", stats => {
const mainAssetName = stats.toJson().assetsByChunkName.main;
const integrity = stats.compilation.assets[mainAssetName].integrity;
});
```
### Options
#### hashFuncNames
Required option, no default value.
An array of strings, each specifying the name of a hash function to be
used for calculating integrity hash values. For example, `['sha256',
'sha512']`.
See [SRI: Cryptographic hash functions](http://www.w3.org/TR/SRI/#cryptographic-hash-functions)
#### enabled
Default value: `true`
When this value is falsy, the plugin doesn't run and no integrity
values are calculated. It is recommended to disable the plugin in
development mode.
#### crossorigin
Default value: `"anonymous"`
When using `HtmlWebpackPlugin({ inject: true })`, this option
specifies the value to be used for the `crossorigin` attribute for
injected assets.
The value will also be available as
`htmlWebpackPlugin.options.sriCrossOrigin` in html-webpack-plugin
templates.
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)
## Caveats
* There is a
[known bug relating to SRI in Chrome versions before 47](https://code.google.com/p/chromium/issues/detail?id=527286)
which will break loading of scripts containing certain UTF-8
characters. You might want to hold off using SRI if you need to
support older Chrome versions.
### Browser support
## Contributing
Browser support for SRI is currently patchy. Your page will still
work on browsers without support for SRI, but subresources won't be
protected from tampering.
If you have discovered a bug or have a feature suggestion, feel free to create an issue on Github.
See [Can I use Subresource Integrity?](http://caniuse.com/#feat=subresource-integrity)
Pull requests are welcome. Please run `npm test` and `npm run lint` on
your branch before submitting it.
### Broken browser versions
You are also welcome to correct any spelling mistakes or any language issues.
There is a
[known bug relating to SRI in Chrome 45 and 46](https://code.google.com/p/chromium/issues/detail?id=527286)
which will break loading of scripts containing certain UTF-8
characters. You might want to hold off using SRI if you need to
support these Chrome versions. (Unfortunately, Chrome 45 still
[holds a relatively high market share](https://www.netmarketshare.com/report.aspx?qprid=3&qpaf=&qpcustom=Chrome+45.0&qpcustomb=0)
of around 5% at the time of this writing.)
### Hot Module Replacement
Chunks loaded via Hot Module Replacement (HMR) are not currently
protected. This shouldn't be a problem because HMR is usually used
only in development mode where SRI is not normally needed.
## Further Reading
- [MDN: Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)
## License

@@ -103,2 +161,2 @@

MIT (see [`LICENSE`](LICENSE))
MIT (see [LICENSE](LICENSE))
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc