Security: HTML attribute injection in generated tags due to missing escaping
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.jsindex.js
Relevant lines in the current tree:
lib/html-tags.js:46-62index.js:417-429index.js:569-576
Root cause
htmlTagObjectToString() serializes attributes as:
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:
<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
- Install dependencies:
NPM_CONFIG_CACHE=/tmp/html-webpack-plugin-npm-cache npm ci --legacy-peer-deps- Run the attached PoC script:
node poc/validate-vulns.js- Observe:
queryAttributeInjection.confirmed === truequeryAttributeInjection.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:
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:
<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:
&->&"->"<-><>->>
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:
filenamecan emit HTML outsideoutput.pathfaviconcan 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