diff --git a/pkg/scanner/backend_adapter.go b/pkg/scanner/backend_adapter.go deleted file mode 100644 index f13e3ec..0000000 --- a/pkg/scanner/backend_adapter.go +++ /dev/null @@ -1,102 +0,0 @@ -package scanner - -import ( - "encoding/base64" - "fmt" - "github.com/sysdiglabs/harbor-scanner-sysdig-secure/pkg/harbor" - "github.com/sysdiglabs/harbor-scanner-sysdig-secure/pkg/secure" - "strings" -) - -var ( - severities = map[harbor.Severity]int{ - harbor.UNKNOWN: 0, - harbor.NEGLIGIBLE: 1, - harbor.LOW: 2, - harbor.MEDIUM: 3, - harbor.HIGH: 4, - harbor.CRITICAL: 5, - } -) - -type backendAdapter struct { - BaseAdapter -} - -/* -func NewBackendAdapter(client secure.Client) Adapter { - return &backendAdapter{ - BaseAdapter: BaseAdapter{secureClient: client}, - } -} -*/ - -/* -func (b *backendAdapter) Scan(req harbor.ScanRequest) (harbor.ScanResponse, error) { - if err := b.setupCredentials(req); err != nil { - return harbor.ScanResponse{}, err - } - - response, err := b.secureClient.AddImage(getImageFrom(req), false) - if err != nil { - return harbor.ScanResponse{}, err - } - - return b.CreateScanResponse(req.Artifact.Repository, response.ImageDigest), nil -} -*/ - -/* -func (b *backendAdapter) setupCredentials(req harbor.ScanRequest) error { - registry := getRegistryFrom(req) - user, password := getUserAndPasswordFrom(req.Registry.Authorization) - - if err := b.secureClient.AddRegistry(registry, user, password); err != nil { - if err != secure.ErrRegistryAlreadyExists { - return err - } - - if err = b.secureClient.UpdateRegistry(registry, user, password); err != nil { - return err - } - } - return nil -} -*/ - -func getRegistryFrom(req harbor.ScanRequest) string { - return strings.ReplaceAll(strings.ReplaceAll(req.Registry.URL, "https://", ""), "http://", "") -} - -func getUserAndPasswordFrom(authorization string) (string, string) { - payload := strings.ReplaceAll(authorization, "Basic ", "") - plain, _ := base64.StdEncoding.DecodeString(payload) - splitted := strings.Split(string(plain), ":") - - return splitted[0], splitted[1] -} - -func getImageFrom(req harbor.ScanRequest) string { - result := fmt.Sprintf("%s/%s", getRegistryFrom(req), req.Artifact.Repository) - if req.Artifact.Tag == "" { - return result + fmt.Sprintf("@%s", req.Artifact.Digest) - } - return result + fmt.Sprintf(":%s", req.Artifact.Tag) -} - -func (b *backendAdapter) GetVulnerabilityReport(scanResponseID harbor.ScanRequestID) (harbor.VulnerabilityReport, error) { - repository, shaDigest := b.DecodeScanResponseID(scanResponseID) - - vulnerabilityReport, err := b.secureClient.GetVulnerabilities(shaDigest) - if err != nil { - switch err { - case secure.ErrImageNotFound: - return harbor.VulnerabilityReport{}, ErrScanRequestIDNotFound - case secure.ErrVulnerabilityReportNotReady: - return harbor.VulnerabilityReport{}, ErrVulnerabilityReportNotReady - } - return harbor.VulnerabilityReport{}, err - } - - return b.ToHarborVulnerabilityReport(repository, shaDigest, &vulnerabilityReport) -} diff --git a/pkg/scanner/backend_adapter_test.go b/pkg/scanner/backend_adapter_test.go deleted file mode 100644 index 844348f..0000000 --- a/pkg/scanner/backend_adapter_test.go +++ /dev/null @@ -1,308 +0,0 @@ -package scanner - -import ( - "errors" - "fmt" - "github.com/sysdiglabs/harbor-scanner-sysdig-secure/pkg/harbor" - "github.com/sysdiglabs/harbor-scanner-sysdig-secure/pkg/secure" - "os" -) - -const ( - imageDigest = "an image digest" - scanID = harbor.ScanRequestID("c3lzZGlnL2FnZW50fGFuIGltYWdlIGRpZ2VzdA==") - user = "robot$9f6711d1-834d-11ea-867f-76103d08dca8" - password = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTAwMDk5OTksImlhdCI6MTU4NzQxNzk5OSwiaXNzIjoiaGFyYm9yLXRva2VuLWRlZmF1bHRJc3N1ZXIiLCJpZCI6OSwicGlkIjoyLCJhY2Nlc3MiOlt7IlJlc291cmNlIjoiL3Byb2plY3QvMi9yZXBvc2l0b3J5IiwiQWN0aW9uIjoic2Nhbm5lci1wdWxsIiwiRWZmZWN0IjoiIn1dfQ.A3_aTzvxqSTvl26pQKa97ay15zRPC9K55NE0WbEyOsY3m0KFz-HuSDatncWLSYvOlcGVdysKlF3JXYWIjQ7tEI4V76WA9UMoi-fr9vEEdWLF5C1uWZJOz_S72sQ3G1BzsLp3HyWe9ZN5EBK9mhXzYNv2rONYrr0UJeBmNnMf2mU3sH71OO_G6JvRl5fwFSLSYx8nQs82PhfVhx50wRuWl_zyeCCDy_ytLzjRBvZwKuI9iVIxgM1pRfKG15NWMHfl0lcYnjm7f1_WFGKtVddkLOTICK0_FPtef1L8A16ozo_2NA32WD9PstdcTuD37XbZ6AFXUAZFoZLfCEW97mtIZBY2uYMwDQtc6Nme4o3Ya-MnBEIAs9Vi9d5a4pkf7Two-xjI-9ESgVz79YqL-_OnecQPNJ9yAFtJuxQ7StfsCIZx84hh5VdcZmW9jlezRHh4hTAjsNmrOBFTAjPyaXk98Se3Fj0Ev3bChod63og4frE7_fE7HnoBKVPHRAdBhJ2yrAiPymfij_kD4ke1Vb0AxmGGOwRP2K3TZNqEdKcq89lU6lHYV2UfrWchuF3u4ieNEC1BGu1_m_c55f0YZH1FAq6evCyA0JnFuXzO4cCxC7WHzXXRGSC9Lm3LF7cbaZAgFj5d34gbgUQmJst8nPlpW-KtwRL-pHC6mipunCBv9bU" -) - -var ( - errSecure = errors.New("an error from Sysdig Secure") - createdAt = generatedAt -) - -/* - var _ = Describe("BackendAdapter", func() { - var ( - controller *gomock.Controller - client *mocks.MockClient - adapter Adapter - ) - - BeforeEach(func() { - controller = gomock.NewController(GinkgoT()) - client = mocks.NewMockClient(controller) - adapter = NewBackendAdapter(client) - }) - - AfterEach(func() { - controller.Finish() - }) - - Context("when scanning an image", func() { - It("sends the repository and tag to Sysdig Secure", func() { - client.EXPECT().AddRegistry("harbor.sysdig-demo.zone", user, password).Return(nil) - - secureResponse := secure.ScanResponse{ImageDigest: imageDigest} - client.EXPECT().AddImage("harbor.sysdig-demo.zone/sysdig/agent:9.7.0", false).Return(secureResponse, nil) - - result, _ := adapter.Scan(scanRequest()) - - Expect(result).To(Equal(harbor.ScanResponse{ID: scanID})) - }) - - Context("and it does not send tag", func() { - It("sends the repository only to Sysdig Secure", func() { - client.EXPECT().AddRegistry("harbor.sysdig-demo.zone", user, password).Return(nil) - - secureResponse := secure.ScanResponse{ImageDigest: imageDigest} - client.EXPECT().AddImage("harbor.sysdig-demo.zone/sysdig/agent@an image digest", false).Return(secureResponse, nil) - - result, _ := adapter.Scan(scanRequestWithoutTag()) - - Expect(result).To(Equal(harbor.ScanResponse{ID: scanID})) - }) - }) - - Context("when registry already exists in Secure", func() { - It("updates registry with new credentials and queues the image", func() { - client.EXPECT().AddRegistry("harbor.sysdig-demo.zone", user, password).Return(secure.ErrRegistryAlreadyExists) - client.EXPECT().UpdateRegistry("harbor.sysdig-demo.zone", user, password) - secureResponse := secure.ScanResponse{ImageDigest: imageDigest} - client.EXPECT().AddImage("harbor.sysdig-demo.zone/sysdig/agent:9.7.0", false).Return(secureResponse, nil) - - result, _ := adapter.Scan(scanRequest()) - - Expect(result).To(Equal(harbor.ScanResponse{ID: scanID})) - }) - }) - - Context("when Secure fails to add the image to the scanning queue", func() { - It("returns the error", func() { - client.EXPECT().AddRegistry("harbor.sysdig-demo.zone", user, password).Return(nil) - client.EXPECT().AddImage("harbor.sysdig-demo.zone/sysdig/agent:9.7.0", false).Return(secure.ScanResponse{}, errSecure) - - _, err := adapter.Scan(scanRequest()) - - Expect(err).To(MatchError(errSecure)) - }) - }) - }) - - Context("when getting the vulnerability report for an image", func() { - It("queries Secure for the vulnerability list", func() { - client.EXPECT().GetVulnerabilities(imageDigest).Return(secureVulnerabilityReport(), nil) - client.EXPECT().GetImage(imageDigest).Return(scanResponse(), nil) - client.EXPECT().GetVulnerabilityDescription("CVE-2019-9948", "CVE-2019-9946").Return(vulnerabilitiesDescription(), nil) - - result, _ := adapter.GetVulnerabilityReport(scanID) - - Expect(result).To(Equal(vulnerabilityReport())) - }) - - Context("when Secure returns an error", func() { - Context("when Secure cannot find the image scanned", func() { - It("returns a ScanRequestID Not Found Error", func() { - client.EXPECT().GetVulnerabilities(imageDigest).Return(secure.VulnerabilityReport{}, secure.ErrImageNotFound) - - _, err := adapter.GetVulnerabilityReport(scanID) - - Expect(err).To(MatchError(ErrScanRequestIDNotFound)) - }) - }) - - Context("when Secure is still scanning the image", func() { - It("returns a VulnerabilityReport is not Ready Error", func() { - client.EXPECT().GetVulnerabilities(imageDigest).Return(secure.VulnerabilityReport{}, secure.ErrVulnerabilityReportNotReady) - - _, err := adapter.GetVulnerabilityReport(scanID) - - Expect(err).To(MatchError(ErrVulnerabilityReportNotReady)) - }) - }) - - It("returns the error", func() { - client.EXPECT().GetVulnerabilities(imageDigest).Return(secure.VulnerabilityReport{}, errSecure) - - _, err := adapter.GetVulnerabilityReport(scanID) - - Expect(err).To(MatchError(errSecure)) - }) - }) - }) - }) -*/ -func scanRequest() harbor.ScanRequest { - return harbor.ScanRequest{ - Registry: &harbor.Registry{ - URL: "https://harbor.sysdig-demo.zone", - Authorization: "Basic cm9ib3QkOWY2NzExZDEtODM0ZC0xMWVhLTg2N2YtNzYxMDNkMDhkY2E4OmV5SmhiR2NpT2lKU1V6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUpsZUhBaU9qRTFPVEF3TURrNU9Ua3NJbWxoZENJNk1UVTROelF4TnprNU9Td2lhWE56SWpvaWFHRnlZbTl5TFhSdmEyVnVMV1JsWm1GMWJIUkpjM04xWlhJaUxDSnBaQ0k2T1N3aWNHbGtJam95TENKaFkyTmxjM01pT2x0N0lsSmxjMjkxY21ObElqb2lMM0J5YjJwbFkzUXZNaTl5WlhCdmMybDBiM0o1SWl3aVFXTjBhVzl1SWpvaWMyTmhibTVsY2kxd2RXeHNJaXdpUldabVpXTjBJam9pSW4xZGZRLkEzX2FUenZ4cVNUdmwyNnBRS2E5N2F5MTV6UlBDOUs1NU5FMFdiRXlPc1kzbTBLRnotSHVTRGF0bmNXTFNZdk9sY0dWZHlzS2xGM0pYWVdJalE3dEVJNFY3NldBOVVNb2ktZnI5dkVFZFdMRjVDMXVXWkpPel9TNzJzUTNHMUJ6c0xwM0h5V2U5Wk41RUJLOW1oWHpZTnYyck9OWXJyMFVKZUJtTm5NZjJtVTNzSDcxT09fRzZKdlJsNWZ3RlNMU1l4OG5RczgyUGhmVmh4NTB3UnVXbF96eWVDQ0R5X3l0THpqUkJ2WndLdUk5aVZJeGdNMXBSZktHMTVOV01IZmwwbGNZbmptN2YxX1dGR0t0VmRka0xPVElDSzBfRlB0ZWYxTDhBMTZvem9fMk5BMzJXRDlQc3RkY1R1RDM3WGJaNkFGWFVBWkZvWkxmQ0VXOTdtdElaQlkydVlNd0RRdGM2Tm1lNG8zWWEtTW5CRUlBczlWaTlkNWE0cGtmN1R3by14akktOUVTZ1Z6NzlZcUwtX09uZWNRUE5KOXlBRnRKdXhRN1N0ZnNDSVp4ODRoaDVWZGNabVc5amxlelJIaDRoVEFqc05tck9CRlRBalB5YVhrOThTZTNGajBFdjNiQ2hvZDYzb2c0ZnJFN19mRTdIbm9CS1ZQSFJBZEJoSjJ5ckFpUHltZmlqX2tENGtlMVZiMEF4bUdHT3dSUDJLM1RaTnFFZEtjcTg5bFU2bEhZVjJVZnJXY2h1RjN1NGllTkVDMUJHdTFfbV9jNTVmMFlaSDFGQXE2ZXZDeUEwSm5GdVh6TzRjQ3hDN1dIelhYUkdTQzlMbTNMRjdjYmFaQWdGajVkMzRnYmdVUW1Kc3Q4blBscFctS3R3UkwtcEhDNm1pcHVuQ0J2OWJV", - }, - Artifact: &harbor.Artifact{ - Repository: "sysdig/agent", - Digest: imageDigest, - Tag: "9.7.0", - MimeType: "application/vnd.docker.distribution.manifest.v2+json", - }, - } -} - -func scanRequestWithoutTag() harbor.ScanRequest { - return harbor.ScanRequest{ - Registry: &harbor.Registry{ - URL: "https://harbor.sysdig-demo.zone", - Authorization: "Basic cm9ib3QkOWY2NzExZDEtODM0ZC0xMWVhLTg2N2YtNzYxMDNkMDhkY2E4OmV5SmhiR2NpT2lKU1V6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUpsZUhBaU9qRTFPVEF3TURrNU9Ua3NJbWxoZENJNk1UVTROelF4TnprNU9Td2lhWE56SWpvaWFHRnlZbTl5TFhSdmEyVnVMV1JsWm1GMWJIUkpjM04xWlhJaUxDSnBaQ0k2T1N3aWNHbGtJam95TENKaFkyTmxjM01pT2x0N0lsSmxjMjkxY21ObElqb2lMM0J5YjJwbFkzUXZNaTl5WlhCdmMybDBiM0o1SWl3aVFXTjBhVzl1SWpvaWMyTmhibTVsY2kxd2RXeHNJaXdpUldabVpXTjBJam9pSW4xZGZRLkEzX2FUenZ4cVNUdmwyNnBRS2E5N2F5MTV6UlBDOUs1NU5FMFdiRXlPc1kzbTBLRnotSHVTRGF0bmNXTFNZdk9sY0dWZHlzS2xGM0pYWVdJalE3dEVJNFY3NldBOVVNb2ktZnI5dkVFZFdMRjVDMXVXWkpPel9TNzJzUTNHMUJ6c0xwM0h5V2U5Wk41RUJLOW1oWHpZTnYyck9OWXJyMFVKZUJtTm5NZjJtVTNzSDcxT09fRzZKdlJsNWZ3RlNMU1l4OG5RczgyUGhmVmh4NTB3UnVXbF96eWVDQ0R5X3l0THpqUkJ2WndLdUk5aVZJeGdNMXBSZktHMTVOV01IZmwwbGNZbmptN2YxX1dGR0t0VmRka0xPVElDSzBfRlB0ZWYxTDhBMTZvem9fMk5BMzJXRDlQc3RkY1R1RDM3WGJaNkFGWFVBWkZvWkxmQ0VXOTdtdElaQlkydVlNd0RRdGM2Tm1lNG8zWWEtTW5CRUlBczlWaTlkNWE0cGtmN1R3by14akktOUVTZ1Z6NzlZcUwtX09uZWNRUE5KOXlBRnRKdXhRN1N0ZnNDSVp4ODRoaDVWZGNabVc5amxlelJIaDRoVEFqc05tck9CRlRBalB5YVhrOThTZTNGajBFdjNiQ2hvZDYzb2c0ZnJFN19mRTdIbm9CS1ZQSFJBZEJoSjJ5ckFpUHltZmlqX2tENGtlMVZiMEF4bUdHT3dSUDJLM1RaTnFFZEtjcTg5bFU2bEhZVjJVZnJXY2h1RjN1NGllTkVDMUJHdTFfbV9jNTVmMFlaSDFGQXE2ZXZDeUEwSm5GdVh6TzRjQ3hDN1dIelhYUkdTQzlMbTNMRjdjYmFaQWdGajVkMzRnYmdVUW1Kc3Q4blBscFctS3R3UkwtcEhDNm1pcHVuQ0J2OWJV", - }, - Artifact: &harbor.Artifact{ - Repository: "sysdig/agent", - Digest: imageDigest, - Tag: "", - MimeType: "application/vnd.docker.distribution.manifest.v2+json", - }, - } -} - -func scanResponse() secure.V2VulnerabilityReport { - return secure.V2VulnerabilityReport{ - Data: []secure.V2VulnerabilityData{ - { - CreatedAt: createdAt, - MainAssetName: fmt.Sprintf("sysdig/agent:%s@%s", "9.7", imageDigest), - }, - }, - } -} - -func secureVulnerabilityReport() secure.VulnerabilityReport { - return secure.VulnerabilityReport{ - ImageDigest: imageDigest, - VulnerabilityType: "all", - Vulnerabilities: []*secure.Vulnerability{ - { - Vuln: "CVE-2019-9948", - PackageName: "Python", - PackageVersion: "2.7.16", - Fix: "None", - Severity: "Critical", - URL: "https://nvd.nist.gov/vuln/detail/CVE-2019-9948", - NVDData: []*secure.NVDData{ - { - ID: "NVD-1234", - CVSSV2: &secure.CVSS{ - BaseScore: 7.5, - ExploitabilityScore: 8.6, - ImpactScore: 6.4, - }, - CVSSV3: &secure.CVSS{ - BaseScore: 9.8, - ExploitabilityScore: 10.0, - ImpactScore: 8.9, - }, - }, - }, - }, - { - Vuln: "CVE-2019-9946", - PackageName: "Python", - PackageVersion: "2.7.16", - Fix: "None", - Severity: "High", - URL: "https://nvd.nist.gov/vuln/detail/CVE-2019-9946", - NVDData: []*secure.NVDData{ - { - ID: "NVD-1234", - CVSSV2: &secure.CVSS{ - BaseScore: 7.5, - ExploitabilityScore: 8.6, - ImpactScore: 6.4, - }, - CVSSV3: &secure.CVSS{ - BaseScore: 9.8, - ExploitabilityScore: 10.0, - ImpactScore: 8.9, - }, - }, - }, - }, - }, - } -} - -func vulnerabilitiesDescription() map[string]string { - return map[string]string{ - "CVE-2019-9948": "Description for CVE-2019-9948", - "CVE-2019-9946": "Description for CVE-2019-9946", - } -} - -func vulnerabilityReport() harbor.VulnerabilityReport { - return harbor.VulnerabilityReport{ - GeneratedAt: createdAt, - Severity: harbor.CRITICAL, - Scanner: &harbor.Scanner{ - Name: "Sysdig Secure", - Vendor: "Sysdig", - Version: secure.BackendVersion, - }, - Artifact: nil, - Vulnerabilities: []harbor.VulnerabilityItem{ - { - ID: "CVE-2019-9948", - Package: "Python", - Version: "2.7.16", - FixVersion: "", - Severity: harbor.CRITICAL, - Description: "Disclosure Date: '', Exploitable: 'false' ", - Links: []string{ - fmt.Sprintf("%s/secure/#/vulnerabilities/results//overview", os.Getenv("SECURE_URL")), - "https://nvd.nist.gov/vuln/detail/CVE-2019-9948", - "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9948", - }, - CVSS: harbor.CVSSData{ - ScoreV3: 9.8, - ScoreV2: 7.5, - VectorV3: "", - VectorV2: "", - }, - VendorAttributes: harbor.CVSS{ - CvssKey: harbor.NVDKey{ - NVD: harbor.CVSSDataVendor{ - ScoreV3: 9.8, - VectorV3: "", - ScoreV2: 7.5, - VectorV2: "", - }, - }, - }, - }, - { - ID: "CVE-2019-9946", - Package: "Python", - Version: "2.7.16", - FixVersion: "", - Severity: harbor.HIGH, - Description: "Disclosure Date: '', Exploitable: 'false' ", - Links: []string{ - fmt.Sprintf("%s/secure/#/vulnerabilities/results//overview", os.Getenv("SECURE_URL")), - "https://nvd.nist.gov/vuln/detail/CVE-2019-9946", - "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9946", - }, - CVSS: harbor.CVSSData{ - ScoreV3: 9.8, - ScoreV2: 7.5, - VectorV3: "", - VectorV2: "", - }, - VendorAttributes: harbor.CVSS{ - CvssKey: harbor.NVDKey{ - NVD: harbor.CVSSDataVendor{ - ScoreV3: 9.8, - VectorV3: "", - ScoreV2: 7.5, - VectorV2: "", - }, - }, - }, - }, - }, - } -} diff --git a/pkg/scanner/base_adapter.go b/pkg/scanner/base_adapter.go index 4523357..3205051 100644 --- a/pkg/scanner/base_adapter.go +++ b/pkg/scanner/base_adapter.go @@ -113,13 +113,25 @@ func (b *BaseAdapter) ToHarborVulnerabilityReport(repository string, shaDigest s } scanResponse, _ := b.secureClient.GetImage(shaDigest) + if len(scanResponse.Data) > 0 { + b.logger.Debugf("ToHarborVulnerabilityReport:: mainAssetName = '%s'", scanResponse.Data[0].MainAssetName) + } for _, imageDetail := range scanResponse.Data { parts := strings.Split(imageDetail.MainAssetName, "@") repoWithTag := parts[0] hash := parts[1] + firstSlash := strings.Index(repoWithTag, "/") lastColon := strings.LastIndex(repoWithTag, ":") + + // Check if there is no colon after the last slash - i.e we are using just a SHA hash and need to fake a tag + lastSlash := strings.LastIndex(repoWithTag, "/") + if lastSlash != -1 && lastColon < lastSlash { + repoWithTag += ":" + lastColon = len(repoWithTag) - 1 + } + repo := repoWithTag[firstSlash+1 : lastColon] tag := repoWithTag[lastColon+1:] if repo == repository { diff --git a/pkg/scanner/inline_adapter.go b/pkg/scanner/inline_adapter.go index 864af87..0f515f0 100644 --- a/pkg/scanner/inline_adapter.go +++ b/pkg/scanner/inline_adapter.go @@ -3,10 +3,12 @@ package scanner import ( "context" "crypto/md5" + "encoding/base64" "errors" "fmt" "io" "os" + "strings" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -21,6 +23,16 @@ import ( const jobDefaultTTL = 86400 var ErrInlineScanError = errors.New("error executing the inline scanner") +var ( + severities = map[harbor.Severity]int{ + harbor.UNKNOWN: 0, + harbor.NEGLIGIBLE: 1, + harbor.LOW: 2, + harbor.MEDIUM: 3, + harbor.HIGH: 4, + harbor.CRITICAL: 5, + } +) type inlineAdapter struct { BaseAdapter @@ -78,7 +90,7 @@ func (i *inlineAdapter) createJobFrom(req harbor.ScanRequest) error { name := jobName(req.Artifact.Repository, req.Artifact.Digest) job := i.buildJob(name, req) - i.logger.Infof("Creating job %s for %s", name, getImageFrom(req)) + i.logger.Infof("Creating job '%s' for %s", name, getImageFrom(req)) _, err := i.k8sClient.BatchV1().Jobs(i.namespace).Create( context.Background(), job, @@ -96,6 +108,8 @@ func (i *inlineAdapter) createJobFrom(req harbor.ScanRequest) error { } func (i *inlineAdapter) buildJob(name string, req harbor.ScanRequest) *batchv1.Job { + i.logger.Infof("Building job for request - Registry: %+v", req.Registry) + i.logger.Infof("Building job for request - Artifact: %+v", req.Artifact) user, password := getUserAndPasswordFrom(req.Registry.Authorization) var envVars = []corev1.EnvVar{ @@ -130,7 +144,7 @@ func (i *inlineAdapter) buildJob(name string, req harbor.ScanRequest) *batchv1.J ValueFrom: nil, }) envVars = appendLocalEnvVar(envVars, "NO_PROXY") - cmdString := fmt.Sprintf("/home/nonroot/sysdig-cli-scanner -a %s --skiptlsverify --output-json=output.json ", i.secureURL) + cmdString := fmt.Sprintf("/home/nonroot/sysdig-cli-scanner -a %s --skiptlsverify --console-log --loglevel info ", i.secureURL) // Add skiptlsverify if insecure if !i.verifySSL { cmdString += "--skiptlsverify " @@ -140,34 +154,42 @@ func (i *inlineAdapter) buildJob(name string, req harbor.ScanRequest) *batchv1.J cmdString += fmt.Sprintf("%s ", i.extraParams) } - cmdString += fmt.Sprintf("pull://%s@%s", getImageFrom(req), req.Artifact.Digest) + cmdString += fmt.Sprintf("pull://%s", getImageFrom(req)) cmdString += "; RC=$?; if [ $RC -eq 1 ]; then exit 0; else exit $RC; fi" //Create security contexts for pod from main deployment // Retrieve the security context from the first container deploymentName := "harbor-scanner-sysdig-secure" - namespace := os.Getenv("NAMESPACE") + cliScannerNamespace := os.Getenv("NAMESPACE_NAME") //Comes from the helm chart .Release.Namespace value var containerSecurityContext *corev1.SecurityContext var podSecurityContext *corev1.PodSecurityContext - k8sDeployment, err := i.k8sClient.AppsV1().Deployments(deploymentName).Get(context.TODO(), namespace, metav1.GetOptions{}) + k8sDeployment, err := i.k8sClient.AppsV1().Deployments(cliScannerNamespace).Get(context.TODO(), deploymentName, metav1.GetOptions{}) if err != nil { if k8serrors.IsNotFound(err) { - i.logger.Debugf("Deployment %s in namespace %s not found\n", deploymentName, namespace) + i.logger.Infof("Deployment %s in namespace %s not found\n", deploymentName, cliScannerNamespace) + } else { + i.logger.Infof("Deployment %s in namespace %s Retrieval Error: %v", deploymentName, cliScannerNamespace, err) } } else { podSecurityContext = k8sDeployment.Spec.Template.Spec.SecurityContext podTemplate := k8sDeployment.Spec.Template if len(podTemplate.Spec.Containers) > 0 && podTemplate.Spec.Containers[0].SecurityContext != nil { containerSecurityContext = podTemplate.Spec.Containers[0].SecurityContext - i.logger.Debugf("Security context for container %s: %+v\n", podTemplate.Spec.Containers[0].Name, containerSecurityContext) + i.logger.Infof("Security context for container %s: %+v\n", podTemplate.Spec.Containers[0].Name, containerSecurityContext) } else { - i.logger.Debug("No security context found for the first container") + i.logger.Infof("No security context found for the container") } } + i.logger.Infof("Building job with pod security context: %+v", podSecurityContext) + i.logger.Infof("Building job with container security context: %+v", containerSecurityContext) + var commandFinal = []string{"/busybox/sh"} + var commandArgs = []string{"-c", cmdString} + i.logger.Infof("Building job with Command Line: '%s'", commandFinal) + i.logger.Infof("Building job with Args: '%s'", commandArgs) var backoffLimit int32 = 0 - return &batchv1.Job{ + var cliScannerJob = &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, @@ -180,13 +202,10 @@ func (i *inlineAdapter) buildJob(name string, req harbor.ScanRequest) *batchv1.J SecurityContext: podSecurityContext, Containers: []corev1.Container{ { - Name: "scanner", - Image: os.Getenv("CLI_SCANNER_IMAGE"), // Using my image but for production we would host it - Command: []string{"/busybox/sh"}, - Args: []string{ - "-c", - cmdString, - }, + Name: "scanner", + Image: os.Getenv("CLI_SCANNER_IMAGE"), // Using my image but for production we would host it + Command: commandFinal, + Args: commandArgs, Env: envVars, SecurityContext: containerSecurityContext, }, @@ -195,6 +214,8 @@ func (i *inlineAdapter) buildJob(name string, req harbor.ScanRequest) *batchv1.J }, }, } + i.logger.Infof("Complete Job Definitions: '%+v'", cliScannerJob) + return cliScannerJob } func appendLocalEnvVar(envVars []corev1.EnvVar, key string) []corev1.EnvVar { @@ -309,3 +330,23 @@ func (i *inlineAdapter) collectPodResults(job *batchv1.Job) (*podResults, error) ExitCode: int(pod.Status.ContainerStatuses[0].State.Terminated.ExitCode), }, nil } + +func getRegistryFrom(req harbor.ScanRequest) string { + return strings.ReplaceAll(strings.ReplaceAll(req.Registry.URL, "https://", ""), "http://", "") +} + +func getUserAndPasswordFrom(authorization string) (string, string) { + payload := strings.ReplaceAll(authorization, "Basic ", "") + plain, _ := base64.StdEncoding.DecodeString(payload) + splitted := strings.Split(string(plain), ":") + + return splitted[0], splitted[1] +} + +func getImageFrom(req harbor.ScanRequest) string { + result := fmt.Sprintf("%s/%s", getRegistryFrom(req), req.Artifact.Repository) + if req.Artifact.Tag == "" { + return result + fmt.Sprintf("@%s", req.Artifact.Digest) + } + return result + fmt.Sprintf(":%s@%s", req.Artifact.Tag, req.Artifact.Digest) +} diff --git a/pkg/scanner/inline_adapter_test.go b/pkg/scanner/inline_adapter_test.go index f1a9db0..2ace2b0 100644 --- a/pkg/scanner/inline_adapter_test.go +++ b/pkg/scanner/inline_adapter_test.go @@ -2,12 +2,13 @@ package scanner import ( "context" - "os" - + "errors" + "fmt" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" log "github.com/sirupsen/logrus" + "os" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -29,6 +30,18 @@ const ( resourceName = "cli-scanner-1e668f7cc4c27e915cfed9793808357e" ) +const ( + imageDigest = "an image digest" + scanID = harbor.ScanRequestID("c3lzZGlnL2FnZW50fGFuIGltYWdlIGRpZ2VzdA==") + user = "robot$9f6711d1-834d-11ea-867f-76103d08dca8" + password = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTAwMDk5OTksImlhdCI6MTU4NzQxNzk5OSwiaXNzIjoiaGFyYm9yLXRva2VuLWRlZmF1bHRJc3N1ZXIiLCJpZCI6OSwicGlkIjoyLCJhY2Nlc3MiOlt7IlJlc291cmNlIjoiL3Byb2plY3QvMi9yZXBvc2l0b3J5IiwiQWN0aW9uIjoic2Nhbm5lci1wdWxsIiwiRWZmZWN0IjoiIn1dfQ.A3_aTzvxqSTvl26pQKa97ay15zRPC9K55NE0WbEyOsY3m0KFz-HuSDatncWLSYvOlcGVdysKlF3JXYWIjQ7tEI4V76WA9UMoi-fr9vEEdWLF5C1uWZJOz_S72sQ3G1BzsLp3HyWe9ZN5EBK9mhXzYNv2rONYrr0UJeBmNnMf2mU3sH71OO_G6JvRl5fwFSLSYx8nQs82PhfVhx50wRuWl_zyeCCDy_ytLzjRBvZwKuI9iVIxgM1pRfKG15NWMHfl0lcYnjm7f1_WFGKtVddkLOTICK0_FPtef1L8A16ozo_2NA32WD9PstdcTuD37XbZ6AFXUAZFoZLfCEW97mtIZBY2uYMwDQtc6Nme4o3Ya-MnBEIAs9Vi9d5a4pkf7Two-xjI-9ESgVz79YqL-_OnecQPNJ9yAFtJuxQ7StfsCIZx84hh5VdcZmW9jlezRHh4hTAjsNmrOBFTAjPyaXk98Se3Fj0Ev3bChod63og4frE7_fE7HnoBKVPHRAdBhJ2yrAiPymfij_kD4ke1Vb0AxmGGOwRP2K3TZNqEdKcq89lU6lHYV2UfrWchuF3u4ieNEC1BGu1_m_c55f0YZH1FAq6evCyA0JnFuXzO4cCxC7WHzXXRGSC9Lm3LF7cbaZAgFj5d34gbgUQmJst8nPlpW-KtwRL-pHC6mipunCBv9bU" +) + +var ( + errSecure = errors.New("an error from Sysdig Secure") + createdAt = generatedAt +) + type envItem struct { value string defined bool @@ -222,7 +235,7 @@ func job() *batchv1.Job { Command: []string{"/busybox/sh"}, Args: []string{ "-c", - "/home/nonroot/sysdig-cli-scanner -a https://secure.sysdig.com --skiptlsverify --output-json=output.json pull://harbor.sysdig-demo.zone/sysdig/agent:9.7.0@an image digest; RC=$?; if [ $RC -eq 1 ]; then exit 0; else exit $RC; fi", + "/home/nonroot/sysdig-cli-scanner -a https://secure.sysdig.com --skiptlsverify --console-log --loglevel info pull://harbor.sysdig-demo.zone/sysdig/agent:9.7.0@an image digest; RC=$?; if [ $RC -eq 1 ]; then exit 0; else exit $RC; fi", }, Env: []corev1.EnvVar{ { @@ -290,3 +303,179 @@ func finishedPodForJob(j *batchv1.Job) *corev1.Pod { }, } } + +func scanRequest() harbor.ScanRequest { + return harbor.ScanRequest{ + Registry: &harbor.Registry{ + URL: "https://harbor.sysdig-demo.zone", + Authorization: "Basic cm9ib3QkOWY2NzExZDEtODM0ZC0xMWVhLTg2N2YtNzYxMDNkMDhkY2E4OmV5SmhiR2NpT2lKU1V6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUpsZUhBaU9qRTFPVEF3TURrNU9Ua3NJbWxoZENJNk1UVTROelF4TnprNU9Td2lhWE56SWpvaWFHRnlZbTl5TFhSdmEyVnVMV1JsWm1GMWJIUkpjM04xWlhJaUxDSnBaQ0k2T1N3aWNHbGtJam95TENKaFkyTmxjM01pT2x0N0lsSmxjMjkxY21ObElqb2lMM0J5YjJwbFkzUXZNaTl5WlhCdmMybDBiM0o1SWl3aVFXTjBhVzl1SWpvaWMyTmhibTVsY2kxd2RXeHNJaXdpUldabVpXTjBJam9pSW4xZGZRLkEzX2FUenZ4cVNUdmwyNnBRS2E5N2F5MTV6UlBDOUs1NU5FMFdiRXlPc1kzbTBLRnotSHVTRGF0bmNXTFNZdk9sY0dWZHlzS2xGM0pYWVdJalE3dEVJNFY3NldBOVVNb2ktZnI5dkVFZFdMRjVDMXVXWkpPel9TNzJzUTNHMUJ6c0xwM0h5V2U5Wk41RUJLOW1oWHpZTnYyck9OWXJyMFVKZUJtTm5NZjJtVTNzSDcxT09fRzZKdlJsNWZ3RlNMU1l4OG5RczgyUGhmVmh4NTB3UnVXbF96eWVDQ0R5X3l0THpqUkJ2WndLdUk5aVZJeGdNMXBSZktHMTVOV01IZmwwbGNZbmptN2YxX1dGR0t0VmRka0xPVElDSzBfRlB0ZWYxTDhBMTZvem9fMk5BMzJXRDlQc3RkY1R1RDM3WGJaNkFGWFVBWkZvWkxmQ0VXOTdtdElaQlkydVlNd0RRdGM2Tm1lNG8zWWEtTW5CRUlBczlWaTlkNWE0cGtmN1R3by14akktOUVTZ1Z6NzlZcUwtX09uZWNRUE5KOXlBRnRKdXhRN1N0ZnNDSVp4ODRoaDVWZGNabVc5amxlelJIaDRoVEFqc05tck9CRlRBalB5YVhrOThTZTNGajBFdjNiQ2hvZDYzb2c0ZnJFN19mRTdIbm9CS1ZQSFJBZEJoSjJ5ckFpUHltZmlqX2tENGtlMVZiMEF4bUdHT3dSUDJLM1RaTnFFZEtjcTg5bFU2bEhZVjJVZnJXY2h1RjN1NGllTkVDMUJHdTFfbV9jNTVmMFlaSDFGQXE2ZXZDeUEwSm5GdVh6TzRjQ3hDN1dIelhYUkdTQzlMbTNMRjdjYmFaQWdGajVkMzRnYmdVUW1Kc3Q4blBscFctS3R3UkwtcEhDNm1pcHVuQ0J2OWJV", + }, + Artifact: &harbor.Artifact{ + Repository: "sysdig/agent", + Digest: imageDigest, + Tag: "9.7.0", + MimeType: "application/vnd.docker.distribution.manifest.v2+json", + }, + } +} + +func scanRequestWithoutTag() harbor.ScanRequest { + return harbor.ScanRequest{ + Registry: &harbor.Registry{ + URL: "https://harbor.sysdig-demo.zone", + Authorization: "Basic cm9ib3QkOWY2NzExZDEtODM0ZC0xMWVhLTg2N2YtNzYxMDNkMDhkY2E4OmV5SmhiR2NpT2lKU1V6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUpsZUhBaU9qRTFPVEF3TURrNU9Ua3NJbWxoZENJNk1UVTROelF4TnprNU9Td2lhWE56SWpvaWFHRnlZbTl5TFhSdmEyVnVMV1JsWm1GMWJIUkpjM04xWlhJaUxDSnBaQ0k2T1N3aWNHbGtJam95TENKaFkyTmxjM01pT2x0N0lsSmxjMjkxY21ObElqb2lMM0J5YjJwbFkzUXZNaTl5WlhCdmMybDBiM0o1SWl3aVFXTjBhVzl1SWpvaWMyTmhibTVsY2kxd2RXeHNJaXdpUldabVpXTjBJam9pSW4xZGZRLkEzX2FUenZ4cVNUdmwyNnBRS2E5N2F5MTV6UlBDOUs1NU5FMFdiRXlPc1kzbTBLRnotSHVTRGF0bmNXTFNZdk9sY0dWZHlzS2xGM0pYWVdJalE3dEVJNFY3NldBOVVNb2ktZnI5dkVFZFdMRjVDMXVXWkpPel9TNzJzUTNHMUJ6c0xwM0h5V2U5Wk41RUJLOW1oWHpZTnYyck9OWXJyMFVKZUJtTm5NZjJtVTNzSDcxT09fRzZKdlJsNWZ3RlNMU1l4OG5RczgyUGhmVmh4NTB3UnVXbF96eWVDQ0R5X3l0THpqUkJ2WndLdUk5aVZJeGdNMXBSZktHMTVOV01IZmwwbGNZbmptN2YxX1dGR0t0VmRka0xPVElDSzBfRlB0ZWYxTDhBMTZvem9fMk5BMzJXRDlQc3RkY1R1RDM3WGJaNkFGWFVBWkZvWkxmQ0VXOTdtdElaQlkydVlNd0RRdGM2Tm1lNG8zWWEtTW5CRUlBczlWaTlkNWE0cGtmN1R3by14akktOUVTZ1Z6NzlZcUwtX09uZWNRUE5KOXlBRnRKdXhRN1N0ZnNDSVp4ODRoaDVWZGNabVc5amxlelJIaDRoVEFqc05tck9CRlRBalB5YVhrOThTZTNGajBFdjNiQ2hvZDYzb2c0ZnJFN19mRTdIbm9CS1ZQSFJBZEJoSjJ5ckFpUHltZmlqX2tENGtlMVZiMEF4bUdHT3dSUDJLM1RaTnFFZEtjcTg5bFU2bEhZVjJVZnJXY2h1RjN1NGllTkVDMUJHdTFfbV9jNTVmMFlaSDFGQXE2ZXZDeUEwSm5GdVh6TzRjQ3hDN1dIelhYUkdTQzlMbTNMRjdjYmFaQWdGajVkMzRnYmdVUW1Kc3Q4blBscFctS3R3UkwtcEhDNm1pcHVuQ0J2OWJV", + }, + Artifact: &harbor.Artifact{ + Repository: "sysdig/agent", + Digest: imageDigest, + Tag: "", + MimeType: "application/vnd.docker.distribution.manifest.v2+json", + }, + } +} + +func scanResponse() secure.V2VulnerabilityReport { + return secure.V2VulnerabilityReport{ + Data: []secure.V2VulnerabilityData{ + { + CreatedAt: createdAt, + MainAssetName: fmt.Sprintf("sysdig/agent:%s@%s", "9.7", imageDigest), + }, + }, + } +} + +func secureVulnerabilityReport() secure.VulnerabilityReport { + return secure.VulnerabilityReport{ + ImageDigest: imageDigest, + VulnerabilityType: "all", + Vulnerabilities: []*secure.Vulnerability{ + { + Vuln: "CVE-2019-9948", + PackageName: "Python", + PackageVersion: "2.7.16", + Fix: "None", + Severity: "Critical", + URL: "https://nvd.nist.gov/vuln/detail/CVE-2019-9948", + NVDData: []*secure.NVDData{ + { + ID: "NVD-1234", + CVSSV2: &secure.CVSS{ + BaseScore: 7.5, + ExploitabilityScore: 8.6, + ImpactScore: 6.4, + }, + CVSSV3: &secure.CVSS{ + BaseScore: 9.8, + ExploitabilityScore: 10.0, + ImpactScore: 8.9, + }, + }, + }, + }, + { + Vuln: "CVE-2019-9946", + PackageName: "Python", + PackageVersion: "2.7.16", + Fix: "None", + Severity: "High", + URL: "https://nvd.nist.gov/vuln/detail/CVE-2019-9946", + NVDData: []*secure.NVDData{ + { + ID: "NVD-1234", + CVSSV2: &secure.CVSS{ + BaseScore: 7.5, + ExploitabilityScore: 8.6, + ImpactScore: 6.4, + }, + CVSSV3: &secure.CVSS{ + BaseScore: 9.8, + ExploitabilityScore: 10.0, + ImpactScore: 8.9, + }, + }, + }, + }, + }, + } +} + +func vulnerabilitiesDescription() map[string]string { + return map[string]string{ + "CVE-2019-9948": "Description for CVE-2019-9948", + "CVE-2019-9946": "Description for CVE-2019-9946", + } +} + +func vulnerabilityReport() harbor.VulnerabilityReport { + return harbor.VulnerabilityReport{ + GeneratedAt: createdAt, + Severity: harbor.CRITICAL, + Scanner: &harbor.Scanner{ + Name: "Sysdig Secure", + Vendor: "Sysdig", + Version: secure.BackendVersion, + }, + Artifact: nil, + Vulnerabilities: []harbor.VulnerabilityItem{ + { + ID: "CVE-2019-9948", + Package: "Python", + Version: "2.7.16", + FixVersion: "", + Severity: harbor.CRITICAL, + Description: "Disclosure Date: '', Exploitable: 'false' ", + Links: []string{ + fmt.Sprintf("%s/secure/#/vulnerabilities/results//overview", os.Getenv("SECURE_URL")), + "https://nvd.nist.gov/vuln/detail/CVE-2019-9948", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9948", + }, + CVSS: harbor.CVSSData{ + ScoreV3: 9.8, + ScoreV2: 7.5, + VectorV3: "", + VectorV2: "", + }, + VendorAttributes: harbor.CVSS{ + CvssKey: harbor.NVDKey{ + NVD: harbor.CVSSDataVendor{ + ScoreV3: 9.8, + VectorV3: "", + ScoreV2: 7.5, + VectorV2: "", + }, + }, + }, + }, + { + ID: "CVE-2019-9946", + Package: "Python", + Version: "2.7.16", + FixVersion: "", + Severity: harbor.HIGH, + Description: "Disclosure Date: '', Exploitable: 'false' ", + Links: []string{ + fmt.Sprintf("%s/secure/#/vulnerabilities/results//overview", os.Getenv("SECURE_URL")), + "https://nvd.nist.gov/vuln/detail/CVE-2019-9946", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9946", + }, + CVSS: harbor.CVSSData{ + ScoreV3: 9.8, + ScoreV2: 7.5, + VectorV3: "", + VectorV2: "", + }, + VendorAttributes: harbor.CVSS{ + CvssKey: harbor.NVDKey{ + NVD: harbor.CVSSDataVendor{ + ScoreV3: 9.8, + VectorV3: "", + ScoreV2: 7.5, + VectorV2: "", + }, + }, + }, + }, + }, + } +}