github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/cataloger/githubactions/package.go (about) 1 package githubactions 2 3 import ( 4 "strings" 5 6 "github.com/anchore/packageurl-go" 7 "github.com/anchore/syft/internal/log" 8 "github.com/anchore/syft/syft/file" 9 "github.com/anchore/syft/syft/pkg" 10 ) 11 12 func newPackageFromUsageStatement(use string, location file.Location) *pkg.Package { 13 name, version := parseStepUsageStatement(use) 14 15 if name == "" { 16 log.WithFields("file", location.RealPath, "statement", use).Trace("unable to parse github action usage statement") 17 return nil 18 } 19 20 if strings.Contains(name, ".github/workflows/") { 21 return newGithubActionWorkflowPackageUsage(name, version, location) 22 } 23 24 return newGithubActionPackageUsage(name, version, location) 25 } 26 27 func newGithubActionWorkflowPackageUsage(name, version string, workflowLocation file.Location) *pkg.Package { 28 p := &pkg.Package{ 29 Name: name, 30 Version: version, 31 Locations: file.NewLocationSet(workflowLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), 32 PURL: packageURL(name, version), 33 Type: pkg.GithubActionWorkflowPkg, 34 } 35 36 p.SetID() 37 38 return p 39 } 40 41 func newGithubActionPackageUsage(name, version string, workflowLocation file.Location) *pkg.Package { 42 p := &pkg.Package{ 43 Name: name, 44 Version: version, 45 Locations: file.NewLocationSet(workflowLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), 46 PURL: packageURL(name, version), 47 Type: pkg.GithubActionPkg, 48 } 49 50 p.SetID() 51 52 return p 53 } 54 55 func parseStepUsageStatement(use string) (string, string) { 56 // from octo-org/another-repo/.github/workflows/workflow.yml@v1 get octo-org/another-repo/.github/workflows/workflow.yml and v1 57 // from ./.github/workflows/workflow-2.yml interpret as only the name 58 59 // from actions/cache@v3 get actions/cache and v3 60 61 fields := strings.Split(use, "@") 62 switch len(fields) { 63 case 1: 64 return use, "" 65 case 2: 66 return fields[0], fields[1] 67 } 68 return "", "" 69 } 70 71 func packageURL(name, version string) string { 72 var qualifiers packageurl.Qualifiers 73 var subPath string 74 var namespace string 75 76 fields := strings.SplitN(name, "/", 3) 77 switch len(fields) { 78 case 1: 79 return "" 80 case 2: 81 namespace = fields[0] 82 name = fields[1] 83 case 3: 84 namespace = fields[0] 85 name = fields[1] 86 subPath = fields[2] 87 } 88 if namespace == "." { 89 // this is a local composite action, which is unclear how to represent in a PURL without more information 90 return "" 91 } 92 93 // there isn't a github actions PURL but there is a github PURL type for referencing github repos, which is the 94 // next best thing until there is a supported type. 95 return packageurl.NewPackageURL( 96 packageurl.TypeGithub, 97 namespace, 98 name, 99 version, 100 qualifiers, 101 subPath, 102 ).ToString() 103 }