Skip to content

Commit

Permalink
Lazy hashes follow-up
Browse files Browse the repository at this point in the history
* Add end-to-end tests for lazy hashes
* Validate hashLoading option values

Ref #171
  • Loading branch information
jscheid committed Jan 12, 2022
1 parent fc7cf19 commit d049da4
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 1 deletion.
1 change: 1 addition & 0 deletions examples/lazy-e2e/index.js
@@ -0,0 +1 @@
import("./nested1");
1 change: 1 addition & 0 deletions examples/lazy-e2e/nested1.js
@@ -0,0 +1 @@
import("./nested2");
1 change: 1 addition & 0 deletions examples/lazy-e2e/nested2.js
@@ -0,0 +1 @@
console.log("ok");
15 changes: 15 additions & 0 deletions examples/lazy-e2e/package.json
@@ -0,0 +1,15 @@
{
"name": "lazy-e2e",
"description": "Basic end-to-end test with lazy-loaded hashes",
"version": "1.0.0",
"license": "MIT",
"private": true,
"devDependencies": {
"html-webpack-plugin": ">= 5.0.0-beta.1",
"nyc": "*",
"webpack": "^5.44.0",
"webpack-cli": "4",
"webpack-subresource-integrity": "*",
"wsi-test-helper": "*"
}
}
21 changes: 21 additions & 0 deletions examples/lazy-e2e/webpack.config.js
@@ -0,0 +1,21 @@
const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { RunInPuppeteerPlugin } = require("wsi-test-helper");

module.exports = {
entry: {
index: "./index.js",
},
output: {
crossOriginLoading: "anonymous",
},
plugins: [
new SubresourceIntegrityPlugin({
hashFuncNames: ["sha256"],
enabled: true,
lazyHashes: true,
}),
new HtmlWebpackPlugin(),
new RunInPuppeteerPlugin(),
],
};
4 changes: 4 additions & 0 deletions examples/lazy-modified/README.md
@@ -0,0 +1,4 @@
# With lazy hashes and a modified dynamically loaded chunk #hwp

Ensure that when a chunk is modified, it fails to load when hashes are
lazy-loaded.
1 change: 1 addition & 0 deletions examples/lazy-modified/corrupt.js
@@ -0,0 +1 @@
console.log("this should never load");
1 change: 1 addition & 0 deletions examples/lazy-modified/index.js
@@ -0,0 +1 @@
import("./nested");
7 changes: 7 additions & 0 deletions examples/lazy-modified/nested.js
@@ -0,0 +1,7 @@
import(/* webpackChunkName: "corrupt" */ "./corrupt")
.then(function error() {
console.log("error");
})
.catch(function ok() {
console.log("ok");
});
15 changes: 15 additions & 0 deletions examples/lazy-modified/package.json
@@ -0,0 +1,15 @@
{
"name": "lazy-modified",
"description": "Ensure that when a chunk is modified, it fails to load when hashes are lazy-loaded.",
"version": "1.0.0",
"license": "MIT",
"private": true,
"devDependencies": {
"html-webpack-plugin": ">= 5.0.0-beta.1",
"nyc": "*",
"webpack": "^5.44.0",
"webpack-cli": "4",
"webpack-subresource-integrity": "*",
"wsi-test-helper": "*"
}
}
43 changes: 43 additions & 0 deletions examples/lazy-modified/webpack.config.js
@@ -0,0 +1,43 @@
const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { RunInPuppeteerPlugin } = require("wsi-test-helper");
const { writeFileSync } = require("fs");

let gotError = false;

module.exports = {
entry: {
index: "./index.js",
},
output: {
crossOriginLoading: "anonymous",
},
plugins: [
new SubresourceIntegrityPlugin({
hashFuncNames: ["sha256", "sha384"],
lazyHashes: true,
}),
new HtmlWebpackPlugin(),
new RunInPuppeteerPlugin({
onStart: (stats) => {
console.log(stats.compilation.assets);
writeFileSync("dist/corrupt.js", 'console.log("corrupted");');
},
onConsoleError: (msg) => {
console.log(msg);
if (
msg.match(
/Failed to find a valid digest in the 'integrity' attribute for resource/
)
) {
gotError = true;
}
},
onDone: () => {
if (!gotError) {
throw new Error("No error was raised");
}
},
}),
],
};
23 changes: 22 additions & 1 deletion webpack-subresource-integrity/index.ts
Expand Up @@ -261,7 +261,9 @@ export class SubresourceIntegrityPlugin {
"See https://w3c.github.io/webappsec-subresource-integrity/#cross-origin-data-leakage"
);
}
return this.validateHashFuncNames(reporter);
return (
this.validateHashFuncNames(reporter) && this.validateHashLoading(reporter)
);
};

