diff --git a/routers/api/packages/debian/debian.go b/routers/api/packages/debian/debian.go index f9bf2960f8..6491f7ed1d 100644 --- a/routers/api/packages/debian/debian.go +++ b/routers/api/packages/debian/debian.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "net/http" + "regexp" "strings" "gitea.dev/models/db" @@ -23,6 +24,16 @@ import ( debian_service "gitea.dev/services/packages/debian" ) +// distribution and component are taken from the request path and written +// verbatim into the generated line-based Release and Packages indices (and +// into the pool// paths referenced from them), so +// they must be restricted to a character set that cannot break that format. +var distributionOrComponentPattern = regexp.MustCompile(`\A[a-zA-Z0-9][a-zA-Z0-9.~+_-]*\z`) + +func isValidDistributionOrComponent(s string) bool { + return distributionOrComponentPattern.MatchString(s) +} + func apiError(ctx *context.Context, status int, obj any) { message := helper.ProcessErrorForUser(ctx, status, obj) ctx.PlainText(status, message) @@ -122,7 +133,7 @@ func GetRepositoryFileByHash(ctx *context.Context) { func UploadPackageFile(ctx *context.Context) { distribution := strings.TrimSpace(ctx.PathParam("distribution")) component := strings.TrimSpace(ctx.PathParam("component")) - if distribution == "" || component == "" { + if !isValidDistributionOrComponent(distribution) || !isValidDistributionOrComponent(component) { apiError(ctx, http.StatusBadRequest, "invalid distribution or component") return } diff --git a/routers/api/packages/debian/debian_test.go b/routers/api/packages/debian/debian_test.go new file mode 100644 index 0000000000..50e1bc7dbf --- /dev/null +++ b/routers/api/packages/debian/debian_test.go @@ -0,0 +1,42 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package debian + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidateDistributionOrComponent(t *testing.T) { + bad := []string{ + "", + ".", + "..", + "-stable", + ".hidden", + "a/b", + "a b", + "bookworm\nSigned-By: evil", + "main\nFilename: pool/x", + "a\tb", + } + for _, name := range bad { + assert.False(t, isValidDistributionOrComponent(name), "bad=%q", name) + } + + good := []string{ + "stable", + "bookworm", + "bookworm-backports", + "stable-updates", + "main", + "non-free-firmware", + "a", + "1", + } + for _, name := range good { + assert.True(t, isValidDistributionOrComponent(name), "good=%q", name) + } +}