Skip to content

Commit

Permalink
Support Trusted Types in EvalSourceMapDevToolPlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
tosmolka committed Sep 8, 2021
1 parent f4400ef commit 1339272
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 37 deletions.
17 changes: 16 additions & 1 deletion lib/EvalDevToolModulePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const { ConcatSource, RawSource } = require("webpack-sources");
const ExternalModule = require("./ExternalModule");
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
const RuntimeGlobals = require("./RuntimeGlobals");

/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("./Compiler")} Compiler */
Expand Down Expand Up @@ -76,7 +77,13 @@ class EvalDevToolModulePlugin {
.replace(/^\//, "")
);
const result = new RawSource(
`eval(${JSON.stringify(content + footer)});`
`eval(${
compilation.outputOptions.trustedTypes
? `${RuntimeGlobals.createScript}(${JSON.stringify(
content + footer
)})`
: JSON.stringify(content + footer)
});`
);
cache.set(source, result);
return result;
Expand All @@ -94,6 +101,14 @@ class EvalDevToolModulePlugin {
hash.update("EvalDevToolModulePlugin");
hash.update("2");
});
if (compilation.outputOptions.trustedTypes) {
compilation.hooks.additionalModuleRuntimeRequirements.tap(
"EvalDevToolModulePlugin",
(module, set, context) => {
set.add(RuntimeGlobals.createScript);
}
);
}
});
}
}
Expand Down
19 changes: 18 additions & 1 deletion lib/EvalSourceMapDevToolPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const SourceMapDevToolModuleOptionsPlugin = require("./SourceMapDevToolModuleOpt
const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
const ConcatenatedModule = require("./optimize/ConcatenatedModule");
const { makePathsAbsolute } = require("./util/identifier");
const RuntimeGlobals = require("./RuntimeGlobals");

/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../declarations/WebpackOptions").DevTool} DevToolOptions */
Expand Down Expand Up @@ -164,7 +165,15 @@ class EvalSourceMapDevToolPlugin {
) + `\n//# sourceURL=webpack-internal:///${moduleId}\n`; // workaround for chrome bug

return result(
new RawSource(`eval(${JSON.stringify(content + footer)});`)
new RawSource(
`eval(${
compilation.outputOptions.trustedTypes
? `${RuntimeGlobals.createScript}(${JSON.stringify(
content + footer
)})`
: JSON.stringify(content + footer)
});`
)
);
}
);
Expand All @@ -180,6 +189,14 @@ class EvalSourceMapDevToolPlugin {
hash.update("EvalSourceMapDevToolPlugin");
hash.update("2");
});
if (compilation.outputOptions.trustedTypes) {
compilation.hooks.additionalModuleRuntimeRequirements.tap(
"EvalSourceMapDevToolPlugin",
(module, set, context) => {
set.add(RuntimeGlobals.createScript);
}
);
}
}
);
}
Expand Down
13 changes: 13 additions & 0 deletions lib/RuntimeGlobals.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,26 @@ exports.scriptNonce = "__webpack_require__.nc";
*/
exports.loadScript = "__webpack_require__.l";

/**
* function to promote a string to a TrustedScript using webpack's Trusted
* Types policy
* Arguments: (script: string) => TrustedScript
*/
exports.createScript = "__webpack_require__.ts";

/**
* function to promote a string to a TrustedScriptURL using webpack's Trusted
* Types policy
* Arguments: (url: string) => TrustedScriptURL
*/
exports.createScriptUrl = "__webpack_require__.tu";

/**
* function to return webpack's Trusted Types policy
* Arguments: () => TrustedTypePolicy
*/
exports.getTrustedTypesPolicy = "__webpack_require__.tt";

