gitea源码

metadata.go 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package rubygems
  4. import (
  5. "archive/tar"
  6. "compress/gzip"
  7. "io"
  8. "regexp"
  9. "strings"
  10. "code.gitea.io/gitea/modules/util"
  11. "code.gitea.io/gitea/modules/validation"
  12. "gopkg.in/yaml.v3"
  13. )
  14. var (
  15. // ErrMissingMetadataFile indicates a missing metadata.gz file
  16. ErrMissingMetadataFile = util.NewInvalidArgumentErrorf("metadata.gz file is missing")
  17. // ErrInvalidName indicates an invalid id in the metadata.gz file
  18. ErrInvalidName = util.NewInvalidArgumentErrorf("package name is invalid")
  19. // ErrInvalidVersion indicates an invalid version in the metadata.gz file
  20. ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
  21. )
  22. var versionMatcher = regexp.MustCompile(`\A[0-9]+(?:\.[0-9a-zA-Z]+)*(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?\z`)
  23. // Package represents a RubyGems package
  24. type Package struct {
  25. Name string
  26. Version string
  27. Metadata *Metadata
  28. }
  29. // Metadata represents the metadata of a RubyGems package
  30. type Metadata struct {
  31. Platform string `json:"platform,omitempty"`
  32. Description string `json:"description,omitempty"`
  33. Summary string `json:"summary,omitempty"`
  34. Authors []string `json:"authors,omitempty"`
  35. Licenses []string `json:"licenses,omitempty"`
  36. RequiredRubyVersion []VersionRequirement `json:"required_ruby_version,omitempty"`
  37. RequiredRubygemsVersion []VersionRequirement `json:"required_rubygems_version,omitempty"`
  38. ProjectURL string `json:"project_url,omitempty"`
  39. RuntimeDependencies []Dependency `json:"runtime_dependencies,omitempty"`
  40. DevelopmentDependencies []Dependency `json:"development_dependencies,omitempty"`
  41. }
  42. // VersionRequirement represents a version restriction
  43. type VersionRequirement struct {
  44. Restriction string `json:"restriction"`
  45. Version string `json:"version"`
  46. }
  47. // Dependency represents a dependency of a RubyGems package
  48. type Dependency struct {
  49. Name string `json:"name"`
  50. Version []VersionRequirement `json:"version"`
  51. }
  52. type gemspec struct {
  53. Name string `yaml:"name"`
  54. Version struct {
  55. Version string `yaml:"version"`
  56. } `yaml:"version"`
  57. Platform string `yaml:"platform"`
  58. Authors []string `yaml:"authors"`
  59. Autorequire any `yaml:"autorequire"`
  60. Bindir string `yaml:"bindir"`
  61. CertChain []any `yaml:"cert_chain"`
  62. Date string `yaml:"date"`
  63. Dependencies []struct {
  64. Name string `yaml:"name"`
  65. Requirement requirement `yaml:"requirement"`
  66. Type string `yaml:"type"`
  67. Prerelease bool `yaml:"prerelease"`
  68. VersionRequirements requirement `yaml:"version_requirements"`
  69. } `yaml:"dependencies"`
  70. Description string `yaml:"description"`
  71. Executables []string `yaml:"executables"`
  72. Extensions []any `yaml:"extensions"`
  73. ExtraRdocFiles []string `yaml:"extra_rdoc_files"`
  74. Files []string `yaml:"files"`
  75. Homepage string `yaml:"homepage"`
  76. Licenses []string `yaml:"licenses"`
  77. Metadata struct {
  78. BugTrackerURI string `yaml:"bug_tracker_uri"`
  79. ChangelogURI string `yaml:"changelog_uri"`
  80. DocumentationURI string `yaml:"documentation_uri"`
  81. SourceCodeURI string `yaml:"source_code_uri"`
  82. } `yaml:"metadata"`
  83. PostInstallMessage any `yaml:"post_install_message"`
  84. RdocOptions []any `yaml:"rdoc_options"`
  85. RequirePaths []string `yaml:"require_paths"`
  86. RequiredRubyVersion requirement `yaml:"required_ruby_version"`
  87. RequiredRubygemsVersion requirement `yaml:"required_rubygems_version"`
  88. Requirements []any `yaml:"requirements"`
  89. RubygemsVersion string `yaml:"rubygems_version"`
  90. SigningKey any `yaml:"signing_key"`
  91. SpecificationVersion int `yaml:"specification_version"`
  92. Summary string `yaml:"summary"`
  93. TestFiles []any `yaml:"test_files"`
  94. }
  95. type requirement struct {
  96. Requirements [][]any `yaml:"requirements"`
  97. }
  98. // AsVersionRequirement converts into []VersionRequirement
  99. func (r requirement) AsVersionRequirement() []VersionRequirement {
  100. requirements := make([]VersionRequirement, 0, len(r.Requirements))
  101. for _, req := range r.Requirements {
  102. if len(req) != 2 {
  103. continue
  104. }
  105. restriction, ok := req[0].(string)
  106. if !ok {
  107. continue
  108. }
  109. vm, ok := req[1].(map[string]any)
  110. if !ok {
  111. continue
  112. }
  113. versionInt, ok := vm["version"]
  114. if !ok {
  115. continue
  116. }
  117. version, ok := versionInt.(string)
  118. if !ok || version == "0" {
  119. continue
  120. }
  121. requirements = append(requirements, VersionRequirement{
  122. Restriction: restriction,
  123. Version: version,
  124. })
  125. }
  126. return requirements
  127. }
  128. // ParsePackageMetaData parses the metadata of a Gem package file
  129. func ParsePackageMetaData(r io.Reader) (*Package, error) {
  130. archive := tar.NewReader(r)
  131. for {
  132. hdr, err := archive.Next()
  133. if err == io.EOF {
  134. break
  135. }
  136. if err != nil {
  137. return nil, err
  138. }
  139. if hdr.Name == "metadata.gz" {
  140. return parseMetadataFile(archive)
  141. }
  142. }
  143. return nil, ErrMissingMetadataFile
  144. }
  145. func parseMetadataFile(r io.Reader) (*Package, error) {
  146. zr, err := gzip.NewReader(r)
  147. if err != nil {
  148. return nil, err
  149. }
  150. defer zr.Close()
  151. var spec gemspec
  152. if err := yaml.NewDecoder(zr).Decode(&spec); err != nil {
  153. return nil, err
  154. }
  155. if len(spec.Name) == 0 || strings.Contains(spec.Name, "/") {
  156. return nil, ErrInvalidName
  157. }
  158. if !versionMatcher.MatchString(spec.Version.Version) {
  159. return nil, ErrInvalidVersion
  160. }
  161. if !validation.IsValidURL(spec.Homepage) {
  162. spec.Homepage = ""
  163. }
  164. if !validation.IsValidURL(spec.Metadata.SourceCodeURI) {
  165. spec.Metadata.SourceCodeURI = ""
  166. }
  167. m := &Metadata{
  168. Platform: spec.Platform,
  169. Description: spec.Description,
  170. Summary: spec.Summary,
  171. Authors: spec.Authors,
  172. Licenses: spec.Licenses,
  173. ProjectURL: spec.Homepage,
  174. RequiredRubyVersion: spec.RequiredRubyVersion.AsVersionRequirement(),
  175. RequiredRubygemsVersion: spec.RequiredRubygemsVersion.AsVersionRequirement(),
  176. DevelopmentDependencies: make([]Dependency, 0, 5),
  177. RuntimeDependencies: make([]Dependency, 0, 5),
  178. }
  179. for _, gemdep := range spec.Dependencies {
  180. dep := Dependency{
  181. Name: gemdep.Name,
  182. Version: gemdep.Requirement.AsVersionRequirement(),
  183. }
  184. if gemdep.Type == ":runtime" {
  185. m.RuntimeDependencies = append(m.RuntimeDependencies, dep)
  186. } else {
  187. m.DevelopmentDependencies = append(m.DevelopmentDependencies, dep)
  188. }
  189. }
  190. return &Package{
  191. Name: spec.Name,
  192. Version: spec.Version.Version,
  193. Metadata: m,
  194. }, nil
  195. }