diff --git a/data/secscan_model/datatypes.py b/data/secscan_model/datatypes.py index 3b2040e2d..025c49345 100644 --- a/data/secscan_model/datatypes.py +++ b/data/secscan_model/datatypes.py @@ -32,7 +32,7 @@ Vulnerability = namedtuple( Feature = namedtuple( "Feature", ["Name", "VersionFormat", "NamespaceName", "AddedBy", "Version", "Vulnerabilities"] ) -Layer = namedtuple("Layer", ["Features"]) +Layer = namedtuple("Layer", ["Name", "NamespaceName", "ParentName", "IndexedByVersion", "Features"]) class SecurityInformation(namedtuple("SecurityInformation", ["Layer"])): @@ -44,7 +44,11 @@ class SecurityInformation(namedtuple("SecurityInformation", ["Layer"])): def from_dict(cls, data_dict): return SecurityInformation( Layer( - [ + Name=data_dict["Layer"].get("Name", ""), + ParentName=data_dict["Layer"].get("ParentName", ""), + NamespaceName=data_dict["Layer"].get("NamespaceName", ""), + IndexedByVersion=data_dict["Layer"].get("IndexedByVersion", None), + Features=[ Feature( Name=f["Name"], VersionFormat=f["VersionFormat"], @@ -65,13 +69,17 @@ class SecurityInformation(namedtuple("SecurityInformation", ["Layer"])): ], ) for f in data_dict["Layer"].get("Features", []) - ] + ], ) ) def to_dict(self): return { "Layer": { + "Name": self.Layer.Name, + "ParentName": self.Layer.ParentName, + "NamespaceName": self.Layer.NamespaceName, + "IndexedByVersion": self.Layer.IndexedByVersion, "Features": [ { "Name": f.Name, @@ -93,7 +101,7 @@ class SecurityInformation(namedtuple("SecurityInformation", ["Layer"])): ], } for f in self.Layer.Features - ] + ], } } diff --git a/data/secscan_model/secscan_v4_model.py b/data/secscan_model/secscan_v4_model.py index f5fde95af..dee27ba35 100644 --- a/data/secscan_model/secscan_v4_model.py +++ b/data/secscan_model/secscan_v4_model.py @@ -109,7 +109,7 @@ class V4SecurityScanner(SecurityScannerInterface): # TODO(alecmerdler): Provide a way to indicate the current scan is outdated (`report.state != status.indexer_hash`) return SecurityInformationLookupResult.for_data( - SecurityInformation(Layer(features_for(report))) + SecurityInformation(Layer(report["manifest_hash"], "", "", 4, features_for(report))) ) def perform_indexing(self, start_token=None): diff --git a/data/secscan_model/test/securityinformation.json b/data/secscan_model/test/securityinformation.json index 2d81a54dd..9f1c9fb60 100644 --- a/data/secscan_model/test/securityinformation.json +++ b/data/secscan_model/test/securityinformation.json @@ -2,6 +2,10 @@ "status":"scanned", "data":{ "Layer":{ + "Name": "sha256:b05ac1eeec8635442fa5d3e55d6ef4ad287b9c66055a552c2fd309c334563b0a", + "NamespaceName": "", + "IndexedByVersion": 4, + "ParentName": "", "Features":[ { "Name":"tzdata", diff --git a/data/secscan_model/test/test_secscan_v2_model.py b/data/secscan_model/test/test_secscan_v2_model.py index 8417f494e..c8d5376a0 100644 --- a/data/secscan_model/test/test_secscan_v2_model.py +++ b/data/secscan_model/test/test_secscan_v2_model.py @@ -100,6 +100,13 @@ def test_load_security_information_api_responses(secscan_api_response, initializ security_information = secscan.load_security_information(manifest).security_information assert isinstance(security_information, SecurityInformation) + assert security_information.Layer.Name == secscan_api_response["Layer"].get("Name", "") + assert security_information.Layer.ParentName == secscan_api_response["Layer"].get( + "ParentName", "" + ) + assert security_information.Layer.IndexedByVersion == secscan_api_response["Layer"].get( + "IndexedByVersion", None + ) assert len(security_information.Layer.Features) == len( secscan_api_response["Layer"].get("Features", []) ) diff --git a/data/secscan_model/test/test_secscan_v4_model.py b/data/secscan_model/test/test_secscan_v4_model.py index 2fc59847e..e95e4b8c1 100644 --- a/data/secscan_model/test/test_secscan_v4_model.py +++ b/data/secscan_model/test/test_secscan_v4_model.py @@ -127,7 +127,7 @@ def test_load_security_information_success(initialized_db): result = secscan.load_security_information(manifest) assert result.status == ScanLookupStatus.SUCCESS - assert result.security_information == SecurityInformation(Layer([])) + assert result.security_information == SecurityInformation(Layer(manifest.digest, "", "", 4, [])) def test_perform_indexing_whitelist(initialized_db): @@ -296,5 +296,14 @@ def test_features_for(): security_info = json.load(security_info_file) assert ( - SecurityInformation(Layer(features_for(vuln_report))).to_dict() == security_info["data"] + SecurityInformation( + Layer( + "sha256:b05ac1eeec8635442fa5d3e55d6ef4ad287b9c66055a552c2fd309c334563b0a", + "", + "", + 4, + features_for(vuln_report), + ) + ).to_dict() + == security_info["data"] ) diff --git a/static/js/services/vulnerability-service.js b/static/js/services/vulnerability-service.js index 8d5e1d3f7..f9e20c98d 100644 --- a/static/js/services/vulnerability-service.js +++ b/static/js/services/vulnerability-service.js @@ -127,9 +127,7 @@ angular.module('quay').factory('VulnerabilityService', ['Config', 'ApiService', 'introducedInVersion': feature.Version, 'imageId': addedByImageId, - 'imageCommand': addedByImageId.includes(':') - ? ImageMetadataService.getManifestCommand(manifest, addedByImageId) - : ImageMetadataService.getImageCommand(manifest.image, addedByImageId), + 'imageCommand': vulnService.imageComamandFor(manifest, addedByImageId), 'expanded': false }; @@ -200,6 +198,15 @@ angular.module('quay').factory('VulnerabilityService', ['Config', 'ApiService', }; }; + vulnService.imageComamandFor = function(manifest, addedByImageId) { + if (!manifest || !addedByImageId) { + return null; + } + return addedByImageId.includes(':') + ? ImageMetadataService.getManifestCommand(manifest, addedByImageId) + : ImageMetadataService.getImageCommand(manifest.image, addedByImageId); + }; + vulnService.buildFeaturesInfo = function(manifest, resp) { var features = []; var severityCountMap = {}; @@ -222,9 +229,7 @@ angular.module('quay').factory('VulnerabilityService', ['Config', 'ApiService', 'version': feature.Version, 'addedBy': feature.AddedBy, 'imageId': addedByImageId, - 'imageCommand': addedByImageId.includes(':') - ? ImageMetadataService.getManifestCommand(manifest, addedByImageId) - : ImageMetadataService.getImageCommand(manifest.image, addedByImageId), + 'imageCommand': vulnService.imageComamandFor(manifest, addedByImageId), 'vulnCount': vulnerabilityInfo.vulnerabilities.length, 'severityBreakdown': vulnerabilityInfo.severityBreakdown, 'fixableBreakdown': vulnerabilityInfo.fixableBreakdown,