CVE-2021-23346: Other (NVD-CWE-Other)
value: This affects the package html-parse-stringify before 2.0.1; all versions of package html-parse-stringify2. Sending certain input could cause one of the regular expressions that is used for parsing to backtrack, freezing the process.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 5.3
Published: 2021-03-04T17:15Z
Repository: https://github.com/HenrikJoreteg/html-parse-stringify
Language: JavaScript
Files changed: README.md, src/parse.js, test/parse.js
Commit date: 2021-02-26 10:37:18 -0800
Commit message: fixing reported ReDoS

Diff: diff --git a/README.md b/README.md
index b06e4dd..6386f1e 100644
--- a/README.md
+++ b/README.md
@@ -133,10 +133,11 @@ properties:
 - `voidElement` - `true` or `false`. Whether this tag is a known void element as defined by [spec](http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements).
 - `children` - it will still have a `children` array, but it will always be empty.
 
 ## changelog
 
+- `2.0.1` Addressing a reported regular expression denial of service issue reported by [Sam Sanoop](https://twitter.com/snoopysecurity) of [Snyk](https://snyk.io/) THANK YOU!. The issue was that sending certain input would cause one of the regular expressions we used to lock up and not finish, freezing the process. See the test that was added for details. To be clear, this lib wasn't meant for parsing non-well formed HTML. But, better safe than sorry! So we're fixing it.
 - `2.0.0` updated to more modern dependencies/build system. Switched to prettier, etc. No big feature differences, just new build system/project structure. Added support for top level text nodes thanks to @jperl. Added support for comments thanks to @pconerly.
 - `1.0.0 - 1.0.3` no big changes, bug fixes and speed improvements.
 
 ## credits
 
diff --git a/src/parse.js b/src/parse.js
index b862197..05f636d 100644
--- a/src/parse.js
+++ b/src/parse.js
@@ -1,6 +1,6 @@
-const tagRE = /<[a-zA-Z\-\!\/](?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])*>/g
+const tagRE = /<[a-zA-Z\-\!\/](?:"[^"]*"|'[^']*'|[^'">])*>/g
 import parseTag from './parse-tag'
 
 // re-used obj for quick lookups of components
 const empty = Object.create(null)
 
diff --git a/test/parse.js b/test/parse.js
index 2a9fa9e..930475b 100644
--- a/test/parse.js
+++ b/test/parse.js
@@ -764,5 +764,21 @@ test('simple speed sanity check', function (t) {
   console.log('min', min)
   console.log('avg', average)
 
   t.end()
 })
+
+test('ReDoS vulnerability reported by Sam Sanoop of Snyk', function (t) {
+  const start = Date.now()
+  // reported problematic string
+  HTML.parse(
+    "<!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!"
+  )
+  // other variant
+  HTML.parse(
+    '<!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""!'
+  )
+  const duration = Date.now() - start
+
+  t.ok(duration < 100, 'should not hang')
+  t.end()
+})

Vulnerable code: const tagRE = /<[a-zA-Z\-\!\/](?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])*>/g
Fixed code: - `2.0.1` Addressing a reported regular expression denial of service issue reported by [Sam Sanoop](https://twitter.com/snoopysecurity) of [Snyk](https://snyk.io/) THANK YOU!. The issue was that sending certain input would cause one of the regular expressions we used to lock up and not finish, freezing the process. See the test that was added for details. To be clear, this lib wasn't meant for parsing non-well formed HTML. But, better safe than sorry! So we're fixing it.
const tagRE = /<[a-zA-Z\-\!\/](?:"[^"]*"|'[^']*'|[^'">])*>/g

test('ReDoS vulnerability reported by Sam Sanoop of Snyk', function (t) {
  const start = Date.now()
  // reported problematic string
  HTML.parse(
    "<!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!"
  )
  // other variant
  HTML.parse(
    '<!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""!'
  )
  const duration = Date.now() - start

  t.ok(duration < 100, 'should not hang')
  t.end()
})