Skip to content

Commit 18ba38b

Browse files
committed
2 parents fb938e0 + f110293 commit 18ba38b

File tree

2 files changed

+25
-11
lines changed

2 files changed

+25
-11
lines changed

src/htmlParser/HtmlParser.js

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ Autolinker.htmlParser.HtmlParser = Autolinker.Util.extend( Object, {
3232
tagNameRegex = /[0-9a-zA-Z][0-9a-zA-Z:]*/,
3333
attrNameRegex = /[^\s"'>\/=\x00-\x1F\x7F]+/, // the unicode range accounts for excluding control chars, and the delete char
3434
attrValueRegex = /(?:"[^"]*?"|'[^']*?'|[^'"=<>`\s]+)/, // double quoted, single quoted, or unquoted attribute values
35-
nameEqualsValueRegex = attrNameRegex.source + '(?:\\s*=\\s*' + attrValueRegex.source + ')?'; // optional '=[value]'
35+
optionalAttrValueRegex = '(?:\\s*?=\\s*?' + attrValueRegex.source + ')?'; // optional '=[value]'
36+
37+
var getNameEqualsValueRegex = function(group) {
38+
return '(?=(' + attrNameRegex.source + '))\\' + group + optionalAttrValueRegex;
39+
};
3640

3741
return new RegExp( [
3842
// for <!DOCTYPE> tag. Ex: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">)
@@ -46,7 +50,8 @@ Autolinker.htmlParser.HtmlParser = Autolinker.Util.extend( Object, {
4650
// Either:
4751
// A. attr="value", or
4852
// B. "value" alone (To cover example doctype tag: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">)
49-
'(?:', nameEqualsValueRegex, '|', attrValueRegex.source + ')',
53+
// *** Capturing Group 2 - Pseudo-atomic group for attrNameRegex
54+
'(?:', getNameEqualsValueRegex(2), '|', attrValueRegex.source + ')',
5055
')*',
5156
'>',
5257
')',
@@ -56,10 +61,10 @@ Autolinker.htmlParser.HtmlParser = Autolinker.Util.extend( Object, {
5661
// All other HTML tags (i.e. tags that are not <!DOCTYPE>)
5762
'(?:',
5863
'<(/)?', // Beginning of a tag or comment. Either '<' for a start tag, or '</' for an end tag.
59-
// *** Capturing Group 2: The slash or an empty string. Slash ('/') for end tag, empty string for start or self-closing tag.
64+
// *** Capturing Group 3: The slash or an empty string. Slash ('/') for end tag, empty string for start or self-closing tag.
6065

6166
'(?:',
62-
commentTagRegex.source, // *** Capturing Group 3 - A Comment Tag's Text
67+
commentTagRegex.source, // *** Capturing Group 4 - A Comment Tag's Text
6368

6469
'|',
6570

@@ -68,7 +73,7 @@ Autolinker.htmlParser.HtmlParser = Autolinker.Util.extend( Object, {
6873
// to fix a regex time complexity issue seen with the
6974
// example in https://github.com/gregjacobs/Autolinker.js/issues/172
7075
'(?:',
71-
// *** Capturing Group 4 - The tag name for a tag without attributes
76+
// *** Capturing Group 5 - The tag name for a tag without attributes
7277
'(' + tagNameRegex.source + ')',
7378

7479
'\\s*/?', // any trailing spaces and optional '/' before the closing '>'
@@ -81,15 +86,16 @@ Autolinker.htmlParser.HtmlParser = Autolinker.Util.extend( Object, {
8186
// to fix a regex time complexity issue seen with the
8287
// example in https://github.com/gregjacobs/Autolinker.js/issues/172
8388
'(?:',
84-
// *** Capturing Group 5 - The tag name for a tag with attributes
89+
// *** Capturing Group 6 - The tag name for a tag with attributes
8590
'(' + tagNameRegex.source + ')',
8691

8792
'\\s+', // must have at least one space after the tag name to prevent ReDoS issue (issue #172)
8893

8994
// Zero or more attributes following the tag name
9095
'(?:',
9196
'(?:\\s+|\\b)', // any number of whitespace chars before an attribute. NOTE: Using \s* here throws Chrome into an infinite loop for some reason, so using \s+|\b instead
92-
nameEqualsValueRegex, // attr="value" (with optional ="value" part)
97+
// *** Capturing Group 7 - Pseudo-atomic group for attrNameRegex
98+
getNameEqualsValueRegex(7), // attr="value" (with optional ="value" part)
9399
')*',
94100

95101
'\\s*/?', // any trailing spaces and optional '/' before the closing '>'
@@ -127,9 +133,9 @@ Autolinker.htmlParser.HtmlParser = Autolinker.Util.extend( Object, {
127133

128134
while( ( currentResult = htmlRegex.exec( html ) ) !== null ) {
129135
var tagText = currentResult[ 0 ],
130-
commentText = currentResult[ 3 ], // if we've matched a comment
131-
tagName = currentResult[ 1 ] || currentResult[ 4 ] || currentResult[ 5 ], // The <!DOCTYPE> tag (ex: "!DOCTYPE"), or another tag (ex: "a" or "img")
132-
isClosingTag = !!currentResult[ 2 ],
136+
commentText = currentResult[ 4 ], // if we've matched a comment
137+
tagName = currentResult[ 1 ] || currentResult[ 5 ] || currentResult[ 6 ], // The <!DOCTYPE> tag (ex: "!DOCTYPE"), or another tag (ex: "a" or "img")
138+
isClosingTag = !!currentResult[ 3 ],
133139
offset = currentResult.index,
134140
inBetweenTagsText = html.substring( lastIndex, offset );
135141

tests/htmlParser/HtmlParserSpec.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,4 +309,12 @@ describe( "Autolinker.htmlParser.HtmlParser", function() {
309309
expectTextNode( nodes[ 0 ], 0, inputStr );
310310
} );
311311

312-
} );
312+
it( "should not freeze up the regular expression engine when presented with the input string in issue #204", function() {
313+
var inputStr = '<img src="http://example.com/Foo" border-radius:2px;moz-border-radius:2px;khtml-border-radius:2px;o-border-radius:2px;webkit-border-radius:2px;ms-border-radius:="" 2px; "=" " class=" ">',
314+
nodes = htmlParser.parse( inputStr );
315+
316+
expect( nodes.length ).toBe( 1 );
317+
expectTextNode( nodes[ 0 ], 0, inputStr );
318+
} );
319+
320+
} );

0 commit comments

Comments
 (0)