Now, the chars `=:;()[]{}~!@#$%^ &` are possible as well Fixes #30134 --------- Co-authored-by: KN4CK3R <admin@oldschoolhack.me>tags/v1.22.0-rc0
"net/http" | "net/http" | ||||
"regexp" | "regexp" | ||||
"strings" | "strings" | ||||
"unicode" | |||||
packages_model "code.gitea.io/gitea/models/packages" | packages_model "code.gitea.io/gitea/models/packages" | ||||
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
) | ) | ||||
var ( | var ( | ||||
packageNameRegex = regexp.MustCompile(`\A[A-Za-z0-9\.\_\-\+]+\z`) | |||||
filenameRegex = packageNameRegex | |||||
packageNameRegex = regexp.MustCompile(`\A[-_+.\w]+\z`) | |||||
filenameRegex = regexp.MustCompile(`\A[-_+=:;.()\[\]{}~!@#$%^& \w]+\z`) | |||||
) | ) | ||||
func apiError(ctx *context.Context, status int, obj any) { | func apiError(ctx *context.Context, status int, obj any) { | ||||
helper.ServePackageFile(ctx, s, u, pf) | helper.ServePackageFile(ctx, s, u, pf) | ||||
} | } | ||||
func isValidPackageName(packageName string) bool { | |||||
if len(packageName) == 1 && !unicode.IsLetter(rune(packageName[0])) && !unicode.IsNumber(rune(packageName[0])) { | |||||
return false | |||||
} | |||||
return packageNameRegex.MatchString(packageName) && packageName != ".." | |||||
} | |||||
func isValidFileName(filename string) bool { | |||||
return filenameRegex.MatchString(filename) && | |||||
strings.TrimSpace(filename) == filename && | |||||
filename != "." && filename != ".." | |||||
} | |||||
// UploadPackage uploads the specific generic package. | // UploadPackage uploads the specific generic package. | ||||
// Duplicated packages get rejected. | // Duplicated packages get rejected. | ||||
func UploadPackage(ctx *context.Context) { | func UploadPackage(ctx *context.Context) { | ||||
packageName := ctx.Params("packagename") | packageName := ctx.Params("packagename") | ||||
filename := ctx.Params("filename") | filename := ctx.Params("filename") | ||||
if !packageNameRegex.MatchString(packageName) || !filenameRegex.MatchString(filename) { | |||||
apiError(ctx, http.StatusBadRequest, errors.New("Invalid package name or filename")) | |||||
if !isValidPackageName(packageName) { | |||||
apiError(ctx, http.StatusBadRequest, errors.New("invalid package name")) | |||||
return | |||||
} | |||||
if !isValidFileName(filename) { | |||||
apiError(ctx, http.StatusBadRequest, errors.New("invalid filename")) | |||||
return | return | ||||
} | } | ||||
packageVersion := ctx.Params("packageversion") | packageVersion := ctx.Params("packageversion") | ||||
if packageVersion != strings.TrimSpace(packageVersion) { | if packageVersion != strings.TrimSpace(packageVersion) { | ||||
apiError(ctx, http.StatusBadRequest, errors.New("Invalid package version")) | |||||
apiError(ctx, http.StatusBadRequest, errors.New("invalid package version")) | |||||
return | return | ||||
} | } | ||||
// Copyright 2024 The Gitea Authors. All rights reserved. | |||||
// SPDX-License-Identifier: MIT | |||||
package generic | |||||
import ( | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
) | |||||
func TestValidatePackageName(t *testing.T) { | |||||
bad := []string{ | |||||
"", | |||||
".", | |||||
"..", | |||||
"-", | |||||
"a?b", | |||||
"a b", | |||||
"a/b", | |||||
} | |||||
for _, name := range bad { | |||||
assert.False(t, isValidPackageName(name), "bad=%q", name) | |||||
} | |||||
good := []string{ | |||||
"a", | |||||
"1", | |||||
"a-", | |||||
"a_b", | |||||
"c.d+", | |||||
} | |||||
for _, name := range good { | |||||
assert.True(t, isValidPackageName(name), "good=%q", name) | |||||
} | |||||
} | |||||
func TestValidateFileName(t *testing.T) { | |||||
bad := []string{ | |||||
"", | |||||
".", | |||||
"..", | |||||
"a?b", | |||||
"a/b", | |||||
" a", | |||||
"a ", | |||||
} | |||||
for _, name := range bad { | |||||
assert.False(t, isValidFileName(name), "bad=%q", name) | |||||
} | |||||
good := []string{ | |||||
"-", | |||||
"a", | |||||
"1", | |||||
"a-", | |||||
"a_b", | |||||
"a b", | |||||
"c.d+", | |||||
`-_+=:;.()[]{}~!@#$%^& aA1`, | |||||
} | |||||
for _, name := range good { | |||||
assert.True(t, isValidFileName(name), "good=%q", name) | |||||
} | |||||
} |
t.Run("InvalidParameter", func(t *testing.T) { | t.Run("InvalidParameter", func(t *testing.T) { | ||||
defer tests.PrintCurrentTest(t)() | defer tests.PrintCurrentTest(t)() | ||||
req := NewRequestWithBody(t, "PUT", fmt.Sprintf("/api/packages/%s/generic/%s/%s/%s", user.Name, "invalid+package name", packageVersion, filename), bytes.NewReader(content)). | |||||
req := NewRequestWithBody(t, "PUT", fmt.Sprintf("/api/packages/%s/generic/%s/%s/%s", user.Name, "invalid package name", packageVersion, filename), bytes.NewReader(content)). | |||||
AddBasicAuth(user.Name) | AddBasicAuth(user.Name) | ||||
MakeRequest(t, req, http.StatusBadRequest) | MakeRequest(t, req, http.StatusBadRequest) | ||||
AddBasicAuth(user.Name) | AddBasicAuth(user.Name) | ||||
MakeRequest(t, req, http.StatusBadRequest) | MakeRequest(t, req, http.StatusBadRequest) | ||||
req = NewRequestWithBody(t, "PUT", fmt.Sprintf("/api/packages/%s/generic/%s/%s/%s", user.Name, packageName, packageVersion, "inval+id.na me"), bytes.NewReader(content)). | |||||
req = NewRequestWithBody(t, "PUT", fmt.Sprintf("/api/packages/%s/generic/%s/%s/%s", user.Name, packageName, packageVersion, "inva|id.name"), bytes.NewReader(content)). | |||||
AddBasicAuth(user.Name) | AddBasicAuth(user.Name) | ||||
MakeRequest(t, req, http.StatusBadRequest) | MakeRequest(t, req, http.StatusBadRequest) | ||||
}) | }) |