jantimon / jantimon/html-webpack-plugin
Security: HTML attribute injection in generated tags due to missing escaping
- Dominant language
- JavaScript
- Stars
- 10.7k
- Forks
- 1.3k
- Avg merge
- 1m
- Merged PRs (30d)
- 1
Description
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:
```js
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 `` / `<link>` tag.
Impact
This can produce executable HTML attribute injection in emitted files.
One concrete validated case is a generated script tag:
```html
<script defer="" src="bundle.js?x=" onerror="alert(1)">
```
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
```
2. Run the attached PoC script:
```bash
node poc/validate-vulns.js
```
3. Observe:
- `queryAttributeInjection.confirmed === true`
- `queryAttributeInjection.parsedScript === ""`
The PoC script creates a real webpack build and then parses the emitted HTML with `jsdom`.
Standalone reproduction
This webpack config is sufficient:
```js
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:
```html
```
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:
- `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.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading htmlTagObjectToString() in lib/html-tags.js, then run node poc/validate-vulns.js to reproduce the issue through the generated HTML. Review the relevant call sites in index.js and verify that attribute values are escaped in emitted script and link tags while the PoC no longer confirms injection.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, webpack
- Domain
- frontend, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100