/* catcrafts.net Copyright (C) 2026 Catcrafts The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. */ // The Markdown renderer, which is the newest place untrusted text becomes // markup — post bodies are fetched from someone else's server, so every one of // these assertions is ultimately about the same thing: nothing in a body can // escape into the document. The structural cases are here too, because a parser // that silently drops a construct loses content invisibly. import std; import Catcrafts.Shared; using namespace Catcrafts; namespace { int failures = 0; void Check(bool ok, std::string_view what, std::string_view got = {}) { if (ok) return; ++failures; std::println(std::cerr, "FAIL: {}{}{}", what, got.empty() ? "" : " got: ", got); } void CheckEq(const Html::SafeHtml& actual, std::string_view expected, std::string_view what) { Check(actual.View() == expected, what, actual.View()); } } // namespace int main() { auto md = [](std::string_view text, std::span media = {}) { return Markdown::Render(text, media); }; // ── the guarantee ───────────────────────────────────────────────── CheckEq(md(""), "

<script>alert(1)</script>

", "md: html is text, never markup"); CheckEq(md("![x](javascript:alert(1))"), R"(
x
)", "md: javascript: image source neutralised"); CheckEq(md("[x](javascript:alert(1))"), R"(

x

)", "md: javascript: link neutralised"); CheckEq(md("a \" b & c"), "

a " b & c

", "md: quotes and ampersands escaped"); // A code span is verbatim text, and verbatim is exactly where an escaper // is most often forgotten. CheckEq(md("``"), "

<b>

", "md: code span escaped"); // ── blocks ──────────────────────────────────────────────────────── CheckEq(md(""), "", "md: empty body renders nothing"); CheckEq(md("plain text"), "

plain text

", "md: paragraph"); // Demotion by one: the page h1 is the post title, so a body's own top-level // heading is a section within it. CheckEq(md("# Heading"), "

Heading

", "md: h1 demoted to h2"); CheckEq(md("### Heading"), "

Heading

", "md: h3 demoted to h4"); CheckEq(md("#nothashtag"), "

#nothashtag

", "md: # without a space is not a heading"); CheckEq(md("> quoted"), R"(

quoted

)", "md: blockquote"); // The quoted lines are re-parsed, so a multi-paragraph quote keeps its // paragraphs instead of collapsing into one run-on line. CheckEq(md("> one\n>\n> two"), R"(

one

two

)", "md: blockquote keeps its paragraphs"); CheckEq(md("- a\n- b"), R"()", "md: unordered list"); CheckEq(md("1. a\n2. b"), R"(
  1. a
  2. b
)", "md: ordered list"); // A list resumed after an interrupting paragraph continues its numbering. // Without the start attribute the mini-guide in one of these posts renders // as steps 1-4 followed by steps 1, 2, 3. CheckEq(md("5. e"), R"(
  1. e
)", "md: ordered list keeps the number it announced"); // Blank lines between items are spacing, not seven one-item lists. CheckEq(md("1. a\n\n2. b"), R"(
  1. a
  2. b
)", "md: blank line inside a list does not split it"); CheckEq(md("---"), "
", "md: thematic break"); CheckEq(md("- - -"), "
", "md: spaced rule is not a one-item list"); // Whitespace in pasted terminal output is the content. CheckEq(md("```\n a\tb\n```"), "
  a\tb\n
