Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Evan Klitzke (evan@eklitzke.org)
Albert Schwarzkopf (dev-maddy@quitesimple.org)
Ivans Saponenko (ivans.saponenko+maddy@gmail.com)
Lucian Smith (lpsmith@uw.edu)
Vardan Petrosyan (github.com/Vardan2009)
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ maddy uses [semver versioning](https://semver.org/).

## Upcoming

* ...
* ![**FIXED**](https://img.shields.io/badge/-FIXED-%23090) HTML escaping in code blocks and inline code

## version 1.6.0 2025-07-26

Expand Down
3 changes: 3 additions & 0 deletions include/maddy/codeblockparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <string>

#include "maddy/blockparser.h"
#include "maddy/common.h"

// -----------------------------------------------------------------------------

Expand Down Expand Up @@ -119,6 +120,8 @@ class CodeBlockParser : public BlockParser
return;
}

line = common::escapeHTML(line);

line += "\n";
}

Expand Down
42 changes: 42 additions & 0 deletions include/maddy/common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include <string>

namespace maddy {

namespace common {

inline std::string escapeHTML(const std::string& input)
{
std::string result;

for (char c : input)
{
switch (c)
{
case '&':
result += "&amp;";
break;
case '<':
result += "&lt;";
break;
case '>':
result += "&gt;";
break;
case '"':
result += "&quot;";
break;
case '\'':
result += "&apos;";
break;
default:
result += c;
}
}

return result;
}

} // namespace common

} // namespace maddy
19 changes: 17 additions & 2 deletions include/maddy/inlinecodeparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <regex>
#include <string>

#include "maddy/common.h"
#include "maddy/lineparser.h"

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -39,9 +40,23 @@ class InlineCodeParser : public LineParser
void Parse(std::string& line) override
{
static std::regex re("`([^`]*)`");
static std::string replacement = "<code>$1</code>";
std::smatch match;
std::string result;

line = std::regex_replace(line, re, replacement);
auto searchStart = line.cbegin();

while (std::regex_search(searchStart, line.cend(), match, re))
{
result.append(match.prefix());

result += "<code>" + common::escapeHTML(match[1].str()) + "</code>";

searchStart = match.suffix().first;
}

result.append(searchStart, line.cend());

line = std::move(result);
}
}; // class InlineCodeParser

Expand Down
29 changes: 29 additions & 0 deletions include/maddy/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,35 @@ class Parser
}
);
}

static std::string escapeHTML(const std::string& input)
{
std::string result;
for (char c : input)
{
switch (c)
{
case '&':
result += "&amp;";
break;
case '<':
result += "&lt;";
break;
case '>':
result += "&gt;";
break;
case '"':
result += "&quot;";
break;
case '\'':
result += "&apos;";
break;
default:
result += c;
}
}
return result;
}
}; // class Parser

// -----------------------------------------------------------------------------
Expand Down
22 changes: 22 additions & 0 deletions tests/maddy/test_maddy_codeblockparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,25 @@ TEST_F(MADDY_CODEBLOCKPARSER, ItShouldUseAnythingBehindFirstBackticksAsClass)

ASSERT_EQ(expected, outputString);
}

TEST_F(MADDY_CODEBLOCKPARSER, ItProperlyEscapesHTML)
{
std::vector<std::string> markdown = {
"```html", "<h1>Hello, World!</h1>", "```"
};

std::string expected =
"<pre class=\"html\"><code>\n&lt;h1&gt;Hello, "
"World!&lt;/h1&gt;\n</code></pre>";

for (std::string md : markdown)
{
cbParser->AddLine(md);
}
ASSERT_TRUE(cbParser->IsFinished());

std::stringstream& output(cbParser->GetResult());
const std::string& outputString = output.str();

ASSERT_EQ(expected, outputString);
}
14 changes: 14 additions & 0 deletions tests/maddy/test_maddy_inlinecodeparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,17 @@ TEST(MADDY_INLINECODEPARSER, ItReplacesMarkdownWithCodeHTML)

ASSERT_EQ(expected, text);
}

TEST(MADDY_INLINECODEPARSER, ItProperlyEscapesHTML)
{
std::string text =
"some text `<h1>Test</h1>` text testing `<span>it</span>` out";
std::string expected =
"some text <code>&lt;h1&gt;Test&lt;/h1&gt;</code> text testing "
"<code>&lt;span&gt;it&lt;/span&gt;</code> out";
auto emphasizedParser = std::make_shared<maddy::InlineCodeParser>();

emphasizedParser->Parse(text);

ASSERT_EQ(expected, text);
}
6 changes: 4 additions & 2 deletions tests/maddy/test_maddy_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ const std::string testHtml =
"<strong>hierarchy</strong><ol><li>and an "
"<em>ordered</em></li><li>list</li><li>directly</li></ol></li><li>inside</"
"li></ul></li></ul><pre><code>\nvar c = "
"'blub';\n</code></pre><blockquote><p>A Quote </p><p>With some <s>text</s> "
"&apos;blub&apos;;\n</code></pre><blockquote><p>A Quote </p><p>With some "
"<s>text</s> "
"blocks inside </p><ul><li>even a list </li><li>should be </li><li>possible "
"</li></ul></blockquote><p>And well <code>inline code</code> should also "
"work. </p><h2>Another Headline</h2><p>And not to forget <a "
Expand All @@ -102,7 +103,8 @@ const std::string testHtml2 =
"simple. </p><ul><li>an <i>unordered</i> list<ul><li>with some "
"<strong>hierarchy</strong><ol><li>and an "
"_ordered_</li><li>list</li><li>directly</li></ol></li><li>inside</li></ul></"
"li></ul><pre><code>\nvar c = 'blub';\n</code></pre><blockquote><p>A Quote "
"li></ul><pre><code>\nvar c = "
"&apos;blub&apos;;\n</code></pre><blockquote><p>A Quote "
"</p><p>With some <s>text</s> blocks inside </p><ul><li>even a list "
"</li><li>should be </li><li>possible </li></ul></blockquote><p>And well "
"<code>inline code</code> should also work. </p><h2>Another "
Expand Down