From fe4ff6003d7e3f684972f88a9e2871a31f1846dd Mon Sep 17 00:00:00 2001 From: Bradley Jones Date: Tue, 5 Sep 2023 15:16:26 +0100 Subject: [PATCH] chore: fix linting for adapter package Signed-off-by: Bradley Jones --- .golangci.yaml | 4 +- cmd/anchore-adapter/main.go | 14 +- pkg/adapter/adapter.go | 10 +- pkg/adapter/anchore/adapter.go | 179 +++++++++-------- pkg/adapter/anchore/adapter_test.go | 5 +- pkg/adapter/anchore/cache.go | 12 +- pkg/adapter/anchore/cache_test.go | 15 +- pkg/adapter/anchore/client/client.go | 208 ++++++++------------ pkg/adapter/anchore/client/client_test.go | 33 ++-- pkg/adapter/anchore/config.go | 89 +++++---- pkg/adapter/anchore/config_test.go | 11 +- pkg/adapter/anchore/credential/awsloader.go | 17 +- pkg/adapter/anchore/credential/factory.go | 4 +- pkg/adapter/anchore/result_store.go | 45 ++--- pkg/adapter/anchore/result_store_test.go | 50 ++--- pkg/http/api/v1/handler.go | 2 +- pkg/http/api/v1/handler_test.go | 4 +- 17 files changed, 341 insertions(+), 361 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index c3794f5..8b2a681 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -14,8 +14,8 @@ linters: - errcheck - errorlint - exportloopref - - funlen - - gocognit + # - funlen + # - gocognit - goconst - gocritic - gocyclo diff --git a/cmd/anchore-adapter/main.go b/cmd/anchore-adapter/main.go index bef5a80..f33b457 100644 --- a/cmd/anchore-adapter/main.go +++ b/cmd/anchore-adapter/main.go @@ -1,6 +1,7 @@ package main import ( + "errors" "net/http" "github.com/anchore/harbor-scanner-adapter/pkg/adapter/anchore" @@ -47,15 +48,20 @@ func main() { // Setup TLS log.WithField("address", adapterConfig.ListenAddr).Info("listening for HTTPS connections") - err = http.ListenAndServeTLS(adapterConfig.ListenAddr, adapterConfig.TLSCertFile, adapterConfig.TLSKeyFile, router) - if err != nil && err != http.ErrServerClosed { + err = http.ListenAndServeTLS( + adapterConfig.ListenAddr, + adapterConfig.TLSCertFile, + adapterConfig.TLSKeyFile, + router, + ) // #nosec G114 + if err != nil && !errors.Is(err, http.ErrServerClosed) { log.WithField("err", err).Fatalf("error in server listener") } } else { // No TLS log.WithField("address", adapterConfig.ListenAddr).Info("listening for HTTP connections") - err = http.ListenAndServe(adapterConfig.ListenAddr, router) - if err != nil && err != http.ErrServerClosed { + err = http.ListenAndServe(adapterConfig.ListenAddr, router) // #nosec G114 + if err != nil && !errors.Is(err, http.ErrServerClosed) { log.WithField("err", err).Fatalf("error in server listener") } } diff --git a/pkg/adapter/adapter.go b/pkg/adapter/adapter.go index 7d91bc0..b89175f 100644 --- a/pkg/adapter/adapter.go +++ b/pkg/adapter/adapter.go @@ -9,9 +9,9 @@ const ( RawVulnReportMimeType = "application/vnd.scanner.adapter.vuln.report.raw+json" DockerImageMimeType = "application/vnd.docker.distribution.manifest.v2+json" OciImageMimeType = "application/vnd.oci.image.manifest.v1+json" - HarborMetadataVulnDbUpdateKey = "harbor.scanner-adapter/vulnerability-database-updated-at" + HarborMetadataVulnDBUpdateKey = "harbor.scanner-adapter/vulnerability-database-updated-at" HarborMetadataScannerTypeKey = "harbor.scanner-adapter/scanner-type" - AdapterType = "os-package-vulnerability" + AdapterType = "os-package-vulnerability" // #nosec G101 AdapterVersion = "1.0.0" AdapterVendor = "Anchore Inc." AdapterName = "Anchore" @@ -36,7 +36,7 @@ var AdapterMetadata = harbor.ScannerAdapterMetadata{ }, }, Properties: map[string]string{ - HarborMetadataVulnDbUpdateKey: "", // This gets updated in response to requests from Harbor + HarborMetadataVulnDBUpdateKey: "", // This gets updated in response to requests from Harbor HarborMetadataScannerTypeKey: AdapterType, }, } @@ -45,6 +45,6 @@ var AdapterMetadata = harbor.ScannerAdapterMetadata{ type ScannerAdapter interface { GetMetadata() (harbor.ScannerAdapterMetadata, error) Scan(req harbor.ScanRequest) (harbor.ScanResponse, error) - GetHarborVulnerabilityReport(scanId string, includeDescriptions bool) (*harbor.VulnerabilityReport, error) - GetRawVulnerabilityReport(scanId string) (harbor.RawReport, error) + GetHarborVulnerabilityReport(scanID string, includeDescriptions bool) (*harbor.VulnerabilityReport, error) + GetRawVulnerabilityReport(scanID string) (harbor.RawReport, error) } diff --git a/pkg/adapter/anchore/adapter.go b/pkg/adapter/anchore/adapter.go index 1a9c930..9a22970 100644 --- a/pkg/adapter/anchore/adapter.go +++ b/pkg/adapter/anchore/adapter.go @@ -31,30 +31,30 @@ type HarborScannerAdapter struct { func cacheKeyForVuln(v *anchore.NamespacedVulnerability) string { if v != nil { return fmt.Sprintf("%v/%v", v.Namespace, v.ID) - } else { - return "" } + return "" } // NewScannerAdapter constructs new HarborScannerAdapter with the given Config. func NewScannerAdapter(cfg *AdapterConfig) (adapter.ScannerAdapter, error) { err := InitCaches(cfg.CacheConfig) if err != nil { - log.WithFields(log.Fields{"err": err, "config": cfg.CacheConfig}).Errorf("could not initialized caches as configuration requested") + log.WithFields(log.Fields{"err": err, "config": cfg.CacheConfig}). + Errorf("could not initialized caches as configuration requested") return nil, err } return &HarborScannerAdapter{cfg}, nil } -// GenerateScanId Create a scan id from the input image properties -func GenerateScanId(repository string, digest string) (string, error) { - scanId := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(`%s@%s`, repository, digest))) - return scanId, nil +// GenerateScanID Create a scan id from the input image properties +func GenerateScanID(repository string, digest string) (string, error) { + scanID := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(`%s@%s`, repository, digest))) + return scanID, nil } -// ScanIdToRegistryDigest Inverse of GenerateScanId, gets the image components from the input ID -func ScanIdToRegistryDigest(scanId string) (string, string, error) { - unsplit, err := base64.URLEncoding.DecodeString(scanId) +// ScanIDToRegistryDigest Inverse of GenerateScanId, gets the image components from the input ID +func ScanIDToRegistryDigest(scanID string) (string, string, error) { + unsplit, err := base64.URLEncoding.DecodeString(scanID) if err != nil { return "", "", fmt.Errorf("invalid scanID") } @@ -68,11 +68,10 @@ func ScanIdToRegistryDigest(scanId string) (string, string, error) { // Handle issue with split parts if the latter part doesn't match a digest format if err != nil { return "", "", err - } else { - return "", "", fmt.Errorf("invalid digest format") } + return "", "", fmt.Errorf("invalid digest format") } - log.WithFields(log.Fields{"scanId": scanId, "repository": parts[0], "imageDigest": parts[1]}).Debug("parsed scanId") + log.WithFields(log.Fields{"scanId": scanID, "repository": parts[0], "imageDigest": parts[1]}).Debug("parsed scanId") return parts[0], parts[1], nil } @@ -82,7 +81,7 @@ func ScanToAnchoreRequest(req harbor.ScanRequest) (*anchore.ImageScanRequest, er tag = req.Artifact.Tag } - registryHostPort, err := client.ExtractRegistryFromUrl(req.Registry.URL) + registryHostPort, err := client.ExtractRegistryFromURL(req.Registry.URL) if err != nil { return nil, err } @@ -116,7 +115,6 @@ func GetUsernamePassword(authorizationInput string) (string, string, error) { return "", "", fmt.Errorf("unsupported authorization type %v", components[0]) } authzValue = components[1] - } else { // Assume just the value authzValue = components[0] @@ -132,7 +130,7 @@ func GetUsernamePassword(authorizationInput string) (string, string, error) { // EnsureRegistryCredentials Add credentials to Anchore for authorizing the image fetch func (s *HarborScannerAdapter) EnsureRegistryCredentials( - registryUrl string, + registryURL string, repository string, username string, password string, @@ -140,7 +138,7 @@ func (s *HarborScannerAdapter) EnsureRegistryCredentials( // New method, using client resp, body, errs := client.AddRegistryCredential( &s.Configuration.AnchoreClientConfig, - registryUrl, + registryURL, repository, username, password, @@ -162,12 +160,13 @@ func (s *HarborScannerAdapter) EnsureRegistryCredentials( // Check if a PUT is needed if anchoreError.Message == "registry already exists in DB" { - log.WithField("msg", anchoreError.Message).Debug("updating registry credential since one already exists but may be expired") + log.WithField("msg", anchoreError.Message). + Debug("updating registry credential since one already exists but may be expired") // Do update - resp, body, errs = client.UpdateRegistryCredential( + resp, _, errs = client.UpdateRegistryCredential( &s.Configuration.AnchoreClientConfig, - registryUrl, + registryURL, repository, username, password, @@ -179,12 +178,12 @@ func (s *HarborScannerAdapter) EnsureRegistryCredentials( return errs[0] } if resp.StatusCode != http.StatusOK { - log.WithFields(log.Fields{"ErrorMessage": anchoreError.Message, "Registry": registryUrl, "repository": repository}). + log.WithFields(log.Fields{"ErrorMessage": anchoreError.Message, "Registry": registryURL, "repository": repository}). Error("unexpected response from anchore api. credential update not successful") return fmt.Errorf("unexpected response on registry credential update from anchore api: %v", resp.StatusCode) } } else { - log.WithFields(log.Fields{"errorMessage": anchoreError.Message, "registry": registryUrl, "repository": repository}).Error("unexpected response from anchore api. could not determine if update action is appropriate") + log.WithFields(log.Fields{"errorMessage": anchoreError.Message, "registry": registryURL, "repository": repository}).Error("unexpected response from anchore api. could not determine if update action is appropriate") return fmt.Errorf("unexpected response from anchore api") } } else if resp.StatusCode != http.StatusOK { @@ -198,33 +197,35 @@ func (s *HarborScannerAdapter) EnsureRegistryCredentials( } func (s *HarborScannerAdapter) Scan(req harbor.ScanRequest) (harbor.ScanResponse, error) { - scanId, err := GenerateScanId(req.Artifact.Repository, req.Artifact.Digest) + scanID, err := GenerateScanID(req.Artifact.Repository, req.Artifact.Digest) if err != nil { log.Errorf("error encountered generating ScanId from Harbor scan request: %s", err) return harbor.ScanResponse{}, err } - log.WithFields(log.Fields{"scanId": scanId, "repository": req.Artifact.Repository, "artifactDigest": req.Artifact.Digest, "artifactTag": req.Artifact.Tag}).Debug("generated ScanId") + log.WithFields(log.Fields{"scanId": scanID, "repository": req.Artifact.Repository, "artifactDigest": req.Artifact.Digest, "artifactTag": req.Artifact.Tag}). + Debug("generated ScanId") tokenLength := len(req.Registry.Authorization) if s.Configuration.UseAnchoreConfiguredCreds { - log.WithFields(log.Fields{"scanId": scanId, "UseAnchoreConfiguredCredentials": s.Configuration.UseAnchoreConfiguredCreds, "ScanRequestTokenLength": tokenLength}).Debug("Skipping adding Harbor authz token to Anchore due to adapter configuration") + log.WithFields(log.Fields{"scanId": scanID, "UseAnchoreConfiguredCredentials": s.Configuration.UseAnchoreConfiguredCreds, "ScanRequestTokenLength": tokenLength}). + Debug("Skipping adding Harbor authz token to Anchore due to adapter configuration") } else { if req.Registry.Authorization == "" { - log.WithFields(log.Fields{"scanId": scanId, "UseAnchoreConfiguredCredentials": s.Configuration.UseAnchoreConfiguredCreds, "ScanRequestTokenLength": tokenLength}).Debug("Skipping adding Harbor authz token to Anchore due to no token provided in request") + log.WithFields(log.Fields{"scanId": scanID, "UseAnchoreConfiguredCredentials": s.Configuration.UseAnchoreConfiguredCreds, "ScanRequestTokenLength": tokenLength}).Debug("Skipping adding Harbor authz token to Anchore due to no token provided in request") } else { - log.WithFields(log.Fields{"scanId": scanId, "UseAnchoreConfiguredCredentials": s.Configuration.UseAnchoreConfiguredCreds}).Debug("ensuring Anchore deployment has credentials for retrieving the image to scan") + log.WithFields(log.Fields{"scanId": scanID, "UseAnchoreConfiguredCredentials": s.Configuration.UseAnchoreConfiguredCreds}).Debug("ensuring Anchore deployment has credentials for retrieving the image to scan") username, password, err2 := GetUsernamePassword(req.Registry.Authorization) if err2 != nil { - log.WithFields(log.Fields{"scanId": scanId, "UseAnchoreConfiguredCredentials": s.Configuration.UseAnchoreConfiguredCreds}).Error("could not extract credentials for image pull access from the scan request authorization field") + log.WithFields(log.Fields{"scanId": scanID, "UseAnchoreConfiguredCredentials": s.Configuration.UseAnchoreConfiguredCreds}).Error("could not extract credentials for image pull access from the scan request authorization field") return harbor.ScanResponse{}, err2 } // Add the credentials for the repository to be scanned err = s.EnsureRegistryCredentials(req.Registry.URL, req.Artifact.Repository, username, password) if err != nil { - log.WithFields(log.Fields{"scanId": scanId}).Error("failed ensuring that Anchore has authorized access to pull the image from Harbor") + log.WithFields(log.Fields{"scanId": scanID}).Error("failed ensuring that Anchore has authorized access to pull the image from Harbor") return harbor.ScanResponse{}, err } } @@ -242,19 +243,20 @@ func (s *HarborScannerAdapter) Scan(req harbor.ScanRequest) (harbor.ScanResponse return harbor.ScanResponse{}, err } - log.WithFields(log.Fields{"scanId": scanId, "repository": req.Artifact.Repository, "artifactDigest": req.Artifact.Digest, "artifactTag": req.Artifact.Tag}).Info("scan successfully initiated against Anchore") + log.WithFields(log.Fields{"scanId": scanID, "repository": req.Artifact.Repository, "artifactDigest": req.Artifact.Digest, "artifactTag": req.Artifact.Tag}). + Info("scan successfully initiated against Anchore") // All ok, so return the scanId to lookup results return harbor.ScanResponse{ - ID: scanId, + ID: scanID, }, nil } // GetHarborVulnerabilityReport Return a vulnerability report in Harbor format if available for the requested ScanId. If not ready yet, returns empty result. func (s *HarborScannerAdapter) GetHarborVulnerabilityReport( - scanId string, + scanID string, includeDescriptions bool, ) (*harbor.VulnerabilityReport, error) { - imageRepository, imageDigest, err := ScanIdToRegistryDigest(scanId) + imageRepository, imageDigest, err := ScanIDToRegistryDigest(scanID) if err != nil { return nil, err } @@ -264,41 +266,48 @@ func (s *HarborScannerAdapter) GetHarborVulnerabilityReport( return nil, err } - log.WithFields(log.Fields{"scanId": scanId, "imageState": imageState, "imageDigest": imageDigest, "imageRepository": imageRepository}).Debug("image analysis state check") + log.WithFields(log.Fields{"scanId": scanID, "imageState": imageState, "imageDigest": imageDigest, "imageRepository": imageRepository}). + Debug("image analysis state check") if imageState != Analyzed { - log.WithFields(log.Fields{"scanId": scanId, "imageState": imageState, "imageDigest": imageDigest, "imageRepository": imageRepository}).Info("image analysis not completed yet") + log.WithFields(log.Fields{"scanId": scanID, "imageState": imageState, "imageDigest": imageDigest, "imageRepository": imageRepository}). + Info("image analysis not completed yet") return &harbor.VulnerabilityReport{}, fmt.Errorf("analysis not complete") } - result, ok := resultStore.PopResult(scanId) - log.WithFields(log.Fields{"scanId": scanId, "resultRecordFound": ok}).Debug("checked result store for scan Id result") + result, ok := resultStore.PopResult(scanID) + log.WithFields(log.Fields{"scanId": scanID, "resultRecordFound": ok}).Debug("checked result store for scan Id result") if ok { - log.WithFields(log.Fields{"scanId": scanId, "resultIsComplete": result.IsComplete, "resultError": result.Error}).Debug("checked result store for scan Id result") + log.WithFields(log.Fields{"scanId": scanID, "resultIsComplete": result.IsComplete, "resultError": result.Error}). + Debug("checked result store for scan Id result") if result.IsComplete { return result.Result, result.Error - } else { - return nil, fmt.Errorf("result not ready") } - } else { - fn := func() (*harbor.VulnerabilityReport, error) { - rep, err := BuildHarborVulnerabilityReport(imageRepository, imageDigest, includeDescriptions, &s.Configuration.AnchoreClientConfig, s.Configuration.FilterVendorIgnoredVulns) - if err != nil { - return nil, err - } - - log.Debug("Got report from BuildHarborVulnerabilityReport for scanId: ", scanId) - return &rep, err + return nil, fmt.Errorf("result not ready") + } + fn := func() (*harbor.VulnerabilityReport, error) { + rep, err := BuildHarborVulnerabilityReport( + imageRepository, + imageDigest, + includeDescriptions, + &s.Configuration.AnchoreClientConfig, + s.Configuration.FilterVendorIgnoredVulns, + ) + if err != nil { + return nil, err } - log.WithField("scanId", scanId).Info("no existing result store entry found for scanId, creating a new one") - requestResult := resultStore.RequestResult(scanId, fn) - if requestResult.Error != nil { - return nil, requestResult.Error - } - return requestResult.Result, nil + log.Debug("Got report from BuildHarborVulnerabilityReport for scanId: ", scanID) + return &rep, err } + + log.WithField("scanId", scanID).Info("no existing result store entry found for scanId, creating a new one") + requestResult := resultStore.RequestResult(scanID, fn) + if requestResult.Error != nil { + return nil, requestResult.Error + } + return requestResult.Result, nil } type ImageState int64 @@ -310,15 +319,14 @@ const ( Analyzed ImageState = 3 ) -func GetImageState(imageDigest string, clientConfig *client.ClientConfig) (ImageState, error) { +func GetImageState(imageDigest string, clientConfig *client.Config) (ImageState, error) { log.WithField("imageDigest", imageDigest).Debug("checking vulnerability report cache") _, ok := ReportCache.Get(imageDigest) if ok { log.WithField("imageDigest", imageDigest).Debug("found report in cache") return Analyzed, nil - } else { - log.WithField("imageDigest", imageDigest).Debug("no report in cache, generating") } + log.WithField("imageDigest", imageDigest).Debug("no report in cache, generating") img, err := client.GetImage(clientConfig, imageDigest) if err != nil { @@ -329,10 +337,12 @@ func GetImageState(imageDigest string, clientConfig *client.ClientConfig) (Image return NotFound, fmt.Errorf("not found") } if len(img) > 1 { - log.WithFields(log.Fields{"imageDigest": imageDigest, "imageCount": len(img)}).Warn("image status check returned more than one expected record. using the first") + log.WithFields(log.Fields{"imageDigest": imageDigest, "imageCount": len(img)}). + Warn("image status check returned more than one expected record. using the first") } - log.WithFields(log.Fields{"imageDigest": imageDigest, "analysis_status": img[0].AnalysisStatus}).Debug("image analysis status") + log.WithFields(log.Fields{"imageDigest": imageDigest, "analysis_status": img[0].AnalysisStatus}). + Debug("image analysis status") switch img[0].AnalysisStatus { case "analyzed": return Analyzed, nil @@ -354,53 +364,55 @@ func BuildHarborVulnerabilityReport( imageRepository string, imageDigest string, includeDescriptions bool, - clientConfig *client.ClientConfig, + clientConfig *client.Config, filterVendorIgnoredVulns bool, ) (harbor.VulnerabilityReport, error) { if imageRepository == "" || imageDigest == "" { return harbor.VulnerabilityReport{}, errors.New("no repository or digest provided to build vuln report for") - } else { - log.WithFields(log.Fields{"repository": imageRepository, "imageDigest": imageDigest}).Debug("getting harbor vulnerability report") } + log.WithFields(log.Fields{"repository": imageRepository, "imageDigest": imageDigest}). + Debug("getting harbor vulnerability report") start := time.Now() anchoreVulnResponse, err := GetAnchoreVulnReport(imageDigest, clientConfig, filterVendorIgnoredVulns) if err != nil { - log.WithFields(log.Fields{"repository": imageRepository, "imageDigest": imageDigest}).Error("error from vulnerability report api call to Anchore") + log.WithFields(log.Fields{"repository": imageRepository, "imageDigest": imageDigest}). + Error("error from vulnerability report api call to Anchore") return harbor.VulnerabilityReport{}, err } vulnListingTime := time.Since(start) - log.WithFields(log.Fields{"repository": imageRepository, "imageDigest": imageDigest, "vulnerabilityListApiCallDuration": vulnListingTime}).Debug("time to get vulnerability listing") + log.WithFields(log.Fields{"repository": imageRepository, "imageDigest": imageDigest, "vulnerabilityListApiCallDuration": vulnListingTime}). + Debug("time to get vulnerability listing") vulnDescriptionMap := make(map[string]string) if includeDescriptions { // Get vulnerability id/group mappings for getting additional metadata // remove duplicates where vuln can have multiple matches - uniqVulnIdNamespacePairs := make(map[anchore.NamespacedVulnerability]bool) + uniqVulnIDNamespacePairs := make(map[anchore.NamespacedVulnerability]bool) for _, v := range anchoreVulnResponse.Vulnerabilities { - vulnId := anchore.NamespacedVulnerability{ + vulnID := anchore.NamespacedVulnerability{ ID: v.VulnerabilityID, Namespace: v.FeedGroup, Description: "", } // Check cache - cachedDescription, ok := DescriptionCache.Get(cacheKeyForVuln(&vulnId)) + cachedDescription, ok := DescriptionCache.Get(cacheKeyForVuln(&vulnID)) if ok { // Found in cache, add to the final map - vulnDescriptionMap[vulnId.ID] = cachedDescription.(string) + vulnDescriptionMap[vulnID.ID] = cachedDescription.(string) } else { // Not in cache, pass to lookup array - uniqVulnIdNamespacePairs[vulnId] = true + uniqVulnIDNamespacePairs[vulnID] = true } } // Convert the map into an array for downstream - vulns := make([]anchore.NamespacedVulnerability, len(uniqVulnIdNamespacePairs)) + vulns := make([]anchore.NamespacedVulnerability, len(uniqVulnIDNamespacePairs)) i := 0 - for v := range uniqVulnIdNamespacePairs { + for v := range uniqVulnIDNamespacePairs { vulns[i] = v i++ } @@ -415,6 +427,7 @@ func BuildHarborVulnerabilityReport( // Pivot to a map for next call for _, desc := range vulns { + desc := desc // Reassign desc to avoid closure issues vulnDescriptionMap[desc.ID] = desc.Description // Add to the cache @@ -422,18 +435,20 @@ func BuildHarborVulnerabilityReport( } descriptionTime := time.Since(start) - log.WithFields(log.Fields{"repository": imageRepository, "imageDigest": imageDigest, "vulnerabilityDescriptionConstructionDuration": descriptionTime}).Debug("time to get descriptions") + log.WithFields(log.Fields{"repository": imageRepository, "imageDigest": imageDigest, "vulnerabilityDescriptionConstructionDuration": descriptionTime}). + Debug("time to get descriptions") } else { log.Debug("Skipping vuln description merge, as dictated by configuration") } - log.WithFields(log.Fields{"repository": imageRepository, "imageDigest": imageDigest}).Debug("finished fetching Anchore data to construct scan result") + log.WithFields(log.Fields{"repository": imageRepository, "imageDigest": imageDigest}). + Debug("finished fetching Anchore data to construct scan result") return ToHarborScanResult(imageRepository, anchoreVulnResponse, vulnDescriptionMap) } func GetAnchoreVulnReport( digest string, - clientConfig *client.ClientConfig, + clientConfig *client.Config, filterVendorIgnoredVulns bool, ) (anchore.ImageVulnerabilityReport, error) { imageState, err := GetImageState(digest, clientConfig) @@ -467,17 +482,18 @@ func GetAnchoreVulnReport( } // GetRawVulnerabilityReport Get an adapter-native (Anchore) formatted vulnerability report for the requested ScanId -func (s *HarborScannerAdapter) GetRawVulnerabilityReport(scanId string) (harbor.RawReport, error) { - if scanId == "" { +func (s *HarborScannerAdapter) GetRawVulnerabilityReport(scanID string) (harbor.RawReport, error) { + if scanID == "" { return harbor.VulnerabilityReport{}, errors.New("no ScanId") } - repository, digest, err := ScanIdToRegistryDigest(scanId) + repository, digest, err := ScanIDToRegistryDigest(scanID) if err != nil { return harbor.VulnerabilityReport{}, err } - log.WithFields(log.Fields{"repository": repository, "imageDigest": digest, "scanId": scanId}).Info("Getting raw Anchore-formatted vulnerability report") + log.WithFields(log.Fields{"repository": repository, "imageDigest": digest, "scanId": scanID}). + Info("Getting raw Anchore-formatted vulnerability report") return GetAnchoreVulnReport(digest, &s.Configuration.AnchoreClientConfig, s.Configuration.FullVulnerabilityDescriptions) } @@ -524,6 +540,7 @@ func ToHarborScanResult( var ok bool for i, v := range srs.Vulnerabilities { + v := v // Reassign v to avoid closure issues sev = harbor.ToHarborSeverity(v.Severity) if vulnDescriptions != nil { @@ -590,7 +607,7 @@ func (s *HarborScannerAdapter) GetMetadata() (harbor.ScannerAdapterMetadata, err if cached, ok := UpdateTimestampCache.Get("db"); ok { feedsUpdated = cached.(time.Time) } else { - feedsUpdated, err = client.GetVulnDbUpdateTime(&s.Configuration.AnchoreClientConfig) + feedsUpdated, err = client.GetVulnDBUpdateTime(&s.Configuration.AnchoreClientConfig) if err != nil { log.WithField("err", err).Error("could not get vulnerability db update time") return harbor.ScannerAdapterMetadata{}, err @@ -601,6 +618,6 @@ func (s *HarborScannerAdapter) GetMetadata() (harbor.ScannerAdapterMetadata, err UpdateTimestampCache.Add("db", feedsUpdated) log.WithField("VulnerabilityDbUpdateTimestamp", feedsUpdated).Debug("vulnerability DB update timestamp retrieved") - adapterMeta.Properties[adapter.HarborMetadataVulnDbUpdateKey] = feedsUpdated.Format(time.RFC3339) + adapterMeta.Properties[adapter.HarborMetadataVulnDBUpdateKey] = feedsUpdated.Format(time.RFC3339) return adapterMeta, nil } diff --git a/pkg/adapter/anchore/adapter_test.go b/pkg/adapter/anchore/adapter_test.go index d3772fb..2eb2fc6 100644 --- a/pkg/adapter/anchore/adapter_test.go +++ b/pkg/adapter/anchore/adapter_test.go @@ -83,15 +83,14 @@ func TestScanIdToRegistryDigest(t *testing.T) { } for _, v := range inputs { - generated, err := GenerateScanId(v[1], v[2]) + generated, err := GenerateScanID(v[1], v[2]) if err != nil { t.Errorf("failed: %v", err) } - if repo, dig, err := ScanIdToRegistryDigest(generated); (err == nil) != (v[3] == "true") { + if repo, dig, err := ScanIDToRegistryDigest(generated); (err == nil) != (v[3] == "true") { t.Errorf("Failed test. Repo=%v, Digest=%v err=%v", repo, dig, err) } - } } diff --git a/pkg/adapter/anchore/cache.go b/pkg/adapter/anchore/cache.go index 9fd82f2..f417eb8 100644 --- a/pkg/adapter/anchore/cache.go +++ b/pkg/adapter/anchore/cache.go @@ -18,8 +18,8 @@ type CacheConfiguration struct { VulnDescriptionCacheEnabled bool VulnDescriptionCacheMaxCount int VulnDescriptionCacheTTL int - DbUpdateCacheEnabled bool - DbUpdatedCacheTTL int + DBUpdateCacheEnabled bool + DBUpdatedCacheTTL int VulnReportCacheEnabled bool VulnReportCacheMaxCount int VulnReportCacheTTL int @@ -38,14 +38,14 @@ type LockingTTLCache struct { Enabled bool // If true, use the cache, else always bypass } -// Cache for vulnerability description text since those must be retrieved from the Anchore APIs separately +// DescriptionCache for vulnerability description text since those must be retrieved from the Anchore APIs separately // This can be removed if/when the Anchore API vulnerability response includes descriptions directly var DescriptionCache *LockingTTLCache -// Cache for the vulnerability response from the Anchore API +// ReportCache for the vulnerability response from the Anchore API var ReportCache *LockingTTLCache -// Cache for storing vuln db update timestamps to minimize the calls to get the db timestamp since it isn't part of +// UpdateTimestampCache for storing vuln db update timestamps to minimize the calls to get the db timestamp since it isn't part of // the vulnerability response var UpdateTimestampCache *LockingTTLCache @@ -113,6 +113,6 @@ func InitCaches(configuration CacheConfiguration) error { configuration.VulnReportCacheMaxCount, configuration.VulnReportCacheTTL, ) - UpdateTimestampCache = NewCache(configuration.DbUpdateCacheEnabled, 1, configuration.DbUpdatedCacheTTL) + UpdateTimestampCache = NewCache(configuration.DBUpdateCacheEnabled, 1, configuration.DBUpdatedCacheTTL) return nil } diff --git a/pkg/adapter/anchore/cache_test.go b/pkg/adapter/anchore/cache_test.go index 036e3ad..c569988 100644 --- a/pkg/adapter/anchore/cache_test.go +++ b/pkg/adapter/anchore/cache_test.go @@ -1,9 +1,9 @@ package anchore import ( + "crypto/rand" "encoding/base64" "fmt" - "math/rand" "testing" "time" @@ -47,10 +47,8 @@ func TestGetCachedDbUpdateTime(t *testing.T) { if t2, ok := UpdateTimestampCache.Get("db"); !ok { t.Fatal("not cached") - } else { - if t2 != testT { - t.Fatal("wrong ts cached") - } + } else if t2 != testT { + t.Fatal("wrong ts cached") } } @@ -127,13 +125,12 @@ func TestCacheVulnDescription(t *testing.T) { if ok { t.Fatal("should not get value after flush") } - } func TestCacheVulnDescriptionTimeout(t *testing.T) { t.SkipNow() err := InitCaches(DefaultCacheConfig) - testTTL := time.Duration(3 * time.Second) + testTTL := 3 * time.Second DescriptionCache.TTL = testTTL if err != nil { t.Fatal(err) @@ -157,7 +154,6 @@ func TestCacheVulnDescriptionTimeout(t *testing.T) { if DescriptionCache.Cache.Len() > 0 { t.Fatal("should not have any cached entries after ttl + request") } - } // Test for manual checks of memory usage for various sizes of data @@ -167,7 +163,7 @@ func TestVulnDescriptionCacheSize(t *testing.T) { if err != nil { t.Fatal(err) } - var tmp = make([]byte, 1000) + tmp := make([]byte, 1000) for i := 0; i < 10000; i++ { _, err := rand.Read(tmp) @@ -183,7 +179,6 @@ func TestVulnDescriptionCacheSize(t *testing.T) { Description: desc, }) } - } // Vuln report cache tests diff --git a/pkg/adapter/anchore/client/client.go b/pkg/adapter/anchore/client/client.go index f0a7892..fa035ef 100644 --- a/pkg/adapter/anchore/client/client.go +++ b/pkg/adapter/anchore/client/client.go @@ -21,17 +21,17 @@ import ( const ( CHUNKSIZE = 100 NVDFEEDGROUP = "nvdv2:cves" - RegistryCredentialUpdateRequestTemplate = `{"registry": "%v", "registry_user": "%v", "registry_pass": "%v", "registry_verify": %v, "registry_type": "docker_v2"}` + RegistryCredentialUpdateRequestTemplate = `{"registry": "%v", "registry_user": "%v", "registry_pass": "%v", "registry_verify": %v, "registry_type": "docker_v2"}` // #nosec G101 AddImageURL = "/v1/images" GetImageURLTemplate = "/v1/images/%s" GetImageVulnerabilitiesURLTemplate = "/v1/images/%s/vuln/all" QueryVulnerabilitiesURLTemplate = "/v1/query/vulnerabilities" RegistriesCollectionURL = "/v1/registries" - RegistryCredentialUpdateURLTemplate = "/v1/registries/%s" + RegistryCredentialUpdateURLTemplate = "/v1/registries/%s" // #nosec G101 FeedsURL = "/v1/system/feeds" ) -type ClientConfig struct { +type Config struct { Endpoint string Username string Password string @@ -39,7 +39,7 @@ type ClientConfig struct { TLSVerify bool } -func getNewRequest(clientConfiguration *ClientConfig) *gorequest.SuperAgent { +func getNewRequest(clientConfiguration *Config) *gorequest.SuperAgent { passwordConfig := clientConfiguration.Password credenitalLoader := credential.CreateCredentialLoader(passwordConfig) clientConfiguration.Password = credenitalLoader.LoadFromCredentialStore(passwordConfig) @@ -48,59 +48,22 @@ func getNewRequest(clientConfiguration *ClientConfig) *gorequest.SuperAgent { return gorequest.New(). TLSClientConfig(&tls.Config{InsecureSkipVerify: clientConfiguration.TLSVerify}). SetBasicAuth(clientConfiguration.Username, clientConfiguration.Password). - Timeout(timeout) + Timeout(timeout) // #nosec G402 } -// Handle error responses generically -func unmarshalError(body []byte, response gorequest.Response) (anchore.Error, error) { - result := anchore.Error{} - - if response != nil && response.Header.Get("Content-Type") == "application/problem+json" { - // Try to unmarshal a json problem and map to anchore error - jsonError := anchore.ApplicationJSONError{} - err := json.Unmarshal(body, &jsonError) - if err != nil { - return anchore.Error{}, err - } - result.Message = jsonError.Title - result.HTTPCode = response.StatusCode - result.Detail["title"] = jsonError.Title - result.Detail["detail"] = jsonError.Detail - result.Detail["type"] = jsonError.Type - result.Detail["instance"] = jsonError.Instance - result.Detail["status"] = jsonError.Status - return result, nil - } else if len(body) > 0 { - // Try to unmarshal an anchore error - err := json.Unmarshal(body, &result) - if err != nil { - // Do a very raw decode - if response != nil { - result.Message = string(body) - result.HTTPCode = response.StatusCode - } else { - return result, err - } - } - return result, nil - } else { - return result, fmt.Errorf("nothing to unmarshal") - } -} - -func AnalyzeImage(clientConfiguration *ClientConfig, analyzeRequest anchore.ImageScanRequest) error { +func AnalyzeImage(clientConfiguration *Config, analyzeRequest anchore.ImageScanRequest) error { log.WithFields(log.Fields{"request": analyzeRequest}).Info("requesting image analysis") request := getNewRequest(clientConfiguration) - reqUrl, err := buildUrl(*clientConfiguration, AddImageURL, nil) + reqURL, err := buildURL(*clientConfiguration, AddImageURL, nil) if err != nil { return err } - log.WithFields(log.Fields{"method": "post", "url": reqUrl}).Debug("sending request to anchore api") + log.WithFields(log.Fields{"method": "post", "url": reqURL}).Debug("sending request to anchore api") // call API get the full report until "analysis_status" = "analyzed" - resp, _, errs := sendRequest(request.Post(reqUrl).Set("Content-Type", "application/json").Send(analyzeRequest)) + resp, _, errs := sendRequest(request.Post(reqURL).Set("Content-Type", "application/json").Send(analyzeRequest)) if errs != nil { log.Errorf("could not contact anchore api") return errs[0] @@ -112,7 +75,7 @@ func AnalyzeImage(clientConfiguration *ClientConfig, analyzeRequest anchore.Imag } // Updates the Description fields in the input array of Description objects -func GetVulnerabilityDescriptions(clientConfiguration *ClientConfig, vulns *[]anchore.NamespacedVulnerability) error { +func GetVulnerabilityDescriptions(clientConfiguration *Config, vulns *[]anchore.NamespacedVulnerability) error { var vulnListing anchore.VulnerabilityQueryResults // Sort and get namespaces for efficient lookups @@ -166,7 +129,6 @@ func GetVulnerabilityDescriptions(clientConfiguration *ClientConfig, vulns *[]an if errs != nil { log.WithField("errs", errs).Debug("error getting vuln records") return errs[0] - } vulnListing.Vulnerabilities = append(vulnListing.Vulnerabilities, qryResults.Vulnerabilities...) @@ -193,30 +155,29 @@ func GetVulnerabilityDescriptions(clientConfiguration *ClientConfig, vulns *[]an foundDescription = "" // update each with rules - if rec, ok := vulnDescriptionMap[vulnRecord.ID]; !ok { + rec, ok := vulnDescriptionMap[vulnRecord.ID] + if !ok { log.WithFields(log.Fields{"vulnerabilityId": vulnRecord.ID, "namespace": vulnRecord.Namespace}). Debug("no vulnerability record in anchore api vulnerability metadata query results") continue - } else { - - if ns, ok := rec[vulnRecord.Namespace]; ok && ns != "" { - foundDescription = ns - } else if vulnRecord.Namespace != NVDFEEDGROUP { - // No record in Namespace, try nvdv2 - if nvdDesc, ok := rec[NVDFEEDGROUP]; ok { - foundDescription = nvdDesc - } + } + if ns, ok := rec[vulnRecord.Namespace]; ok && ns != "" { + foundDescription = ns + } else if vulnRecord.Namespace != NVDFEEDGROUP { + // No record in Namespace, try nvdv2 + if nvdDesc, ok := rec[NVDFEEDGROUP]; ok { + foundDescription = nvdDesc } + } - if foundDescription == "" { - log.WithField("vulnerabilityId", vulnRecord.ID).Trace("no description found for vulnerability") + if foundDescription == "" { + log.WithField("vulnerabilityId", vulnRecord.ID).Trace("no description found for vulnerability") + } else { + if vulnRecord.Description == "" { + log.WithField("vulnerabilityId", vulnRecord.ID).Trace("updating vulnerability description in response report") + vulnRecord.Description = foundDescription } else { - if vulnRecord.Description == "" { - log.WithField("vulnerabilityId", vulnRecord.ID).Trace("updating vulnerability description in response report") - vulnRecord.Description = foundDescription - } else { - log.WithField("vulnerabilityId", vulnRecord.ID).Trace("vulnerability already has description, skipping update") - } + log.WithField("vulnerabilityId", vulnRecord.ID).Trace("vulnerability already has description, skipping update") } } @@ -229,7 +190,7 @@ func GetVulnerabilityDescriptions(clientConfiguration *ClientConfig, vulns *[]an // Simple query that handles pagination and returns the results func QueryVulnerabilityRecords( - clientConfiguration *ClientConfig, + clientConfiguration *Config, ids []string, namespaces []string, ) (anchore.VulnerabilityQueryResults, []error) { @@ -242,18 +203,18 @@ func QueryVulnerabilityRecords( namespaceStr := strings.Join(namespaces, ",") request := getNewRequest(clientConfiguration) - more_pages := true + morePages := true start = time.Now() - reqUrl, err := buildUrl(*clientConfiguration, QueryVulnerabilitiesURLTemplate, nil) + reqURL, err := buildURL(*clientConfiguration, QueryVulnerabilitiesURLTemplate, nil) if err != nil { return vulnListing, []error{err} } - for more_pages { + for morePages { pageStart = time.Now() - req := request.Get(reqUrl).Param("id", vulnIdsStr).Param("namespace", namespaceStr) + req := request.Get(reqURL).Param("id", vulnIdsStr).Param("namespace", namespaceStr) if page != "" { req = req.Param("page", page) } @@ -261,31 +222,30 @@ func QueryVulnerabilityRecords( resp, body, errs := sendRequest(req) if errs != nil { return vulnListing, errs - } else { - if resp.StatusCode == 200 { - err := json.Unmarshal(body, &vulnPage) - if err != nil { - return vulnListing, []error{fmt.Errorf("failed getting vulnerability metadata")} - } - - if vulnPage.NextPage != "" { - more_pages = true - page = vulnPage.NextPage - log.WithField("nextPage", vulnPage.NextPage).Debug("more pages found") - } else { - log.Debug("no more pages of results") - more_pages = false - page = "" - } - - vulnListing.Vulnerabilities = append(vulnListing.Vulnerabilities, vulnPage.Vulnerabilities...) + } + if resp.StatusCode == 200 { + err := json.Unmarshal(body, &vulnPage) + if err != nil { + return vulnListing, []error{fmt.Errorf("failed getting vulnerability metadata")} + } - // Merge the counts so the response to caller looks like the result of a single call - vulnListing.ReturnedCount += vulnPage.ReturnedCount + if vulnPage.NextPage != "" { + morePages = true + page = vulnPage.NextPage + log.WithField("nextPage", vulnPage.NextPage).Debug("more pages found") } else { - log.WithFields(log.Fields{"status": resp.StatusCode, "body": string(body), "url": resp.Request.URL}).Errorf("got non 200 response from server for vuln query") - return vulnListing, []error{fmt.Errorf("error response from server")} + log.Debug("no more pages of results") + morePages = false + page = "" } + + vulnListing.Vulnerabilities = append(vulnListing.Vulnerabilities, vulnPage.Vulnerabilities...) + + // Merge the counts so the response to caller looks like the result of a single call + vulnListing.ReturnedCount += vulnPage.ReturnedCount + } else { + log.WithFields(log.Fields{"status": resp.StatusCode, "body": string(body), "url": resp.Request.URL}).Errorf("got non 200 response from server for vuln query") + return vulnListing, []error{fmt.Errorf("error response from server")} } pageEnd = time.Now() @@ -307,15 +267,14 @@ func getVulnProcessingChunks(itemCount, chunkToGet, chunkSize int) (int, int) { cs := float64(chunkSize) if chunkToGet*chunkSize < itemCount { return chunkToGet * chunkSize, int(math.Min(float64(chunkToGet+1)*cs, float64(last))) - } else { - // Out of range - return -1, -1 } + // Out of range + return -1, -1 } // Retrieve the vulnerabilities func GetImageVulnerabilities( - clientConfiguration *ClientConfig, + clientConfiguration *Config, digest string, filterIgnored bool, ) (anchore.ImageVulnerabilityReport, error) { @@ -323,13 +282,13 @@ func GetImageVulnerabilities( var imageVulnerabilityReport anchore.ImageVulnerabilityReport - reqUrl, err := buildUrl(*clientConfiguration, GetImageVulnerabilitiesURLTemplate, []interface{}{digest}) + reqURL, err := buildURL(*clientConfiguration, GetImageVulnerabilitiesURLTemplate, []interface{}{digest}) if err != nil { return imageVulnerabilityReport, err } request := getNewRequest(clientConfiguration) - resp, body, errs := sendRequest(request.Get(reqUrl).Param("vendor_only", strconv.FormatBool(filterIgnored))) + resp, body, errs := sendRequest(request.Get(reqURL).Param("vendor_only", strconv.FormatBool(filterIgnored))) if errs != nil { return imageVulnerabilityReport, errs[0] } @@ -340,25 +299,24 @@ func GetImageVulnerabilities( return imageVulnerabilityReport, err } return imageVulnerabilityReport, nil - } else { - return imageVulnerabilityReport, fmt.Errorf("error response from anchore api") } + return imageVulnerabilityReport, fmt.Errorf("error response from anchore api") } -func GetImage(clientConfiguration *ClientConfig, digest string) (anchore.ImageList, error) { +func GetImage(clientConfiguration *Config, digest string) (anchore.ImageList, error) { log.WithFields(log.Fields{"digest": digest}).Debug("retrieving anchore state for image") var imageList anchore.ImageList request := getNewRequest(clientConfiguration) - reqUrl, err := buildUrl(*clientConfiguration, GetImageURLTemplate, []interface{}{digest}) + reqURL, err := buildURL(*clientConfiguration, GetImageURLTemplate, []interface{}{digest}) if err != nil { return imageList, err } - log.WithFields(log.Fields{"method": "get", "url": reqUrl}).Debug("sending request to anchore api") + log.WithFields(log.Fields{"method": "get", "url": reqURL}).Debug("sending request to anchore api") // call API get the full report until "analysis_status" = "analyzed" - _, body, errs := sendRequest(request.Get(reqUrl)) + _, body, errs := sendRequest(request.Get(reqURL)) if errs != nil { log.Errorf("could not contact anchore api") return imageList, errs[0] @@ -372,14 +330,14 @@ func GetImage(clientConfiguration *ClientConfig, digest string) (anchore.ImageLi return imageList, nil } -func GetVulnDbUpdateTime(clientConfiguration *ClientConfig) (time.Time, error) { +func GetVulnDBUpdateTime(clientConfiguration *Config) (time.Time, error) { request := getNewRequest(clientConfiguration) - reqUrl, err := buildUrl(*clientConfiguration, FeedsURL, nil) + reqURL, err := buildURL(*clientConfiguration, FeedsURL, nil) if err != nil { return time.Time{}, err } - _, body, errs := sendRequest(request.Get(reqUrl)) + _, body, errs := sendRequest(request.Get(reqURL)) if errs != nil { return time.Time{}, errs[0] } @@ -397,10 +355,9 @@ func GetVulnDbUpdateTime(clientConfiguration *ClientConfig) (time.Time, error) { for _, group := range feed.Groups { ts := group.LastSync if ts != "" { - // Adjust the time to ensure it has trailing Z for UTC if ts[len(ts)-1] != 'Z' { - ts = ts + "Z" + ts += "Z" } t, err := StringToTime(ts) if err != nil { @@ -434,22 +391,21 @@ func StringToTime(timestampString string) (time.Time, error) { } // Process the registry URL to return only the hostname and port as docker pull strings support -func ExtractRegistryFromUrl(registryUrl string) (string, error) { - u, err := url.Parse(registryUrl) +func ExtractRegistryFromURL(registryURL string) (string, error) { + u, err := url.Parse(registryURL) if err != nil { return "", err } if u.Host != "" { return u.Host, nil - } else { - return "", fmt.Errorf("no host portion found in the input url. must be a full url with scheme") } + return "", fmt.Errorf("no host portion found in the input url. must be a full url with scheme") } // Return the registry credential entry as anchore will use it. This is the registry url minus the scheme + / + repository name -func RegistryNameFromRepo(registryUrl string, repository string) (string, error) { - reg, err := ExtractRegistryFromUrl(registryUrl) +func RegistryNameFromRepo(registryURL string, repository string) (string, error) { + reg, err := ExtractRegistryFromURL(registryURL) if err != nil { return "", err } @@ -458,7 +414,7 @@ func RegistryNameFromRepo(registryUrl string, repository string) (string, error) } // Build the request URL -func buildUrl(config ClientConfig, requestPathTemplate string, args []interface{}) (string, error) { +func buildURL(config Config, requestPathTemplate string, args []interface{}) (string, error) { u, err := url.Parse(config.Endpoint) if err != nil { return "", err @@ -470,8 +426,8 @@ func buildUrl(config ClientConfig, requestPathTemplate string, args []interface{ // Add a new registry credential to anchore func AddRegistryCredential( - clientConfiguration *ClientConfig, - registryUrl string, + clientConfiguration *Config, + registryURL string, repository string, username string, password string, @@ -479,12 +435,12 @@ func AddRegistryCredential( validateCreds bool, ) (gorequest.Response, []byte, []error) { request := getNewRequest(clientConfiguration) - registryName, err := RegistryNameFromRepo(registryUrl, repository) + registryName, err := RegistryNameFromRepo(registryURL, repository) if err != nil { return nil, nil, []error{err} } - registryAddUrl, err := buildUrl(*clientConfiguration, RegistriesCollectionURL, nil) + registryAddURL, err := buildURL(*clientConfiguration, RegistriesCollectionURL, nil) if err != nil { return nil, nil, []error{err} } @@ -492,7 +448,7 @@ func AddRegistryCredential( payload := fmt.Sprintf(RegistryCredentialUpdateRequestTemplate, registryName, username, password, registryTLSVerify) return sendRequest( - request.Post(registryAddUrl). + request.Post(registryAddURL). Set("Content-Type", "application/json"). Param("validate", strconv.FormatBool(validateCreds)). Send(payload), @@ -501,8 +457,8 @@ func AddRegistryCredential( // Update an existing credential record func UpdateRegistryCredential( - clientConfiguration *ClientConfig, - registryUrl string, + clientConfiguration *Config, + registryURL string, repository string, username string, password string, @@ -510,7 +466,7 @@ func UpdateRegistryCredential( validateCreds bool, ) (gorequest.Response, []byte, []error) { request := getNewRequest(clientConfiguration) - registryName, err := RegistryNameFromRepo(registryUrl, repository) + registryName, err := RegistryNameFromRepo(registryURL, repository) if err != nil { log.WithField("err", err).Error("cannot update pull credential due to registryUrl name construction failing") return nil, nil, []error{err} @@ -565,6 +521,6 @@ func sendRequest(req *gorequest.SuperAgent) (gorequest.Response, []byte, []error "method": req.Method, }).Debug("sending request") resp, body, errs := req.EndBytes() - log.WithField("duration", time.Now().Sub(t)).Debug("api call duration") + log.WithField("duration", time.Since(t)).Debug("api call duration") return logResponse(resp, body, errs) } diff --git a/pkg/adapter/anchore/client/client_test.go b/pkg/adapter/anchore/client/client_test.go index 79fb859..ed2da98 100644 --- a/pkg/adapter/anchore/client/client_test.go +++ b/pkg/adapter/anchore/client/client_test.go @@ -1,9 +1,10 @@ package client import ( - "github.com/anchore/harbor-scanner-adapter/pkg/model/anchore" "sort" "testing" + + "github.com/anchore/harbor-scanner-adapter/pkg/model/anchore" ) const ( @@ -104,24 +105,24 @@ const ( "httpcode": 400, "message": "cannot fetch image digest/manifest from registry" }` -) +) // #nosec G101 func TestSorting(t *testing.T) { vulns := []anchore.NamespacedVulnerability{ { - "id1", - "namespace1", - "", + ID: "id1", + Namespace: "namespace1", + Description: "", }, { - "id2", - "namespace2", - "", + ID: "id2", + Namespace: "namespace2", + Description: "", }, { - "id3", - "namespace1", - "", + ID: "id3", + Namespace: "namespace1", + Description: "", }, } @@ -161,7 +162,7 @@ func TestExtractRegistryFromUrl(t *testing.T) { for _, r := range registries { expected := r[1] input := r[0] - got, err := ExtractRegistryFromUrl(input) + got, err := ExtractRegistryFromURL(input) if err != nil { if expected != "" { t.Errorf("error on %v: %v", input, err) @@ -172,14 +173,17 @@ func TestExtractRegistryFromUrl(t *testing.T) { t.Errorf("Expected %v, Got %v", r[1], got) } } - return } func TestRegistryNameFromRepo(t *testing.T) { registries := [][]string{ // Ok, should work {"http://core.harbor.domain:8080", "library", "core.harbor.domain:8080/library"}, - {"https://core.harbor.domain:8080", "testproject/repository/image", "core.harbor.domain:8080/testproject/repository/image"}, + { + "https://core.harbor.domain:8080", + "testproject/repository/image", + "core.harbor.domain:8080/testproject/repository/image", + }, {"https://core.harbor.domain", "some/repository/with/slashes", "core.harbor.domain/some/repository/with/slashes"}, {"https://core.harbor.domain/path/v1/", "repo1", "core.harbor.domain/repo1"}, // These fail since no scheme. @@ -201,5 +205,4 @@ func TestRegistryNameFromRepo(t *testing.T) { t.Errorf("Expected %v, Got %v", r[2], got) } } - return } diff --git a/pkg/adapter/anchore/config.go b/pkg/adapter/anchore/config.go index d956239..da4485b 100644 --- a/pkg/adapter/anchore/config.go +++ b/pkg/adapter/anchore/config.go @@ -14,17 +14,17 @@ import ( type AdapterConfig struct { ListenAddr string // Address to listen on, e.g ":8080" or "127.0.0.1:80" - ApiKey string // Key for auth, used as a Bearer token + APIKey string // Key for auth, used as a Bearer token LogFormat string LogLevel log.Level FullVulnerabilityDescriptions bool // If true, the scanner adapter will query anchore to get vuln descriptions, else will use cvss string and defer to the link url TLSKeyFile string // Path to key file TLSCertFile string // Path to cert file FilterVendorIgnoredVulns bool - TLSVerify bool // Enable TLS verification on api calls to the adapter - RegistryTLSVerify bool // Enable TLS verification on Anchore's calls to the registry on the data path - RegistryValidateCreds bool // Validate registry credentials when adding them to Anchore via the Anchore API - AnchoreClientConfig client.ClientConfig // Credentials and client configuration + TLSVerify bool // Enable TLS verification on api calls to the adapter + RegistryTLSVerify bool // Enable TLS verification on Anchore's calls to the registry on the data path + RegistryValidateCreds bool // Validate registry credentials when adding them to Anchore via the Anchore API + AnchoreClientConfig client.Config // Credentials and client configuration CacheConfig CacheConfiguration UseAnchoreConfiguredCreds bool // If true, the adapter will ignore the dynamic credentials that are provided by harbor for each scan and will instead expect that the admin has configured Anchore with credentials out-of-band. Default is False. } @@ -36,10 +36,10 @@ const ( ListenAddrEnvVar = "SCANNER_ADAPTER_LISTEN_ADDR" LogLevelEnvVar = "SCANNER_ADAPTER_LOG_LEVEL" LogFormatEnvVar = "SCANNER_ADAPTER_LOG_FORMAT" - ApiKeyEnvVar = "SCANNER_ADAPTER_APIKEY" + APIKeyEnvVar = "SCANNER_ADAPTER_APIKEY" FullVulnDescriptionsEnvVar = "SCANNER_ADAPTER_FULL_VULN_DESCRIPTIONS" - TlsKeyEnvVar = "SCANNER_ADAPTER_TLS_KEY_FILE" - TlsCertEnvVar = "SCANNER_ADAPTER_TLS_CERT_FILE" + TLSKeyEnvVar = "SCANNER_ADAPTER_TLS_KEY_FILE" + TLSCertEnvVar = "SCANNER_ADAPTER_TLS_CERT_FILE" FilterVendorIgnoredVulns = "SCANNER_ADAPTER_FILTER_VENDOR_IGNORED" TLSVerifyEnvVarName = "SCANNER_ADAPTER_TLS_VERIFY" RegistryValidateCredsEnvVarName = "SCANNER_ADAPTER_REGISTRY_VALIDATE_CREDS" @@ -51,31 +51,31 @@ const ( TimeoutEnvVarName = "ANCHORE_CLIENT_TIMEOUT_SECONDS" DescriptionCacheEnabledEnvVarName = "SCANNER_ADAPTER_DESCRIPTION_CACHE_ENABLED" DescriptionCacheItemCount = "SCANNER_ADAPTER_DESCRIPTION_CACHE_COUNT" - DescriptionCacheTtl = "SCANNER_ADAPTER_DESCRIPTION_CACHE_TTL" + DescriptionCacheTTL = "SCANNER_ADAPTER_DESCRIPTION_CACHE_TTL" DefaultDescriptionCacheEnabled = true DefaultDescriptionCacheTTL = 60 * 60 * 24 DefaultDescriptionCacheItemCount = 10000 ReportCacheEnabledEnvVarName = "SCANNER_ADAPTER_REPORT_CACHE_ENABLED" ReportCacheItemCount = "SCANNER_ADAPTER_REPORT_CACHE_COUNT" - ReportCacheTtl = "SCANNER_ADAPTER_REPORT_CACHE_TTL" + ReportCacheTTL = "SCANNER_ADAPTER_REPORT_CACHE_TTL" DefaultReportCacheEnabled = true DefaultReportCacheTTL = 180 DefaultReportCacheItemCount = 100 - DbUpdateCacheEnabledEnvVarName = "SCANNER_ADAPTER_DB_UPDATE_CACHE_ENABLED" - DbUpdateCacheTtl = "SCANNER_ADAPTER_DB_UPDATE_CACHE_TTL" - DefaultDbUpdateCacheEnabled = true - DefaultDbUpdateCacheTTL = 60 + DBUpdateCacheEnabledEnvVarName = "SCANNER_ADAPTER_DB_UPDATE_CACHE_ENABLED" + DBUpdateCacheTTL = "SCANNER_ADAPTER_DB_UPDATE_CACHE_TTL" + DefaultDBUpdateCacheEnabled = true + DefaultDBUpdateCacheTTL = 60 UseAnchoreConfigCredsEnvVarName = "SCANNER_ADAPTER_IGNORE_HARBOR_CREDS" UseAnchoreConfigCredsDefault = false -) +) // #nosec G101 -// Initialized to defaults +// DefaultCacheConfig Initialized to defaults var DefaultCacheConfig = CacheConfiguration{ VulnDescriptionCacheEnabled: DefaultDescriptionCacheEnabled, VulnDescriptionCacheMaxCount: DefaultDescriptionCacheItemCount, VulnDescriptionCacheTTL: DefaultDescriptionCacheTTL, - DbUpdateCacheEnabled: DefaultDbUpdateCacheEnabled, - DbUpdatedCacheTTL: DefaultDbUpdateCacheTTL, + DBUpdateCacheEnabled: DefaultDBUpdateCacheEnabled, + DBUpdatedCacheTTL: DefaultDBUpdateCacheTTL, VulnReportCacheEnabled: DefaultReportCacheEnabled, VulnReportCacheMaxCount: DefaultReportCacheItemCount, VulnReportCacheTTL: DefaultReportCacheTTL, @@ -90,12 +90,14 @@ func GetEnvBoolean(varName string, defaultValue bool) (bool, error) { if value, ok := os.LookupEnv(varName); ok { value = strings.ToLower(value) // Normalize - if value == "true" || value == "y" || value == "yes" { + switch { + case value == "true" || value == "y" || value == "yes": return true, nil - } else if value == "false" || value == "n" || value == "no" { + case value == "false" || value == "n" || value == "no": return false, nil - } else { - log.WithFields(log.Fields{"value": value, "key": varName, "type": "bool"}).Error("invalid format for environment variable value") + default: + log.WithFields(log.Fields{"value": value, "key": varName, "type": "bool"}). + Error("invalid format for environment variable value") return false, fmt.Errorf("value %v cannot be parsed as a bool", value) } } @@ -104,6 +106,8 @@ func GetEnvBoolean(varName string, defaultValue bool) (bool, error) { } // Load the service configuration, from environment variables since there are no secrets here. If not set, uses default listen addr :8080 +// +//gocyclo:ignore func GetConfig() (AdapterConfig, error) { cfg := AdapterConfig{} var ok bool @@ -114,14 +118,9 @@ func GetConfig() (AdapterConfig, error) { // Verify the format as valid comps := strings.Split(cfg.ListenAddr, ":") if len(comps) == 2 { - if len(comps[0]) == 0 { - // Ok, like ":8080" - } else { - if net.ParseIP(comps[0]) == nil { - return cfg, fmt.Errorf("invalid IP component of listen address %s", cfg.ListenAddr) - } + if len(comps[0]) != 0 && net.ParseIP(comps[0]) == nil { + return cfg, fmt.Errorf("invalid IP component of listen address %s", cfg.ListenAddr) } - if _, err := strconv.Atoi(comps[1]); err != nil { return cfg, fmt.Errorf("invalid port format in listen address %s", cfg.ListenAddr) } @@ -132,7 +131,7 @@ func GetConfig() (AdapterConfig, error) { cfg.ListenAddr = DefaultListenAddr } - if cfg.ApiKey, ok = os.LookupEnv(ApiKeyEnvVar); ok { + if cfg.APIKey, ok = os.LookupEnv(APIKeyEnvVar); ok { log.Info("Detected api key in configuration") } else { log.Info("No api key detected in configuration") @@ -155,21 +154,21 @@ func GetConfig() (AdapterConfig, error) { var useVulnDescription string if useVulnDescription, ok = os.LookupEnv(FullVulnDescriptionsEnvVar); ok { log.Info("Full vuln description value detected in configuration") - cfg.FullVulnerabilityDescriptions = "false" != strings.ToLower(useVulnDescription) + cfg.FullVulnerabilityDescriptions = strings.ToLower(useVulnDescription) != "false" } else { log.Info("No full vulnerability description value found in env, defaulting to 'true'") cfg.FullVulnerabilityDescriptions = true } - if cfg.TLSCertFile, ok = os.LookupEnv(TlsCertEnvVar); !ok { + if cfg.TLSCertFile, ok = os.LookupEnv(TLSCertEnvVar); !ok { cfg.TLSCertFile = "" } - if cfg.TLSKeyFile, ok = os.LookupEnv(TlsKeyEnvVar); !ok { + if cfg.TLSKeyFile, ok = os.LookupEnv(TLSKeyEnvVar); !ok { cfg.TLSKeyFile = "" } - cfg.AnchoreClientConfig = client.ClientConfig{} + cfg.AnchoreClientConfig = client.Config{} if path, ok := os.LookupEnv(AuthConfigFile); ok { log.Printf("Using config file at %v", path) @@ -243,7 +242,7 @@ func GetConfig() (AdapterConfig, error) { cfg.CacheConfig = DefaultCacheConfig - cfg.CacheConfig.VulnDescriptionCacheEnabled, err = GetEnvBoolean( + cfg.CacheConfig.VulnDescriptionCacheEnabled, _ = GetEnvBoolean( DescriptionCacheEnabledEnvVarName, DefaultDescriptionCacheEnabled, ) @@ -257,16 +256,16 @@ func GetConfig() (AdapterConfig, error) { } } - if ttl, ok := os.LookupEnv(DescriptionCacheTtl); ok { + if ttl, ok := os.LookupEnv(DescriptionCacheTTL); ok { cfg.CacheConfig.VulnDescriptionCacheMaxCount, err = strconv.Atoi(ttl) if err != nil { - log.WithFields(log.Fields{"value": ttl, "key": DescriptionCacheTtl, "type": "int"}). + log.WithFields(log.Fields{"value": ttl, "key": DescriptionCacheTTL, "type": "int"}). Error("invalid format for environment variable value") return cfg, err } } - cfg.CacheConfig.VulnReportCacheEnabled, err = GetEnvBoolean(ReportCacheEnabledEnvVarName, DefaultReportCacheEnabled) + cfg.CacheConfig.VulnReportCacheEnabled, _ = GetEnvBoolean(ReportCacheEnabledEnvVarName, DefaultReportCacheEnabled) if count, ok := os.LookupEnv(ReportCacheItemCount); ok { cfg.CacheConfig.VulnReportCacheMaxCount, err = strconv.Atoi(count) @@ -277,27 +276,27 @@ func GetConfig() (AdapterConfig, error) { } } - if ttl, ok := os.LookupEnv(ReportCacheTtl); ok { + if ttl, ok := os.LookupEnv(ReportCacheTTL); ok { cfg.CacheConfig.VulnReportCacheMaxCount, err = strconv.Atoi(ttl) if err != nil { - log.WithFields(log.Fields{"value": ttl, "key": ReportCacheTtl, "type": "int"}). + log.WithFields(log.Fields{"value": ttl, "key": ReportCacheTTL, "type": "int"}). Error("invalid format for environment variable value") return cfg, err } } - cfg.CacheConfig.DbUpdateCacheEnabled, err = GetEnvBoolean(DbUpdateCacheEnabledEnvVarName, DefaultDbUpdateCacheEnabled) + cfg.CacheConfig.DBUpdateCacheEnabled, _ = GetEnvBoolean(DBUpdateCacheEnabledEnvVarName, DefaultDBUpdateCacheEnabled) - if ttl, ok := os.LookupEnv(DbUpdateCacheTtl); ok { - cfg.CacheConfig.DbUpdatedCacheTTL, err = strconv.Atoi(ttl) + if ttl, ok := os.LookupEnv(DBUpdateCacheTTL); ok { + cfg.CacheConfig.DBUpdatedCacheTTL, err = strconv.Atoi(ttl) if err != nil { - log.WithFields(log.Fields{"value": ttl, "key": DbUpdateCacheTtl, "type": "int"}). + log.WithFields(log.Fields{"value": ttl, "key": DBUpdateCacheTTL, "type": "int"}). Error("invalid format for environment variable value") return cfg, err } } - cfg.UseAnchoreConfiguredCreds, err = GetEnvBoolean(UseAnchoreConfigCredsEnvVarName, UseAnchoreConfigCredsDefault) + cfg.UseAnchoreConfiguredCreds, _ = GetEnvBoolean(UseAnchoreConfigCredsEnvVarName, UseAnchoreConfigCredsDefault) return cfg, nil } diff --git a/pkg/adapter/anchore/config_test.go b/pkg/adapter/anchore/config_test.go index 360f27f..986d033 100644 --- a/pkg/adapter/anchore/config_test.go +++ b/pkg/adapter/anchore/config_test.go @@ -36,9 +36,9 @@ func TestFileConfigLoad(t *testing.T) { func TestEnvConfig(t *testing.T) { var err error - err = os.Setenv(EndpointEnvVarName, "https://somehost:8228") - err = os.Setenv(UsernameEnvVarName, "harboruser") - err = os.Setenv(PasswordEnvVarName, "harboruserpassword") + _ = os.Setenv(EndpointEnvVarName, "https://somehost:8228") + _ = os.Setenv(UsernameEnvVarName, "harboruser") + _ = os.Setenv(PasswordEnvVarName, "harboruserpassword") conf, err := GetConfig() if err != nil { @@ -50,8 +50,3 @@ func TestEnvConfig(t *testing.T) { t.Fail() } } - -// Test both in place, ensure file has precedence -func TestFileOverride(t *testing.T) { - -} diff --git a/pkg/adapter/anchore/credential/awsloader.go b/pkg/adapter/anchore/credential/awsloader.go index 0d8b1a1..f7edfc3 100644 --- a/pkg/adapter/anchore/credential/awsloader.go +++ b/pkg/adapter/anchore/credential/awsloader.go @@ -32,8 +32,13 @@ func getAWSSecret(configValue string) string { log.WithFields(log.Fields{"region": region, "name": name, "key": key}).Debug("pass in secret manager parameters") - //Create a Secrets Manager client - svc := secretsmanager.New(session.New(), &aws.Config{Region: aws.String(region)}) + // Create a Secrets Manager client + awsSession, err := session.NewSession() + if err != nil { + log.WithFields(log.Fields{"err": err}).Error("failed to create aws session") + return "" + } + svc := secretsmanager.New(awsSession, &aws.Config{Region: aws.String(region)}) input := &secretsmanager.GetSecretValueInput{ SecretId: aws.String(name), VersionStage: aws.String("AWSCURRENT"), // VersionStage defaults to AWSCURRENT if unspecified @@ -41,7 +46,7 @@ func getAWSSecret(configValue string) string { result, err := svc.GetSecretValue(input) if err != nil { - if aerr, ok := err.(awserr.Error); ok { + if aerr, ok := err.(awserr.Error); ok { //nolint:errorlint switch aerr.Code() { case secretsmanager.ErrCodeDecryptionFailure: // Secrets Manager can't decrypt the protected secret text using the provided KMS key. @@ -75,7 +80,11 @@ func getAWSSecret(configValue string) string { secretString = *result.SecretString // a map container to decode the JSON structure into kmap := make(map[string]string) - json.Unmarshal([]byte(secretString), &kmap) + err := json.Unmarshal([]byte(secretString), &kmap) + if err != nil { + log.WithFields(log.Fields{"err": err}).Error("failed to unmarshal secret string") + return "" + } return kmap[key] } } diff --git a/pkg/adapter/anchore/credential/factory.go b/pkg/adapter/anchore/credential/factory.go index 5485b68..820d3be 100644 --- a/pkg/adapter/anchore/credential/factory.go +++ b/pkg/adapter/anchore/credential/factory.go @@ -4,11 +4,11 @@ import ( "strings" ) -type CredentialLoader interface { +type Loader interface { LoadFromCredentialStore(passwordConfig string) string } -func CreateCredentialLoader(passwordConfig string) CredentialLoader { +func CreateCredentialLoader(passwordConfig string) Loader { if strings.HasPrefix(passwordConfig, "aws:secretmanager") { return &AWSCredenitalLoader{} } diff --git a/pkg/adapter/anchore/result_store.go b/pkg/adapter/anchore/result_store.go index 6120f18..3660035 100644 --- a/pkg/adapter/anchore/result_store.go +++ b/pkg/adapter/anchore/result_store.go @@ -12,19 +12,19 @@ import ( type ResultStore interface { HasResult( - scanId string, + scanID string, ) bool // Check if a result is available RequestResult( - scanId string, + scanID string, buildFn func() (*harbor.VulnerabilityReport, error), ) VulnerabilityResult // Request a result to be created PopResult( - scanId string, + scanID string, ) (VulnerabilityResult, bool) // Returns a result and true if found, false if not (e.g. like hash map interface) } type VulnerabilityResult struct { - ScanId string + ScanID string IsComplete bool Result *harbor.VulnerabilityReport Error error @@ -45,53 +45,54 @@ func NewResultStore() ResultStore { return newStore } -func (m MemoryResultStore) HasResult(scanId string) bool { - found, ok := m.Results[scanId] +func (m MemoryResultStore) HasResult(scanID string) bool { + found, ok := m.Results[scanID] log.Debugf("HasResult: %v", found) return ok && found.IsComplete } -func (m MemoryResultStore) PopResult(scanId string) (VulnerabilityResult, bool) { - found, ok := m.Results[scanId] +func (m MemoryResultStore) PopResult(scanID string) (VulnerabilityResult, bool) { + found, ok := m.Results[scanID] if found.IsComplete { - log.WithField("scanId", scanId).Debug("found completed result and removing from store to return to caller") - delete(m.Results, scanId) + log.WithField("scanId", scanID).Debug("found completed result and removing from store to return to caller") + delete(m.Results, scanID) } else { - log.WithField("scanId", scanId).Debug("found result in store, but not complete, so not removing from store") + log.WithField("scanId", scanID).Debug("found result in store, but not complete, so not removing from store") } return found, ok } func (m MemoryResultStore) RequestResult( - scanId string, + scanID string, buildFn func() (*harbor.VulnerabilityReport, error), ) VulnerabilityResult { - existing, ok := m.PopResult(scanId) + existing, ok := m.PopResult(scanID) if !ok { // Result not found so begin the async fetch go func() { result, err := buildFn() if err != nil { - log.Debugf("error building result for %v: %v", scanId, err) - resultChannel <- VulnerabilityResult{scanId, true, nil, err} + log.Debugf("error building result for %v: %v", scanID, err) + resultChannel <- VulnerabilityResult{scanID, true, nil, err} } else { - log.Debugf("result built for %v", scanId) - resultChannel <- VulnerabilityResult{scanId, true, result, nil} + log.Debugf("result built for %v", scanID) + resultChannel <- VulnerabilityResult{scanID, true, result, nil} } }() - existing = VulnerabilityResult{ScanId: scanId, IsComplete: false, Result: nil, Error: fmt.Errorf("result not ready")} - m.Results[scanId] = existing + existing = VulnerabilityResult{ScanID: scanID, IsComplete: false, Result: nil, Error: fmt.Errorf("result not ready")} + m.Results[scanID] = existing } return existing } func (m MemoryResultStore) resultRetriever() { - for true { + for { report := <-resultChannel - log.WithFields(log.Fields{"scanId": report.ScanId, "isComplete": report.IsComplete, "reportError": report.Error}).Debug("scan result added to result store") - m.Results[report.ScanId] = report + log.WithFields(log.Fields{"scanId": report.ScanID, "isComplete": report.IsComplete, "reportError": report.Error}). + Debug("scan result added to result store") + m.Results[report.ScanID] = report } } diff --git a/pkg/adapter/anchore/result_store_test.go b/pkg/adapter/anchore/result_store_test.go index 8b5aa13..6278bf7 100644 --- a/pkg/adapter/anchore/result_store_test.go +++ b/pkg/adapter/anchore/result_store_test.go @@ -14,7 +14,7 @@ func TestMemoryResultStore_HasResult(t *testing.T) { Results map[string]VulnerabilityResult } type args struct { - scanId string + scanID string } tests := []struct { name string @@ -24,13 +24,13 @@ func TestMemoryResultStore_HasResult(t *testing.T) { }{ { name: "result found and scan complete", - fields: fields{Results: map[string]VulnerabilityResult{"test1": {ScanId: "test1", IsComplete: true}}}, + fields: fields{Results: map[string]VulnerabilityResult{"test1": {ScanID: "test1", IsComplete: true}}}, args: args{"test1"}, want: true, }, { name: "result found and scan incomplete", - fields: fields{Results: map[string]VulnerabilityResult{"test1": {ScanId: "test1", IsComplete: false}}}, + fields: fields{Results: map[string]VulnerabilityResult{"test1": {ScanID: "test1", IsComplete: false}}}, args: args{"test1"}, want: false, }, @@ -46,7 +46,7 @@ func TestMemoryResultStore_HasResult(t *testing.T) { m := MemoryResultStore{ Results: tt.fields.Results, } - got := m.HasResult(tt.args.scanId) + got := m.HasResult(tt.args.scanID) assert.Equal(t, tt.want, got) }) } @@ -57,7 +57,7 @@ func TestMemoryResultStore_PopResult(t *testing.T) { Results map[string]VulnerabilityResult } type args struct { - scanId string + scanID string } tests := []struct { name string @@ -70,10 +70,10 @@ func TestMemoryResultStore_PopResult(t *testing.T) { { name: "result found and scan complete", fields: fields{ - Results: map[string]VulnerabilityResult{"test1": {ScanId: "test1", IsComplete: true}}, + Results: map[string]VulnerabilityResult{"test1": {ScanID: "test1", IsComplete: true}}, }, args: args{"test1"}, - want: VulnerabilityResult{ScanId: "test1", IsComplete: true}, + want: VulnerabilityResult{ScanID: "test1", IsComplete: true}, want1: true, expectedResultStoreAfterPopResult: fields{Results: map[string]VulnerabilityResult{}}, }, @@ -90,13 +90,13 @@ func TestMemoryResultStore_PopResult(t *testing.T) { { name: "result found and scan incomplete", fields: fields{ - Results: map[string]VulnerabilityResult{"test1": {ScanId: "test1", IsComplete: false}}, + Results: map[string]VulnerabilityResult{"test1": {ScanID: "test1", IsComplete: false}}, }, args: args{"test1"}, - want: VulnerabilityResult{ScanId: "test1", IsComplete: false}, + want: VulnerabilityResult{ScanID: "test1", IsComplete: false}, want1: true, expectedResultStoreAfterPopResult: fields{ - Results: map[string]VulnerabilityResult{"test1": {ScanId: "test1", IsComplete: false}}, + Results: map[string]VulnerabilityResult{"test1": {ScanID: "test1", IsComplete: false}}, }, }, } @@ -105,7 +105,7 @@ func TestMemoryResultStore_PopResult(t *testing.T) { m := MemoryResultStore{ Results: tt.fields.Results, } - got, got1 := m.PopResult(tt.args.scanId) + got, got1 := m.PopResult(tt.args.scanID) assert.Equal(t, tt.want, got) assert.Equal(t, tt.want1, got1) assert.Equal(t, tt.expectedResultStoreAfterPopResult.Results, m.Results) @@ -118,7 +118,7 @@ func TestMemoryResultStore_RequestResult(t *testing.T) { Results map[string]VulnerabilityResult } type args struct { - scanId string + scanID string buildFn func() (*harbor.VulnerabilityReport, error) } resultStore = MemoryResultStore{Results: make(map[string]VulnerabilityResult, 1000)} @@ -137,7 +137,7 @@ func TestMemoryResultStore_RequestResult(t *testing.T) { name: "result found and scan complete", fields: fields{Results: map[string]VulnerabilityResult{ "test1": { - ScanId: "test1", + ScanID: "test1", IsComplete: true, Result: &harbor.VulnerabilityReport{ GeneratedAt: testTime, @@ -167,13 +167,13 @@ func TestMemoryResultStore_RequestResult(t *testing.T) { }, }}, args: args{ - scanId: "test1", + scanID: "test1", buildFn: func() (*harbor.VulnerabilityReport, error) { return &harbor.VulnerabilityReport{}, nil }, }, want: VulnerabilityResult{ - ScanId: "test1", + ScanID: "test1", IsComplete: true, Result: &harbor.VulnerabilityReport{ GeneratedAt: testTime, @@ -207,18 +207,18 @@ func TestMemoryResultStore_RequestResult(t *testing.T) { name: "result found and scan incomplete", fields: fields{Results: map[string]VulnerabilityResult{ "test1": { - ScanId: "test1", + ScanID: "test1", IsComplete: false, }, }}, args: args{ - scanId: "test1", + scanID: "test1", buildFn: func() (*harbor.VulnerabilityReport, error) { return &harbor.VulnerabilityReport{}, nil }, }, want: VulnerabilityResult{ - ScanId: "test1", + ScanID: "test1", IsComplete: false, Result: nil, }, @@ -228,20 +228,20 @@ func TestMemoryResultStore_RequestResult(t *testing.T) { name: "error in build function", fields: fields{Results: map[string]VulnerabilityResult{}}, args: args{ - scanId: "test1", + scanID: "test1", buildFn: func() (*harbor.VulnerabilityReport, error) { return &harbor.VulnerabilityReport{}, fmt.Errorf("test error") }, }, want: VulnerabilityResult{ - ScanId: "test1", + ScanID: "test1", IsComplete: false, Result: nil, }, wantResponseOnChannel: true, wantErr: true, wantChannelResult: VulnerabilityResult{ - ScanId: "test1", + ScanID: "test1", IsComplete: true, Result: nil, Error: fmt.Errorf("test error"), @@ -251,7 +251,7 @@ func TestMemoryResultStore_RequestResult(t *testing.T) { name: "result not found", fields: fields{Results: map[string]VulnerabilityResult{}}, args: args{ - scanId: "test1", + scanID: "test1", buildFn: func() (*harbor.VulnerabilityReport, error) { return &harbor.VulnerabilityReport{ GeneratedAt: testTime, @@ -281,7 +281,7 @@ func TestMemoryResultStore_RequestResult(t *testing.T) { }, }, want: VulnerabilityResult{ - ScanId: "test1", + ScanID: "test1", IsComplete: false, Result: nil, Error: fmt.Errorf("result not ready"), @@ -289,7 +289,7 @@ func TestMemoryResultStore_RequestResult(t *testing.T) { wantResponseOnChannel: true, wantErr: true, wantChannelResult: VulnerabilityResult{ - ScanId: "test1", + ScanID: "test1", IsComplete: true, Result: &harbor.VulnerabilityReport{ GeneratedAt: testTime, @@ -324,7 +324,7 @@ func TestMemoryResultStore_RequestResult(t *testing.T) { m := MemoryResultStore{ Results: tt.fields.Results, } - got := m.RequestResult(tt.args.scanId, tt.args.buildFn) + got := m.RequestResult(tt.args.scanID, tt.args.buildFn) if tt.wantErr { assert.Error(t, got.Error) } else { diff --git a/pkg/http/api/v1/handler.go b/pkg/http/api/v1/handler.go index b715b8a..4875b2f 100644 --- a/pkg/http/api/v1/handler.go +++ b/pkg/http/api/v1/handler.go @@ -59,7 +59,7 @@ func isAuthenticated(apiKey string, r *http.Request) bool { // Middleware function, which will be called for each request func (h *APIHandler) AuthenticationMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if isAuthenticated(h.config.ApiKey, r) { + if isAuthenticated(h.config.APIKey, r) { next.ServeHTTP(w, r) } else { // Write an error and stop the handler chain diff --git a/pkg/http/api/v1/handler_test.go b/pkg/http/api/v1/handler_test.go index e398c93..2f62308 100644 --- a/pkg/http/api/v1/handler_test.go +++ b/pkg/http/api/v1/handler_test.go @@ -22,7 +22,7 @@ const ( var okConfig = anchore.AdapterConfig{ ListenAddr: ":8080", - ApiKey: "apikey123", + APIKey: "apikey123", LogFormat: "", LogLevel: logrus.InfoLevel, FullVulnerabilityDescriptions: false, @@ -44,7 +44,7 @@ func (m *MockAdapter) GetMetadata() (harbor.ScannerAdapterMetadata, error) { } func (m *MockAdapter) Scan(req harbor.ScanRequest) (harbor.ScanResponse, error) { - id, err := anchore.GenerateScanId(req.Artifact.Repository, req.Artifact.Digest) + id, err := anchore.GenerateScanID(req.Artifact.Repository, req.Artifact.Digest) if err != nil { return harbor.ScanResponse{}, err }