New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
webpack: lockdown inlining #1101
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
43f3975
feat(webpack): ability to inline lockdown
naugtur a7b82a2
chore(webpack): refresh README.md
naugtur c245b23
chore(webpack): Apply suggestions from code review
naugtur 1954531
chore(webpack): inlining description tweak
naugtur 0eaa1d2
chore(webpack): Apply suggestions from code review
naugtur File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,79 @@ | ||
const { readFileSync } = require('node:fs') | ||
const path = require('node:path') | ||
const { | ||
sources: { RawSource }, | ||
sources: { RawSource, ConcatSource }, | ||
} = require('webpack') | ||
|
||
const lockdownSource = readFileSync( | ||
path.join(require.resolve('ses'), '../lockdown.umd.min.js'), | ||
'utf-8' | ||
) | ||
const lockdownSourcePrefix = `;(function(){\n${lockdownSource}\n})();\n` | ||
|
||
module.exports = { | ||
/** | ||
* @param {object} options | ||
* @param {import('webpack').Compilation} options.compilation | ||
* @param {string[]} options.inlineLockdown | ||
* @returns {() => void} | ||
*/ | ||
sesPrefixFiles: | ||
({ compilation, inlineLockdown: files }) => | ||
() => { | ||
files.forEach((file) => { | ||
const asset = compilation.assets[file] | ||
if (!asset) { | ||
throw new Error( | ||
`LavaMoatPlugin: file specified in inlineLockdown not found in compilation: ${file}` | ||
) | ||
} | ||
compilation.assets[file] = new ConcatSource(lockdownSourcePrefix, asset) | ||
}) | ||
}, | ||
/** | ||
* @param {object} options | ||
* @param {import('webpack').Compilation} options.compilation | ||
* @param {import('webpack').WebpackPluginInstance} [options.HtmlWebpackPluginInUse] | ||
* @param {boolean} [options.HtmlWebpackPluginInterop] | ||
* @returns {() => void} | ||
*/ | ||
sesEmitHook: | ||
({ compilation, HtmlWebpackPluginInUse, HtmlWebpackPluginInterop }) => | ||
() => { | ||
const sesFile = readFileSync(require.resolve('ses'), 'utf-8') | ||
// TODO: to consider: instead manually copy to compiler.options.output.path | ||
const asset = new RawSource(sesFile) | ||
const asset = new RawSource(lockdownSource) | ||
|
||
compilation.emitAsset('lockdown', asset) | ||
|
||
if (HtmlWebpackPluginInUse && HtmlWebpackPluginInterop) { | ||
HtmlWebpackPluginInUse.constructor | ||
// @ts-expect-error - incomplete types | ||
.getHooks(compilation) | ||
.beforeEmit.tapAsync('LavaMoatWebpackPlugin-lockdown', (data, cb) => { | ||
const scriptTag = '<script src="./lockdown"></script>' | ||
const headTagRegex = /<head[^>]*>/iu | ||
const scriptTagRegex = /<script/iu | ||
.beforeEmit.tapAsync( | ||
'LavaMoatWebpackPlugin-lockdown', | ||
( | ||
/** @type {{ html: string }} */ data, | ||
/** @type {(arg0: null, arg1: any) => void} */ cb | ||
) => { | ||
const scriptTag = '<script src="./lockdown"></script>' | ||
const headTagRegex = /<head[^>]*>/iu | ||
const scriptTagRegex = /<script/iu | ||
|
||
if (headTagRegex.test(data.html)) { | ||
data.html = data.html.replace(headTagRegex, `$&${scriptTag}`) | ||
} else if (scriptTagRegex.test(data.html)) { | ||
data.html = data.html.replace( | ||
scriptTagRegex, | ||
`${scriptTag}<script` | ||
) | ||
} else { | ||
throw Error( | ||
'LavaMoat: Could not insert lockdown script tag, no suitable location found in the html template' | ||
) | ||
if (headTagRegex.test(data.html)) { | ||
data.html = data.html.replace(headTagRegex, `$&${scriptTag}`) | ||
} else if (scriptTagRegex.test(data.html)) { | ||
data.html = data.html.replace( | ||
scriptTagRegex, | ||
`${scriptTag}$&` | ||
) | ||
} else { | ||
throw Error( | ||
'LavaMoat: Could not insert lockdown script tag, no suitable location found in the html template' | ||
) | ||
} | ||
cb(null, data) | ||
} | ||
cb(null, data) | ||
}) | ||
) | ||
} | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
const test = require('ava') | ||
const { scaffold, runScript } = require('./scaffold.js') | ||
const { makeConfig } = require('./fixtures/main/webpack.config.js') | ||
|
||
test.before(async (t) => { | ||
const webpackConfig = makeConfig({ | ||
inlineLockdown: ['app.js'], | ||
}) | ||
await t.notThrowsAsync(async () => { | ||
t.context.build = await scaffold(webpackConfig) | ||
}, 'Expected the build to succeed') | ||
t.context.bundle = t.context.build.snapshot['/dist/app.js'] | ||
}) | ||
|
||
test('webpack/lockdown-inline - bundle runs under lockdown', (t) => { | ||
t.notThrows(() => { | ||
runScript( | ||
t.context.bundle + | ||
` | ||
;; | ||
if (!Object.isFrozen(Object.prototype)) { | ||
throw Error('expected Object.prototype to be frozen'); | ||
} | ||
if (!Compartment || !harden) { | ||
throw Error('expected SES globals to be available'); | ||
}` | ||
) | ||
}) | ||
}) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the side-effect here is little malodorous to me, but I'm not sure what a better solution would look like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean is a side effect here. scaffold is running the build and returning information it collects. If it fails, we report it as a testsuite failure.