github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/report/github/github.go (about) 1 package github 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "os" 8 "strings" 9 "time" 10 11 "golang.org/x/xerrors" 12 13 "github.com/devseccon/trivy/pkg/clock" 14 ftypes "github.com/devseccon/trivy/pkg/fanal/types" 15 "github.com/devseccon/trivy/pkg/purl" 16 "github.com/devseccon/trivy/pkg/types" 17 ) 18 19 const ( 20 DirectRelationship string = "direct" 21 IndirectRelationship string = "indirect" 22 RuntimeScope string = "runtime" 23 DevelopmentScope string = "development" 24 ) 25 26 type Package struct { 27 PackageUrl string `json:"package_url,omitempty"` 28 Relationship string `json:"relationship,omitempty"` 29 Dependencies []string `json:"dependencies,omitempty"` 30 Scope string `json:"scope,omitempty"` 31 Metadata Metadata `json:"metadata,omitempty"` 32 } 33 34 type File struct { 35 SrcLocation string `json:"source_location,omitempty"` 36 } 37 38 type Metadata map[string]interface{} 39 40 type Manifest struct { 41 Name string `json:"name,omitempty"` 42 File *File `json:"file,omitempty"` 43 Metadata Metadata `json:"metadata,omitempty"` 44 Resolved map[string]Package `json:"resolved,omitempty"` 45 } 46 47 type Job struct { 48 Correlator string `json:"correlator,omitempty"` 49 Id string `json:"id,omitempty"` 50 } 51 type Detector struct { 52 Name string `json:"name"` 53 Version string `json:"version"` 54 Url string `json:"url"` 55 } 56 57 type DependencySnapshot struct { 58 Version int `json:"version"` 59 Detector Detector `json:"detector"` 60 Metadata Metadata `json:"metadata,omitempty"` 61 Ref string `json:"ref,omitempty"` 62 Sha string `json:"sha,omitempty"` 63 Job *Job `json:"job,omitempty"` 64 Scanned string `json:"scanned,omitempty"` 65 Manifests map[string]Manifest `json:"manifests,omitempty"` 66 } 67 68 // Writer generates JSON for GitHub Dependency Snapshots 69 type Writer struct { 70 Output io.Writer 71 Version string 72 } 73 74 func (w Writer) Write(report types.Report) error { 75 snapshot := &DependencySnapshot{} 76 77 // use now() method that can be overwritten while integration tests run 78 snapshot.Scanned = clock.Now().Format(time.RFC3339) 79 snapshot.Detector = Detector{ 80 Name: "trivy", 81 Version: w.Version, 82 Url: "https://github.com/devseccon/trivy", 83 } 84 snapshot.Version = 0 // The version of the repository snapshot submission. 85 86 snapshot.Ref = os.Getenv("GITHUB_REF") 87 snapshot.Sha = os.Getenv("GITHUB_SHA") 88 89 snapshot.Job = &Job{ 90 Correlator: fmt.Sprintf("%s_%s", os.Getenv("GITHUB_WORKFLOW"), os.Getenv("GITHUB_JOB")), 91 Id: os.Getenv("GITHUB_RUN_ID"), 92 } 93 94 snapshot.Metadata = getMetadata(report) 95 96 manifests := make(map[string]Manifest) 97 98 for _, result := range report.Results { 99 if result.Packages == nil { 100 continue 101 } 102 103 manifest := Manifest{} 104 manifest.Name = string(result.Type) 105 // show path for language-specific packages only 106 if result.Class == types.ClassLangPkg { 107 manifest.File = &File{ 108 SrcLocation: result.Target, 109 } 110 } 111 112 resolved := make(map[string]Package) 113 114 for _, pkg := range result.Packages { 115 var err error 116 githubPkg := Package{} 117 githubPkg.Scope = RuntimeScope 118 githubPkg.Relationship = getPkgRelationshipType(pkg) 119 githubPkg.Dependencies = pkg.DependsOn // TODO: replace with PURL 120 githubPkg.PackageUrl, err = buildPurl(result.Type, pkg) 121 if err != nil { 122 return xerrors.Errorf("unable to build purl for %s: %w", pkg.Name, err) 123 } 124 125 resolved[pkg.Name] = githubPkg 126 } 127 128 manifest.Resolved = resolved 129 manifests[result.Target] = manifest 130 } 131 132 snapshot.Manifests = manifests 133 134 output, err := json.MarshalIndent(snapshot, "", " ") 135 if err != nil { 136 return xerrors.Errorf("failed to marshal github dependency snapshots: %w", err) 137 } 138 139 if _, err = fmt.Fprint(w.Output, string(output)); err != nil { 140 return xerrors.Errorf("failed to write github dependency snapshots: %w", err) 141 } 142 return nil 143 } 144 145 func getMetadata(report types.Report) Metadata { 146 metadata := Metadata{} 147 if report.Metadata.RepoTags != nil { 148 metadata["aquasecurity:trivy:RepoTag"] = strings.Join(report.Metadata.RepoTags, ", ") 149 } 150 if report.Metadata.RepoDigests != nil { 151 metadata["aquasecurity:trivy:RepoDigest"] = strings.Join(report.Metadata.RepoDigests, ", ") 152 } 153 return metadata 154 } 155 156 func getPkgRelationshipType(pkg ftypes.Package) string { 157 if pkg.Indirect { 158 return IndirectRelationship 159 } 160 return DirectRelationship 161 } 162 163 func buildPurl(t ftypes.TargetType, pkg ftypes.Package) (string, error) { 164 packageUrl, err := purl.NewPackageURL(t, types.Metadata{}, pkg) 165 if err != nil { 166 return "", xerrors.Errorf("purl error: %w", err) 167 } 168 if packageUrl == nil { 169 return "", nil 170 } 171 return packageUrl.ToString(), nil 172 }