@@ -554,9 +554,68 @@ inline.normal = merge({}, inline);
554
554
inline . pedantic = merge ( { } , inline . normal , {
555
555
strong : / ^ _ _ (? = \S ) ( [ \s \S ] * ?\S ) _ _ (? ! _ ) | ^ \* \* (? = \S ) ( [ \s \S ] * ?\S ) \* \* (? ! \* ) / ,
556
556
em : / ^ _ (? = \S ) ( [ \s \S ] * ?\S ) _ (? ! _ ) | ^ \* (? = \S ) ( [ \s \S ] * ?\S ) \* (? ! \* ) / ,
557
- link : edit ( / ^ ! ? \[ ( l a b e l ) \] \( \s * < ? ( [ \s \S ] * ?) > ? (?: \s + ( [ ' " ] [ \s \S ] * ?[ ' " ] ) ) ? \s * \) / )
558
- . replace ( 'label' , inline . _label )
559
- . getRegex ( ) ,
557
+ /* Original link re: /^!?\[(label)\]\(\s*<?([\s\S]*?)>?(?:\s+(['"][\s\S]*?['"]))?\s*\)/
558
+ * This captures the spec reasonably well but is vulnerable to REDOS.
559
+ * Instead we use a custom parser that follows the RegExp.exec semantics. */
560
+ link : {
561
+ exec : function ( s ) {
562
+ // [TEXT](DESTINATION)
563
+ var generalLinkRe = edit ( / ^ ! ? \[ ( l a b e l ) \] \( ( .* ?) \) / )
564
+ . replace ( 'label' , inline . _label )
565
+ . getRegex ( ) ;
566
+
567
+ // destination: DESTINATION from generalLinkRe
568
+ // returns [destination, title]: no angle-brackets on destination, no quotes on title
569
+ function splitIntoDestinationAndTitle ( destination ) {
570
+ function unwrapAngleBrackets ( str ) {
571
+ if ( str . match ( / ^ < .* > $ / ) ) {
572
+ str = str . slice ( 1 , - 1 ) ;
573
+ }
574
+ return str ;
575
+ }
576
+
577
+ // Valid DESTINATIONs, in decreasing specificity.
578
+ var destinationAndTitleRe = / ^ ( [ ^ ' " ( ] * [ ^ \s ] ) \s + ( [ ' " ( ] .* [ ' " ) ] ) / ;
579
+ var destinationRe = / ^ ( < ? [ \s \S ] * > ? ) / ;
580
+ var parsingRegexes = [ destinationAndTitleRe , destinationRe ] ;
581
+
582
+ var match = false ;
583
+ for ( var i = 0 ; i < parsingRegexes . length ; i ++ ) {
584
+ match = parsingRegexes [ i ] . exec ( destination ) ;
585
+ if ( match ) {
586
+ break ;
587
+ }
588
+ }
589
+
590
+ if ( ! match ) {
591
+ return null ;
592
+ }
593
+
594
+ var dest = match [ 1 ] ;
595
+ var title = match [ 2 ] || '' ; // Not all parsingRegexes have 2 groups.
596
+
597
+ // Format dest.
598
+ dest = dest . trim ( ) ;
599
+ dest = unwrapAngleBrackets ( dest ) ;
600
+
601
+ return [ dest , title ] ;
602
+ }
603
+
604
+ var fullMatch = generalLinkRe . exec ( s ) ;
605
+ if ( ! fullMatch ) {
606
+ return null ;
607
+ }
608
+
609
+ var text = fullMatch [ 1 ] ;
610
+ var destination = fullMatch [ 2 ] ;
611
+
612
+ var destinationAndTitle = splitIntoDestinationAndTitle ( destination ) ;
613
+ if ( ! destinationAndTitle ) {
614
+ return null ;
615
+ }
616
+ return [ fullMatch [ 0 ] , text , destinationAndTitle [ 0 ] , destinationAndTitle [ 1 ] ] ;
617
+ }
618
+ } ,
560
619
reflink : edit ( / ^ ! ? \[ ( l a b e l ) \] \s * \[ ( [ ^ \] ] * ) \] / )
561
620
. replace ( 'label' , inline . _label )
562
621
. getRegex ( )
0 commit comments