code.gitea.io/gitea@v1.19.3/modules/packages/chef/metadata.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package chef
     5  
     6  import (
     7  	"archive/tar"
     8  	"compress/gzip"
     9  	"io"
    10  	"regexp"
    11  	"strings"
    12  
    13  	"code.gitea.io/gitea/modules/json"
    14  	"code.gitea.io/gitea/modules/util"
    15  	"code.gitea.io/gitea/modules/validation"
    16  )
    17  
    18  const (
    19  	KeyBits          = 4096
    20  	SettingPublicPem = "chef.public_pem"
    21  )
    22  
    23  var (
    24  	ErrMissingMetadataFile = util.NewInvalidArgumentErrorf("metadata.json file is missing")
    25  	ErrInvalidName         = util.NewInvalidArgumentErrorf("package name is invalid")
    26  	ErrInvalidVersion      = util.NewInvalidArgumentErrorf("package version is invalid")
    27  
    28  	namePattern    = regexp.MustCompile(`\A\S+\z`)
    29  	versionPattern = regexp.MustCompile(`\A\d+\.\d+(?:\.\d+)?\z`)
    30  )
    31  
    32  // Package represents a Chef package
    33  type Package struct {
    34  	Name     string
    35  	Version  string
    36  	Metadata *Metadata
    37  }
    38  
    39  // Metadata represents the metadata of a Chef package
    40  type Metadata struct {
    41  	Description     string            `json:"description,omitempty"`
    42  	LongDescription string            `json:"long_description,omitempty"`
    43  	Author          string            `json:"author,omitempty"`
    44  	License         string            `json:"license,omitempty"`
    45  	RepositoryURL   string            `json:"repository_url,omitempty"`
    46  	Dependencies    map[string]string `json:"dependencies,omitempty"`
    47  }
    48  
    49  type chefMetadata struct {
    50  	Name               string            `json:"name"`
    51  	Description        string            `json:"description"`
    52  	LongDescription    string            `json:"long_description"`
    53  	Maintainer         string            `json:"maintainer"`
    54  	MaintainerEmail    string            `json:"maintainer_email"`
    55  	License            string            `json:"license"`
    56  	Platforms          map[string]string `json:"platforms"`
    57  	Dependencies       map[string]string `json:"dependencies"`
    58  	Providing          map[string]string `json:"providing"`
    59  	Recipes            map[string]string `json:"recipes"`
    60  	Version            string            `json:"version"`
    61  	SourceURL          string            `json:"source_url"`
    62  	IssuesURL          string            `json:"issues_url"`
    63  	Privacy            bool              `json:"privacy"`
    64  	ChefVersions       [][]string        `json:"chef_versions"`
    65  	Gems               [][]string        `json:"gems"`
    66  	EagerLoadLibraries bool              `json:"eager_load_libraries"`
    67  }
    68  
    69  // ParsePackage parses the Chef package file
    70  func ParsePackage(r io.Reader) (*Package, error) {
    71  	gzr, err := gzip.NewReader(r)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	defer gzr.Close()
    76  
    77  	tr := tar.NewReader(gzr)
    78  	for {
    79  		hd, err := tr.Next()
    80  		if err == io.EOF {
    81  			break
    82  		}
    83  		if err != nil {
    84  			return nil, err
    85  		}
    86  
    87  		if hd.Typeflag != tar.TypeReg {
    88  			continue
    89  		}
    90  
    91  		if strings.Count(hd.Name, "/") != 1 {
    92  			continue
    93  		}
    94  
    95  		if hd.FileInfo().Name() == "metadata.json" {
    96  			return ParseChefMetadata(tr)
    97  		}
    98  	}
    99  
   100  	return nil, ErrMissingMetadataFile
   101  }
   102  
   103  // ParseChefMetadata parses a metadata.json file to retrieve the metadata of a Chef package
   104  func ParseChefMetadata(r io.Reader) (*Package, error) {
   105  	var cm chefMetadata
   106  	if err := json.NewDecoder(r).Decode(&cm); err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	if !namePattern.MatchString(cm.Name) {
   111  		return nil, ErrInvalidName
   112  	}
   113  
   114  	if !versionPattern.MatchString(cm.Version) {
   115  		return nil, ErrInvalidVersion
   116  	}
   117  
   118  	if !validation.IsValidURL(cm.SourceURL) {
   119  		cm.SourceURL = ""
   120  	}
   121  
   122  	return &Package{
   123  		Name:    cm.Name,
   124  		Version: cm.Version,
   125  		Metadata: &Metadata{
   126  			Description:     cm.Description,
   127  			LongDescription: cm.LongDescription,
   128  			Author:          cm.Maintainer,
   129  			License:         cm.License,
   130  			RepositoryURL:   cm.SourceURL,
   131  			Dependencies:    cm.Dependencies,
   132  		},
   133  	}, nil
   134  }