/**
Expand Down Expand Up @@ -291,6 +293,25 @@ export class SubresourceIntegrityPlugin {
}
};

/**
* @internal
*/
private validateHashLoading = (reporter: Reporter): boolean => {
const supportedHashLoadingOptions = Object.freeze(["eager", "lazy"]);
if (supportedHashLoadingOptions.includes(this.options.hashLoading)) {
return true;
}

const optionsStr = supportedHashLoadingOptions
.map((opt) => `'${opt}'`)
.join(", ");

reporter.error(
`options.hashLoading must be one of ${optionsStr}, instead got '${this.options.hashLoading}'`
);
return false;
};

/**
* @internal
*/
Expand Down
21 changes: 21 additions & 0 deletions webpack-subresource-integrity/unit.test.ts
Expand Up @@ -8,6 +8,7 @@
import webpack, { Compiler, Compilation, Configuration, Chunk } from "webpack";
import HtmlWebpackPlugin from "html-webpack-plugin";
import { SubresourceIntegrityPlugin } from "./index.js";
import type { SubresourceIntegrityPluginOptions } from "./index.js";

jest.unmock("html-webpack-plugin");

Expand Down Expand Up @@ -162,6 +163,26 @@ test("errors if hash function names contains unsupported digest", async () => {
);
});

test("errors if hashLoading option uses unknown value", async () => {
const plugin = new SubresourceIntegrityPlugin({
hashLoading:
"invalid" as unknown as SubresourceIntegrityPluginOptions["hashLoading"],
});

const compilation = await runCompilation(
webpack({
...defaultOptions,
plugins: [plugin],
})
);

expect(compilation.errors.length).toBe(1);
expect(compilation.warnings.length).toBe(0);
expect(compilation.errors[0].message).toMatch(
/options.hashLoading must be one of 'eager', 'lazy', instead got 'invalid'/
);
});

test("uses default options", async () => {
const plugin = new SubresourceIntegrityPlugin({
hashFuncNames: ["sha256"],
Expand Down
26 changes: 26 additions & 0 deletions yarn.lock
Expand Up @@ -6815,6 +6815,19 @@ fsevents@^2.1.2:
languageName: node
linkType: hard

"lazy-e2e@workspace:examples/lazy-e2e":
version: 0.0.0-use.local
resolution: "lazy-e2e@workspace:examples/lazy-e2e"
dependencies:
html-webpack-plugin: ">= 5.0.0-beta.1"
nyc: "*"
webpack: ^5.44.0
webpack-cli: 4
webpack-subresource-integrity: "*"
wsi-test-helper: "*"
languageName: unknown
linkType: soft

"lazy-hashes-cycles@workspace:examples/lazy-hashes-cycles":
version: 0.0.0-use.local
resolution: "lazy-hashes-cycles@workspace:examples/lazy-hashes-cycles"
Expand Down Expand Up @@ -6857,6 +6870,19 @@ fsevents@^2.1.2:
languageName: unknown
linkType: soft

"lazy-modified@workspace:examples/lazy-modified":
version: 0.0.0-use.local
resolution: "lazy-modified@workspace:examples/lazy-modified"
dependencies:
html-webpack-plugin: ">= 5.0.0-beta.1"
nyc: "*"
webpack: ^5.44.0
webpack-cli: 4
webpack-subresource-integrity: "*"
wsi-test-helper: "*"
languageName: unknown
linkType: soft

"lcov-parse@npm:^1.0.0":
version: 1.0.0
resolution: "lcov-parse@npm:1.0.0"
Expand Down

0 comments on commit d049da4

Please sign in to comment.