github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/cataloger/swift/parse_package_resolved.go (about)

     1  package swift
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/anchore/syft/syft/artifact"
    10  	"github.com/anchore/syft/syft/file"
    11  	"github.com/anchore/syft/syft/pkg"
    12  	"github.com/anchore/syft/syft/pkg/cataloger/generic"
    13  )
    14  
    15  var _ generic.Parser = parsePackageResolved
    16  
    17  // swift package manager has two versions (1 and 2) of the resolved files, the types below describes the serialization strategies for each version
    18  // with its suffix indicating which version its specific to.
    19  
    20  type packageResolvedV1 struct {
    21  	PackageObject packageObjectV1 `json:"object"`
    22  	Version       int             `json:"version"`
    23  }
    24  
    25  type packageObjectV1 struct {
    26  	Pins []packagePinsV1
    27  }
    28  
    29  type packagePinsV1 struct {
    30  	Name          string       `json:"package"`
    31  	RepositoryURL string       `json:"repositoryURL"`
    32  	State         packageState `json:"state"`
    33  }
    34  
    35  type packageResolvedV2 struct {
    36  	Pins []packagePinsV2
    37  }
    38  
    39  type packagePinsV2 struct {
    40  	Identity string       `json:"identity"`
    41  	Kind     string       `json:"kind"`
    42  	Location string       `json:"location"`
    43  	State    packageState `json:"state"`
    44  }
    45  
    46  type packagePin struct {
    47  	Identity string
    48  	Location string
    49  	Revision string
    50  	Version  string
    51  }
    52  
    53  type packageState struct {
    54  	Revision string `json:"revision"`
    55  	Version  string `json:"version"`
    56  }
    57  
    58  // parsePackageResolved is a parser for the contents of a Package.resolved file, which is generated by Xcode after it's resolved Swift Package Manger packages.
    59  func parsePackageResolved(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
    60  	dec := json.NewDecoder(reader)
    61  	var packageResolvedData map[string]interface{}
    62  	for {
    63  		if err := dec.Decode(&packageResolvedData); errors.Is(err, io.EOF) {
    64  			break
    65  		} else if err != nil {
    66  			return nil, nil, fmt.Errorf("failed to parse Package.resolved file: %w", err)
    67  		}
    68  	}
    69  
    70  	var pins, err = pinsForVersion(packageResolvedData, packageResolvedData["version"].(float64))
    71  	if err != nil {
    72  		return nil, nil, err
    73  	}
    74  
    75  	var pkgs []pkg.Package
    76  	for _, packagePin := range pins {
    77  		pkgs = append(
    78  			pkgs,
    79  			newSwiftPackageManagerPackage(
    80  				packagePin.Identity,
    81  				packagePin.Version,
    82  				packagePin.Location,
    83  				packagePin.Revision,
    84  				reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
    85  			),
    86  		)
    87  	}
    88  	return pkgs, nil, nil
    89  }
    90  
    91  func pinsForVersion(data map[string]interface{}, version float64) ([]packagePin, error) {
    92  	var genericPins []packagePin
    93  	switch version {
    94  	case 1:
    95  		t := packageResolvedV1{}
    96  		jsonString, err := json.Marshal(data)
    97  		if err != nil {
    98  			return nil, err
    99  		}
   100  		parseErr := json.Unmarshal(jsonString, &t)
   101  		if parseErr != nil {
   102  			return nil, parseErr
   103  		}
   104  		for _, pin := range t.PackageObject.Pins {
   105  			genericPins = append(genericPins, packagePin{
   106  				pin.Name,
   107  				pin.RepositoryURL,
   108  				pin.State.Revision,
   109  				pin.State.Version,
   110  			})
   111  		}
   112  	case 2:
   113  		t := packageResolvedV2{}
   114  		jsonString, err := json.Marshal(data)
   115  		if err != nil {
   116  			return nil, err
   117  		}
   118  		parseErr := json.Unmarshal(jsonString, &t)
   119  		if parseErr != nil {
   120  			return nil, parseErr
   121  		}
   122  		for _, pin := range t.Pins {
   123  			genericPins = append(genericPins, packagePin{
   124  				pin.Identity,
   125  				pin.Location,
   126  				pin.State.Revision,
   127  				pin.State.Version,
   128  			})
   129  		}
   130  	default:
   131  		return nil, fmt.Errorf("unknown swift package manager version, %f", version)
   132  	}
   133  	return genericPins, nil
   134  }