Skip to content

Commit 0b0d555

Browse files
Claudegaby
andauthored
Fix XSS vulnerability in AutoFormat HTML output
Agent-Logs-Url: https://github.com/gofiber/fiber/sessions/42d9cb22-074d-4c33-a38e-c250234957a0 Co-authored-by: gaby <835733+gaby@users.noreply.github.com>
1 parent 9fd194f commit 0b0d555

2 files changed

Lines changed: 80 additions & 1 deletion

File tree

ctx_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2096,6 +2096,84 @@ func Test_Ctx_AutoFormat_Struct(t *testing.T) {
20962096
)
20972097
}
20982098

2099+
// go test -run Test_Ctx_AutoFormat_XSS_Prevention
2100+
func Test_Ctx_AutoFormat_XSS_Prevention(t *testing.T) {
2101+
t.Parallel()
2102+
app := New()
2103+
c := app.AcquireCtx(&fasthttp.RequestCtx{})
2104+
2105+
// Test basic XSS with script tag
2106+
c.Request().Header.Set(HeaderAccept, MIMETextHTML)
2107+
err := c.AutoFormat("<script>alert('XSS')</script>")
2108+
require.NoError(t, err)
2109+
require.Equal(t, MIMETextHTMLCharsetUTF8, c.GetRespHeader(HeaderContentType))
2110+
require.Equal(t, "<p>&lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;</p>", string(c.Response().Body()))
2111+
require.NotContains(t, string(c.Response().Body()), "<script>", "Script tags should be escaped")
2112+
2113+
// Test XSS with img onerror
2114+
c.Request().Header.Set(HeaderAccept, MIMETextHTML)
2115+
err = c.AutoFormat("<img src=x onerror=alert('XSS')>")
2116+
require.NoError(t, err)
2117+
require.Equal(t, "<p>&lt;img src=x onerror=alert(&#39;XSS&#39;)&gt;</p>", string(c.Response().Body()))
2118+
require.NotContains(t, string(c.Response().Body()), "<img", "Img tags should be escaped")
2119+
2120+
// Test XSS with iframe
2121+
c.Request().Header.Set(HeaderAccept, MIMETextHTML)
2122+
err = c.AutoFormat("<iframe src='javascript:alert(\"XSS\")'></iframe>")
2123+
require.NoError(t, err)
2124+
require.Equal(t, "<p>&lt;iframe src=&#39;javascript:alert(&#34;XSS&#34;)&#39;&gt;&lt;/iframe&gt;</p>", string(c.Response().Body()))
2125+
require.NotContains(t, string(c.Response().Body()), "<iframe", "Iframe tags should be escaped")
2126+
2127+
// Test XSS with event handler attributes
2128+
c.Request().Header.Set(HeaderAccept, MIMETextHTML)
2129+
err = c.AutoFormat("<div onload=alert('XSS')>test</div>")
2130+
require.NoError(t, err)
2131+
require.Equal(t, "<p>&lt;div onload=alert(&#39;XSS&#39;)&gt;test&lt;/div&gt;</p>", string(c.Response().Body()))
2132+
require.NotContains(t, string(c.Response().Body()), "<div", "Div tags should be escaped")
2133+
2134+
// Test XSS with link javascript protocol
2135+
c.Request().Header.Set(HeaderAccept, MIMETextHTML)
2136+
err = c.AutoFormat("<a href='javascript:alert(\"XSS\")'>Click me</a>")
2137+
require.NoError(t, err)
2138+
require.Equal(t, "<p>&lt;a href=&#39;javascript:alert(&#34;XSS&#34;)&#39;&gt;Click me&lt;/a&gt;</p>", string(c.Response().Body()))
2139+
require.NotContains(t, string(c.Response().Body()), "<a href", "Anchor tags should be escaped")
2140+
2141+
// Test XSS with mixed quotes
2142+
c.Request().Header.Set(HeaderAccept, MIMETextHTML)
2143+
err = c.AutoFormat(`"><script>alert('XSS')</script><"`)
2144+
require.NoError(t, err)
2145+
require.Equal(t, `<p>&#34;&gt;&lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;&lt;&#34;</p>`, string(c.Response().Body()))
2146+
require.NotContains(t, string(c.Response().Body()), "<script>", "Script tags with quotes should be escaped")
2147+
2148+
// Test legitimate HTML special characters are escaped
2149+
c.Request().Header.Set(HeaderAccept, MIMETextHTML)
2150+
err = c.AutoFormat("Price: 5 < 10 & 20 > 15")
2151+
require.NoError(t, err)
2152+
require.Equal(t, "<p>Price: 5 &lt; 10 &amp; 20 &gt; 15</p>", string(c.Response().Body()))
2153+
2154+
// Test that normal text without special chars works fine
2155+
c.Request().Header.Set(HeaderAccept, MIMETextHTML)
2156+
err = c.AutoFormat("Hello, World!")
2157+
require.NoError(t, err)
2158+
require.Equal(t, "<p>Hello, World!</p>", string(c.Response().Body()))
2159+
2160+
// Test XSS prevention with byte slice
2161+
c.Request().Header.Set(HeaderAccept, MIMETextHTML)
2162+
err = c.AutoFormat([]byte("<script>alert('XSS')</script>"))
2163+
require.NoError(t, err)
2164+
require.Equal(t, "<p>&lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;</p>", string(c.Response().Body()))
2165+
2166+
// Test XSS prevention with complex struct formatting
2167+
c.Request().Header.Set(HeaderAccept, MIMETextHTML)
2168+
err = c.AutoFormat(struct {
2169+
Value string
2170+
}{Value: "<script>alert('XSS')</script>"})
2171+
require.NoError(t, err)
2172+
// When formatted as string via fmt.Sprintf, the struct becomes something like {Value:<script>...}
2173+
require.NotContains(t, string(c.Response().Body()), "<script>", "Script tags in struct should be escaped")
2174+
require.Contains(t, string(c.Response().Body()), "&lt;script&gt;", "Escaped script tags should be present")
2175+
}
2176+
20992177
// go test -v -run=^$ -bench=Benchmark_Ctx_AutoFormat -benchmem -count=4
21002178
func Benchmark_Ctx_AutoFormat(b *testing.B) {
21012179
app := New()

res.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package fiber
33
import (
44
"bufio"
55
"fmt"
6+
"html"
67
"html/template"
78
"io"
89
"io/fs"
@@ -457,7 +458,7 @@ func (r *DefaultRes) AutoFormat(body any) error {
457458
case "xml":
458459
return r.XML(body)
459460
case "html":
460-
return r.SendString("<p>" + b + "</p>")
461+
return r.SendString("<p>" + html.EscapeString(b) + "</p>")
461462
case "msgpack":
462463
return r.MsgPack(body)
463464
case "cbor":

0 commit comments

Comments
 (0)