", "md: fenced code is verbatim"); // An unterminated fence must not swallow the document into nothing. Check(md("```\nx").View().find("x") != std::string_view::npos, "md: unterminated fence still renders its content"); // ── tables ──────────────────────────────────────────────────────── CheckEq(md("| a | b |\n|---|---|\n| 1 | 2 |"), R"(
)" R"()" R"()" R"(
ab
12
)", "md: pipe table"); // The shape the carrier table in the camera post is actually written in: // leading pipe, no trailing one. Requiring both would leave it as prose. CheckEq(md("| Device | OS\n|---|---|\n| FP6 | pmOS |"), R"(
)" R"()" R"()" R"(
DeviceOS
FP6pmOS
)", "md: outer pipes are optional independently"); // The whole reason recognition needs two lines. These posts paste pipelines // and or-lists into prose constantly; none of them is a table. CheckEq(md("dmesg | grep ufs"), "

dmesg | grep ufs

", "md: a pipe in prose is not a table"); // A mismatched column count is far more likely to be prose with pipes in it // than a table whose author miscounted — so it stays prose, visibly odd. Check(md("| a | b |\n|---|").View().starts_with("

"), "md: delimiter row must agree about the column count", md("| a | b |\n|---|").View()); // `---` is a rule, as it is everywhere else in this file. A one-column // table has to write the pipe. CheckEq(md("| Device\n---"), "

| Device


", "md: a bare dash row is still a thematic break"); CheckEq(md("| a | b | c |\n|:---|:---:|---:|\n| 1 | 2 | 3 |"), R"(
)" R"()" R"()" R"()" R"()" R"()" R"()" R"(
abc
123
)", "md: alignment row moves the column, left needs no class"); // A short row is padded so the grid stays rectangular. Check(md("| a | b |\n|---|---|\n| 1 |").View().find("1") != std::string_view::npos, "md: a short row is padded to the header width", md("| a | b |\n|---|---|\n| 1 |").View()); // Cells are prose, so everything inline works in them — and everything // inline is still escaped in them. Check(md("| [x](https://e.example) | `` |\n|---|---|").View().find( R"(x)" R"(<b>)") != std::string_view::npos, "md: cells render inline markup and stay escaped", md("| [x](https://e.example) | `` |\n|---|---|").View()); // `\|` is how a cell contains a pipe. Check(md("| a \\| b | c |\n|---|---|").View().find("a | bc") != std::string_view::npos, "md: an escaped pipe is cell content, not a cell boundary", md("| a \\| b | c |\n|---|---|").View()); // The GPU comparison in one of these posts is a table inside a quote. Check(md("> | a | b |\n> |---|---|\n> | 1 | 2 |").View().starts_with( R"(
)"), "md: a table inside a blockquote is still a table", md("> | a | b |\n> |---|---|\n> | 1 | 2 |").View()); // A table under a prose line with no blank line between is still a table: // absorbing it into the paragraph is the exact failure this all fixes. Check(md("text\n| a | b |\n|---|---|").View().starts_with("

text

after

"), "md: a line with no pipe ends the table", md("| a | b |\n|---|---|\n| 1 | 2 |\nafter").View()); // A list marker wins over a table: a line that opens one is a list item. Check(md("- a | b\n|---|---|").View().starts_with("bold

", "md: strong"); CheckEq(md("*em*"), "

em

", "md: emphasis"); CheckEq(md("2 * 3 * 4"), "

2 * 3 * 4

", "md: spaced asterisks stay literal"); // , not GFM's : nothing was removed from this document, and the one // body that uses this is striking a joke through for effect. CheckEq(md("~~struck~~"), "

struck

", "md: strikethrough"); CheckEq(md("~~**both**~~"), "

both

", "md: strikethrough nests"); // A lone tilde is a home directory or an approximation, never a delimiter. CheckEq(md("~/.local and ~5 minutes"), "

~/.local and ~5 minutes

", "md: a single tilde strikes nothing"); CheckEq(md("a ~~ b ~~ c"), "

a ~~ b ~~ c

", "md: spaced tildes stay literal"); // Underscores are deliberately inert: these posts paste kernel symbol // names into prose, and italicising half of one is worse than not // italicising a word that used the underscore form. CheckEq(md("kworker/u16:8-qc_ufs_qos_swq"), "

kworker/u16:8-qc_ufs_qos_swq

", "md: underscores are not emphasis"); CheckEq(md("\\*literal\\*"), "

*literal*

", "md: backslash escape"); CheckEq(md("[label](https://x.example/y)"), R"(

label

)", "md: link"); // Bare addresses are pasted constantly in these posts; leaving them inert // would strip most of the outbound value out of the page. CheckEq(md("see https://x.example/y"), R"(

see https://x.example/y

)", "md: bare URL autolinked"); // ── embedded media ──────────────────────────────────────────────── // A paragraph that is nothing but images becomes the same media block the // cards use, rather than a

of pictures. CheckEq(md("![a](/media/x.webp)"), R"(

a
)", "md: image-only paragraph is a media block"); Check(md("text ![a](/media/x.webp)").View().starts_with("

text media; PostMedia img; img.src = "/media/x.webp"; img.kind = "image"; img.avif = "/media/x.avif"; img.fallback = "/media/x.png"; img.width = 800; img.height = 600; media.push_back(img); PostMedia vid; vid.src = "/media/v.mp4"; vid.kind = "video"; vid.poster = "/media/v.poster.webp"; vid.fallback = "/media/v.h264.mp4"; vid.width = 1080; vid.height = 1920; media.push_back(vid); const auto out = md("![a](/media/x.webp)", media); Check(out.View().find(R"(width="800" height="600")") != std::string_view::npos, "md: inline image carries its dimensions", out.View()); // Routed through :Media, so a body image gets the same format ladder a // card image does rather than a second, plainer implementation. Check(out.View().find(R"()") != std::string_view::npos && out.View().find(R"(src="/media/x.png")") != std::string_view::npos, "md: inline image gets the avif/png ladder", out.View()); // An inline video gets the same treatment a headline one does, // fallback source and all. const auto vout = md("![](/media/v.mp4)", media); Check(vout.View().find(R"(poster="/media/v.poster.webp")") != std::string_view::npos && vout.View().find("codecs=av01") != std::string_view::npos && vout.View().find(R"( > > > > > > > deep").View().empty(), "md: over-deep nesting terminates"); Check(!md("|||\n|||").View().empty(), "md: degenerate table terminates"); Check(!md("|---|---|").View().empty(), "md: a lone delimiter row terminates"); Check(!md("| a |\n| - |\n|").View().empty(), "md: a ragged table terminates"); if (failures != 0) { std::println(std::cerr, "{} check(s) failed", failures); return 1; } return 0; }