You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

metadata.go 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package pub
  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. "github.com/hashicorp/go-version"
  13. "gopkg.in/yaml.v3"
  14. )
  15. var (
  16. ErrMissingPubspecFile = util.NewInvalidArgumentErrorf("Pubspec file is missing")
  17. ErrPubspecFileTooLarge = util.NewInvalidArgumentErrorf("Pubspec file is too large")
  18. ErrInvalidName = util.NewInvalidArgumentErrorf("package name is invalid")
  19. ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
  20. )
  21. var namePattern = regexp.MustCompile(`\A[a-zA-Z_][a-zA-Z0-9_]*\z`)
  22. // https://github.com/dart-lang/pub-dev/blob/4d582302a8d10152a5cd6129f65bf4f4dbca239d/pkg/pub_package_reader/lib/pub_package_reader.dart#L143
  23. const maxPubspecFileSize = 128 * 1024
  24. // Package represents a Pub package
  25. type Package struct {
  26. Name string
  27. Version string
  28. Metadata *Metadata
  29. }
  30. // Metadata represents the metadata of a Pub package
  31. type Metadata struct {
  32. Description string `json:"description,omitempty"`
  33. ProjectURL string `json:"project_url,omitempty"`
  34. RepositoryURL string `json:"repository_url,omitempty"`
  35. DocumentationURL string `json:"documentation_url,omitempty"`
  36. Readme string `json:"readme,omitempty"`
  37. Pubspec interface{} `json:"pubspec"`
  38. }
  39. type pubspecPackage struct {
  40. Name string `yaml:"name"`
  41. Version string `yaml:"version"`
  42. Description string `yaml:"description"`
  43. Homepage string `yaml:"homepage"`
  44. Repository string `yaml:"repository"`
  45. Documentation string `yaml:"documentation"`
  46. }
  47. // ParsePackage parses the Pub package file
  48. func ParsePackage(r io.Reader) (*Package, error) {
  49. gzr, err := gzip.NewReader(r)
  50. if err != nil {
  51. return nil, err
  52. }
  53. defer gzr.Close()
  54. var p *Package
  55. var readme string
  56. tr := tar.NewReader(gzr)
  57. for {
  58. hd, err := tr.Next()
  59. if err == io.EOF {
  60. break
  61. }
  62. if err != nil {
  63. return nil, err
  64. }
  65. if hd.Typeflag != tar.TypeReg {
  66. continue
  67. }
  68. if hd.Name == "pubspec.yaml" {
  69. if hd.Size > maxPubspecFileSize {
  70. return nil, ErrPubspecFileTooLarge
  71. }
  72. p, err = ParsePubspecMetadata(tr)
  73. if err != nil {
  74. return nil, err
  75. }
  76. } else if strings.ToLower(hd.Name) == "readme.md" {
  77. data, err := io.ReadAll(tr)
  78. if err != nil {
  79. return nil, err
  80. }
  81. readme = string(data)
  82. }
  83. }
  84. if p == nil {
  85. return nil, ErrMissingPubspecFile
  86. }
  87. p.Metadata.Readme = readme
  88. return p, nil
  89. }
  90. // ParsePubspecMetadata parses a Pubspec file to retrieve the metadata of a Pub package
  91. func ParsePubspecMetadata(r io.Reader) (*Package, error) {
  92. buf, err := io.ReadAll(io.LimitReader(r, maxPubspecFileSize))
  93. if err != nil {
  94. return nil, err
  95. }
  96. var p pubspecPackage
  97. if err := yaml.Unmarshal(buf, &p); err != nil {
  98. return nil, err
  99. }
  100. if !namePattern.MatchString(p.Name) {
  101. return nil, ErrInvalidName
  102. }
  103. v, err := version.NewSemver(p.Version)
  104. if err != nil {
  105. return nil, ErrInvalidVersion
  106. }
  107. if !validation.IsValidURL(p.Homepage) {
  108. p.Homepage = ""
  109. }
  110. if !validation.IsValidURL(p.Repository) {
  111. p.Repository = ""
  112. }
  113. var pubspec interface{}
  114. if err := yaml.Unmarshal(buf, &pubspec); err != nil {
  115. return nil, err
  116. }
  117. return &Package{
  118. Name: p.Name,
  119. Version: v.String(),
  120. Metadata: &Metadata{
  121. Description: p.Description,
  122. ProjectURL: p.Homepage,
  123. RepositoryURL: p.Repository,
  124. DocumentationURL: p.Documentation,
  125. Pubspec: pubspec,
  126. },
  127. }, nil
  128. }