diff --git a/go.mod b/go.mod index a5f42c6cafc..9d23a994104 100644 --- a/go.mod +++ b/go.mod @@ -105,6 +105,7 @@ require ( go.yaml.in/yaml/v4 v4.0.0-rc.5 golang.org/x/crypto v0.53.0 golang.org/x/image v0.42.0 + golang.org/x/mod v0.37.0 golang.org/x/net v0.56.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.21.0 @@ -267,7 +268,6 @@ require ( go.uber.org/zap/exp v0.3.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org v0.0.0-20260112195520-a5071408f32f // indirect - golang.org/x/mod v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.45.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad // indirect diff --git a/modules/packages/goproxy/metadata.go b/modules/packages/goproxy/metadata.go index b19738db56d..db50ac7faa3 100644 --- a/modules/packages/goproxy/metadata.go +++ b/modules/packages/goproxy/metadata.go @@ -10,6 +10,8 @@ import ( "strings" "gitea.dev/modules/util" + + "golang.org/x/mod/semver" ) const ( @@ -20,6 +22,7 @@ const ( var ( ErrInvalidStructure = util.NewInvalidArgumentErrorf("package has invalid structure") + ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid") ErrGoModFileTooLarge = util.NewInvalidArgumentErrorf("go.mod file is too large") ) @@ -54,6 +57,13 @@ func ParsePackage(r io.ReaderAt, size int64) (*Package, error) { Name: strings.TrimSuffix(nameAndVersion, "@"+parts[1]), Version: versionParts[0], } + + // the version is taken verbatim from the zip path and later written + // one per line into the @v/list proxy response, so it has to be a + // valid module version (no newlines or other stray characters) + if !semver.IsValid(p.Version) { + return nil, ErrInvalidVersion + } } if len(versionParts) > 1 { diff --git a/modules/packages/goproxy/metadata_test.go b/modules/packages/goproxy/metadata_test.go index 4e7f394f8bc..24db3596510 100644 --- a/modules/packages/goproxy/metadata_test.go +++ b/modules/packages/goproxy/metadata_test.go @@ -59,6 +59,16 @@ func TestParsePackage(t *testing.T) { assert.Equal(t, "module gitea.com/go-gitea/gitea", p.GoMod) }) + t.Run("InvalidVersion", func(t *testing.T) { + data := createArchive(map[string][]byte{ + packageName + "@v1.0.0\nv99.0.0/go.mod": []byte("module " + packageName), + }) + + p, err := ParsePackage(data, int64(data.Len())) + assert.Nil(t, p) + assert.ErrorIs(t, err, ErrInvalidVersion) + }) + t.Run("Valid", func(t *testing.T) { data := createArchive(map[string][]byte{ packageName + "@" + packageVersion + "/subdir/go.mod": []byte("invalid"),