Alex Goodman 5b7ec60f8d add package dependency quality notes
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-11-15 09:51:20 -05:00

109 lines
3.0 KiB
Go

package githubactions
import (
"fmt"
"strings"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
func newPackageFromUsageStatement(use string, location file.Location) (*pkg.Package, error) {
name, version := parseStepUsageStatement(use)
if name == "" {
log.WithFields("file", location.RealPath, "statement", use).Trace("unable to parse github action usage statement")
return nil, fmt.Errorf("unable to parse github action usage statement")
}
if strings.Contains(name, ".github/workflows/") {
return newGithubActionWorkflowPackageUsage(name, version, location), nil
}
return newGithubActionPackageUsage(name, version, location), nil
}
func newGithubActionWorkflowPackageUsage(name, version string, workflowLocation file.Location) *pkg.Package {
p := &pkg.Package{
Name: name,
Version: version,
Locations: file.NewLocationSet(workflowLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
PURL: packageURL(name, version),
// we have full visibility into the dependencies of a workflow file except for when using shared workflows
Dependencies: pkg.CompleteDependencies,
Type: pkg.GithubActionWorkflowPkg,
}
p.SetID()
return p
}
func newGithubActionPackageUsage(name, version string, workflowLocation file.Location) *pkg.Package {
p := &pkg.Package{
Name: name,
Version: version,
Locations: file.NewLocationSet(workflowLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
PURL: packageURL(name, version),
// we cannot see what the dependencies are for a github action are locally from workflow yaml files
Dependencies: pkg.IncompleteDependencies,
Type: pkg.GithubActionPkg,
}
p.SetID()
return p
}
func parseStepUsageStatement(use string) (string, string) {
// from octo-org/another-repo/.github/workflows/workflow.yml@v1 get octo-org/another-repo/.github/workflows/workflow.yml and v1
// from ./.github/workflows/workflow-2.yml interpret as only the name
// from actions/cache@v3 get actions/cache and v3
fields := strings.Split(use, "@")
switch len(fields) {
case 1:
return use, ""
case 2:
return fields[0], fields[1]
}
return "", ""
}
func packageURL(name, version string) string {
var qualifiers packageurl.Qualifiers
var subPath string
var namespace string
fields := strings.SplitN(name, "/", 3)
switch len(fields) {
case 1:
return ""
case 2:
namespace = fields[0]
name = fields[1]
case 3:
namespace = fields[0]
name = fields[1]
subPath = fields[2]
}
if namespace == "." {
// this is a local composite action, which is unclear how to represent in a PURL without more information
return ""
}
// there isn't a github actions PURL but there is a github PURL type for referencing github repos, which is the
// next best thing until there is a supported type.
return packageurl.NewPackageURL(
packageurl.TypeGithub,
namespace,
name,
version,
qualifiers,
subPath,
).ToString()
}