Automattic / Automattic/jetpack

codeblock_preserve() incorrectly detects and encodes Markdown code fences

Open
#38,444 1 comment 0 reactions 0 assignees View on GitHub
[Feature] Markdown [Plugin] Jetpack [Pri] Low [Status] Stale Bug Triaged
Dominant language
PHP
Stars
1.8k
Forks
898
Avg merge
1d 18h
Merged PRs (30d)
774

Description

### Impacted plugin

Jetpack

### Quick summary

When the JetPack setting "Write in Markdown" is enabled, the function `codeblock_preserve()` in
`/wp-content/plugins/jetpack/_inc/lib/markdown/gfm.php` incorrectly assumes all occurrences of triple backticks or tildes are code fences and HTML-encodes the contents, including when the backticks/tildes are included in the HTML output of the Markdown parser in Markdown blocks, or when they are in code blocks in a Custom HTML block which the Markdown parser does not even process.

I encounter this every time I use a code tag in a Custom HTML block or a code fence in a Markdown block to demonstrate how to write a Markdown code fence. Whether I write the HTML manually in a Custom HTML block, or write it as Markdown in a Markdown block for the parser to convert, the end result is the same: The HTML post content being saved includes PRE/CODE tags that happen to contain backticks or tildes, which at this point is _not_ Markdown. Once `codeblock_preserve()` is invoked, it incorrectly assumes the backticks/tildes are Markdown code fences, and HTML-encodes the contents. Not only that, but it also misjudges where the code fences are opened and closed, and actually ends up encoding the post content between the two code fences.

Consider this HTML post content before it is processed by codeblock_preserve():
![01 - HTML Before Butchered](https://github.com/user-attachments/assets/cb1b9346-491d-4920-8631-cc96d774e601)

This is the same post content after it was processed by codeblock_preserve():
![03 - HTML After Butchered - Annotated](https://github.com/user-attachments/assets/3237b6b2-3c5b-4323-a31c-71c0f24e919f)
Everything between triple tilde # 2 and # 3 is HTML-encoded because codeblock_preserve() thinks it is a code fence. It does this even when this HTML content is included as a Custom HTML block, where Markdown does not get parsed, or when this HTML is the output of the Markdown parser, at which point the Markdown has already been parsed. (It ignores triple tilde # 1 because it knows a code fence must start on a new line, and it ignores triple tilde # 4 because there's no associated end tag.)

### Steps to reproduce

1.) Enable the Jetpack setting "Write in Markdown"

2.) Choose one of the following 3 options to generate content that demonstrates the issue:

Option 1: In a Markdown block, write the following Markdown which demonstrates how to use a code fence in Markdown. In the CommonMark spec, this is accomplished by using a code fence with 4+ backticks/tildes that surround a code fence with 3 backticks/tildes. The inner code fence is like any other code, so it should not be seen as Markdown or receive any special processing. It should be displayed as-is inside HTML pre/code tags, like any other code.

`````
### A code tag that shows us how to write a code fence:

````
```
console.log('1');
```
````

### Another code tag that shows us how to write a code fence:

````
```
console.log('2');
```
````
`````

Option 2: In a Custom HTML block, write the following HTML which demonstrates how to use a code fence in Markdown. This HTML is the same HTML generated by the Markdown parser when it parses the Markdown in Option 1. (Obviously before it is processed by `codeblock_preserve()`.)

````

A code tag that shows us how to write a code fence:


```

console.log('1');
```

Another code tag that shows us how to write a code fence:


```

console.log('2');
```

````

Option 3: In the Classic editor, create a post with the Markdown from Option 1 or the HTML from Option 2.

3.) Save the post.

### A clear and concise description of what you expected to happen.

The HTML saved in post_content for the associated Custom HTML block or Markdown block should be as follows, which should match the input under Option B under "Steps to reproduce":

````

A code tag that shows us how to write a code fence:


```

console.log('1');
```

Another code tag that shows us how to write a code fence:


```

console.log('2');
```

````

And this is how the post should appear when viewed:

![05 - HTML OutputVisual](https://github.com/user-attachments/assets/f90f99e2-4c90-4c9f-9198-899307d96689)

### What actually happened

Parts of the HTML are encoded when they shouldn't be.

The HTML saved in post_content for the associated Custom HTML block or Markdown block is as follows:

````

A code tag that shows us how to write a code fence:


```

console.log('1');
```
</code></pre>
<h3>Another code tag that shows us how to write a code fence:</h3>
<pre><code>```
console.log('2');
```

````

And this is how the post actually appears when viewed:

![06 - Butchered HTML Output Visual](https://github.com/user-attachments/assets/1bb3623d-5804-409c-b3b6-14782c50ece2)

### Impact

Some (< 50%)

### Available workarounds?

Yes, difficult to implement

### Platform (Simple and/or Atomic)

_No response_

### Logs or notes

_No response_

Contributor guide

Open the contributing guide

Research direction

Read /wp-content/plugins/jetpack/_inc/lib/markdown/gfm.php, focusing on codeblock_preserve(), then reproduce the behavior with the three provided Markdown and HTML options. Done means triple backticks or tildes inside existing PRE/CODE output are left unencoded, while actual Markdown fences still receive their intended processing.

Written by the indexing model from the issue text.

Assessment

Tech stack
markdown, php
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.