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