markdown · 4596 bytes Raw Blame History

Markdown on shithub

shithub renders user-authored markdown — issue bodies, PR descriptions, comments, READMEs — through one canonical pipeline. This page documents what's supported and what's deliberately not.

Supported

CommonMark + GFM

The full CommonMark spec plus the curated GFM additions:

  • Headings (# Title through ###### h6) with auto-generated anchor IDs (<h1 id="title">).
  • Paragraphs, soft line breaks (rendered as <br> in comment-style contexts; preserved as whitespace in READMEs).
  • Bullet, numbered, and task lists (- [x] / - [ ]).
  • Block quotes, fenced + indented code blocks.
  • Inline code, bold, italic, strikethrough.
  • Tables (GFM pipe syntax).
  • Autolinks (https://example.com becomes a link automatically).

Code blocks with syntax highlighting

Fenced code with a language tag turns on Chroma highlighting:

```go
fmt.Println("hello")
```

Languages we recognize: every language Chroma supports (~250). Unknown languages render as plain <pre><code> with no highlighting.

shithub-specific inline patterns

You write We render
@alice Link to /alice if the user exists
#42 Link to issue/PR #42 in the current repo, if visible
alice/proj#42 Cross-repo issue/PR link, if visible to you
abc1234 Commit link in the current repo (7+ hex chars)
:rocket: / :+1: Emoji from a curated set (~150 shortcodes)

These patterns do not match inside code blocks or inline code`#42` stays literal.

If a reference can't be resolved (the issue doesn't exist, the user doesn't exist, the cross-repo target isn't visible to you), we render the text as-is. No broken links, no "deleted" labels, no existence leaks.

Safe HTML (allowlisted)

These tags pass through unchanged:

  • <details> / <summary> (collapsible sections)
  • <kbd> (keyboard markers)
  • <sup>, <sub> (superscript / subscript)
  • README presentation attributes GitHub commonly allows: align="left|center|right" on paragraphs / headings / divs, and width / height on images.
  • Standard text formatting tags Goldmark emits (em, strong, code, pre, blockquote, ul, ol, li, table family).

Not supported

We deliberately do not match GitHub's looser markdown surface:

Feature Why
Raw HTML beyond allowlist XSS prevention. Anything outside the list is stripped.
data: URIs Avoids tracking pixels and decompression bombs.
javascript: URLs Always XSS.
<script>, <style>, <iframe>, <object>, <embed>, <base>, <meta> XSS / unwanted side effects.
Inline event handlers (onclick, onerror, etc.) XSS.
Math (KaTeX) Post-MVP.
Mermaid diagrams Post-MVP.
GFM Footnotes Deferred — file an issue if you want them.

For inline images, repo-relative paths work via the /raw/ route: ![diagram](docs/img/diagram.png). External-host images are also allowed; remote tracking pixels are inherent to that — we don't proxy.

Newline handling

There are two render modes, picked per surface:

  • Comment / issue / PR body: newlines render as <br> (matches GitHub's UI). You can write a paragraph by leaving a blank line.
  • README and other structured docs: standard CommonMark newline rules (paragraphs separated by blank lines, soft newlines join words).

Cache + version

Rendered HTML is cached on the source row alongside a pipeline version. Bumping the renderer (a sanitizer-policy change, a new extension, a major Goldmark/bluemonday upgrade with output drift) re-renders comments lazily on next read — we never run a "re-render every comment" batch.

Contributing

Markdown changes go through internal/markdown/. The boundary is enforced: importing goldmark or bluemonday outside that package fails CI (scripts/lint-markdown-boundary.sh).

If a new XSS vector lands in the wild, add a fixture to internal/markdown/markdown_test.go::TestRender_HostileInputs and fix the policy.

View source
1 # Markdown on shithub
2
3 shithub renders user-authored markdown — issue bodies, PR
4 descriptions, comments, READMEs — through one canonical pipeline.
5 This page documents what's supported and what's deliberately not.
6
7 ## Supported
8
9 ### CommonMark + GFM
10
11 The full CommonMark spec plus the curated GFM additions:
12
13 - Headings (`# Title` through `###### h6`) with auto-generated
14 anchor IDs (`<h1 id="title">`).
15 - Paragraphs, soft line breaks (rendered as `<br>` in
16 comment-style contexts; preserved as whitespace in READMEs).
17 - Bullet, numbered, and **task lists** (`- [x]` / `- [ ]`).
18 - Block quotes, fenced + indented code blocks.
19 - Inline `code`, **bold**, *italic*, ~~strikethrough~~.
20 - Tables (GFM pipe syntax).
21 - Autolinks (`https://example.com` becomes a link automatically).
22
23 ### Code blocks with syntax highlighting
24
25 Fenced code with a language tag turns on Chroma highlighting:
26
27 ````
28 ```go
29 fmt.Println("hello")
30 ```
31 ````
32
33 Languages we recognize: every language Chroma supports (~250).
34 Unknown languages render as plain `<pre><code>` with no
35 highlighting.
36
37 ### shithub-specific inline patterns
38
39 | You write | We render |
40 | --------------------- | ---------------------------------------------------- |
41 | `@alice` | Link to `/alice` if the user exists |
42 | `#42` | Link to issue/PR #42 in the current repo, if visible |
43 | `alice/proj#42` | Cross-repo issue/PR link, if visible to you |
44 | `abc1234` | Commit link in the current repo (7+ hex chars) |
45 | `:rocket:` / `:+1:` | Emoji from a curated set (~150 shortcodes) |
46
47 These patterns *do not match inside code blocks or inline code* —
48 `` `#42` `` stays literal.
49
50 If a reference can't be resolved (the issue doesn't exist, the
51 user doesn't exist, the cross-repo target isn't visible to you),
52 we render the text as-is. No broken links, no "deleted" labels,
53 no existence leaks.
54
55 ### Safe HTML (allowlisted)
56
57 These tags pass through unchanged:
58
59 - `<details>` / `<summary>` (collapsible sections)
60 - `<kbd>` (keyboard markers)
61 - `<sup>`, `<sub>` (superscript / subscript)
62 - README presentation attributes GitHub commonly allows:
63 `align="left|center|right"` on paragraphs / headings / divs,
64 and `width` / `height` on images.
65 - Standard text formatting tags Goldmark emits (em, strong, code,
66 pre, blockquote, ul, ol, li, table family).
67
68 ## Not supported
69
70 We deliberately do **not** match GitHub's looser markdown surface:
71
72 | Feature | Why |
73 | ------------------------ | --------------------------------------------------- |
74 | Raw HTML beyond allowlist | XSS prevention. Anything outside the list is stripped. |
75 | `data:` URIs | Avoids tracking pixels and decompression bombs. |
76 | `javascript:` URLs | Always XSS. |
77 | `<script>`, `<style>`, `<iframe>`, `<object>`, `<embed>`, `<base>`, `<meta>` | XSS / unwanted side effects. |
78 | Inline event handlers (`onclick`, `onerror`, etc.) | XSS. |
79 | Math (KaTeX) | Post-MVP. |
80 | Mermaid diagrams | Post-MVP. |
81 | GFM Footnotes | Deferred — file an issue if you want them. |
82
83 For inline images, repo-relative paths work via the `/raw/` route:
84 `![diagram](docs/img/diagram.png)`. External-host images are also
85 allowed; remote tracking pixels are inherent to that — we don't
86 proxy.
87
88 ## Newline handling
89
90 There are two render modes, picked per surface:
91
92 - **Comment / issue / PR body**: newlines render as `<br>`
93 (matches GitHub's UI). You can write a paragraph by leaving a
94 blank line.
95 - **README and other structured docs**: standard CommonMark
96 newline rules (paragraphs separated by blank lines, soft
97 newlines join words).
98
99 ## Cache + version
100
101 Rendered HTML is cached on the source row alongside a pipeline
102 version. Bumping the renderer (a sanitizer-policy change, a new
103 extension, a major Goldmark/bluemonday upgrade with output drift)
104 re-renders comments lazily on next read — we never run a "re-render
105 every comment" batch.
106
107 ## Contributing
108
109 Markdown changes go through `internal/markdown/`. The boundary is
110 enforced: importing `goldmark` or `bluemonday` outside that
111 package fails CI (`scripts/lint-markdown-boundary.sh`).
112
113 If a new XSS vector lands in the wild, add a fixture to
114 `internal/markdown/markdown_test.go::TestRender_HostileInputs` and
115 fix the policy.