Files
trivy/pkg/report/github/github.go
2022-05-26 21:34:15 +03:00

179 lines
4.7 KiB
Go

package github
import (
"encoding/json"
"fmt"
"io"
"os"
"strings"
"time"
"golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/clock"
"github.com/aquasecurity/trivy/pkg/purl"
"github.com/aquasecurity/trivy/pkg/types"
)
const (
DirectRelationship string = "direct"
IndirectRelationship string = "indirect"
RuntimeScope string = "runtime"
DevelopmentScope string = "development"
)
type Package struct {
PackageUrl string `json:"package_url,omitempty"`
Relationship string `json:"relationship,omitempty"`
Dependencies []string `json:"dependencies,omitempty"`
Scope string `json:"scope,omitempty"`
Metadata Metadata `json:"metadata,omitempty"`
}
type File struct {
SrcLocation string `json:"source_location,omitempty"`
}
type Metadata map[string]interface{}
type Manifest struct {
Name string `json:"name,omitempty"`
File *File `json:"file,omitempty"`
Metadata Metadata `json:"metadata,omitempty"`
Resolved map[string]Package `json:"resolved,omitempty"`
}
type Job struct {
Correlator string `json:"correlator,omitempty"`
Id string `json:"id,omitempty"`
}
type Detector struct {
Name string `json:"name"`
Version string `json:"version"`
Url string `json:"url"`
}
type DependencySnapshot struct {
Version int `json:"version,omitempty"`
Detector Detector `json:"detector"`
Metadata Metadata `json:"metadata,omitempty"`
Ref string `json:"ref,omitempty"`
Sha string `json:"sha,omitempty"`
Job *Job `json:"job,omitempty"`
Scanned string `json:"scanned,omitempty"`
Manifests map[string]Manifest `json:"manifests,omitempty"`
}
// Writer generates JSON for GitHub Dependency Snapshots
type Writer struct {
Output io.Writer
Version string
}
func (w Writer) Write(report types.Report) error {
snapshot := &DependencySnapshot{}
//use now() method that can be overwritten while integration tests run
snapshot.Scanned = clock.Now().Format(time.RFC3339)
snapshot.Detector = Detector{
Name: "trivy",
Version: w.Version,
Url: "https://github.com/aquasecurity/trivy",
}
snapshot.Version = 0 // The version of the repository snapshot submission.
snapshot.Ref = os.Getenv("GITHUB_REF")
snapshot.Sha = os.Getenv("GITHUB_SHA")
snapshot.Job = &Job{
Correlator: fmt.Sprintf("%s_%s", os.Getenv("GITHUB_WORKFLOW"), os.Getenv("GITHUB_JOB")),
Id: os.Getenv("GITHUB_RUN_ID"),
}
snapshot.Metadata = getMetadata(report)
manifests := make(map[string]Manifest)
for _, result := range report.Results {
if result.Packages == nil {
continue
}
manifest := Manifest{}
manifest.Name = result.Type
//show path for languages only
if result.Class == types.ClassLangPkg {
manifest.File = &File{
SrcLocation: result.Target,
}
}
resolved := make(map[string]Package)
for _, pkg := range result.Packages {
var err error
githubPkg := Package{}
githubPkg.Scope = RuntimeScope
githubPkg.Relationship = getPkgRelationshipType(pkg)
githubPkg.Dependencies = getDependencies(result, pkg)
githubPkg.PackageUrl, err = buildPurl(result.Type, pkg)
if err != nil {
return xerrors.Errorf("unable to build purl for %s: %w", pkg.Name, err)
}
resolved[pkg.Name] = githubPkg
}
manifest.Resolved = resolved
manifests[result.Target] = manifest
}
snapshot.Manifests = manifests
output, err := json.MarshalIndent(snapshot, "", " ")
if err != nil {
return xerrors.Errorf("failed to marshal github dependency snapshots: %w", err)
}
if _, err = fmt.Fprint(w.Output, string(output)); err != nil {
return xerrors.Errorf("failed to write github dependency snapshots: %w", err)
}
return nil
}
func getMetadata(report types.Report) Metadata {
metadata := Metadata{}
if report.Metadata.RepoTags != nil {
metadata["aquasecurity:trivy:RepoTag"] = strings.Join(report.Metadata.RepoTags, ", ")
}
if report.Metadata.RepoDigests != nil {
metadata["aquasecurity:trivy:RepoDigest"] = strings.Join(report.Metadata.RepoDigests, ", ")
}
return metadata
}
func getDependencies(result types.Result, pkg ftypes.Package) []string {
for _, dep := range result.Dependencies {
if dep.ID == pkg.ID {
return dep.DependsOn
}
}
return []string{}
}
func getPkgRelationshipType(pkg ftypes.Package) string {
if pkg.Indirect {
return IndirectRelationship
}
return DirectRelationship
}
func buildPurl(t string, pkg ftypes.Package) (string, error) {
packageUrl, err := purl.NewPackageURL(t, types.Metadata{}, pkg)
if err != nil {
return "", xerrors.Errorf("purl error: %w", err)
}
return packageUrl.ToString(), nil
}