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 }