Markdown
Markdown extensions available in challenge descriptions, home page content, and sponsor descriptions.
rCTF renders Markdown in challenge descriptions, the home page content, and sponsor descriptions. marked does the rendering with two custom extensions (alerts and timer), and the output then runs through DOMPurify for sanitization before it hits the page.
The renderer is defined in apps/web/src/lib/utils/markdown.ts and hydrated by apps/web/src/lib/components/markdown.svelte.
Alerts#
rCTF supports GitHub-style blockquote alerts. The opening line is a blockquote containing [!TYPE], and any following blockquote lines form the alert body. The body is parsed as Markdown (except for CONNECTION, see below).
> [!NOTE] The flag format is `rctf{...}`.
> [!WARNING] Brute-forcing the endpoint will result in a rate limit.Six alert types are recognized. Anything else (e.g. > [!INFO]) falls back to a plain blockquote.
The trigger keyword is case-insensitive. Indentation and additional blockquote lines follow standard Markdown blockquote rules.
Warning (Visual reference only)
The docs site renders alerts with its own component, which looks different from rCTF’s runtime alert styling. The behaviour described here is what challenge authors actually see in the rCTF frontend.
Connection callout#
> [!CONNECTION] is a real, distinct alert type and not just a styled NOTE. It’s built for the Remote field of a challenge, which holds a single line containing a connection string or URL.
> [!CONNECTION] `nc challs.example.com 1337`
> [!CONNECTION] https://web-chall.example.comUnlike the other alerts, the connection body is not parsed as Markdown. Wrapping backticks are stripped, and the content is rendered as either:
- A clickable link, if it starts with
http://orhttps://. - A monospaced code block, otherwise.
A copy button sits next to the value so players can paste it straight into a terminal.
Timer#
The <timer /> inline element renders a live countdown for the competition.
The CTF begins in <timer />. Good luck!The element takes no attributes. It always targets the global CTF schedule (startTime / endTime from the runtime client config). Behaviour:
- Before the CTF starts, counts down to the start time and is labelled “CTF starts in”.
- After the start time, counts down to the end time and is labelled “CTF ends in”.
- After the end time, shows “The CTF is over.”
- If the event is archived, shows “The CTF is archived.”
The timer updates once per second on the client.
Note (No per-element target)
rCTF doesn’t support <timer to="..."> or <timer until="...">. The countdown target is always the global competition schedule. If you need per-challenge deadlines, spell them out in plain text inside the description.
Standard Markdown#
The renderer uses marked with its default options, so no GFM plugin is registered. CommonMark features (headings, lists, emphasis, links, images, fenced code blocks, inline code, blockquotes) plus marked’s built-in tables and autolinks all pass through. There are no task lists, footnotes, or strikethrough.
# Setup
Download the [source archive](./source.tar.gz) and run:
```bashdocker compose up```
The service listens on port `8080`.For the full list of what CommonMark supports, see the CommonMark spec.
Sanitization#
After parsing, output passes through DOMPurify before insertion. Author-facing implications:
<script>tags, inline event handlers (onclick=,onerror=, etc.), and dangerous protocols are stripped. Inline JavaScript will never execute.- Most HTML tags from DOMPurify’s default profile are allowed (e.g.
<details>,<summary>,<sub>,<sup>,<kbd>). - The only custom attributes preserved beyond defaults are
data-alert,data-type,data-content, anddata-timer. The renderer uses them as hydration markers for the alert and timer extensions. Writing these attributes by hand has no effect, since only the extensions emit them.
If you need richer interactivity than these extensions cover, add it in the frontend code, not through embedded HTML in a description.