UNPKG

webpack-subresource-integrity

Version:
203 lines 10.8 kB
"use strict"; /** * 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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const path_1 = require("path"); const webpack_1 = __importDefault(require("webpack")); const html_webpack_plugin_1 = __importDefault(require("html-webpack-plugin")); const __1 = require(".."); const util_1 = require("../util"); jest.unmock("html-webpack-plugin"); process.on("unhandledRejection", (error) => { console.log(error); // eslint-disable-line no-console process.exit(1); }); test("throws an error when options is not an object", () => __awaiter(void 0, void 0, void 0, function* () { expect(() => { new __1.SubresourceIntegrityPlugin(function dummy() { // dummy function, never called }); // eslint-disable-line no-new }).toThrow(/argument must be an object/); })); const runCompilation = (compiler) => new Promise((resolve, reject) => { compiler.run((err, stats) => { if (err) { reject(err); } else if (!stats) { reject(new Error("Missing stats")); } else { resolve(stats.compilation); } }); }); const disableOutputPlugin = { apply(compiler) { compiler.hooks.compilation.tap("DisableOutputWebpackPlugin", (compilation) => { compilation.hooks.afterProcessAssets.tap({ name: "DisableOutputWebpackPlugin", stage: 10000, }, (compilationAssets) => { Object.keys(compilation.assets).forEach((asset) => { delete compilation.assets[asset]; }); Object.keys(compilationAssets).forEach((asset) => { delete compilationAssets[asset]; }); }); }); }, }; const defaultOptions = { mode: "none", entry: (0, path_1.resolve)(__dirname, "./__fixtures__/simple-project/src/."), output: { crossOriginLoading: "anonymous", }, }; test("warns when no standard hash function name is specified", () => __awaiter(void 0, void 0, void 0, function* () { var _a; const plugin = new __1.SubresourceIntegrityPlugin({ hashFuncNames: ["md5"], }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { plugins: [plugin] }))); expect(compilation.errors).toEqual([]); expect((_a = compilation.warnings[0]) === null || _a === void 0 ? void 0 : _a.message).toMatch(new RegExp("It is recommended that at least one hash function is part of " + "the set for which support is mandated by the specification")); expect(compilation.warnings[1]).toBeUndefined(); })); test("supports new constructor with array of hash function names", () => __awaiter(void 0, void 0, void 0, function* () { const plugin = new __1.SubresourceIntegrityPlugin({ hashFuncNames: ["sha256", "sha384"], }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { plugins: [plugin, disableOutputPlugin] }))); expect(compilation.errors.length).toBe(0); expect(compilation.warnings.length).toBe(0); })); test("errors if hash function names is not an array", () => __awaiter(void 0, void 0, void 0, function* () { var _b; const plugin = new __1.SubresourceIntegrityPlugin({ hashFuncNames: "sha256", }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { plugins: [plugin, disableOutputPlugin] }))); expect(compilation.errors.length).toBe(1); expect(compilation.warnings.length).toBe(0); expect((_b = compilation.errors[0]) === null || _b === void 0 ? void 0 : _b.message).toMatch(/options.hashFuncNames must be an array of hash function names, instead got 'sha256'/); })); test("errors if hash function names contains non-string", () => __awaiter(void 0, void 0, void 0, function* () { var _c; const plugin = new __1.SubresourceIntegrityPlugin({ hashFuncNames: [1234], }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { plugins: [plugin, disableOutputPlugin] }))); expect(compilation.errors.length).toBe(1); expect(compilation.warnings.length).toBe(0); expect((_c = compilation.errors[0]) === null || _c === void 0 ? void 0 : _c.message).toMatch(/options.hashFuncNames must be an array of hash function names, but contained 1234/); })); test("errors if hash function names are empty", () => __awaiter(void 0, void 0, void 0, function* () { var _d; const plugin = new __1.SubresourceIntegrityPlugin({ hashFuncNames: [], }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { plugins: [plugin, disableOutputPlugin] }))); expect(compilation.errors.length).toBe(1); expect(compilation.warnings.length).toBe(0); expect((_d = compilation.errors[0]) === null || _d === void 0 ? void 0 : _d.message).toMatch(/Must specify at least one hash function name/); })); test("errors if hash function names contains unsupported digest", () => __awaiter(void 0, void 0, void 0, function* () { var _e; const plugin = new __1.SubresourceIntegrityPlugin({ hashFuncNames: ["frobnicate"], }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { plugins: [plugin, disableOutputPlugin] }))); expect(compilation.errors.length).toBe(1); expect(compilation.warnings.length).toBe(0); expect((_e = compilation.errors[0]) === null || _e === void 0 ? void 0 : _e.message).toMatch(/Cannot use hash function 'frobnicate': Digest method not supported/); })); test("errors if hashLoading option uses unknown value", () => __awaiter(void 0, void 0, void 0, function* () { var _f; const plugin = new __1.SubresourceIntegrityPlugin({ hashLoading: "invalid", }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { plugins: [plugin, disableOutputPlugin] }))); expect(compilation.errors.length).toBe(1); expect(compilation.warnings.length).toBe(0); expect((_f = compilation.errors[0]) === null || _f === void 0 ? void 0 : _f.message).toMatch(/options.hashLoading must be one of 'eager', 'lazy', instead got 'invalid'/); })); test("uses default options", () => __awaiter(void 0, void 0, void 0, function* () { const plugin = new __1.SubresourceIntegrityPlugin({ hashFuncNames: ["sha256"], }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { plugins: [plugin, disableOutputPlugin] }))); expect(plugin["options"].hashFuncNames).toEqual(["sha256"]); expect(plugin["options"].enabled).toBeTruthy(); expect(compilation.errors.length).toBe(0); expect(compilation.warnings.length).toBe(0); })); test("should warn when output.crossOriginLoading is not set", () => __awaiter(void 0, void 0, void 0, function* () { var _g, _h; const plugin = new __1.SubresourceIntegrityPlugin({ hashFuncNames: ["sha256"] }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { output: { crossOriginLoading: false }, plugins: [plugin, disableOutputPlugin] }))); compilation.mainTemplate.hooks.jsonpScript.call("", {}); compilation.mainTemplate.hooks.linkPreload.call("", {}); expect(compilation.errors.length).toBe(1); expect(compilation.warnings.length).toBe(1); expect((_g = compilation.warnings[0]) === null || _g === void 0 ? void 0 : _g.message).toMatch(/Set webpack option output\.crossOriginLoading/); expect((_h = compilation.errors[0]) === null || _h === void 0 ? void 0 : _h.message).toMatch(/webpack option output\.crossOriginLoading not set, code splitting will not work!/); })); test("should ignore tags without attributes", () => __awaiter(void 0, void 0, void 0, function* () { const plugin = new __1.SubresourceIntegrityPlugin({ hashFuncNames: ["sha256"] }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { plugins: [plugin, disableOutputPlugin] }))); const tag = { tagName: "script", voidTag: false, attributes: {}, meta: {}, }; html_webpack_plugin_1.default.getHooks(compilation).alterAssetTagGroups.promise({ headTags: [], bodyTags: [tag], outputName: "foo", publicPath: "public", plugin: new html_webpack_plugin_1.default(), }); expect(Object.keys(tag.attributes)).not.toContain(["integrity"]); expect(compilation.errors).toEqual([]); expect(compilation.warnings).toEqual([]); })); test("positive assertion", () => { (0, util_1.assert)(true, "Pass"); }); test("negative assertion", () => { expect(() => { (0, util_1.assert)(false, "Fail"); }).toThrow(new Error("Fail")); }); test("errors with unresolved integrity", () => __awaiter(void 0, void 0, void 0, function* () { var _j; const plugin = new __1.SubresourceIntegrityPlugin({ hashFuncNames: ["sha256", "sha384"], }); const compilation = yield runCompilation((0, webpack_1.default)(Object.assign(Object.assign({}, defaultOptions), { entry: (0, path_1.resolve)(__dirname, "./__fixtures__/unresolved/src/."), plugins: [plugin, disableOutputPlugin] }))); expect(compilation.errors.length).toBe(1); expect(compilation.warnings.length).toBe(0); expect((_j = compilation.errors[0]) === null || _j === void 0 ? void 0 : _j.message).toMatch(new RegExp("contains unresolved integrity placeholders")); })); //# sourceMappingURL=unit.test.js.map