diff --git a/packages/@vue/cli-plugin-pwa/__tests__/pwaGenerator.spec.js b/packages/@vue/cli-plugin-pwa/__tests__/pwaGenerator.spec.js
index 222f5f0c7e..b8c2ab26b5 100644
--- a/packages/@vue/cli-plugin-pwa/__tests__/pwaGenerator.spec.js
+++ b/packages/@vue/cli-plugin-pwa/__tests__/pwaGenerator.spec.js
@@ -1,4 +1,8 @@
const generateWithPlugin = require('@vue/cli-test-utils/generateWithPlugin')
+const HtmlPwaPlugin = require('../lib/HtmlPwaPlugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const { performance } = require('perf_hooks')
+global.performance = performance
test('inject import statement for service worker', async () => {
const { files } = await generateWithPlugin([
@@ -38,3 +42,42 @@ test('inject import statement for service worker (with TS)', async () => {
expect(files['src/main.ts']).toMatch(`import './registerServiceWorker'`)
})
+
+test('ReDos test', async () => {
+ HtmlWebpackPlugin.getHooks = () => ({
+ beforeEmit: {
+ tapAsync: (id, handler) => {
+ const hugeHtml = ' {})
+ }
+ },
+ alterAssetTagGroups: {
+ tapAsync: () => {}
+ }
+ })
+ const plugin = new HtmlPwaPlugin()
+ const fakeCompiler = {
+ options: { output: { publicPath: '/' } },
+ hooks: {
+ compilation: {
+ tap: (_id, cb) => {
+ const fakeCompilation = {
+ hooks: {
+ processAssets: {
+ tap: (_opts, fn) => {}
+ }
+ }
+ }
+ cb(fakeCompilation)
+ }
+ }
+ }
+ }
+ const startTime = performance.now()
+ plugin.apply(fakeCompiler)
+ const endTime = performance.now()
+ const timeTaken = endTime - startTime
+ console.log(`time taken: ${timeTaken.toFixed(3)} ms`)
+ expect(timeTaken).toBeLessThan(3000)
+}, 3000)
diff --git a/packages/@vue/cli-plugin-pwa/lib/HtmlPwaPlugin.js b/packages/@vue/cli-plugin-pwa/lib/HtmlPwaPlugin.js
index ba86f2a23b..2dc18dc06a 100644
--- a/packages/@vue/cli-plugin-pwa/lib/HtmlPwaPlugin.js
+++ b/packages/@vue/cli-plugin-pwa/lib/HtmlPwaPlugin.js
@@ -64,7 +64,7 @@ module.exports = class HtmlPwaPlugin {
compiler.hooks.compilation.tap(ID, compilation => {
HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(ID, (data, cb) => {
// wrap favicon in the base template with IE only comment
- data.html = data.html.replace(/]+>/, '')
+ data.html = data.html.replace(/]+>/, '')
cb(null, data)
})