Repository: pulldown-cmark/pulldown-cmark (rust)

Issue:
[fuzz result] spurious inline HTML
Events from pulldown-cmark:

```rust
"<\\!p>" -> [
  Start(Paragraph)
    InlineHtml(Borrowed("<\\!p>"))
  End(Paragraph)
]
```

Events from commonmark.js:

```rust
"<\\!p>" -> [
  Start(Paragraph)
    Text(Boxed("<!p>"))
  End(Paragraph)
]
```

commonmark.js is in the right here. It matches most other implementations, and [the inline tag grammar](https://spec.commonmark.org/0.31.2/#raw-html) doesn't seem to allow this.


Pull request description:
Do not look for HTML tags that start with backslash
Because of the way the tree is used in HTML scanning, the backslash gets skipped.

Fixes #847

Patch:
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
deleted file mode 100644
index 2c68c45..0000000
--- a/.github/workflows/rust.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: Rust
-
-on:
-  - push
-  - pull_request
-
-env:
-  CARGO_TERM_COLOR: always
-
-jobs:
-  build:
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: actions/checkout@v4
-    - name: Install Rust
-      working-directory: pulldown-cmark
-      run: rustup default 1.70.0
-    - name: Cargo build
-      working-directory: pulldown-cmark
-      run: cargo build --verbose --all
-    - name: Cargo test
-      working-directory: pulldown-cmark
-      run: cargo test --verbose --all
-    - name: Cargo test with simd feature enabled
-      working-directory: pulldown-cmark
-      run: cargo test --all --features=simd,gen-tests
-    - name: Cargo test with serde feature enabled
-      working-directory: pulldown-cmark
-      run: cargo test --all --features=serde
-    - name: Cargo test without default features
-      working-directory: pulldown-cmark
-      run: cargo test --all --no-default-features
-    - name: Test for superlinear time regressions
-      working-directory: fuzzer
-      run: cargo run --release -- --regressions
diff --git a/README.md b/README.md
index 5754464..4ba9c8f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # pulldown-cmark
 
-[![Tests](https://github.com/pulldown-cmark/pulldown-cmark/actions/workflows/rust.yml/badge.svg)](https://github.com/pulldown-cmark/pulldown-cmark/actions/workflows/rust.yml)
+[![Build Status](https://dev.azure.com/raphlinus/pulldown-cmark/_apis/build/status/pulldown-cmark-CI?branchName=master)](https://dev.azure.com/raphlinus/pulldown-cmark/_build/latest?definitionId=2&branchName=master)
 [![Docs](https://docs.rs/pulldown-cmark/badge.svg)](https://docs.rs/pulldown-cmark)
 [![Crates.io](https://img.shields.io/crates/v/pulldown-cmark.svg?maxAge=2592000)](https://crates.io/crates/pulldown-cmark)
 
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 0000000..4d5acbe
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,25 @@
+pool:
+  vmImage: 'ubuntu-20.04'
+
+steps:
+  - script: rustup default 1.70.0
+    workingDirectory: pulldown-cmark
+    displayName: Install rust
+  - script: cargo build --all
+    workingDirectory: pulldown-cmark
+    displayName: Cargo build
+  - script: cargo test --all
+    workingDirectory: pulldown-cmark
+    displayName: Cargo test
+  - script: cargo test --all --features=simd,gen-tests
+    workingDirectory: pulldown-cmark
+    displayName: Cargo test with simd feature enabled
+  - script: cargo test --all --features=serde
+    workingDirectory: pulldown-cmark
+    displayName: Cargo test with serde feature enabled
+  - script: cargo test --all --no-default-features
+    workingDirectory: pulldown-cmark
+    displayName: Cargo test without default features    
+  - script: cargo run --release -- --regressions
+    workingDirectory: fuzzer
+    displayName: Test for superlinear time regressions
diff --git a/pulldown-cmark/specs/regression.txt b/pulldown-cmark/specs/regression.txt
index 7290d58..410a032 100644
--- a/pulldown-cmark/specs/regression.txt
+++ b/pulldown-cmark/specs/regression.txt
@@ -2423,3 +2423,14 @@ ISSUE 840
 .
 <p><a href="test" title="&#39;">link</a></p>
 ````````````````````````````````
+
+ISSUE 847
+
+```````````````````````````````` example
+<!p>
+
+<\!p>
+.
+<!p>
+<p>&lt;!p&gt;</p>
+````````````````````````````````
diff --git a/pulldown-cmark/src/firstpass.rs b/pulldown-cmark/src/firstpass.rs
index 71607e3..7e5e96b 100644
--- a/pulldown-cmark/src/firstpass.rs
+++ b/pulldown-cmark/src/firstpass.rs
@@ -784,7 +784,7 @@ impl<'a, 'b> FirstPass<'a, 'b> {
                     begin_text = ix + count;
                     LoopInstruction::ContinueAndSkip(count - 1)
                 }
-                b'<' => {
+                b'<' if bytes.get(ix + 1) != Some(&b'\\') => {
                     // Note: could detect some non-HTML cases and early escape here, but not
                     // clear that's a win.
                     self.tree.append_text(begin_text, ix, backslash_escaped);


Test patch:
diff --git a/pulldown-cmark/tests/suite/regression.rs b/pulldown-cmark/tests/suite/regression.rs
index 1d0dac0..836106e 100644
--- a/pulldown-cmark/tests/suite/regression.rs
+++ b/pulldown-cmark/tests/suite/regression.rs
@@ -2890,3 +2890,16 @@ fn regression_test_182() {
 
     test_markdown_html(original, expected, false, false, false);
 }
+
+#[test]
+fn regression_test_183() {
+    let original = r##"<!p>
+
+<\!p>
+"##;
+    let expected = r##"<!p>
+<p>&lt;!p&gt;</p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}