Repository: pulldown-cmark/pulldown-cmark
Pull request #854 (2024-03-01 20:29:17)

Use same limit for refdef as inline links
Fixes  #853

https://spec.commonmark.org/0.31.2/#link-reference-definitions

> A link reference definition consists of a link label, optionally preceded by up to three spaces of indentation, followed by a colon (`:`), optional spaces or tabs (including up to one line ending), a **link destination**, optional spaces or tabs (including up to one line ending), and an optional link title, which if it is present must be separated from the link destination by spaces or tabs. No further character may occur.

https://spec.commonmark.org/0.31.2/#link-destination

> A link destination consists of either
>
> - a sequence of zero or more characters between an opening `<` and a closing `>` that contains no line endings or unescaped `<` or `>` characters, or
> - a nonempty sequence of characters that does not start with `<`, does not include ASCII control characters or space character, and includes parentheses only if (a) they are backslash-escaped or (b) they are part of a balanced pair of unescaped parentheses. (Implementations may impose limits on parentheses nesting to avoid performance issues, but **at least three levels of nesting** should be supported.)



Patch:
diff --git a/pulldown-cmark/specs/regression.txt b/pulldown-cmark/specs/regression.txt
index 1aff9f1..5aca680 100644
--- a/pulldown-cmark/specs/regression.txt
+++ b/pulldown-cmark/specs/regression.txt
@@ -2501,3 +2501,13 @@ ISSUE 847
 <!p>
 <p>&lt;!p&gt;</p>
 ````````````````````````````````
+
+ISSUE 853
+
+```````````````````````````````` example
+[linky]
+
+[linky]: ((()))
+.
+<p><a href="((()))">linky</a></p>
+````````````````````````````````
diff --git a/pulldown-cmark/src/firstpass.rs b/pulldown-cmark/src/firstpass.rs
index 8093773..e2eb6c3 100644
--- a/pulldown-cmark/src/firstpass.rs
+++ b/pulldown-cmark/src/firstpass.rs
@@ -5,7 +5,7 @@ use std::cmp::max;
 use std::ops::Range;
 
 use crate::parse::{
-    scan_containers, Allocations, FootnoteDef, HeadingAttributes, Item, ItemBody, LinkDef,
+    scan_containers, Allocations, FootnoteDef, HeadingAttributes, Item, ItemBody, LinkDef, LINK_MAX_NESTED_PARENS,
 };
 use crate::strings::CowStr;
 use crate::tree::{Tree, TreeIndex};
@@ -1678,7 +1678,7 @@ impl<'a, 'b> FirstPass<'a, 'b> {
         let (mut i, _newlines) = self.scan_refdef_space(bytes, start)?;
 
         // scan link dest
-        let (dest_length, dest) = scan_link_dest(self.text, i, 1)?;
+        let (dest_length, dest) = scan_link_dest(self.text, i, LINK_MAX_NESTED_PARENS)?;
         if dest_length == 0 {
             return None;
         }
diff --git a/pulldown-cmark/src/parse.rs b/pulldown-cmark/src/parse.rs
index 0d6165e..054900e 100644
--- a/pulldown-cmark/src/parse.rs
+++ b/pulldown-cmark/src/parse.rs
@@ -40,7 +40,7 @@ use crate::{Alignment, CodeBlockKind, Event, HeadingLevel, LinkType, Options, Ta
 // The simplest countermeasure is to limit their depth, which is
 // explicitly allowed by the spec as long as the limit is at least 3:
 // https://spec.commonmark.org/0.29/#link-destination
-const LINK_MAX_NESTED_PARENS: usize = 5;
+pub(crate) const LINK_MAX_NESTED_PARENS: usize = 5;
 
 #[derive(Debug, Default, Clone, Copy)]
 pub(crate) struct Item {


Test patch:
diff --git a/pulldown-cmark/tests/suite/regression.rs b/pulldown-cmark/tests/suite/regression.rs
index b7d37fd..d5e1fad 100644
--- a/pulldown-cmark/tests/suite/regression.rs
+++ b/pulldown-cmark/tests/suite/regression.rs
@@ -2988,3 +2988,15 @@ fn regression_test_188() {
 
     test_markdown_html(original, expected, false, false, false);
 }
+
+#[test]
+fn regression_test_189() {
+    let original = r##"[linky]
+
+[linky]: ((()))
+"##;
+    let expected = r##"<p><a href="((()))">linky</a></p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}