Skip to content

security: replace vulnerable regex with parser - #1223

Merged
styfle merged 5 commits into
markedjs:masterfrom
davisjam:FixLinkRegex
Apr 17, 2018
Merged

security: replace vulnerable regex with parser#1223
styfle merged 5 commits into
markedjs:masterfrom
davisjam:FixLinkRegex

Conversation

@davisjam

Copy link
Copy Markdown
Contributor

Problem: link regex was vulnerable
Solution: dedicated parser

Fixes: #1222

Problem: link regex was vulnerable
Solution: dedicated parser
Fixes: markedjs#1222
@davisjam

Copy link
Copy Markdown
ContributorAuthor

If this is merged, a new CI on #1220 should come back clean.

@UziTech

Copy link
Copy Markdown
Member

I feel like this complicates the code a lot. Is there no way we can do this with a constructed regex?

@davisjam

Copy link
Copy Markdown
ContributorAuthor

Is there no way we can do this with a constructed regex?

I spent about 30 minutes tinkering with regex variations but couldn't find one. The difficulty is that the "destination" can contain characters overlapping with the "title".

The relatively straightforward regex I replaced captured the specification (reasonably) appropriately, though it actually cheated in a few aspects (e.g. that a title can be opened by ' and closed by "). However, because the regex engine is backtracking-based, untrusted input cannot be evaluated safely by such a regex.

@styflestyfle mentioned this pull request Apr 16, 2018
Comment threadlib/marked.js Outdated
if (m = destinationRe.exec(destination)) {
// <destination> -> destination
var dest2 = m[1].trim();
destination = unwrapCarats(dest2);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

destination is assigned but never used. Is this intentional?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. Will fix.

@styfle
styfle requested review from UziTech and joshbruceApril 16, 2018 13:07
Comment threadlib/marked.js Outdated
.replace('label', inline._label)
.getRegex();

function unwrapCarats (str) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Carets?? Carat and carrot are different. :)

Angle brackets might be most appropriate if I'm reading the regex correctly.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha I was wondering if I was spelling that right. I'll switch to AngleBrackets anyway.

Comment threadlib/marked.js
.replace('label', inline._label)
.getRegex(),
link: {
exec: function (s) {

@joshbrucejoshbruceApr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth adding some doc blocks to introduce the why behind some of this...nothing too major, just to help those new to the code.

@davisjam

Copy link
Copy Markdown
ContributorAuthor

@styfle@joshbrucefbf93a8 addresses your comments.

Comment threadlib/marked.js Outdated
}

var destinationRe = /^(<?[\s\S]*>?)/;
if (m = destinationRe.exec(destination)) {

@styflestyfleApr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two if blocks are nearly identical. Can you make a common function for those? Something like this:

functiongetMatch(r,fullMatch){varm=r.exec(fullMatch[2]);if(m){vardest=unwrapAngleBrackets(m[1].trim());vartitle=m[2];return[fullMatch[0],fullMatch[1],dest,title];}}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, but not a deal breaker for my review.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment threadlib/marked.js Outdated
}
}

if (match) {

@styflestyfleApr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you flip this so that the if statement is smaller:

if (!match) {
return null;
}
// ... get dest and title here
return [dest, title];

Comment threadlib/marked.js Outdated
}

var fullMatch = generalLinkRe.exec(s);
if (fullMatch) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you flip this so that the if statement is smaller:

if (!fullMatch) {
return null;
}
// ... split and such here
return [fullMatch[0], text, destinationAndTitle[0], destinationAndTitle[1]];

Comment threadlib/marked.js Outdated
match = parsingRegexes[i].exec(destination);
if (match) {
dest = match[1];
title = match[2];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to assign dest and title here. Simply use match below.

@davisjam

Copy link
Copy Markdown
ContributorAuthor

@styfle Addressed your comments in 47f4388, thank you.

@styflestyfle left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@styfle

Copy link
Copy Markdown
Member

@UziTech Can you take a look at the code now? Any other reservations?

@UziTechUziTech left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason marked is so fast is because of the speed of regexes. I just don't want this type of thing to become the norm. There is no reason to have another slow markdown parser.

@styfle

Copy link
Copy Markdown
Member

@UziTech Good point.

Why is CI not running on this PR?

@UziTech

Copy link
Copy Markdown
Member

Not sure. That seems to be happening a lot lately. Maybe a travis bug?

@joshbruce

Copy link
Copy Markdown
Member

@UziTech: Good point. I think parsers like this should be done as a last resort unless we can demonstrate through benchmarking that there isn't a big performance difference...I don't think we should get in the position of asserting something is more performant in all cases.

The CI thing is weird. Is it possible that the key thing is causing issues?

@styfle

Copy link
Copy Markdown
Member

Looks like this failed lint: https://travis-ci.org/markedjs/marked/builds/367190715

@davisjam Can you try running lint and tests on your machine and fix any issues?

@davisjam

Copy link
Copy Markdown
ContributorAuthor

Lint passes now, that CI was on a previous version.

@styfle
styfle merged commit 5ab4ae3 into markedjs:masterApr 17, 2018
zhenalexfan pushed a commit to zhenalexfan/MarkdownHan that referenced this pull request Nov 8, 2021
* security: replace vulnerable regex with parser
Problem: link regex was vulnerable
Solution: dedicated parser
Fixes: markedjs#1222
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@davisjam@UziTech@styfle@joshbruce