/**
* the chunk name of the chunk with the runtime
*/
Expand Down
26 changes: 26 additions & 0 deletions lib/RuntimePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ const AutoPublicPathRuntimeModule = require("./runtime/AutoPublicPathRuntimeModu
const CompatGetDefaultExportRuntimeModule = require("./runtime/CompatGetDefaultExportRuntimeModule");
const CompatRuntimeModule = require("./runtime/CompatRuntimeModule");
const CreateFakeNamespaceObjectRuntimeModule = require("./runtime/CreateFakeNamespaceObjectRuntimeModule");
const CreateScriptRuntimeModule = require("./runtime/CreateScriptRuntimeModule");
const CreateScriptUrlRuntimeModule = require("./runtime/CreateScriptUrlRuntimeModule");
const GetTrustedTypesPolicyRuntimeModule = require("./runtime/GetTrustedTypesPolicyRuntimeModule");
const DefinePropertyGettersRuntimeModule = require("./runtime/DefinePropertyGettersRuntimeModule");
const EnsureChunkRuntimeModule = require("./runtime/EnsureChunkRuntimeModule");
const GetChunkFilenameRuntimeModule = require("./runtime/GetChunkFilenameRuntimeModule");
Expand All @@ -39,7 +41,9 @@ const GLOBALS_ON_REQUIRE = [
RuntimeGlobals.runtimeId,
RuntimeGlobals.compatGetDefaultExport,
RuntimeGlobals.createFakeNamespaceObject,
RuntimeGlobals.createScript,
RuntimeGlobals.createScriptUrl,
RuntimeGlobals.getTrustedTypesPolicy,
RuntimeGlobals.definePropertyGetters,
RuntimeGlobals.ensureChunk,
RuntimeGlobals.entryModuleId,
Expand Down Expand Up @@ -323,6 +327,7 @@ class RuntimePlugin {
.tap("RuntimePlugin", (chunk, set) => {
const withCreateScriptUrl = !!compilation.outputOptions.trustedTypes;
if (withCreateScriptUrl) {
set.add(RuntimeGlobals.getTrustedTypesPolicy);
set.add(RuntimeGlobals.createScriptUrl);
}
compilation.addRuntimeModule(
Expand All @@ -331,15 +336,36 @@ class RuntimePlugin {
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.createScript)
.tap("RuntimePlugin", (chunk, set) => {
if (compilation.outputOptions.trustedTypes) {
set.add(RuntimeGlobals.getTrustedTypesPolicy);
}
compilation.addRuntimeModule(chunk, new CreateScriptRuntimeModule());
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.createScriptUrl)
.tap("RuntimePlugin", (chunk, set) => {
if (compilation.outputOptions.trustedTypes) {
set.add(RuntimeGlobals.getTrustedTypesPolicy);
}
compilation.addRuntimeModule(
chunk,
new CreateScriptUrlRuntimeModule()
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.getTrustedTypesPolicy)
.tap("RuntimePlugin", (chunk, set) => {
compilation.addRuntimeModule(
chunk,
new GetTrustedTypesPolicyRuntimeModule(set)
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.relativeUrl)
.tap("RuntimePlugin", (chunk, set) => {
Expand Down
1 change: 1 addition & 0 deletions lib/dependencies/CreateScriptUrlDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ CreateScriptUrlDependency.Template = class CreateScriptUrlDependencyTemplate ext
apply(dependency, source, { runtimeRequirements }) {
const dep = /** @type {CreateScriptUrlDependency} */ (dependency);

runtimeRequirements.add(RuntimeGlobals.getTrustedTypesPolicy);
runtimeRequirements.add(RuntimeGlobals.createScriptUrl);

source.insert(dep.range[0], `${RuntimeGlobals.createScriptUrl}(`);
Expand Down
36 changes: 36 additions & 0 deletions lib/runtime/CreateScriptRuntimeModule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
*/

"use strict";

const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const HelperRuntimeModule = require("./HelperRuntimeModule");

class CreateScriptRuntimeModule extends HelperRuntimeModule {
constructor() {
super("trusted types script");
}

/**
* @returns {string} runtime code
*/
generate() {
const { compilation } = this;
const { runtimeTemplate, outputOptions } = compilation;
const { trustedTypes } = outputOptions;
const fn = RuntimeGlobals.createScript;

return Template.asString(
`${fn} = ${runtimeTemplate.returningFunction(
trustedTypes
? `${RuntimeGlobals.getTrustedTypesPolicy}().createScript(script)`
: "script",
"script"
)};`
);
}
}

module.exports = CreateScriptRuntimeModule;
43 changes: 9 additions & 34 deletions lib/runtime/CreateScriptUrlRuntimeModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const HelperRuntimeModule = require("./HelperRuntimeModule");

class CreateScriptUrlRuntimeModule extends HelperRuntimeModule {
constructor() {
super("trusted types");
super("trusted types script url");
}

/**
Expand All @@ -22,39 +22,14 @@ class CreateScriptUrlRuntimeModule extends HelperRuntimeModule {
const { trustedTypes } = outputOptions;
const fn = RuntimeGlobals.createScriptUrl;

if (!trustedTypes) {
// Skip Trusted Types logic.
return Template.asString([
`${fn} = ${runtimeTemplate.returningFunction("url", "url")};`
]);
}

return Template.asString([
"var policy;",
`${fn} = ${runtimeTemplate.basicFunction("url", [
"// Create Trusted Type policy if Trusted Types are available and the policy doesn't exist yet.",
"if (policy === undefined) {",
Template.indent([
"policy = {",
Template.indent([
`createScriptURL: ${runtimeTemplate.returningFunction(
"url",
"url"
)}`
]),
"};",
'if (typeof trustedTypes !== "undefined" && trustedTypes.createPolicy) {',
Template.indent([
`policy = trustedTypes.createPolicy(${JSON.stringify(
trustedTypes.policyName
)}, policy);`
]),
"}"
]),
"}",
"return policy.createScriptURL(url);"
])};`
]);
return Template.asString(
`${fn} = ${runtimeTemplate.returningFunction(
trustedTypes
? `${RuntimeGlobals.getTrustedTypesPolicy}().createScriptURL(url)`
: "url",
"url"
)};`
);
}
}

Expand Down
76 changes: 76 additions & 0 deletions lib/runtime/GetTrustedTypesPolicyRuntimeModule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
*/

"use strict";

const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const HelperRuntimeModule = require("./HelperRuntimeModule");

class GetTrustedTypesPolicyRuntimeModule extends HelperRuntimeModule {
/**
* @param {Set<string>} runtimeRequirements runtime requirements
*/
constructor(runtimeRequirements) {
super("trusted types policy");
this.runtimeRequirements = runtimeRequirements;
}

/**
* @returns {string} runtime code
*/
generate() {
const { compilation } = this;
const { runtimeTemplate, outputOptions } = compilation;
const { trustedTypes } = outputOptions;
const fn = RuntimeGlobals.getTrustedTypesPolicy;

return Template.asString([
"var policy;",
`${fn} = ${runtimeTemplate.basicFunction("", [
"// Create Trusted Type policy if Trusted Types are available and the policy doesn't exist yet.",
"if (policy === undefined) {",
Template.indent([
"policy = {",
Template.indent(
[
...(this.runtimeRequirements.has(RuntimeGlobals.createScript)
? [
`createScript: ${runtimeTemplate.returningFunction(
"script",
"script"
)}`
]
: []),
...(this.runtimeRequirements.has(RuntimeGlobals.createScriptUrl)
? [
`createScriptURL: ${runtimeTemplate.returningFunction(
"url",
"url"
)}`
]
: [])
].join(",\n")
),
"};",
...(trustedTypes
? [
'if (typeof trustedTypes !== "undefined" && trustedTypes.createPolicy) {',
Template.indent([
`policy = trustedTypes.createPolicy(${JSON.stringify(
trustedTypes.policyName
)}, policy);`
]),
"}"
]
: [])
]),
"}",
"return policy;"
])};`
]);
}
}

module.exports = GetTrustedTypesPolicyRuntimeModule;
5 changes: 4 additions & 1 deletion lib/webworker/ImportScriptsChunkLoadingPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ class ImportScriptsChunkLoadingPlugin {
const withCreateScriptUrl = !!compilation.outputOptions.trustedTypes;
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
set.add(RuntimeGlobals.hasOwnProperty);
if (withCreateScriptUrl) set.add(RuntimeGlobals.createScriptUrl);
if (withCreateScriptUrl) {
set.add(RuntimeGlobals.getTrustedTypesPolicy);
set.add(RuntimeGlobals.createScriptUrl);
}
compilation.addRuntimeModule(
chunk,
new ImportScriptsChunkLoadingRuntimeModule(set, withCreateScriptUrl)
Expand Down
34 changes: 34 additions & 0 deletions test/configCases/trusted-types/devtool-eval/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
it("should pass TrustedScript to eval", function() {
class TrustedScript {
constructor(script) {
this._script = script;
}
};

var policy = __webpack_require__.tt();
policy.createScript = jest.fn((script) => {
expect(typeof script).toEqual("string");
return new TrustedScript(script);
});

const globalEval = eval;
window.module = {};
window.eval = jest.fn((x) => {
expect(x).toBeInstanceOf(TrustedScript);
return globalEval(x._script);
});

require("./test.js");
expect(window.module.exports).toBeInstanceOf(Object);
expect(window.module.exports.foo).toEqual('bar');

const testPattern = "var test = {\\s*foo: \'bar\'\\s*};\\s*module.exports = test;";
expect(policy.createScript).toBeCalledWith(
expect.stringMatching(testPattern)
);
expect(window.eval).toBeCalledWith(
expect.objectContaining({
_script: expect.stringMatching(testPattern)
})
);
});

0 comments on commit 1339272

Please sign in to comment.