chore: fix form string abuse (#38106)

This commit is contained in:
wxiaoguang
2026-06-15 02:26:22 +08:00
committed by GitHub
parent 3417bc8979
commit 47d48eb208
8 changed files with 25 additions and 47 deletions
+8 -4
View File
@@ -63,20 +63,24 @@ func (s *Sitemap) Add(u URL) {
// WriteTo writes the sitemap to a response // WriteTo writes the sitemap to a response
func (s *Sitemap) WriteTo(w io.Writer) (int64, error) { func (s *Sitemap) WriteTo(w io.Writer) (int64, error) {
if l := len(s.URLs); l > urlsLimit { if l := len(s.URLs); l > urlsLimit {
return 0, fmt.Errorf("The sitemap contains %d URLs, but only %d are allowed", l, urlsLimit) return 0, fmt.Errorf("sitemap contains %d URLs, but only %d are allowed", l, urlsLimit)
} }
if l := len(s.Sitemaps); l > urlsLimit { if l := len(s.Sitemaps); l > urlsLimit {
return 0, fmt.Errorf("The sitemap contains %d sub-sitemaps, but only %d are allowed", l, urlsLimit) return 0, fmt.Errorf("sitemap contains %d sub-sitemaps, but only %d are allowed", l, urlsLimit)
} }
buf := bytes.NewBufferString(xml.Header) buf := bytes.NewBufferString(xml.Header)
if err := xml.NewEncoder(buf).Encode(s); err != nil { encoder := xml.NewEncoder(buf)
defer encoder.Close()
if err := encoder.Encode(s); err != nil {
return 0, err return 0, err
} }
_ = encoder.Flush()
if err := buf.WriteByte('\n'); err != nil { if err := buf.WriteByte('\n'); err != nil {
return 0, err return 0, err
} }
// FIXME: such limit is not right, the content has been written, it would have already caused OOM
if buf.Len() > sitemapFileLimit { if buf.Len() > sitemapFileLimit {
return 0, fmt.Errorf("The sitemap has %d bytes, but only %d are allowed", buf.Len(), sitemapFileLimit) return 0, fmt.Errorf("sitemap has %d bytes, but only %d are allowed", buf.Len(), sitemapFileLimit)
} }
return buf.WriteTo(w) return buf.WriteTo(w)
} }
+4 -4
View File
@@ -61,14 +61,14 @@ func TestNewSitemap(t *testing.T) {
{ {
name: "too many urls", name: "too many urls",
urls: make([]URL, 50001), urls: make([]URL, 50001),
wantErr: "The sitemap contains 50001 URLs, but only 50000 are allowed", wantErr: "sitemap contains 50001 URLs, but only 50000 are allowed",
}, },
{ {
name: "too big file", name: "too big file",
urls: []URL{ urls: []URL{
{URL: strings.Repeat("b", 50*1024*1024+1)}, {URL: strings.Repeat("b", 50*1024*1024+1)},
}, },
wantErr: "The sitemap has 52428932 bytes, but only 52428800 are allowed", wantErr: "sitemap has 52428932 bytes, but only 52428800 are allowed",
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@@ -137,14 +137,14 @@ func TestNewSitemapIndex(t *testing.T) {
{ {
name: "too many sitemaps", name: "too many sitemaps",
urls: make([]URL, 50001), urls: make([]URL, 50001),
wantErr: "The sitemap contains 50001 sub-sitemaps, but only 50000 are allowed", wantErr: "sitemap contains 50001 sub-sitemaps, but only 50000 are allowed",
}, },
{ {
name: "too big file", name: "too big file",
urls: []URL{ urls: []URL{
{URL: strings.Repeat("b", 50*1024*1024+1)}, {URL: strings.Repeat("b", 50*1024*1024+1)},
}, },
wantErr: "The sitemap has 52428952 bytes, but only 52428800 are allowed", wantErr: "sitemap has 52428952 bytes, but only 52428800 are allowed",
}, },
} }
for _, tt := range tests { for _, tt := range tests {
+2 -4
View File
@@ -23,10 +23,7 @@ func Organizations(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.organizations") ctx.Data["Title"] = ctx.Tr("admin.organizations")
ctx.Data["PageIsAdminOrganizations"] = true ctx.Data["PageIsAdminOrganizations"] = true
if ctx.FormString("sort") == "" { sortOrder := ctx.FormString("sort", UserSearchDefaultAdminSort)
ctx.SetFormString("sort", UserSearchDefaultAdminSort)
}
explore.RenderUserSearch(ctx, user_model.SearchUserOptions{ explore.RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Types: []user_model.UserType{user_model.UserTypeOrganization}, Types: []user_model.UserType{user_model.UserTypeOrganization},
@@ -35,5 +32,6 @@ func Organizations(ctx *context.Context) {
PageSize: setting.UI.Admin.OrgPagingNum, PageSize: setting.UI.Admin.OrgPagingNum,
}, },
Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
OrderBy: db.SearchOrderBy(sortOrder),
}, tplOrgs) }, tplOrgs)
} }
+2 -5
View File
@@ -55,11 +55,7 @@ func Users(ctx *context.Context) {
statusFilterMap[filterKey] = paramVal statusFilterMap[filterKey] = paramVal
} }
sortType := ctx.FormString("sort") sortType := ctx.FormString("sort", UserSearchDefaultAdminSort)
if sortType == "" {
sortType = UserSearchDefaultAdminSort
ctx.SetFormString("sort", sortType)
}
ctx.PageData["adminUserListSearchForm"] = map[string]any{ ctx.PageData["adminUserListSearchForm"] = map[string]any{
"StatusFilterMap": statusFilterMap, "StatusFilterMap": statusFilterMap,
"SortType": sortType, "SortType": sortType,
@@ -78,6 +74,7 @@ func Users(ctx *context.Context) {
IsTwoFactorEnabled: optional.ParseBool(statusFilterMap["is_2fa_enabled"]), IsTwoFactorEnabled: optional.ParseBool(statusFilterMap["is_2fa_enabled"]),
IsProhibitLogin: optional.ParseBool(statusFilterMap["is_prohibit_login"]), IsProhibitLogin: optional.ParseBool(statusFilterMap["is_prohibit_login"]),
IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones
OrderBy: db.SearchOrderBy(sortType),
}, tplUsers) }, tplUsers)
} }
+3 -6
View File
@@ -38,17 +38,14 @@ func Organizations(ctx *context.Context) {
"alphabetically", "alphabetically",
"reversealphabetically", "reversealphabetically",
) )
sortOrder := ctx.FormString("sort") sortOrderDefault := util.Iif(supportedSortOrders.Contains(setting.UI.ExploreDefaultSort), setting.UI.ExploreDefaultSort, "newest")
if sortOrder == "" { sortOrder := ctx.FormString("sort", sortOrderDefault)
sortOrder = util.Iif(supportedSortOrders.Contains(setting.UI.ExploreDefaultSort), setting.UI.ExploreDefaultSort, "newest")
ctx.SetFormString("sort", sortOrder)
}
RenderUserSearch(ctx, user_model.SearchUserOptions{ RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Types: []user_model.UserType{user_model.UserTypeOrganization}, Types: []user_model.UserType{user_model.UserTypeOrganization},
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
Visible: visibleTypes, Visible: visibleTypes,
OrderBy: db.SearchOrderBy(sortOrder),
SupportedSortOrders: supportedSortOrders, SupportedSortOrders: supportedSortOrders,
}, tplExploreUsers) }, tplExploreUsers)
+4 -11
View File
@@ -55,11 +55,7 @@ func RenderUserSearch(ctx *context.Context, opts user_model.SearchUserOptions, t
) )
// we can not set orderBy to `models.SearchOrderByXxx`, because there may be a JOIN in the statement, different tables may have the same name columns // we can not set orderBy to `models.SearchOrderByXxx`, because there may be a JOIN in the statement, different tables may have the same name columns
sortOrder := util.IfZero(string(opts.OrderBy), ctx.FormString("sort", setting.UI.ExploreDefaultSort))
sortOrder := ctx.FormString("sort")
if sortOrder == "" {
sortOrder = setting.UI.ExploreDefaultSort
}
ctx.Data["SortType"] = sortOrder ctx.Data["SortType"] = sortOrder
switch sortOrder { switch sortOrder {
@@ -145,18 +141,15 @@ func Users(ctx *context.Context) {
"alphabetically", "alphabetically",
"reversealphabetically", "reversealphabetically",
) )
sortOrder := ctx.FormString("sort") sortOrderDefault := util.Iif(supportedSortOrders.Contains(setting.UI.ExploreDefaultSort), setting.UI.ExploreDefaultSort, "newest")
if sortOrder == "" { sortOrder := ctx.FormString("sort", sortOrderDefault)
sortOrder = util.Iif(supportedSortOrders.Contains(setting.UI.ExploreDefaultSort), setting.UI.ExploreDefaultSort, "newest")
ctx.SetFormString("sort", sortOrder)
}
RenderUserSearch(ctx, user_model.SearchUserOptions{ RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Types: []user_model.UserType{user_model.UserTypeIndividual}, Types: []user_model.UserType{user_model.UserTypeIndividual},
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
IsActive: optional.Some(true), IsActive: optional.Some(true),
Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
OrderBy: db.SearchOrderBy(sortOrder),
SupportedSortOrders: supportedSortOrders, SupportedSortOrders: supportedSortOrders,
}, tplExploreUsers) }, tplExploreUsers)
@@ -15,22 +15,16 @@ import (
func TestDeleteOpenIDReturnsNotFoundForOtherUsersAddress(t *testing.T) { func TestDeleteOpenIDReturnsNotFoundForOtherUsersAddress(t *testing.T) {
unittest.PrepareTestEnv(t) unittest.PrepareTestEnv(t)
ctx, _ := contexttest.MockContext(t, "POST /user/settings/security") ctx, _ := contexttest.MockContext(t, "POST /user/settings/security?id=1")
contexttest.LoadUser(t, ctx, 2) contexttest.LoadUser(t, ctx, 2)
ctx.SetFormString("id", "1")
DeleteOpenID(ctx) DeleteOpenID(ctx)
assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus()) assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus())
} }
func TestToggleOpenIDVisibilityReturnsNotFoundForOtherUsersAddress(t *testing.T) { func TestToggleOpenIDVisibilityReturnsNotFoundForOtherUsersAddress(t *testing.T) {
unittest.PrepareTestEnv(t) unittest.PrepareTestEnv(t)
ctx, _ := contexttest.MockContext(t, "POST /user/settings/security") ctx, _ := contexttest.MockContext(t, "POST /user/settings/security?id=1")
contexttest.LoadUser(t, ctx, 2) contexttest.LoadUser(t, ctx, 2)
ctx.SetFormString("id", "1")
ToggleOpenIDVisibility(ctx) ToggleOpenIDVisibility(ctx)
assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus()) assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus())
} }
-5
View File
@@ -78,8 +78,3 @@ func (b *Base) FormOptionalBool(key string) optional.Option[bool] {
v = v || strings.EqualFold(s, "on") v = v || strings.EqualFold(s, "on")
return optional.Some(v) return optional.Some(v)
} }
func (b *Base) SetFormString(key, value string) {
_ = b.Req.FormValue(key) // force parse form
b.Req.Form.Set(key, value)
}