Repository: pulldown-cmark/pulldown-cmark
Pull request #1004 (2025-01-11 01:07:09)

Fix panic when symbols are present in wikilink before pipe
This probably shouldn't panic:

```
[frostu8@SHAFT pulldown-cmark]$ echo "[[Wiki<|Link]]" | cargo run -- -W
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s
     Running `/home/frostu8/pulldown-cmark/target/debug/pulldown-cmark -W`
thread 'main' panicked at pulldown-cmark/src/parse.rs:2270:57:
begin <= end (8 <= 7) when slicing `[[Wiki<|Link]]
`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
<p><a href="Wiki%3C">
```

This PR fixes this bug, adds a regression test and refactors [the problematic code](https://github.com/frostu8/pulldown-cmark/blob/ec2ace53bbce81602b52a5ce8964c711bf971fcd/pulldown-cmark/src/scanners.rs#L935-L947) for readability. I knew this smelled and I wanted to get this out before a version bump.


Patch:
diff --git a/pulldown-cmark/specs/regression.txt b/pulldown-cmark/specs/regression.txt
index f7ccb68..8d55c80 100644
--- a/pulldown-cmark/specs/regression.txt
+++ b/pulldown-cmark/specs/regression.txt
@@ -2782,3 +2782,9 @@ the trailing space after the &gt; should be stripped
 <h1 class="bar">the trailing space after the &gt; should be stripped
 &gt;</h1>
 ````````````````````````````````
+
+```````````````````````````````` example_wikilinks
+[[Wiki<|Link]]
+.
+<p><a href="Wiki%3C">Link</a></p>
+````````````````````````````````
diff --git a/pulldown-cmark/src/parse.rs b/pulldown-cmark/src/parse.rs
index 1a55c95..3eb4080 100644
--- a/pulldown-cmark/src/parse.rs
+++ b/pulldown-cmark/src/parse.rs
@@ -923,7 +923,7 @@ impl<'input, F: BrokenLinkCallback<'input>> Parser<'input, F> {
                     if let Some(body_node) = body_node {
                         // break node so passes can actually format
                         // the display text
-                        self.tree[body_node].item.start = start_ix + rest;
+                        self.tree[body_node].item.start = rest;
                         Some((true, body_node, wikitext))
                     } else {
                         None
diff --git a/pulldown-cmark/src/scanners.rs b/pulldown-cmark/src/scanners.rs
index e872e1d..7359979 100644
--- a/pulldown-cmark/src/scanners.rs
+++ b/pulldown-cmark/src/scanners.rs
@@ -933,12 +933,13 @@ pub(crate) fn scan_entity(bytes: &[u8]) -> (usize, Option<CowStr<'static>>) {
 }
 
 pub(crate) fn scan_wikilink_pipe(data: &str, start_ix: usize, len: usize) -> Option<(usize, &str)> {
-    let bytes = &data.as_bytes()[start_ix..];
-    let mut i = 0;
+    let bytes = data.as_bytes();
+    let end_ix = std::cmp::min(start_ix + len, bytes.len());
+    let mut i = start_ix;
 
-    while i < bytes.len() && i < len {
+    while i < end_ix {
         if bytes[i] == b'|' {
-            return Some((i + 1, &data[start_ix..start_ix + i]));
+            return Some((i + 1, &data[start_ix..i]));
         }
         i += 1;
     }


Test patch:
diff --git a/pulldown-cmark/tests/suite/regression.rs b/pulldown-cmark/tests/suite/regression.rs
index 118d89b..3839734 100644
--- a/pulldown-cmark/tests/suite/regression.rs
+++ b/pulldown-cmark/tests/suite/regression.rs
@@ -3315,3 +3315,13 @@ fn regression_test_207() {
 
     test_markdown_html(original, expected, false, false, false, false, false);
 }
+
+#[test]
+fn regression_test_208() {
+    let original = r##"[[Wiki<|Link]]
+"##;
+    let expected = r##"<p><a href="Wiki%3C">Link</a></p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false, false, true);
+}