-
-
Notifications
You must be signed in to change notification settings - Fork 2
fix: address CVE ReDoS issue #20
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
Changes from all commits
9e37e94
9c56bf8
82e14de
bf32265
45d571a
ea41da3
9e3e6de
1475993
5d1c127
072b9b6
b5aa1da
bd446c2
5308689
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
# HTML Minifier Next (HTMLMinifier) | ||
|
||
[](https://www.npmjs.com/package/html-minifier-next) | ||
[](https://www.npmjs.com/package/html-minifier-next) | ||
<!-- [](https://github.com/j9t/html-minifier-next/actions?workflow=CI) --> | ||
|
||
(This project is based on [Terser’s html-minifier-terser](https://github.com/terser/html-minifier-terser), which in turn is based on [Juriy Zaytsev’s html-minifier](https://github.com/kangax/html-minifier). It was set up because as of May 2025, both html-minifier-terser and html-minifier seem unmaintained. **This project is currently under test.** If it seems maintainable to me, [Jens](https://meiert.com/), even without community support, the project will be updated and documented further. The following documentation largely matches the original project.) | ||
|
@@ -70,14 +70,15 @@ How does HTMLMinifier compare to other solutions — [HTML Minifier from Will Pe | |
| [W3C](https://www.w3.org/) | 51 | **36** | 42 | n/a | | ||
| [Wikipedia](https://en.wikipedia.org/wiki/Main_Page) | 114 | **100** | 107 | n/a | | ||
|
||
## Options Quick Reference | ||
## Options quick reference | ||
|
||
Most of the options are disabled by default. | ||
|
||
| Option | Description | Default | | ||
| --- | --- | --- | | ||
| `caseSensitive` | Treat attributes in case sensitive manner (useful for custom HTML tags) | `false` | | ||
| `collapseBooleanAttributes` | [Omit attribute values from boolean attributes](http://perfectionkills.com/experimenting-with-html-minifier#collapse_boolean_attributes) | `false` | | ||
| `customFragmentQuantifierLimit` | Set maximum quantifier limit for custom fragments to prevent ReDoS attacks | `200` | | ||
| `collapseInlineTagWhitespace` | Don’t leave any spaces between `display:inline;` elements when collapsing. Must be used in conjunction with `collapseWhitespace=true` | `false` | | ||
| `collapseWhitespace` | [Collapse white space that contributes to text nodes in a document tree](http://perfectionkills.com/experimenting-with-html-minifier#collapse_whitespace) | `false` | | ||
| `conservativeCollapse` | Always collapse to 1 space (never remove it entirely). Must be used in conjunction with `collapseWhitespace=true` | `false` | | ||
|
@@ -92,6 +93,7 @@ Most of the options are disabled by default. | |
| `ignoreCustomFragments` | Array of regexes that allow to ignore certain fragments, when matched (e.g. `<?php ... ?>`, `{{ ... }}`, etc.) | `[ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]` | | ||
| `includeAutoGeneratedTags` | Insert tags generated by HTML parser | `true` | | ||
| `keepClosingSlash` | Keep the trailing slash on singleton elements | `false` | | ||
| `maxInputLength` | Maximum input length to prevent ReDoS attacks (disabled by default) | `undefined` | | ||
| `maxLineLength` | Specify a maximum line length. Compressed output will be split by newlines at valid HTML split-points | | ||
| `minifyCSS` | Minify CSS in style elements and style attributes (uses [clean-css](https://github.com/jakubpawlowicz/clean-css)) | `false` (could be `true`, `Object`, `Function(text, type)`) | | ||
| `minifyJS` | Minify JavaScript in script elements and event attributes (uses [Terser](https://github.com/terser/terser)) | `false` (could be `true`, `Object`, `Function(text, inline)`) | | ||
|
@@ -154,6 +156,63 @@ Output of resulting markup (e.g. `<p>foo</p>`) | |
|
||
HTMLMinifier can’t know that original markup was only half of the tree; it does its best to try to parse it as a full tree and it loses information about tree being malformed or partial in the beginning. As a result, it can’t create a partial/malformed tree at the time of the output. | ||
|
||
## Security | ||
|
||
### ReDoS protection | ||
|
||
This minifier includes protection against regular expression denial of service (ReDoS) attacks: | ||
|
||
* Custom fragment quantifier limits: The `customFragmentQuantifierLimit` option (default: 200) prevents exponential backtracking by replacing unlimited quantifiers (`*`, `+`) with bounded ones in regular expressions. | ||
|
||
* Input length limits: The `maxInputLength` option allows you to set a maximum input size to prevent processing of excessively large inputs that could cause performance issues. | ||
|
||
* Enhanced pattern detection: The minifier detects and warns about various ReDoS-prone patterns including nested quantifiers, alternation with quantifiers, and multiple unlimited quantifiers. | ||
|
||
**Important:** When using custom `ignoreCustomFragments`, ensure your regular expressions don’t contain unlimited quantifiers (`*`, `+`) without bounds, as these can lead to ReDoS vulnerabilities. | ||
|
||
(Further improvements are needed. Contributions welcome.) | ||
|
||
#### Custom fragment examples | ||
|
||
**Safe patterns** (recommended): | ||
|
||
```javascript | ||
ignoreCustomFragments: [ | ||
/<%[\s\S]{0,1000}?%>/, // JSP/ASP with explicit bounds | ||
/<\?php[\s\S]{0,5000}?\?>/, // PHP with bounds | ||
/\{\{[^}]{0,500}\}\}/ // Handlebars without nested braces | ||
] | ||
``` | ||
|
||
**Potentially unsafe patterns** (will trigger warnings): | ||
|
||
```javascript | ||
ignoreCustomFragments: [ | ||
/<%[\s\S]*?%>/, // Unlimited quantifiers | ||
/<!--[\s\S]*?-->/, // Could cause issues with very long comments | ||
/\{\{.*?\}\}/, // Nested unlimited quantifiers | ||
/(script|style)[\s\S]*?/ // Multiple unlimited quantifiers | ||
] | ||
``` | ||
|
||
**Template engine configurations:** | ||
|
||
```javascript | ||
// Handlebars/Mustache | ||
ignoreCustomFragments: [/\{\{[\s\S]{0,1000}?\}\}/] | ||
|
||
// Liquid (Jekyll) | ||
ignoreCustomFragments: [/\{%[\s\S]{0,500}?%\}/, /\{\{[\s\S]{0,500}?\}\}/] | ||
|
||
// Angular | ||
ignoreCustomFragments: [/\{\{[\s\S]{0,500}?\}\}/] | ||
|
||
// Vue.js | ||
ignoreCustomFragments: [/\{\{[\s\S]{0,500}?\}\}/] | ||
``` | ||
Comment on lines
+200
to
+212
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing closing code fence for template engine examples Apply: ignoreCustomFragments: [/\{\{[\s\S]{0,500}?\}\}/]
// Vue.js
ignoreCustomFragments: [/\{\{[\s\S]{0,500}?\}\}/]
+ ``` 🤖 Prompt for AI Agents
|
||
|
||
**Important:** When using custom `ignoreCustomFragments`, the minifier automatically applies bounded quantifiers to prevent ReDoS attacks, but you can also write safer patterns yourself using explicit bounds. | ||
|
||
## Running benchmarks | ||
|
||
Benchmarks for minified HTML: | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing closing code fence for safe patterns block
The safe patterns example starts with “
javascript” but lacks the corresponding closing “
”, which will break Markdown rendering.Apply:
/\{\{[^}]{0,500}\}\}/ // Handlebars without nested braces ] + ```
🤖 Prompt for AI Agents