From 7d9a13241cbee9586382387c830e78deefd947e3 Mon Sep 17 00:00:00 2001 From: Rohan Guliani Date: Thu, 2 Apr 2026 19:59:34 +0000 Subject: [PATCH] Add comment and integration test for RPM routing collision Signed-off-by: Rohan Guliani --- routers/api/packages/api.go | 3 +++ tests/integration/api_packages_rpm_test.go | 24 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index ec5326130e..65fe7636b7 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -473,6 +473,9 @@ func CommonRoutes() *web.Router { g.MatchPath("GET", "//repodata/", rpm.GetRepositoryFile) g.MatchPath("PUT", "//upload", reqPackageAccess(perm.AccessModeWrite), rpm.UploadPackageFile) // this URL pattern is only used internally in the RPM index, it is generated by us, the filename part is not really used (can be anything) + // Order is important here: the more specific long route (with filename) must come first. + // If the short route is first, it can greedily match the filename as part of the architecture + // when the package name itself contains the string "package". g.MatchPath("HEAD,GET", "//package////", rpm.DownloadPackageFile) g.MatchPath("HEAD,GET", "//package///", rpm.DownloadPackageFile) g.MatchPath("DELETE", "//package///", reqPackageAccess(perm.AccessModeWrite), rpm.DeletePackageFile) diff --git a/tests/integration/api_packages_rpm_test.go b/tests/integration/api_packages_rpm_test.go index 61bd8ffc63..59f6c4a1f1 100644 --- a/tests/integration/api_packages_rpm_test.go +++ b/tests/integration/api_packages_rpm_test.go @@ -15,6 +15,7 @@ import ( "strings" "testing" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -164,6 +165,29 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s/any-file-name", groupURL, packageName, packageVersion, packageArchitecture)) resp = MakeRequest(t, req, http.StatusOK) assert.Equal(t, content, resp.Body.Bytes()) + + // Test with "package" in the name (reproduces bug if fix not applied) + pvs, err := packages.GetVersionsByPackageType(t.Context(), user.ID, packages.TypeRpm) + assert.NoError(t, err) + require.NotEmpty(t, pvs) + + // Rename package to "gitea-package" in DB to test collision + _, err = db.GetEngine(t.Context()).Exec("UPDATE package SET name = ?, lower_name = ? WHERE id = ?", "gitea-package", "gitea-package", pvs[0].PackageID) + assert.NoError(t, err) + + // Rename package_file to match new name + _, err = db.GetEngine(t.Context()).Exec("UPDATE package_file SET name = ?, lower_name = ? WHERE version_id = ?", "gitea-package-1.0.2-1.x86_64.rpm", "gitea-package-1.0.2-1.x86_64.rpm", pvs[0].ID) + assert.NoError(t, err) + + defer func() { + // Restore name + db.GetEngine(t.Context()).Exec("UPDATE package SET name = ?, lower_name = ? WHERE id = ?", packageName, strings.ToLower(packageName), pvs[0].PackageID) + db.GetEngine(t.Context()).Exec("UPDATE package_file SET name = ?, lower_name = ? WHERE version_id = ?", fmt.Sprintf("%s-%s.%s.rpm", packageName, packageVersion, packageArchitecture), strings.ToLower(fmt.Sprintf("%s-%s.%s.rpm", packageName, packageVersion, packageArchitecture)), pvs[0].ID) + }() + + req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s/any-file-name", groupURL, "gitea-package", packageVersion, packageArchitecture)) + resp = MakeRequest(t, req, http.StatusOK) + assert.Equal(t, content, resp.Body.Bytes()) }) t.Run("Repository", func(t *testing.T) {