VYPR
Critical severityNVD Advisory· Published Mar 31, 2025· Updated Mar 31, 2025

Beego allows Reflected/Stored XSS in Beego's RenderForm() Function Due to Unescaped User Input

CVE-2025-30223

Description

Beego is an open-source web framework for the Go programming language. Prior to 2.3.6, a Cross-Site Scripting (XSS) vulnerability exists in Beego's RenderForm() function due to improper HTML escaping of user-controlled data. This vulnerability allows attackers to inject malicious JavaScript code that executes in victims' browsers, potentially leading to session hijacking, credential theft, or account takeover. The vulnerability affects any application using Beego's RenderForm() function with user-provided data. Since it is a high-level function generating an entire form markup, many developers would assume it automatically escapes attributes (the way most frameworks do). This vulnerability is fixed in 2.3.6.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Beego prior to 2.3.6 has an XSS vulnerability in RenderForm() due to improper HTML escaping, allowing attackers to inject malicious JavaScript via user-controlled data.

Vulnerability

Overview

CVE-2025-30223 is a Cross-Site Scripting (XSS) vulnerability in Beego's RenderForm() function, present in versions prior to 2.3.6. The root cause lies in the renderFormField() helper, which directly interpolates user-controlled values (such as label, name, id, class, and value) into HTML without proper escaping [2][3]. Because RenderForm() returns template.HTML, Go's automatic HTML escaping is bypassed, making the flaw particularly dangerous [2].

Exploitation

An attacker can exploit this vulnerability by injecting malicious payloads into any user-controlled field that is rendered via RenderForm(). For example, injecting JavaScript into a DisplayName field can break out of an HTML attribute context, while injecting HTML tags into a textarea content field can directly execute scripts [2]. No special authentication is required if the application renders user-supplied data through this function, making the attack surface broad [1].

Impact

Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of a victim's browser. This can lead to session hijacking, credential theft, or account takeover, as the injected script can access cookies, local storage, and perform actions on behalf of the user [1][2].

Mitigation

The vulnerability is fixed in Beego version 2.3.6. The fix, introduced in commit 939bb18, adds proper HTML escaping using template.HTMLEscapeString() for all user-controlled attributes and values [3]. Users are strongly advised to upgrade to the latest version or apply the patch manually [1][4].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/beego/beego/v2Go
< 2.3.62.3.6
github.com/beego/beegoGo
<= 1.12.14

Affected products

2
  • Beego/Beegollm-create
    Range: <2.3.6
  • beego/beegov5
    Range: < 2.3.6

Patches

1
939bb18c6640

fix: add proper HTML escaping in renderFormField

