Skip to content

[Webhook] Keep the HTML of the {content} placeholder intact - #562

Merged
Alkarex merged 1 commit into
FreshRSS:mainfrom
NopeCharon:webhook-content-placeholder
Oct 27, 2026
Merged

Alkarex merged 1 commit into
FreshRSS:mainfrom
NopeCharon:webhook-content-placeholder

Conversation

@NopeCharon

Copy link
Copy Markdown
Contributor

Summary

  • insert {content} as is in custom webhook bodies, without running htmlspecialchars_decode() on it: FreshRSS_Entry::content() is already HTML, so decoding turned escaped text into markup (an unclosed <b> from &lt;b&gt;, live <script> tags from escaped code samples, bare &, broken double-quoted attributes)
  • the plain-text placeholders ({title}, {author}, {url}, {feed_name}, …) keep being decoded, which is correct for them
  • README: replace the {date}, {date_timestamp} and {thumbnail_url} placeholders, which never existed and were sent literally, with the actual {date_published}, {date_received}, {date_modified} and {date_user_modified} (as listed on the configuration page), and update the examples
  • bump the extension version to 0.2.6

Fixes #560

Validation

End to end, with FreshRSS edge through its CLI (do-install, import-for-user, actualize-user), a local feed, and a local endpoint that records the requests it receives. The webhook body is {"title": "{title}", "content": "{content}"}, and the feed item has the description <p>Use the &lt;b&gt; tag &amp; <a href="…" title="say &quot;hi&quot;">this link</a></p>.

Before:

{"title":"Tom & Jerry","content":"<p>Use the <b> tag & <a href=\"http://127.0.0.1:8081/x\" title='say \"hi\"'>this link</a></p>"}

After:

{"title":"Tom & Jerry","content":"<p>Use the &lt;b&gt; tag &amp; <a href=\"http://127.0.0.1:8081/x\" title='say \"hi\"'>this link</a></p>"}

Also ran composer run-script php-lint, phtml-lint, phpcs, test-image-proxy and phpstan (with ../FreshRSS), plus markdownlint-cli2 on the README.

The `{content}` placeholder is documented as the article content in HTML
format, but it was passed through `htmlspecialchars_decode()` like the
plain-text placeholders. `FreshRSS_Entry::content()` already returns
HTML, so decoding it turned escaped text into markup: an article about
the `&lt;b&gt;` tag produced an unclosed `<b>` element, escaped code
samples such as `&lt;script&gt;` became live tags, `&amp;` became a bare
`&`, and `&quot;` could end a double-quoted attribute early.

Insert the content as is. The plain-text placeholders (`{title}`,
`{author}`, `{url}`, ...) are still decoded, which is correct for them.

Also fix the README, which documented `{date}`, `{date_timestamp}` and
`{thumbnail_url}` placeholders that have never existed in the extension
and are sent literally. Document `{date_published}`, `{date_received}`,
`{date_modified}` and `{date_user_modified}` instead, like the
configuration page does, and use them in the examples.

Bump the extension version to 0.2.6.

Fixes FreshRSS#560
@Alkarex

Alkarex commented Oct 27, 2026

Copy link
Copy Markdown
Member

Hum, I am not sure this is a good idea. I do not see it as good practice to have HTML-encoded content in JSON, where plain HTML is possible.
Do you have an example of standard or well-known API that expects this format?

@NopeCharon

Copy link
Copy Markdown
Contributor Author

The payload still carries plain HTML (real <p>, <a> tags), this only stops decoding the entities that are part of that HTML, like &lt;b&gt; or &amp; in the article text. Decoding them yields different, broken HTML (an unclosed <b>, live <script> from escaped code samples), so any receiver that renders {content} as HTML gets the article as FreshRSS shows it.

@Alkarex

Alkarex commented Oct 27, 2026

Copy link
Copy Markdown
Member

You are right. Sorry, I had read a bit too fast

@Alkarex
Alkarex merged commit 67f5878 into FreshRSS:main Oct 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Webhook] {content} placeholder turns escaped text into HTML markup

2 participants