mirror of
https://github.com/go-gitea/gitea.git
synced 2026-06-17 19:10:22 +03:00
fix(packages): validate module version in goproxy ParsePackage (#38104)
**Unvalidated version in goproxy ParsePackage** The module version is read straight from the zip directory path and never checked, so a crafted upload can leave a newline in it; `EnumeratePackageVersions` then writes each stored version on its own line for the `@v/list` endpoint, letting a module advertise fabricated versions to `go` clients. Validated the parsed version with `semver.IsValid` inside the parser, matching the version checks the other package parsers already do. Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
@@ -105,6 +105,7 @@ require (
|
|||||||
go.yaml.in/yaml/v4 v4.0.0-rc.5
|
go.yaml.in/yaml/v4 v4.0.0-rc.5
|
||||||
golang.org/x/crypto v0.53.0
|
golang.org/x/crypto v0.53.0
|
||||||
golang.org/x/image v0.42.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/net v0.56.0
|
||||||
golang.org/x/oauth2 v0.36.0
|
golang.org/x/oauth2 v0.36.0
|
||||||
golang.org/x/sync v0.21.0
|
golang.org/x/sync v0.21.0
|
||||||
@@ -267,7 +268,6 @@ require (
|
|||||||
go.uber.org/zap/exp v0.3.0 // indirect
|
go.uber.org/zap/exp v0.3.0 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
go4.org v0.0.0-20260112195520-a5071408f32f // 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/time v0.15.0 // indirect
|
||||||
golang.org/x/tools v0.45.0 // indirect
|
golang.org/x/tools v0.45.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad // indirect
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.dev/modules/util"
|
"gitea.dev/modules/util"
|
||||||
|
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -20,6 +22,7 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidStructure = util.NewInvalidArgumentErrorf("package has invalid structure")
|
ErrInvalidStructure = util.NewInvalidArgumentErrorf("package has invalid structure")
|
||||||
|
ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
|
||||||
ErrGoModFileTooLarge = util.NewInvalidArgumentErrorf("go.mod file is too large")
|
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]),
|
Name: strings.TrimSuffix(nameAndVersion, "@"+parts[1]),
|
||||||
Version: versionParts[0],
|
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 {
|
if len(versionParts) > 1 {
|
||||||
|
|||||||
@@ -59,6 +59,16 @@ func TestParsePackage(t *testing.T) {
|
|||||||
assert.Equal(t, "module gitea.com/go-gitea/gitea", p.GoMod)
|
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) {
|
t.Run("Valid", func(t *testing.T) {
|
||||||
data := createArchive(map[string][]byte{
|
data := createArchive(map[string][]byte{
|
||||||
packageName + "@" + packageVersion + "/subdir/go.mod": []byte("invalid"),
|
packageName + "@" + packageVersion + "/subdir/go.mod": []byte("invalid"),
|
||||||
|
|||||||
Reference in New Issue
Block a user