VYPR
Low severityOSV Advisory· Published Jan 16, 2026· Updated Jan 16, 2026

DoS from quadratic complexity in model.ParseHashtags

CVE-2025-14822

Description

Mattermost versions 10.11.x <= 10.11.8 fail to validate input size before processing hashtags which allows an authenticated attacker to exhaust CPU resources via a single HTTP request containing a post with thousands space-separated tokens

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/mattermost/mattermost-serverGo
>= 10.11.0, < 10.11.910.11.9
github.com/mattermost/mattermost-serverGo
>= 11.0.0, < 11.2.011.2.0

Affected products

1

Patches

2
b3d6c0c564c1

perf: apply perfpsrint linter (#33967) (#34632)

https://github.com/mattermost/mattermostHarshil SharmaDec 2, 2025via ghsa
6 files changed · +131 60
  • server/public/model/utils.go+6 5 modified
    @@ -717,8 +717,8 @@ var (
     func ParseHashtags(text string) (string, string) {
     	words := strings.Fields(text)
     
    -	hashtagString := ""
    -	plainString := ""
    +	var hashtagStringSb strings.Builder
    +	var plainString strings.Builder
     	for _, word := range words {
     		// trim off surrounding punctuation
     		word = puncStart.ReplaceAllString(word, "")
    @@ -728,11 +728,12 @@ func ParseHashtags(text string) (string, string) {
     		word = hashtagStart.ReplaceAllString(word, "#")
     
     		if validHashtag.MatchString(word) {
    -			hashtagString += " " + word
    +			hashtagStringSb.WriteString(" " + word)
     		} else {
    -			plainString += " " + word
    +			plainString.WriteString(" " + word)
     		}
     	}
    +	hashtagString := hashtagStringSb.String()
     
     	if len(hashtagString) > 1000 {
     		hashtagString = hashtagString[:999]
    @@ -744,7 +745,7 @@ func ParseHashtags(text string) (string, string) {
     		}
     	}
     
    -	return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString)
    +	return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString.String())
     }
     
     func ClearMentionTags(post string) string {
    
  • server/public/model/utils_test.go+52 4 modified
    @@ -541,10 +541,58 @@ func TestStringArray_Equal(t *testing.T) {
     }
     
     func TestParseHashtags(t *testing.T) {
    -	for input, output := range hashtags {
    -		o, _ := ParseHashtags(input)
    -		require.Equal(t, o, output, "failed to parse hashtags from input="+input+" expected="+output+" actual="+o)
    -	}
    +	t.Run("basic hashtag extraction", func(t *testing.T) {
    +		for input, output := range hashtags {
    +			o, _ := ParseHashtags(input)
    +			require.Equal(t, o, output, "failed to parse hashtags from input="+input+" expected="+output+" actual="+o)
    +		}
    +	})
    +
    +	t.Run("long hashtag string truncation", func(t *testing.T) {
    +		// Test case where hashtag string exceeds 1000 characters with a space to truncate at
    +		longHashtags := "#test " + strings.Repeat("#verylonghashtag ", 50)
    +		hashtagString, plainString := ParseHashtags(longHashtags)
    +		require.NotEmpty(t, hashtagString)
    +		require.LessOrEqual(t, len(hashtagString), 1000)
    +		require.Empty(t, plainString)
    +		// Ensure it truncated at a space
    +		require.NotEqual(t, "", hashtagString)
    +		require.True(t, hashtagString[len(hashtagString)-1] != ' ')
    +	})
    +
    +	t.Run("long hashtag string truncation without spaces", func(t *testing.T) {
    +		// Test case where hashtag string exceeds 1000 characters with no space after position 999
    +		// Create a single very long hashtag that will be truncated
    +		veryLongHashtag := "#" + strings.Repeat("a", 1010)
    +		hashtagString, plainString := ParseHashtags(veryLongHashtag)
    +		// Should be empty because no space was found to truncate at
    +		require.Equal(t, "", hashtagString)
    +		require.Empty(t, plainString)
    +	})
    +
    +	t.Run("plain text extraction", func(t *testing.T) {
    +		hashtagString, plainString := ParseHashtags("hello #world this is #test plain text")
    +		require.Equal(t, "#world #test", hashtagString)
    +		require.Equal(t, "hello this is plain text", plainString)
    +	})
    +
    +	t.Run("only plain text", func(t *testing.T) {
    +		hashtagString, plainString := ParseHashtags("no hashtags here")
    +		require.Empty(t, hashtagString)
    +		require.Equal(t, "no hashtags here", plainString)
    +	})
    +
    +	t.Run("only hashtags", func(t *testing.T) {
    +		hashtagString, plainString := ParseHashtags("#one #two #three")
    +		require.Equal(t, "#one #two #three", hashtagString)
    +		require.Empty(t, plainString)
    +	})
    +
    +	t.Run("empty string", func(t *testing.T) {
    +		hashtagString, plainString := ParseHashtags("")
    +		require.Empty(t, hashtagString)
    +		require.Empty(t, plainString)
    +	})
     }
     
     func TestIsValidAlphaNum(t *testing.T) {
    
  • server/public/shared/markdown/fenced_code.go+4 3 modified
    @@ -23,11 +23,12 @@ type FencedCode struct {
     	RawCode      []FencedCodeLine
     }
     
    -func (b *FencedCode) Code() (result string) {
    +func (b *FencedCode) Code() string {
    +	var resultSb strings.Builder
     	for _, code := range b.RawCode {
    -		result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End]
    +		resultSb.WriteString(strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End])
     	}
    -	return
    +	return resultSb.String()
     }
     
     func (b *FencedCode) Info() string {
    
  • server/public/shared/markdown/html.go+60 43 modified
    @@ -27,69 +27,75 @@ func RenderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition) (
     	return renderBlockHTML(block, referenceDefinitions, false)
     }
     
    -func renderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition, isTightList bool) (result string) {
    +func renderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition, isTightList bool) string {
    +	var resultSb strings.Builder
    +
     	switch v := block.(type) {
     	case *Document:
     		for _, block := range v.Children {
    -			result += RenderBlockHTML(block, referenceDefinitions)
    +			resultSb.WriteString(RenderBlockHTML(block, referenceDefinitions))
     		}
     	case *Paragraph:
     		if len(v.Text) == 0 {
    -			return
    +			return ""
     		}
     		if !isTightList {
    -			result += "<p>"
    +			resultSb.WriteString("<p>")
     		}
     		for _, inline := range v.ParseInlines(referenceDefinitions) {
    -			result += RenderInlineHTML(inline)
    +			resultSb.WriteString(RenderInlineHTML(inline))
     		}
     		if !isTightList {
    -			result += "</p>"
    +			resultSb.WriteString("</p>")
     		}
     	case *List:
     		if v.IsOrdered {
     			if v.OrderedStart != 1 {
    -				result += fmt.Sprintf(`<ol start="%v">`, v.OrderedStart)
    +				resultSb.WriteString(fmt.Sprintf(`<ol start="%v">`, v.OrderedStart))
     			} else {
    -				result += "<ol>"
    +				resultSb.WriteString("<ol>")
     			}
     		} else {
    -			result += "<ul>"
    +			resultSb.WriteString("<ul>")
     		}
     		for _, block := range v.Children {
    -			result += renderBlockHTML(block, referenceDefinitions, !v.IsLoose)
    +			resultSb.WriteString(renderBlockHTML(block, referenceDefinitions, !v.IsLoose))
     		}
     		if v.IsOrdered {
    -			result += "</ol>"
    +			resultSb.WriteString("</ol>")
     		} else {
    -			result += "</ul>"
    +			resultSb.WriteString("</ul>")
     		}
     	case *ListItem:
    -		result += "<li>"
    +		resultSb.WriteString("<li>")
     		for _, block := range v.Children {
    -			result += renderBlockHTML(block, referenceDefinitions, isTightList)
    +			resultSb.WriteString(renderBlockHTML(block, referenceDefinitions, isTightList))
     		}
    -		result += "</li>"
    +		resultSb.WriteString("</li>")
     	case *BlockQuote:
    -		result += "<blockquote>"
    +		resultSb.WriteString("<blockquote>")
     		for _, block := range v.Children {
    -			result += RenderBlockHTML(block, referenceDefinitions)
    +			resultSb.WriteString(RenderBlockHTML(block, referenceDefinitions))
     		}
    -		result += "</blockquote>"
    +		resultSb.WriteString("</blockquote>")
     	case *FencedCode:
     		if info := v.Info(); info != "" {
     			language := strings.Fields(info)[0]
    -			result += `<pre><code class="language-` + htmlEscaper.Replace(language) + `">`
    +			resultSb.WriteString(`<pre><code class="language-` + htmlEscaper.Replace(language) + `">`)
     		} else {
    -			result += "<pre><code>"
    +			resultSb.WriteString("<pre><code>")
     		}
    -		result += htmlEscaper.Replace(v.Code()) + "</code></pre>"
    +		resultSb.WriteString(htmlEscaper.Replace(v.Code()))
    +		resultSb.WriteString("</code></pre>")
     	case *IndentedCode:
    -		result += "<pre><code>" + htmlEscaper.Replace(v.Code()) + "</code></pre>"
    +		resultSb.WriteString("<pre><code>")
    +		resultSb.WriteString(htmlEscaper.Replace(v.Code()))
    +		resultSb.WriteString("</code></pre>")
     	default:
     		panic(fmt.Sprintf("missing case for type %T", v))
     	}
    -	return
    +
    +	return resultSb.String()
     }
     
     func escapeURL(url string) (result string) {
    @@ -137,31 +143,37 @@ func RenderInlineHTML(inline Inline) (result string) {
     		}
     		result += ` />`
     	case *InlineLink:
    -		result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`
    +		var resultSb strings.Builder
    +		resultSb.WriteString(`<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`)
     		if title := v.Title(); title != "" {
    -			result += ` title="` + htmlEscaper.Replace(title) + `"`
    +			resultSb.WriteString(` title="` + htmlEscaper.Replace(title) + `"`)
     		}
    -		result += `>`
    +		resultSb.WriteString(`>`)
     		for _, inline := range v.Children {
    -			result += RenderInlineHTML(inline)
    +			resultSb.WriteString(RenderInlineHTML(inline))
     		}
    -		result += "</a>"
    +		resultSb.WriteString("</a>")
    +		return resultSb.String()
     	case *ReferenceLink:
    -		result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`
    +		var resultSb strings.Builder
    +		resultSb.WriteString(`<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`)
     		if title := v.Title(); title != "" {
    -			result += ` title="` + htmlEscaper.Replace(title) + `"`
    +			resultSb.WriteString(` title="` + htmlEscaper.Replace(title) + `"`)
     		}
    -		result += `>`
    +		resultSb.WriteString(`>`)
     		for _, inline := range v.Children {
    -			result += RenderInlineHTML(inline)
    +			resultSb.WriteString(RenderInlineHTML(inline))
     		}
    -		result += "</a>"
    +		resultSb.WriteString("</a>")
    +		return resultSb.String()
     	case *Autolink:
    -		result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `">`
    +		var resultSb strings.Builder
    +		resultSb.WriteString(`<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `">`)
     		for _, inline := range v.Children {
    -			result += RenderInlineHTML(inline)
    +			resultSb.WriteString(RenderInlineHTML(inline))
     		}
    -		result += "</a>"
    +		resultSb.WriteString("</a>")
    +		return resultSb.String()
     	case *Emoji:
     		escapedName := htmlEscaper.Replace(v.Name)
     		result += fmt.Sprintf(`<span data-emoji-name="%s" data-literal=":%s:" />`, escapedName, escapedName)
    @@ -172,25 +184,30 @@ func RenderInlineHTML(inline Inline) (result string) {
     	return
     }
     
    -func renderImageAltText(children []Inline) (result string) {
    +func renderImageAltText(children []Inline) string {
    +	var resultSb strings.Builder
     	for _, inline := range children {
    -		result += renderImageChildAltText(inline)
    +		resultSb.WriteString(renderImageChildAltText(inline))
     	}
    -	return
    +	return resultSb.String()
     }
     
    -func renderImageChildAltText(inline Inline) (result string) {
    +func renderImageChildAltText(inline Inline) string {
     	switch v := inline.(type) {
     	case *Text:
     		return v.Text
     	case *InlineImage:
    +		var resultSb strings.Builder
     		for _, inline := range v.Children {
    -			result += renderImageChildAltText(inline)
    +			resultSb.WriteString(renderImageChildAltText(inline))
     		}
    +		return resultSb.String()
     	case *InlineLink:
    +		var resultSb strings.Builder
     		for _, inline := range v.Children {
    -			result += renderImageChildAltText(inline)
    +			resultSb.WriteString(renderImageChildAltText(inline))
     		}
    +		return resultSb.String()
     	}
    -	return
    +	return ""
     }
    
  • server/public/shared/markdown/indented_code.go+4 3 modified
    @@ -19,11 +19,12 @@ type IndentedCode struct {
     	RawCode []IndentedCodeLine
     }
     
    -func (b *IndentedCode) Code() (result string) {
    +func (b *IndentedCode) Code() string {
    +	var resultSb strings.Builder
     	for _, code := range b.RawCode {
    -		result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End]
    +		resultSb.WriteString(strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End])
     	}
    -	return
    +	return resultSb.String()
     }
     
     func (b *IndentedCode) Continuation(indentation int, r Range) *continuation {
    
  • server/public/shared/markdown/reference_definition.go+5 2 modified
    @@ -3,6 +3,8 @@
     
     package markdown
     
    +import "strings"
    +
     type ReferenceDefinition struct {
     	RawDestination Range
     
    @@ -24,10 +26,11 @@ func (d *ReferenceDefinition) Title() string {
     }
     
     func parseReferenceDefinition(markdown string, ranges []Range) (*ReferenceDefinition, []Range) {
    -	raw := ""
    +	var rawSb strings.Builder
     	for _, r := range ranges {
    -		raw += markdown[r.Position:r.End]
    +		rawSb.WriteString(markdown[r.Position:r.End])
     	}
    +	raw := rawSb.String()
     
     	label, next, ok := parseLinkLabel(raw, 0)
     	if !ok {
    
4d86263f5430

perf: apply perfpsrint linter (#33967) (#34619)

https://github.com/mattermost/mattermostMattermost BuildDec 1, 2025via ghsa
6 files changed · +128 57
  • server/public/model/utils.go+3 2 modified
    @@ -712,7 +712,7 @@ var (
     func ParseHashtags(text string) (string, string) {
     	words := strings.Fields(text)
     
    -	hashtagString := ""
    +	var hashtagStringSb strings.Builder
     	var plainString strings.Builder
     	for _, word := range words {
     		// trim off surrounding punctuation
    @@ -723,11 +723,12 @@ func ParseHashtags(text string) (string, string) {
     		word = hashtagStart.ReplaceAllString(word, "#")
     
     		if validHashtag.MatchString(word) {
    -			hashtagString += " " + word
    +			hashtagStringSb.WriteString(" " + word)
     		} else {
     			plainString.WriteString(" " + word)
     		}
     	}
    +	hashtagString := hashtagStringSb.String()
     
     	if len(hashtagString) > 1000 {
     		hashtagString = hashtagString[:999]
    
  • server/public/model/utils_test.go+52 4 modified
    @@ -541,10 +541,58 @@ func TestStringArray_Equal(t *testing.T) {
     }
     
     func TestParseHashtags(t *testing.T) {
    -	for input, output := range hashtags {
    -		o, _ := ParseHashtags(input)
    -		require.Equal(t, o, output, "failed to parse hashtags from input="+input+" expected="+output+" actual="+o)
    -	}
    +	t.Run("basic hashtag extraction", func(t *testing.T) {
    +		for input, output := range hashtags {
    +			o, _ := ParseHashtags(input)
    +			require.Equal(t, o, output, "failed to parse hashtags from input="+input+" expected="+output+" actual="+o)
    +		}
    +	})
    +
    +	t.Run("long hashtag string truncation", func(t *testing.T) {
    +		// Test case where hashtag string exceeds 1000 characters with a space to truncate at
    +		longHashtags := "#test " + strings.Repeat("#verylonghashtag ", 50)
    +		hashtagString, plainString := ParseHashtags(longHashtags)
    +		require.NotEmpty(t, hashtagString)
    +		require.LessOrEqual(t, len(hashtagString), 1000)
    +		require.Empty(t, plainString)
    +		// Ensure it truncated at a space
    +		require.NotEqual(t, "", hashtagString)
    +		require.True(t, hashtagString[len(hashtagString)-1] != ' ')
    +	})
    +
    +	t.Run("long hashtag string truncation without spaces", func(t *testing.T) {
    +		// Test case where hashtag string exceeds 1000 characters with no space after position 999
    +		// Create a single very long hashtag that will be truncated
    +		veryLongHashtag := "#" + strings.Repeat("a", 1010)
    +		hashtagString, plainString := ParseHashtags(veryLongHashtag)
    +		// Should be empty because no space was found to truncate at
    +		require.Equal(t, "", hashtagString)
    +		require.Empty(t, plainString)
    +	})
    +
    +	t.Run("plain text extraction", func(t *testing.T) {
    +		hashtagString, plainString := ParseHashtags("hello #world this is #test plain text")
    +		require.Equal(t, "#world #test", hashtagString)
    +		require.Equal(t, "hello this is plain text", plainString)
    +	})
    +
    +	t.Run("only plain text", func(t *testing.T) {
    +		hashtagString, plainString := ParseHashtags("no hashtags here")
    +		require.Empty(t, hashtagString)
    +		require.Equal(t, "no hashtags here", plainString)
    +	})
    +
    +	t.Run("only hashtags", func(t *testing.T) {
    +		hashtagString, plainString := ParseHashtags("#one #two #three")
    +		require.Equal(t, "#one #two #three", hashtagString)
    +		require.Empty(t, plainString)
    +	})
    +
    +	t.Run("empty string", func(t *testing.T) {
    +		hashtagString, plainString := ParseHashtags("")
    +		require.Empty(t, hashtagString)
    +		require.Empty(t, plainString)
    +	})
     }
     
     func TestIsValidAlphaNum(t *testing.T) {
    
  • server/public/shared/markdown/fenced_code.go+4 3 modified
    @@ -23,11 +23,12 @@ type FencedCode struct {
     	RawCode      []FencedCodeLine
     }
     
    -func (b *FencedCode) Code() (result string) {
    +func (b *FencedCode) Code() string {
    +	var resultSb strings.Builder
     	for _, code := range b.RawCode {
    -		result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End]
    +		resultSb.WriteString(strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End])
     	}
    -	return
    +	return resultSb.String()
     }
     
     func (b *FencedCode) Info() string {
    
  • server/public/shared/markdown/html.go+60 43 modified
    @@ -27,69 +27,75 @@ func RenderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition) (
     	return renderBlockHTML(block, referenceDefinitions, false)
     }
     
    -func renderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition, isTightList bool) (result string) {
    +func renderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition, isTightList bool) string {
    +	var resultSb strings.Builder
    +
     	switch v := block.(type) {
     	case *Document:
     		for _, block := range v.Children {
    -			result += RenderBlockHTML(block, referenceDefinitions)
    +			resultSb.WriteString(RenderBlockHTML(block, referenceDefinitions))
     		}
     	case *Paragraph:
     		if len(v.Text) == 0 {
    -			return
    +			return ""
     		}
     		if !isTightList {
    -			result += "<p>"
    +			resultSb.WriteString("<p>")
     		}
     		for _, inline := range v.ParseInlines(referenceDefinitions) {
    -			result += RenderInlineHTML(inline)
    +			resultSb.WriteString(RenderInlineHTML(inline))
     		}
     		if !isTightList {
    -			result += "</p>"
    +			resultSb.WriteString("</p>")
     		}
     	case *List:
     		if v.IsOrdered {
     			if v.OrderedStart != 1 {
    -				result += fmt.Sprintf(`<ol start="%v">`, v.OrderedStart)
    +				resultSb.WriteString(fmt.Sprintf(`<ol start="%v">`, v.OrderedStart))
     			} else {
    -				result += "<ol>"
    +				resultSb.WriteString("<ol>")
     			}
     		} else {
    -			result += "<ul>"
    +			resultSb.WriteString("<ul>")
     		}
     		for _, block := range v.Children {
    -			result += renderBlockHTML(block, referenceDefinitions, !v.IsLoose)
    +			resultSb.WriteString(renderBlockHTML(block, referenceDefinitions, !v.IsLoose))
     		}
     		if v.IsOrdered {
    -			result += "</ol>"
    +			resultSb.WriteString("</ol>")
     		} else {
    -			result += "</ul>"
    +			resultSb.WriteString("</ul>")
     		}
     	case *ListItem:
    -		result += "<li>"
    +		resultSb.WriteString("<li>")
     		for _, block := range v.Children {
    -			result += renderBlockHTML(block, referenceDefinitions, isTightList)
    +			resultSb.WriteString(renderBlockHTML(block, referenceDefinitions, isTightList))
     		}
    -		result += "</li>"
    +		resultSb.WriteString("</li>")
     	case *BlockQuote:
    -		result += "<blockquote>"
    +		resultSb.WriteString("<blockquote>")
     		for _, block := range v.Children {
    -			result += RenderBlockHTML(block, referenceDefinitions)
    +			resultSb.WriteString(RenderBlockHTML(block, referenceDefinitions))
     		}
    -		result += "</blockquote>"
    +		resultSb.WriteString("</blockquote>")
     	case *FencedCode:
     		if info := v.Info(); info != "" {
     			language := strings.Fields(info)[0]
    -			result += `<pre><code class="language-` + htmlEscaper.Replace(language) + `">`
    +			resultSb.WriteString(`<pre><code class="language-` + htmlEscaper.Replace(language) + `">`)
     		} else {
    -			result += "<pre><code>"
    +			resultSb.WriteString("<pre><code>")
     		}
    -		result += htmlEscaper.Replace(v.Code()) + "</code></pre>"
    +		resultSb.WriteString(htmlEscaper.Replace(v.Code()))
    +		resultSb.WriteString("</code></pre>")
     	case *IndentedCode:
    -		result += "<pre><code>" + htmlEscaper.Replace(v.Code()) + "</code></pre>"
    +		resultSb.WriteString("<pre><code>")
    +		resultSb.WriteString(htmlEscaper.Replace(v.Code()))
    +		resultSb.WriteString("</code></pre>")
     	default:
     		panic(fmt.Sprintf("missing case for type %T", v))
     	}
    -	return
    +
    +	return resultSb.String()
     }
     
     func escapeURL(url string) (result string) {
    @@ -137,31 +143,37 @@ func RenderInlineHTML(inline Inline) (result string) {
     		}
     		result += ` />`
     	case *InlineLink:
    -		result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`
    +		var resultSb strings.Builder
    +		resultSb.WriteString(`<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`)
     		if title := v.Title(); title != "" {
    -			result += ` title="` + htmlEscaper.Replace(title) + `"`
    +			resultSb.WriteString(` title="` + htmlEscaper.Replace(title) + `"`)
     		}
    -		result += `>`
    +		resultSb.WriteString(`>`)
     		for _, inline := range v.Children {
    -			result += RenderInlineHTML(inline)
    +			resultSb.WriteString(RenderInlineHTML(inline))
     		}
    -		result += "</a>"
    +		resultSb.WriteString("</a>")
    +		return resultSb.String()
     	case *ReferenceLink:
    -		result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`
    +		var resultSb strings.Builder
    +		resultSb.WriteString(`<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`)
     		if title := v.Title(); title != "" {
    -			result += ` title="` + htmlEscaper.Replace(title) + `"`
    +			resultSb.WriteString(` title="` + htmlEscaper.Replace(title) + `"`)
     		}
    -		result += `>`
    +		resultSb.WriteString(`>`)
     		for _, inline := range v.Children {
    -			result += RenderInlineHTML(inline)
    +			resultSb.WriteString(RenderInlineHTML(inline))
     		}
    -		result += "</a>"
    +		resultSb.WriteString("</a>")
    +		return resultSb.String()
     	case *Autolink:
    -		result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `">`
    +		var resultSb strings.Builder
    +		resultSb.WriteString(`<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `">`)
     		for _, inline := range v.Children {
    -			result += RenderInlineHTML(inline)
    +			resultSb.WriteString(RenderInlineHTML(inline))
     		}
    -		result += "</a>"
    +		resultSb.WriteString("</a>")
    +		return resultSb.String()
     	case *Emoji:
     		escapedName := htmlEscaper.Replace(v.Name)
     		result += fmt.Sprintf(`<span data-emoji-name="%s" data-literal=":%s:" />`, escapedName, escapedName)
    @@ -172,25 +184,30 @@ func RenderInlineHTML(inline Inline) (result string) {
     	return
     }
     
    -func renderImageAltText(children []Inline) (result string) {
    +func renderImageAltText(children []Inline) string {
    +	var resultSb strings.Builder
     	for _, inline := range children {
    -		result += renderImageChildAltText(inline)
    +		resultSb.WriteString(renderImageChildAltText(inline))
     	}
    -	return
    +	return resultSb.String()
     }
     
    -func renderImageChildAltText(inline Inline) (result string) {
    +func renderImageChildAltText(inline Inline) string {
     	switch v := inline.(type) {
     	case *Text:
     		return v.Text
     	case *InlineImage:
    +		var resultSb strings.Builder
     		for _, inline := range v.Children {
    -			result += renderImageChildAltText(inline)
    +			resultSb.WriteString(renderImageChildAltText(inline))
     		}
    +		return resultSb.String()
     	case *InlineLink:
    +		var resultSb strings.Builder
     		for _, inline := range v.Children {
    -			result += renderImageChildAltText(inline)
    +			resultSb.WriteString(renderImageChildAltText(inline))
     		}
    +		return resultSb.String()
     	}
    -	return
    +	return ""
     }
    
  • server/public/shared/markdown/indented_code.go+4 3 modified
    @@ -19,11 +19,12 @@ type IndentedCode struct {
     	RawCode []IndentedCodeLine
     }
     
    -func (b *IndentedCode) Code() (result string) {
    +func (b *IndentedCode) Code() string {
    +	var resultSb strings.Builder
     	for _, code := range b.RawCode {
    -		result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End]
    +		resultSb.WriteString(strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End])
     	}
    -	return
    +	return resultSb.String()
     }
     
     func (b *IndentedCode) Continuation(indentation int, r Range) *continuation {
    
  • server/public/shared/markdown/reference_definition.go+5 2 modified
    @@ -3,6 +3,8 @@
     
     package markdown
     
    +import "strings"
    +
     type ReferenceDefinition struct {
     	RawDestination Range
     
    @@ -24,10 +26,11 @@ func (d *ReferenceDefinition) Title() string {
     }
     
     func parseReferenceDefinition(markdown string, ranges []Range) (*ReferenceDefinition, []Range) {
    -	raw := ""
    +	var rawSb strings.Builder
     	for _, r := range ranges {
    -		raw += markdown[r.Position:r.End]
    +		rawSb.WriteString(markdown[r.Position:r.End])
     	}
    +	raw := rawSb.String()
     
     	label, next, ok := parseLinkLabel(raw, 0)
     	if !ok {
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.