1package rpc
2
3import (
4	"github.com/golang/protobuf/ptypes"
5
6	ftypes "github.com/aquasecurity/fanal/types"
7	deptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
8	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
9	"github.com/aquasecurity/trivy/pkg/log"
10	"github.com/aquasecurity/trivy/pkg/report"
11	"github.com/aquasecurity/trivy/pkg/types"
12	"github.com/aquasecurity/trivy/rpc/cache"
13	"github.com/aquasecurity/trivy/rpc/common"
14	"github.com/aquasecurity/trivy/rpc/scanner"
15)
16
17func ConvertToRpcPkgs(pkgs []ftypes.Package) []*common.Package {
18	var rpcPkgs []*common.Package
19	for _, pkg := range pkgs {
20		rpcPkgs = append(rpcPkgs, &common.Package{
21			Name:       pkg.Name,
22			Version:    pkg.Version,
23			Release:    pkg.Release,
24			Epoch:      int32(pkg.Epoch),
25			Arch:       pkg.Arch,
26			SrcName:    pkg.SrcName,
27			SrcVersion: pkg.SrcVersion,
28			SrcRelease: pkg.SrcRelease,
29			SrcEpoch:   int32(pkg.SrcEpoch),
30		})
31	}
32	return rpcPkgs
33}
34
35func ConvertFromRpcPkgs(rpcPkgs []*common.Package) []ftypes.Package {
36	var pkgs []ftypes.Package
37	for _, pkg := range rpcPkgs {
38		pkgs = append(pkgs, ftypes.Package{
39			Name:       pkg.Name,
40			Version:    pkg.Version,
41			Release:    pkg.Release,
42			Epoch:      int(pkg.Epoch),
43			Arch:       pkg.Arch,
44			SrcName:    pkg.SrcName,
45			SrcVersion: pkg.SrcVersion,
46			SrcRelease: pkg.SrcRelease,
47			SrcEpoch:   int(pkg.SrcEpoch),
48		})
49	}
50	return pkgs
51}
52
53func ConvertFromRpcLibraries(rpcLibs []*common.Library) []ftypes.LibraryInfo {
54	var libs []ftypes.LibraryInfo
55	for _, l := range rpcLibs {
56		libs = append(libs, ftypes.LibraryInfo{
57			Library: deptypes.Library{
58				Name:    l.Name,
59				Version: l.Version,
60			},
61		})
62	}
63	return libs
64}
65
66func ConvertToRpcLibraries(libs []deptypes.Library) []*common.Library {
67	var rpcLibs []*common.Library
68	for _, l := range libs {
69		rpcLibs = append(rpcLibs, &common.Library{
70			Name:    l.Name,
71			Version: l.Version,
72		})
73	}
74	return rpcLibs
75}
76
77func ConvertFromRpcVulns(rpcVulns []*common.Vulnerability) []types.DetectedVulnerability {
78	var vulns []types.DetectedVulnerability
79	for _, vuln := range rpcVulns {
80		severity := dbTypes.Severity(vuln.Severity)
81		vulns = append(vulns, types.DetectedVulnerability{
82			VulnerabilityID:  vuln.VulnerabilityId,
83			PkgName:          vuln.PkgName,
84			InstalledVersion: vuln.InstalledVersion,
85			FixedVersion:     vuln.FixedVersion,
86			Vulnerability: dbTypes.Vulnerability{
87				Title:       vuln.Title,
88				Description: vuln.Description,
89				Severity:    severity.String(),
90				References:  vuln.References,
91			},
92		})
93	}
94	return vulns
95}
96
97func ConvertToRpcVulns(vulns []types.DetectedVulnerability) []*common.Vulnerability {
98	var rpcVulns []*common.Vulnerability
99	for _, vuln := range vulns {
100		severity, err := dbTypes.NewSeverity(vuln.Severity)
101		if err != nil {
102			log.Logger.Warn(err)
103		}
104		cvssMap := make(map[string]*common.CVSS) // This is needed because protobuf generates a map[string]*CVSS type
105		for vendor, vendorSeverity := range vuln.CVSS {
106			cvssMap[vendor] = &common.CVSS{
107				V2Vector: vendorSeverity.V2Vector,
108				V3Vector: vendorSeverity.V3Vector,
109				V2Score:  vendorSeverity.V2Score,
110				V3Score:  vendorSeverity.V3Score,
111			}
112		}
113
114		rpcVulns = append(rpcVulns, &common.Vulnerability{
115			VulnerabilityId:  vuln.VulnerabilityID,
116			PkgName:          vuln.PkgName,
117			InstalledVersion: vuln.InstalledVersion,
118			FixedVersion:     vuln.FixedVersion,
119			Title:            vuln.Title,
120			Description:      vuln.Description,
121			Severity:         common.Severity(severity),
122			References:       vuln.References,
123			Layer: &common.Layer{
124				Digest: vuln.Layer.Digest,
125				DiffId: vuln.Layer.DiffID,
126			},
127			Cvss:           cvssMap,
128			SeveritySource: vuln.SeveritySource,
129			CweIds:         vuln.CweIDs,
130		})
131	}
132	return rpcVulns
133}
134
135func ConvertFromRpcResults(rpcResults []*scanner.Result) []report.Result {
136	var results []report.Result
137
138	for _, result := range rpcResults {
139		var vulns []types.DetectedVulnerability
140		for _, vuln := range result.Vulnerabilities {
141			severity := dbTypes.Severity(vuln.Severity)
142			cvssMap := make(dbTypes.VendorCVSS) // This is needed because protobuf generates a map[string]*CVSS type
143			for vendor, vendorSeverity := range vuln.Cvss {
144				cvssMap[vendor] = dbTypes.CVSS{
145					V2Vector: vendorSeverity.V2Vector,
146					V3Vector: vendorSeverity.V3Vector,
147					V2Score:  vendorSeverity.V2Score,
148					V3Score:  vendorSeverity.V3Score,
149				}
150			}
151
152			vulns = append(vulns, types.DetectedVulnerability{
153				VulnerabilityID:  vuln.VulnerabilityId,
154				PkgName:          vuln.PkgName,
155				InstalledVersion: vuln.InstalledVersion,
156				FixedVersion:     vuln.FixedVersion,
157				Vulnerability: dbTypes.Vulnerability{
158					Title:       vuln.Title,
159					Description: vuln.Description,
160					Severity:    severity.String(),
161					CVSS:        cvssMap,
162					References:  vuln.References,
163					CweIDs:      vuln.CweIds,
164				},
165				Layer: ftypes.Layer{
166					Digest: vuln.Layer.Digest,
167					DiffID: vuln.Layer.DiffId,
168				},
169				SeveritySource: vuln.SeveritySource,
170			})
171		}
172		results = append(results, report.Result{
173			Target:          result.Target,
174			Vulnerabilities: vulns,
175			Type:            result.Type,
176		})
177	}
178	return results
179}
180
181func ConvertFromRpcOS(rpcOS *common.OS) *ftypes.OS {
182	if rpcOS == nil {
183		return nil
184	}
185	return &ftypes.OS{
186		Family: rpcOS.Family,
187		Name:   rpcOS.Name,
188	}
189}
190
191func ConvertFromRpcPackageInfos(rpcPkgInfos []*common.PackageInfo) []ftypes.PackageInfo {
192	var pkgInfos []ftypes.PackageInfo
193	for _, rpcPkgInfo := range rpcPkgInfos {
194		pkgInfos = append(pkgInfos, ftypes.PackageInfo{
195			FilePath: rpcPkgInfo.FilePath,
196			Packages: ConvertFromRpcPkgs(rpcPkgInfo.Packages),
197		})
198	}
199	return pkgInfos
200}
201
202func ConvertFromRpcApplications(rpcApps []*common.Application) []ftypes.Application {
203	var apps []ftypes.Application
204	for _, rpcApp := range rpcApps {
205		apps = append(apps, ftypes.Application{
206			Type:      rpcApp.Type,
207			FilePath:  rpcApp.FilePath,
208			Libraries: ConvertFromRpcLibraries(rpcApp.Libraries),
209		})
210	}
211	return apps
212}
213
214func ConvertFromRpcPutArtifactRequest(req *cache.PutArtifactRequest) ftypes.ArtifactInfo {
215	created, _ := ptypes.Timestamp(req.ArtifactInfo.Created)
216	return ftypes.ArtifactInfo{
217		SchemaVersion:   int(req.ArtifactInfo.SchemaVersion),
218		Architecture:    req.ArtifactInfo.Architecture,
219		Created:         created,
220		DockerVersion:   req.ArtifactInfo.DockerVersion,
221		OS:              req.ArtifactInfo.Os,
222		HistoryPackages: ConvertFromRpcPkgs(req.ArtifactInfo.HistoryPackages),
223	}
224}
225
226func ConvertFromRpcPutBlobRequest(req *cache.PutBlobRequest) ftypes.BlobInfo {
227	return ftypes.BlobInfo{
228		SchemaVersion: int(req.BlobInfo.SchemaVersion),
229		Digest:        req.BlobInfo.Digest,
230		DiffID:        req.BlobInfo.DiffId,
231		OS:            ConvertFromRpcOS(req.BlobInfo.Os),
232		PackageInfos:  ConvertFromRpcPackageInfos(req.BlobInfo.PackageInfos),
233		Applications:  ConvertFromRpcApplications(req.BlobInfo.Applications),
234		OpaqueDirs:    req.BlobInfo.OpaqueDirs,
235		WhiteoutFiles: req.BlobInfo.WhiteoutFiles,
236	}
237}
238
239func ConvertToRpcOS(fos *ftypes.OS) *common.OS {
240	if fos == nil {
241		return nil
242	}
243	return &common.OS{
244		Family: fos.Family,
245		Name:   fos.Name,
246	}
247}
248
249func ConvertToRpcArtifactInfo(imageID string, imageInfo ftypes.ArtifactInfo) *cache.PutArtifactRequest {
250	t, err := ptypes.TimestampProto(imageInfo.Created)
251	if err != nil {
252		log.Logger.Warnf("invalid timestamp: %s", err)
253	}
254
255	return &cache.PutArtifactRequest{
256		ArtifactId: imageID,
257		ArtifactInfo: &cache.ArtifactInfo{
258			SchemaVersion:   int32(imageInfo.SchemaVersion),
259			Architecture:    imageInfo.Architecture,
260			Created:         t,
261			DockerVersion:   imageInfo.DockerVersion,
262			Os:              imageInfo.OS,
263			HistoryPackages: ConvertToRpcPkgs(imageInfo.HistoryPackages),
264		},
265	}
266}
267
268func ConvertToRpcBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBlobRequest {
269	var packageInfos []*common.PackageInfo
270	for _, pkgInfo := range layerInfo.PackageInfos {
271		packageInfos = append(packageInfos, &common.PackageInfo{
272			FilePath: pkgInfo.FilePath,
273			Packages: ConvertToRpcPkgs(pkgInfo.Packages),
274		})
275	}
276
277	var applications []*common.Application
278	for _, app := range layerInfo.Applications {
279		var libs []*common.Library
280		for _, lib := range app.Libraries {
281			libs = append(libs, &common.Library{
282				Name:    lib.Library.Name,
283				Version: lib.Library.Version,
284			})
285
286		}
287		applications = append(applications, &common.Application{
288			Type:      app.Type,
289			FilePath:  app.FilePath,
290			Libraries: libs,
291		})
292	}
293
294	return &cache.PutBlobRequest{
295		DiffId: diffID,
296		BlobInfo: &cache.BlobInfo{
297			SchemaVersion: ftypes.BlobJSONSchemaVersion,
298			Digest:        layerInfo.Digest,
299			DiffId:        layerInfo.DiffID,
300			Os:            ConvertToRpcOS(layerInfo.OS),
301			PackageInfos:  packageInfos,
302			Applications:  applications,
303			OpaqueDirs:    layerInfo.OpaqueDirs,
304			WhiteoutFiles: layerInfo.WhiteoutFiles,
305		},
306	}
307}
308
309func ConvertToMissingBlobsRequest(imageID string, layerIDs []string) *cache.MissingBlobsRequest {
310	return &cache.MissingBlobsRequest{
311		ArtifactId: imageID,
312		BlobIds:    layerIDs,
313	}
314}
315
316func ConvertToRpcScanResponse(results report.Results, os *ftypes.OS, eosl bool) *scanner.ScanResponse {
317	rpcOS := &common.OS{}
318	if os != nil {
319		rpcOS.Family = os.Family
320		rpcOS.Name = os.Name
321	}
322
323	var rpcResults []*scanner.Result
324	for _, result := range results {
325		rpcResults = append(rpcResults, &scanner.Result{
326			Target:          result.Target,
327			Vulnerabilities: ConvertToRpcVulns(result.Vulnerabilities),
328			Type:            result.Type,
329		})
330	}
331
332	return &scanner.ScanResponse{
333		Os:      rpcOS,
334		Eosl:    eosl,
335		Results: rpcResults,
336	}
337}
338