Markdown

Markdown extensions available in challenge descriptions, home page content, and sponsor descriptions.

Edit this page View Markdown

rCTF supports Markdown in challenge descriptions, homepage content, and sponsor descriptions. marked parses it, rCTF adds alerts and a countdown timer, and DOMPurify sanitizes the resulting HTML.

The parser is in apps/web/src/lib/utils/markdown.ts, and apps/web/src/lib/components/markdown.svelte handles the interactive parts.

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).

Challenge description
> [!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.

Type Trigger Use for
note > [!NOTE] General information.
tip > [!TIP] Optional advice or shortcuts.
important > [!IMPORTANT] Information players must read.
warning > [!WARNING] Behavior that can cause problems if ignored.
caution > [!CAUTION] Actions with potentially destructive consequences.
connection > [!CONNECTION] Remote connection info for hosted challenges (see below).

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 behavior described here is what challenge authors actually see in the rCTF frontend.

Connection callout

Use > [!CONNECTION] for the connection string or URL in a challenge’s Remote field.

Challenge description
> [!CONNECTION]
> `nc challs.example.com 1337`
> [!CONNECTION]
> https://web-chall.example.com

Unlike 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:// or https://.
  • 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 /> element renders a live countdown for the competition. It must stand alone on its own line.

Home content
The CTF is live. Good luck!
<timer />

The element takes no attributes. It always targets the global CTF schedule (startTime / endTime from the runtime client config). Behavior:

  • Before the CTF starts, counts down to the start time with the label CTF starts in.
  • After the start time, counts down to the end time with the label 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 supports CommonMark headings, lists, emphasis, links, images, code, and blockquotes, along with marked tables and autolinks. Task lists, footnotes, and strikethrough are not supported.

Challenge description
## Setup
Download the [source archive](./source.tar.gz) and run:
```bash
docker compose up
```
The service listens on port `8080`.

For the full list of what CommonMark supports, see the CommonMark spec.

Sanitization

DOMPurify sanitizes the parsed HTML before it reaches the page. This has the following effects.

  • <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 alert and timer extensions use data-alert, data-type, data-content, and data-timer to mark elements that need client-side behavior. Each element they create also receives a temporary, secret data-nonce. A DOMPurify hook keeps the hydration attributes only when that nonce matches, then removes the nonce before returning the HTML. Markers written by hand have no effect because they do not carry the nonce and are stripped during sanitization.

If you need richer interactivity than these extensions cover, add it in the frontend code, not through embedded HTML in a description.

Type to search.