https://github.com/beego/beegoVille VesilehtoMar 21, 2025via ghsa
2 files changed · +71 10
  • server/web/templatefunc.go+29 10 modified
    @@ -314,43 +314,62 @@ func RenderForm(obj interface{}) template.HTML {
     // renderFormField returns a string containing HTML of a single form field. In case of select fType, it will retrun
     // select tag with options. Value for select fType must be comma separated string which are use are
     func renderFormField(label, name, fType string, value interface{}, id string, class string, required bool) string {
    +	// Format attributes with spaces first
    +	idAttr := ""
     	if id != "" {
    -		id = " id=\"" + id + "\""
    +		idAttr = " id=\"" + template.HTMLEscapeString(id) + "\""
     	}
     
    +	classAttr := ""
     	if class != "" {
    -		class = " class=\"" + class + "\""
    +		classAttr = " class=\"" + template.HTMLEscapeString(class) + "\""
     	}
     
    -	requiredString := ""
    +	requiredAttr := ""
     	if required {
    -		requiredString = " required"
    +		requiredAttr = " required"
    +	}
    +
    +	// Escape all string values
    +	escapedName := template.HTMLEscapeString(name)
    +	escapedLabel := template.HTMLEscapeString(label)
    +	escapedType := template.HTMLEscapeString(fType)
    +
    +	// Handle value specially as it's an interface{}
    +	escapedValue := ""
    +	if value != nil {
    +		escapedValue = template.HTMLEscapeString(fmt.Sprintf("%v", value))
     	}
     
     	if isValidForInput(fType) {
    -		return fmt.Sprintf(`%v<input%v%v name="%v" type="%v" value="%v"%v>`, label, id, class, name, fType, value, requiredString)
    +		return fmt.Sprintf(`%v<input%v%v name="%v" type="%v" value="%v"%v>`,
    +			escapedLabel, idAttr, classAttr, escapedName, escapedType, escapedValue, requiredAttr)
     	}
     
     	if fType == "select" {
    -		valueStr, ok := value.(string)
    +		rawValueStr, ok := value.(string)
     		if !ok {
     			logs.Error("for select value must comma separated string that are the options for select")
     			return ""
     		}
     
     		var selectBuilder strings.Builder
    -		selectBuilder.WriteString(fmt.Sprintf(`%v<select%v%v name="%v"></br>`, label, id, class, name))
    +		selectBuilder.WriteString(fmt.Sprintf(`%v<select%v%v name="%v"></br>`,
    +			escapedLabel, idAttr, classAttr, escapedName))
     
    -		for _, option := range strings.Split(valueStr, ",") {
    -			selectBuilder.WriteString(fmt.Sprintf(`  <option value="%v"> %v </option></br>`, option, option))
    +		for _, option := range strings.Split(rawValueStr, ",") {
    +			escapedOption := template.HTMLEscapeString(option)
    +			selectBuilder.WriteString(fmt.Sprintf(`  <option value="%v"> %v </option></br>`,
    +				escapedOption, escapedOption))
     		}
     
     		selectBuilder.WriteString(`</select>`)
     
     		return selectBuilder.String()
     	}
     
    -	return fmt.Sprintf(`%v<%v%v%v name="%v"%v>%v</%v>`, label, fType, id, class, name, requiredString, value, fType)
    +	return fmt.Sprintf(`%v<%v%v%v name="%v"%v>%v</%v>`,
    +		escapedLabel, escapedType, idAttr, classAttr, escapedName, requiredAttr, escapedValue, escapedType)
     }
     
     // isValidForInput checks if fType is a valid value for the `type` property of an HTML input element.
    
  • server/web/templatefunc_test.go+42 0 modified
    @@ -18,6 +18,7 @@ import (
     	"html/template"
     	"net/url"
     	"reflect"
    +	"strings"
     	"testing"
     	"time"
     )
    @@ -583,3 +584,44 @@ func Test_lt(t *testing.T) {
     		}
     	}
     }
    +
    +func TestRenderFormSecurity(t *testing.T) {
    +	type UserProfile struct {
    +		DisplayName string `form:"displayName,text,Name:"`
    +		Bio         string `form:",textarea"`
    +	}
    +
    +	// Test case 1: Test proper escaping of special characters in attributes
    +	specialCharsProfile := UserProfile{
    +		DisplayName: `Special " ' < > & Characters`,
    +		Bio:         "Normal text content",
    +	}
    +
    +	output := string(RenderForm(&specialCharsProfile))
    +
    +	// Verify the output has all special characters properly escaped
    +	if strings.Contains(output, `"Special "`) {
    +		t.Errorf("Quotation mark not properly escaped in attribute")
    +	}
    +
    +	if !strings.Contains(output, `value="Special`) {
    +		t.Errorf("Expected escaped attribute value not found")
    +	}
    +
    +	// Test case 2: Test proper escaping of HTML-like content
    +	htmlContentProfile := UserProfile{
    +		DisplayName: "Normal Name",
    +		Bio:         `<div>Sample HTML content</div>`,
    +	}
    +
    +	output = string(RenderForm(&htmlContentProfile))
    +
    +	// Verify the output has HTML content properly escaped
    +	if strings.Contains(output, `<div>`) {
    +		t.Errorf("HTML tags not properly escaped in content")
    +	}
    +
    +	if !strings.Contains(output, `&lt;div&gt;`) {
    +		t.Errorf("Expected escaped HTML not found")
    +	}
    +}
    

Vulnerability mechanics

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

References

5

News mentions

0

No linked articles in our index yet.