/** * Copyright (c) 2015-present, Waysact Pty Ltd * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var crypto = require('crypto'); var path = require('path'); var webpack = require('webpack'); // eslint-disable-next-line global-require var ReplaceSource = (webpack.sources || require('webpack-sources')).ReplaceSource; var util = require('./util'); var WebIntegrityJsonpMainTemplatePlugin = require('./jmtp'); var HtmlWebpackPlugin; // https://www.w3.org/TR/2016/REC-SRI-20160623/#cryptographic-hash-functions var standardHashFuncNames = ['sha256', 'sha384', 'sha512']; try { // eslint-disable-next-line global-require HtmlWebpackPlugin = require('html-webpack-plugin'); } catch (e) { if (!(e instanceof Error) || e.code !== 'MODULE_NOT_FOUND') { throw e; } } function SubresourceIntegrityPlugin(options) { var useOptions; if (options === null || typeof options === 'undefined') { useOptions = {}; } else if (typeof options === 'object') { useOptions = options; } else { throw new Error('webpack-subresource-integrity: argument must be an object'); } this.options = { enabled: true }; Object.assign(this.options, useOptions); this.emittedMessages = {}; this.assetIntegrity = new Map(); } SubresourceIntegrityPlugin.prototype.emitMessage = function emitMessage(messages, message) { messages.push(new Error('webpack-subresource-integrity: ' + message)); }; SubresourceIntegrityPlugin.prototype.emitMessageOnce = function emitMessageOnce(messages, message) { if (!this.emittedMessages[message]) { this.emittedMessages[message] = true; this.emitMessage(messages, message); } }; SubresourceIntegrityPlugin.prototype.warnOnce = function warn(compilation, message) { this.emitMessageOnce(compilation.warnings, message); }; SubresourceIntegrityPlugin.prototype.error = function error(compilation, message) { this.emitMessage(compilation.errors, message); }; SubresourceIntegrityPlugin.prototype.errorOnce = function error(compilation, message) { this.emitMessageOnce(compilation.errors, message); }; SubresourceIntegrityPlugin.prototype.validateOptions = function validateOptions(compilation) { if (this.optionsValidated) { return; } this.optionsValidated = true; if (this.options.enabled && !compilation.compiler.options.output.crossOriginLoading) { this.warnOnce( compilation, 'SRI requires a cross-origin policy, defaulting to "anonymous". ' + 'Set webpack option output.crossOriginLoading to a value other than false ' + 'to make this warning go away. ' + 'See https://w3c.github.io/webappsec-subresource-integrity/#cross-origin-data-leakage' ); } this.validateHashFuncNames(compilation); }; SubresourceIntegrityPlugin.prototype.validateHashFuncNames = function validateHashFuncNames(compilation) { 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 if ( !this.options.hashFuncNames.every(this.validateHashFuncName.bind(this, compilation)) ) { this.options.enabled = false; } else { this.warnStandardHashFunc(compilation); } }; SubresourceIntegrityPlugin.prototype.warnStandardHashFunc = function warnStandardHashFunc(compilation) { var foundStandardHashFunc = false; var i; for (i = 0; i < this.options.hashFuncNames.length; i += 1) { if (standardHashFuncNames.indexOf(this.options.hashFuncNames[i]) >= 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.'); } }; SubresourceIntegrityPlugin.prototype.validateHashFuncName = function validateHashFuncName(compilation, hashFuncName) { if (typeof hashFuncName !== 'string' && !(hashFuncName instanceof String)) { this.error( compilation, 'options.hashFuncNames must be an array of hash function names, ' + 'but contained ' + hashFuncName + '.'); return false; } try { crypto.createHash(hashFuncName); } catch (error) { this.error( compilation, 'Cannot use hash function \'' + hashFuncName + '\': ' + error.message); return false; } return true; }; /* Given a public URL path to an asset, as generated by * HtmlWebpackPlugin for use as a `