Skip to content
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

[Bug] chainWebpack配置SRI 插件webpack-subresource-integrity 不生效 #12793

Open
keer6 opened this issue Nov 20, 2024 · 2 comments
Open

Comments

@keer6
Copy link

keer6 commented Nov 20, 2024

What happens?

为了实现SRI 功能,在UMI4项目当中引入webpack-subresource-integrity 插件,在chainWebpack配置下,构建出来的文件
没有正确添加anonymous 和integrity 属性

image

其中打印webpack 配置信息,有看到插件成功注入

image

但是结果并没有生效

Mini Showcase Repository(REQUIRED)

/**

*/
chainWebpack: (memo) => {
// const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity');

// 1. 确保正确设置 output
memo.output
  .crossOriginLoading('anonymous')
  .publicPath('/') // 确保这里的 publicPath 与你的部署路径匹配
  .end();

// 2. 添加 SRI 插件,并确保在生产环境中启用
// if (process.env.NODE_ENV === 'production') {
memo.plugin('subresource-integrity').use(SubresourceIntegrityPlugin, [
  {
    hashFuncNames: ['sha256', 'sha384'],
    enabled: true,
    // hashLoading: 'eager', // 添加这个选项
    // validateAssets: true, // 添加验证
  },
]);
// }

// 3. 添加调试日志
console.log('Current NODE_ENV:', process.env.NODE_ENV);
console.log('SRI Plugin Config:', memo.toConfig().plugins);

// return memo;

},

How To Reproduce

Steps to reproduce the behavior: 1. 2.

Expected behavior 1. 2.

Context

  • Umi Version: "@umijs/max": "^4.3.33",
  • Node Version: v16.19.0
  • Platform:
@keer6
Copy link
Author

keer6 commented Nov 21, 2024

image
他这里有提到,HtmlWebpackPlugin 这个插件,但是umi 并没有用到这个生成html,所以只能采取下面这种方式,问题是这个只是获取asset 资源,如何注入到html 才是问题的关键呀~

@keer6
Copy link
Author

keer6 commented Nov 22, 2024

这个问题折腾我三天了,提了issues看一直没人回,只能曲线救国了。

针对该问题,排查多方应该确认的就是因为umi4 模板不是用HtmlWebpackPlugin 插件生成的,导致webpack-subresource-integrity 不生效,所以我只能从构建后二次编译修改html 文件入手,写了一个项目级别的插件,等待webpack编译完成以后,
利用钩子,获取生成后的asset 资源,读取html文件,正则匹配标签,手动替换标签,增加 integrity 和 crossorigin 属性。
踩了好多坑。
其中

  1. 正则匹配的问题
  2. 文件输出路径和获取
  3. *href="${assetName}" 这居然是个关键属性,没有这个属性样式会丢失错乱
  4. 一定要加一个setTimeout,时间尽量长一点,保证源文件正确编译后再二次修改
plugins/htmlIntegrityPlugin.js

  /**
   * @name 执行umi拆包策略
   * @description 这会按照一定的优化策略进行自动分包
   * @doc https://umijs.org/blog/code-splitting#%E5%88%86%E6%9E%90%E4%BA%A7%E7%89%A9%E6%9E%84%E6%88%90
   */
  codeSplitting: {
    jsStrategy: 'granularChunks',
  },
  /**
   * @name webpack 配置额外配置
   * @description 为了扩展 Umi 内置的 webpack 配置,我们提供了用链式编程的方式修改 webpack 配置,基于 webpack-chain,具体 API 可参考 webpack-api 的文档。
   *
   * @doc https://umijs.org/docs/api/config#chainwebpack
   *
   */
  chainWebpack: (memo) => {
    // 配置 output
    memo.output.crossOriginLoading('anonymous');

    // 添加 SRI 插件
    memo.plugin('subresource-integrity').use(
      new SubresourceIntegrityPlugin({
        hashFuncNames: ['sha256', 'sha384'],
        enabled: process.env.NODE_ENV === 'production',
      }),
    );
    // 只有打包的时候才启用
    if (process.env.NODE_ENV === 'production') {
      memo.plugin('html-integrity').use(HtmlIntegrityPlugin);
    }
  },
const fs = require('fs');
const path = require('path');

class HtmlIntegrityPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('HtmlIntegrityPlugin', (stats) => {
      setTimeout(() => {
        const assets = stats.toJson().assets || [];
        const integrityMap = assets.reduce((acc, asset) => {
          if (asset.integrity) {
            acc[asset.name] = asset.integrity;
          }
          return acc;
        }, {});

        const outputPath = compiler.options.output.path;
        const htmlFilePath = path.join(outputPath, '../dist/index.html');

        if (fs.existsSync(htmlFilePath)) {
          let html = fs.readFileSync(htmlFilePath, 'utf-8');

          // 处理所有资源
          Object.entries(integrityMap).forEach(([asset, integrity]) => {
            const escapedAsset = asset.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
            const assetName = asset.startsWith('/') ? asset : `/${asset}`;

            if (asset.endsWith('.js')) {
              const regex = new RegExp(`<script\\s+[^>]*src=["']/${escapedAsset}["'][^>]*>`, 'g');

              html = html.replace(
                regex,
                `<script src="${assetName}" integrity="${integrity}" crossorigin="anonymous">`,
              );
            }

            if (asset.endsWith('.css')) {
              const regex = new RegExp(`<link\\s+[^>]*href=["']/${escapedAsset}["'][^>]*>`, 'g');
              html = html.replace(
                regex,
                `<link rel="stylesheet" href="${assetName}" integrity="${integrity}" crossorigin="anonymous">`,
              );
            }
          });

          fs.writeFileSync(htmlFilePath, html, 'utf-8');
        }
      }, 10000);
    });
  }
}

module.exports = HtmlIntegrityPlugin;

我知道我这是个毕竟愚蠢的办法,但是没办法,水平只能这样了,期待大佬给个更好的解决办法~跪谢。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant