Files
grafana/scripts/webpack/plugins/FeatureFlaggedSriPlugin.js
Jack Westbrook bbfeb8d220 Feature: Introduce subresource integrity checks (SRI) for frontend assets (#100983)
* feat(featuremgmt): introduce feature toggle for enabling sri checks

* feat(frontend): use assetSriChecks feature toggle to inject integrity hash into script tags

* chore(webpack): align sri algorithms across dev and prod builds

* docs(featuremgmt): update assetSriChecks to pass CI

* docs(featuremgmt): fix more spelling complaints with assetSriChecks

* Add crossorigin attribute

* chore(webpack): add subresource-integrity plugin

* build(webpack): wrap webpack jsonp loader integrity checks in feature flag checks

* revert(index.html): remove crossorigin attribute if assertSriChecks is disabled

---------

Co-authored-by: Kristian Bremberg <kristian.bremberg@grafana.com>
2025-03-04 11:56:35 +01:00

67 lines
2.1 KiB
JavaScript

// @ts-check
const webpack = require('webpack');
/** @typedef {import('webpack/lib/Compiler.js')} Compiler */
const PLUGIN_NAME = 'FeatureFlaggedSRIPlugin';
const FEATURE_TOGGLE_WRAP = [
'if (window.grafanaBootData && window.grafanaBootData.settings && window.grafanaBootData.settings.featureToggles && window.grafanaBootData.settings.featureToggles.assetSriChecks) {',
'}',
];
/**
* Webpack plugin that wraps Webpack runtime integrity checks in a feature flag
* This allows us to disable SRI checks in both the initial chunks but also in the
* dynamically loaded chunks.
*/
class FeatureFlaggedSRIPlugin {
/**
* @param {Compiler} compiler The webpack compiler instance
*/
apply(compiler) {
compiler.hooks.afterPlugins.tap(PLUGIN_NAME, (compiler) => {
const logger = compiler.getInfrastructureLogger(PLUGIN_NAME);
compiler.hooks.thisCompilation.tap(
{
name: PLUGIN_NAME,
},
(compilation) => {
const { mainTemplate } = compilation;
mainTemplate.hooks.jsonpScript.tap(
PLUGIN_NAME,
/**
* @param {string} source
*/
(source) => {
if (source.includes('script.integrity =')) {
logger.log('FeatureFlaggedSRIPlugin: Wrapping SRI checks in feature flag');
return createFeatureFlaggedSRITemplate(source);
}
return source;
}
);
}
);
});
}
}
/**
* Creates a template string wrapping the integrity and crossorigin attributes in a feature flag
* @param {string} source The original webpack template source
* @returns {string} The modified template source
*/
function createFeatureFlaggedSRITemplate(source) {
const lines = source.split('\n');
const integrityAttributeLineNumber = lines.findIndex((line) => line.includes('script.integrity ='));
const [prefix, suffix] = FEATURE_TOGGLE_WRAP;
return webpack.Template.asString([
...lines.slice(0, integrityAttributeLineNumber),
prefix,
webpack.Template.indent(lines.slice(integrityAttributeLineNumber)),
suffix,
]);
}
module.exports = FeatureFlaggedSRIPlugin;