code.gitea.io/gitea@v1.22.3/modules/packages/goproxy/metadata.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package goproxy 5 6 import ( 7 "archive/zip" 8 "fmt" 9 "io" 10 "path" 11 "strings" 12 13 "code.gitea.io/gitea/modules/util" 14 ) 15 16 const ( 17 PropertyGoMod = "go.mod" 18 19 maxGoModFileSize = 16 * 1024 * 1024 // https://go.dev/ref/mod#zip-path-size-constraints 20 ) 21 22 var ( 23 ErrInvalidStructure = util.NewInvalidArgumentErrorf("package has invalid structure") 24 ErrGoModFileTooLarge = util.NewInvalidArgumentErrorf("go.mod file is too large") 25 ) 26 27 type Package struct { 28 Name string 29 Version string 30 GoMod string 31 } 32 33 // ParsePackage parses the Go package file 34 // https://go.dev/ref/mod#zip-files 35 func ParsePackage(r io.ReaderAt, size int64) (*Package, error) { 36 archive, err := zip.NewReader(r, size) 37 if err != nil { 38 return nil, err 39 } 40 41 var p *Package 42 43 for _, file := range archive.File { 44 nameAndVersion := path.Dir(file.Name) 45 46 parts := strings.SplitN(nameAndVersion, "@", 2) 47 if len(parts) != 2 { 48 continue 49 } 50 51 versionParts := strings.SplitN(parts[1], "/", 2) 52 53 if p == nil { 54 p = &Package{ 55 Name: strings.TrimSuffix(nameAndVersion, "@"+parts[1]), 56 Version: versionParts[0], 57 } 58 } 59 60 if len(versionParts) > 1 { 61 // files are expected in the "root" folder 62 continue 63 } 64 65 if path.Base(file.Name) == "go.mod" { 66 if file.UncompressedSize64 > maxGoModFileSize { 67 return nil, ErrGoModFileTooLarge 68 } 69 70 f, err := archive.Open(file.Name) 71 if err != nil { 72 return nil, err 73 } 74 defer f.Close() 75 76 bytes, err := io.ReadAll(&io.LimitedReader{R: f, N: maxGoModFileSize}) 77 if err != nil { 78 return nil, err 79 } 80 81 p.GoMod = string(bytes) 82 83 return p, nil 84 } 85 } 86 87 if p == nil { 88 return nil, ErrInvalidStructure 89 } 90 91 p.GoMod = fmt.Sprintf("module %s", p.Name) 92 93 return p, nil 94 }