Skip to content

Commit

Permalink
feat: support Trusted Types for client overlay
Browse files Browse the repository at this point in the history
  • Loading branch information
Siegrift committed Apr 24, 2022
1 parent 7c1d680 commit 05c2242
Show file tree
Hide file tree
Showing 12 changed files with 522 additions and 117 deletions.
10 changes: 7 additions & 3 deletions client-src/index.js
Expand Up @@ -15,7 +15,7 @@ import createSocketURL from "./utils/createSocketURL.js";
* @property {boolean} hot
* @property {boolean} liveReload
* @property {boolean} progress
* @property {boolean | { warnings?: boolean, errors?: boolean }} overlay
* @property {boolean | { warnings?: boolean, errors?: boolean, policyName?: string }} overlay
* @property {string} [logging]
* @property {number} [reconnect]
*/
Expand Down Expand Up @@ -230,7 +230,9 @@ const onSocketMessage = {
: options.overlay && options.overlay.warnings;

if (needShowOverlayForWarnings) {
show("warning", warnings);
const policyName =
typeof options.overlay === "object" && options.overlay.policyName;
show("warning", warnings, policyName || null);
}

if (params && params.preventReloading) {
Expand Down Expand Up @@ -263,7 +265,9 @@ const onSocketMessage = {
: options.overlay && options.overlay.errors;

if (needShowOverlayForErrors) {
show("error", errors);
const policyName =
typeof options.overlay === "object" && options.overlay.policyName;
show("error", errors, policyName || null);
}
},
/**
Expand Down
32 changes: 26 additions & 6 deletions client-src/overlay.js
Expand Up @@ -23,10 +23,26 @@ let iframeContainerElement;
let containerElement;
/** @type {Array<(element: HTMLDivElement) => void>} */
let onLoadQueue = [];
/** @type {any} */
let overlayTrustedTypesPolicy;
/** @type {any} */

ansiHTML.setColors(colors);

function createContainer() {
/**
* @param {string | null} policyName
*/
function createContainer(policyName) {
// Enable Trusted Types if they are available in the current browser.
if (window.trustedTypes) {
overlayTrustedTypesPolicy = window.trustedTypes.createPolicy(
policyName || "webpack-dev-server#overlay",
{
createHTML: (value) => value,
}
);
}

iframeContainerElement = document.createElement("iframe");
iframeContainerElement.id = "webpack-dev-server-client-overlay";
iframeContainerElement.src = "about:blank";
Expand Down Expand Up @@ -109,8 +125,9 @@ function createContainer() {

/**
* @param {(element: HTMLDivElement) => void} callback
* @param {string | null} policyName
*/
function ensureOverlayExists(callback) {
function ensureOverlayExists(callback, policyName) {
if (containerElement) {
// Everything is ready, call the callback right away.
callback(containerElement);
Expand All @@ -124,7 +141,7 @@ function ensureOverlayExists(callback) {
return;
}

createContainer();
createContainer(policyName);
}

// Successful compilation.
Expand Down Expand Up @@ -178,8 +195,9 @@ function formatProblem(type, item) {
/**
* @param {string} type
* @param {Array<string | { file?: string, moduleName?: string, loc?: string, message?: string }>} messages
* @param {string | null} policyName
*/
function show(type, messages) {
function show(type, messages, policyName) {
ensureOverlayExists(() => {
messages.forEach((message) => {
const entryElement = document.createElement("div");
Expand All @@ -193,7 +211,9 @@ function show(type, messages) {
const text = ansiHTML(encode(body));
const messageTextNode = document.createElement("div");

messageTextNode.innerHTML = text;
messageTextNode.innerHTML = overlayTrustedTypesPolicy
? overlayTrustedTypesPolicy.createHTML(text)
: text;

entryElement.appendChild(typeElement);
entryElement.appendChild(document.createElement("br"));
Expand All @@ -205,7 +225,7 @@ function show(type, messages) {
/** @type {HTMLDivElement} */
(containerElement).appendChild(entryElement);
});
});
}, policyName);
}

export { formatProblem, show, hide };
36 changes: 36 additions & 0 deletions examples/client/trusted-types-overlay/README.md
@@ -0,0 +1,36 @@
# client.overlay.policyName option

**webpack.config.js**

```js
module.exports = {
// ...
output: {
trustedTypes: { policyName: "webpack" },
},
devServer: {
client: {
overlay: {
policyName: "overlay-policy",
},
},
},
};
```

Usage via CLI:

```shell
npx webpack serve --open
```

## What Should Happen

1. The script should open `http://localhost:8080/` in your default browser.
2. You should see an overlay in browser for compilation errors.
3. Modify `devServer.client.overlay.policyName` in webpack.config.js to `disallowed-policy` and save.
4. Restart the command and you should not see an overlay at all. In the console you should see the following error:

```
Refused to create a TrustedTypePolicy named 'disallowed-policy' because it violates the following Content Security Policy directive: "trusted-types webpack overlay-policy".
```
6 changes: 6 additions & 0 deletions examples/client/trusted-types-overlay/app.js
@@ -0,0 +1,6 @@
"use strict";

const target = document.querySelector("#target");

target.classList.add("pass");
target.textContent = "Success!";
38 changes: 38 additions & 0 deletions examples/client/trusted-types-overlay/layout.html
@@ -0,0 +1,38 @@
<!-- Originally copied from "../../.assets/layout.html" -->
<!DOCTYPE html>
<html>
<head>
<!-- Enable Trusted Types -->
<meta
http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'; trusted-types webpack overlay-policy;"
/>

<title>WDS ▻ <%= htmlWebpackPlugin.options.title %></title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="shortcut icon" href=".assets/favicon.ico" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,600|Source+Sans+Pro:400,400i,500,600"
/>
<link rel="stylesheet" href=".assets/style.css" />
</head>
<body>
<main>
<header>
<h1>
<img
src=".assets/icon-square.svg"
style="width: 35px; height: 35px"
/>
webpack-dev-server
</h1>
</header>
<section>
<h2><%= htmlWebpackPlugin.options.title %></h2>
<div id="target"></div>
</section>
</main>
</body>
</html>
32 changes: 32 additions & 0 deletions examples/client/trusted-types-overlay/webpack.config.js
@@ -0,0 +1,32 @@
"use strict";

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require("../../util");

const config = setup({
context: __dirname,
// create error for overlay
entry: "./invalid.js",
output: {
trustedTypes: { policyName: "webpack" },
},
devServer: {
client: {
overlay: {
policyName: "overlay-policy",
},
},
},
});

// overwrite the index.html with our own to enable Trusted Types
config.plugins[0] = new HtmlWebpackPlugin({
filename: "index.html",
template: path.join(__dirname, "./layout.html"),
title: "trusted types overlay",
});

module.exports = config;
4 changes: 4 additions & 0 deletions lib/options.json
Expand Up @@ -110,6 +110,10 @@
"cli": {
"negatedDescription": "Disables the full-screen overlay in the browser when there are compiler warnings."
}
},
"policyName": {
"description": "The name of a Trusted Types policy for the overlay. Defaults to 'webpack-dev-server#overlay'.",
"type": "string"
}
}
}
Expand Down
73 changes: 73 additions & 0 deletions test/e2e/__snapshots__/overlay.test.js.snap.webpack5
Expand Up @@ -364,6 +364,14 @@ exports[`overlay should not show initially, then show on an error, then show oth
"
`;

exports[`overlay should not show overlay when Trusted Types are enabled, but policy is not allowed: page html 1`] = `
"<body>
<h1>webpack-dev-server is running...</h1>
<script type=\\"text/javascript\\" charset=\\"utf-8\\" src=\\"/main.js\\"></script>
</body>
"
`;

exports[`overlay should show a warning after invalidation: overlay html 1`] = `
"<body>
<div
Expand Down Expand Up @@ -1319,3 +1327,68 @@ exports[`overlay should show an error when "client.overlay.warnings" is "true":
</body>
"
`;

exports[`overlay should show overlay when Trusted Types are enabled: overlay html 1`] = `
"<body>
<div
id=\\"webpack-dev-server-client-overlay-div\\"
style=\\"
position: fixed;
box-sizing: border-box;
inset: 0px;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.85);
color: rgb(232, 232, 232);
font-family: Menlo, Consolas, monospace;
font-size: large;
padding: 2rem;
line-height: 1.2;
white-space: pre-wrap;
overflow: auto;
\\"
>
<span>Compiled with problems:</span
><button
style=\\"
background: transparent;
border: none;
font-size: 20px;
font-weight: bold;
color: white;
cursor: pointer;
float: right;
\\"
>
X</button
><br /><br />
<div>
<span style=\\"color: rgb(227, 96, 73)\\">ERROR</span><br /><br />
<div>Error from compilation. Can't find 'test' module.</div>
<br /><br />
</div>
</div>
</body>
"
`;

exports[`overlay should show overlay when Trusted Types are enabled: page html 1`] = `
"<body>
<h1>webpack-dev-server is running...</h1>
<script type=\\"text/javascript\\" charset=\\"utf-8\\" src=\\"/main.js\\"></script>

<iframe
id=\\"webpack-dev-server-client-overlay\\"
src=\\"about:blank\\"
style=\\"
position: fixed;
inset: 0px;
width: 100vw;
height: 100vh;
border: none;
z-index: 2147483647;
\\"
></iframe>
</body>
"
`;

0 comments on commit 05c2242

Please sign in to comment.