Security: HTML attribute injection in generated tags due to missing escaping

Author: xaccefyCreated Mar 14, 2026Updated Mar 17, 2026

Title: Security: HTML attribute injection in generated tags due to missing escaping

Hi,

I found and validated a security issue in html-webpack-plugin's HTML tag serialization.

Summary

Generated HTML attributes are written without HTML escaping in lib/html-tags.js. When a generated asset URL or tag attribute contains a double quote, it breaks out of the attribute context and injects attacker-controlled attributes into the final HTML.

Validated on current main (5.6.6 in package.json).

Affected code

  • lib/html-tags.js
  • index.js

Relevant lines in the current tree:

  • lib/html-tags.js:46-62
  • index.js:417-429
  • index.js:569-576

Root cause

htmlTagObjectToString() serializes attributes as:

javascript
attributeName + '="' + tagDefinition.attributes[attributeName] + '"'

with no escaping for ", <, >, &, etc.

At the same time, urlencodePath() intentionally preserves the query string portion of asset URLs, so quotes placed in a filename query string survive into the generated <script> / <link> tag.

Impact

This can produce executable HTML attribute injection in emitted files.

One concrete validated case is a generated script tag:

xml
<script defer="" src="bundle.js?x=" onerror="alert(1)"></script>

If a consumer allows untrusted input to influence asset URL query strings or tag-bearing options such as base, this becomes XSS in the generated HTML output.

Minimal reproduction

  1. Install dependencies:
bash
NPM_CONFIG_CACHE=/tmp/html-webpack-plugin-npm-cache npm ci --legacy-peer-deps
  1. Run the attached PoC script:
bash
node poc/validate-vulns.js
  1. Observe:
  • queryAttributeInjection.confirmed === true
  • queryAttributeInjection.parsedScript === "<script defer=\"\" src=\"bundle.js?x=\" onerror=\"alert(1)\"></script>"

The PoC script creates a real webpack build and then parses the emitted HTML with jsdom.

Standalone reproduction

This webpack config is sufficient:

javascript
const path = require("path");
const HtmlWebpackPlugin = require("./index.js");

module.exports = {
  mode: "development",
  entry: path.join(process.cwd(), "spec/fixtures/index.js"),
  output: {
    path: "/tmp/hwp-poc-onerror",
    filename: 'bundle.js?x=" onerror="alert(1)',
  },
  plugins: [new HtmlWebpackPlugin({ minify: false })],
};

The emitted index.html contains:

xml
<script defer src="bundle.js?x=" onerror="alert(1)"></script>

Expected behavior

Attribute values in generated tags should be HTML-escaped before serialization.

Suggested fix

Escape attribute values centrally in htmlTagObjectToString() before concatenation. At minimum:

  • & -> &amp;
  • " -> &quot;
  • < -> &lt;
  • > -> &gt;

It would also be worth reviewing whether innerHTML should remain raw or be clearly separated from safe attribute/tag serialization.

Secondary observations

While testing I also confirmed:

  • filename can emit HTML outside output.path
  • favicon can read and republish arbitrary local files

I am not treating those as the main report here because they are more obviously configuration trust-boundary issues. The attribute injection bug above is the cleanest and strongest issue.

If you prefer a private disclosure channel for security issues, I can move details there instead of keeping this public.

Source: jantimon/html-webpack-plugin