mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-21 23:00:42 -08:00
Co-authored-by: Shira Cohen <97398476+ShiraCohen33@users.noreply.github.com> Co-authored-by: knqyf263 <knqyf263@gmail.com>
179 lines
4.7 KiB
Go
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
|
|
}
|