Merge pull request #14173 from tosmolka/tosmolka/14075

Support Trusted Types in EvalSourceMapDevToolPlugin
This commit is contained in:
Tobias Koppers 2022-01-20 17:36:55 +01:00 committed by GitHub
commit 1ed8aaf2e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 267 additions and 47 deletions

View File

@ -8,6 +8,7 @@
const { ConcatSource, RawSource } = require("webpack-sources");
const ExternalModule = require("./ExternalModule");
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const RuntimeGlobals = require("./RuntimeGlobals");
const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
/** @typedef {import("webpack-sources").Source} Source */
@ -77,7 +78,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;
@ -95,6 +102,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);
}
);
}
});
}
}

View File

@ -8,6 +8,7 @@
const { ConcatSource, RawSource } = require("webpack-sources");
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const NormalModule = require("./NormalModule");
const RuntimeGlobals = require("./RuntimeGlobals");
const SourceMapDevToolModuleOptionsPlugin = require("./SourceMapDevToolModuleOptionsPlugin");
const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
const ConcatenatedModule = require("./optimize/ConcatenatedModule");
@ -165,7 +166,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)
});`
)
);
}
);
@ -181,6 +190,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);
}
);
}
}
);
}

View File

@ -168,6 +168,13 @@ 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
@ -175,6 +182,12 @@ exports.loadScript = "__webpack_require__.l";
*/
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
*/

View File

@ -14,11 +14,13 @@ 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 DefinePropertyGettersRuntimeModule = require("./runtime/DefinePropertyGettersRuntimeModule");
const EnsureChunkRuntimeModule = require("./runtime/EnsureChunkRuntimeModule");
const GetChunkFilenameRuntimeModule = require("./runtime/GetChunkFilenameRuntimeModule");
const GetMainFilenameRuntimeModule = require("./runtime/GetMainFilenameRuntimeModule");
const GetTrustedTypesPolicyRuntimeModule = require("./runtime/GetTrustedTypesPolicyRuntimeModule");
const GlobalRuntimeModule = require("./runtime/GlobalRuntimeModule");
const HasOwnPropertyRuntimeModule = require("./runtime/HasOwnPropertyRuntimeModule");
const LoadScriptRuntimeModule = require("./runtime/LoadScriptRuntimeModule");
@ -40,7 +42,9 @@ const GLOBALS_ON_REQUIRE = [
RuntimeGlobals.runtimeId,
RuntimeGlobals.compatGetDefaultExport,
RuntimeGlobals.createFakeNamespaceObject,
RuntimeGlobals.createScript,
RuntimeGlobals.createScriptUrl,
RuntimeGlobals.getTrustedTypesPolicy,
RuntimeGlobals.definePropertyGetters,
RuntimeGlobals.ensureChunk,
RuntimeGlobals.entryModuleId,
@ -364,15 +368,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) => {

View File

@ -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;

View File

@ -10,7 +10,7 @@ const HelperRuntimeModule = require("./HelperRuntimeModule");
class CreateScriptUrlRuntimeModule extends HelperRuntimeModule {
constructor() {
super("trusted types");
super("trusted types script url");
}
/**
@ -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"
)};`
);
}
}

View File

@ -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;

View File

@ -6,7 +6,6 @@
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const CreateScriptUrlRuntimeModule = require("../runtime/CreateScriptUrlRuntimeModule");
const StartupChunkDependenciesPlugin = require("../runtime/StartupChunkDependenciesPlugin");
const ImportScriptsChunkLoadingRuntimeModule = require("./ImportScriptsChunkLoadingRuntimeModule");
@ -43,7 +42,9 @@ 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.createScriptUrl);
}
compilation.addRuntimeModule(
chunk,
new ImportScriptsChunkLoadingRuntimeModule(set, withCreateScriptUrl)
@ -61,15 +62,6 @@ class ImportScriptsChunkLoadingPlugin {
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.baseURI)
.tap("ImportScriptsChunkLoadingPlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.createScriptUrl)
.tap("RuntimePlugin", (chunk, set) => {
compilation.addRuntimeModule(
chunk,
new CreateScriptUrlRuntimeModule()
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)

View File

@ -0,0 +1,45 @@
it("should pass TrustedScript to eval", function () {
var policy = __webpack_require__.tt();
policy.createScript = jest.fn(script => {
expect(typeof script).toEqual("string");
return new TrustedScript(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)
})
);
});
class TrustedScript {
constructor(script) {
this._script = script;
}
}
let globalEval;
beforeEach(done => {
globalEval = eval;
window.module = {};
window.eval = jest.fn(x => {
expect(x).toBeInstanceOf(TrustedScript);
return globalEval(x._script);
});
done();
});
afterEach(done => {
delete window.module;
window.eval = globalEval;
done();
});

View File

@ -0,0 +1,5 @@
var test = {
foo: 'bar'
};
module.exports = test;

View File

@ -0,0 +1,19 @@
/** @type {import("../../../../").Configuration[]} */
module.exports = [
{
target: "web",
output: {
filename: "bundle0.js",
trustedTypes: true
},
devtool: "eval-source-map"
},
{
target: "web",
output: {
filename: "bundle1.js",
trustedTypes: true
},
devtool: "eval"
}
];

2
types.d.ts vendored
View File

@ -12403,7 +12403,9 @@ declare namespace exports {
export let uncaughtErrorHandler: string;
export let scriptNonce: string;
export let loadScript: string;
export let createScript: string;
export let createScriptUrl: string;
export let getTrustedTypesPolicy: string;
export let chunkName: string;
export let runtimeId: string;
export let getChunkScriptFilename: string;