11import base64
2- import hashlib
32import json
43import re
4+ import time
5+
56import requests
67from requests_toolbelt .multipart .encoder import MultipartEncoder
7- import time
88
99try :
1010 from urllib .parse import quote_plus , unquote_plus
@@ -164,7 +164,8 @@ def query_image_vuln(self, image, vuln_type="", vendor_only=True):
164164 '''
165165 return self ._query_image (image , query_group = 'vuln' , query_type = vuln_type , vendor_only = vendor_only )
166166
167- def query_images_by_vulnerability (self , vulnerability_id , namespace = None , package = None , severity = None , vendor_only = True ):
167+ def query_images_by_vulnerability (self , vulnerability_id , namespace = None , package = None , severity = None ,
168+ vendor_only = True ):
168169 '''**Description**
169170 Search system for images with the given vulnerability ID present
170171
@@ -408,17 +409,18 @@ def get_image_scan_result_by_id(self, image_id, full_tag_name, detail):
408409 A JSON object containing pass/fail status of image scan policy.
409410 '''
410411 url = "{base_url}/api/scanning/v1/anchore/images/by_id/{image_id}/check?tag={full_tag_name}&detail={detail}" .format (
411- base_url = self .url ,
412- image_id = image_id ,
413- full_tag_name = full_tag_name ,
414- detail = detail )
412+ base_url = self .url ,
413+ image_id = image_id ,
414+ full_tag_name = full_tag_name ,
415+ detail = detail )
415416 res = requests .get (url , headers = self .hdrs , verify = self .ssl_verify )
416417 if not self ._checkResponse (res ):
417418 return [False , self .lasterr ]
418419
419420 return [True , res .json ()]
420421
421- def add_registry (self , registry , registry_user , registry_pass , insecure = False , registry_type = "docker_v2" , validate = True ):
422+ def add_registry (self , registry , registry_user , registry_pass , insecure = False , registry_type = "docker_v2" ,
423+ validate = True ):
422424 '''**Description**
423425 Add image registry
424426
@@ -437,7 +439,8 @@ def add_registry(self, registry, registry_user, registry_pass, insecure=False, r
437439 if registry_type and registry_type not in registry_types :
438440 return [False , "input registry type not supported (supported registry_types: " + str (registry_types )]
439441 if self ._registry_string_is_valid (registry ):
440- return [False , "input registry name cannot contain '/' characters - valid registry names are of the form <host>:<port> where :<port> is optional" ]
442+ return [False ,
443+ "input registry name cannot contain '/' characters - valid registry names are of the form <host>:<port> where :<port> is optional" ]
441444
442445 if not registry_type :
443446 registry_type = self ._get_registry_type (registry )
@@ -458,7 +461,8 @@ def add_registry(self, registry, registry_user, registry_pass, insecure=False, r
458461
459462 return [True , res .json ()]
460463
461- def update_registry (self , registry , registry_user , registry_pass , insecure = False , registry_type = "docker_v2" , validate = True ):
464+ def update_registry (self , registry , registry_user , registry_pass , insecure = False , registry_type = "docker_v2" ,
465+ validate = True ):
462466 '''**Description**
463467 Update an existing image registry.
464468
@@ -474,7 +478,8 @@ def update_registry(self, registry, registry_user, registry_pass, insecure=False
474478 A JSON object representing the registry.
475479 '''
476480 if self ._registry_string_is_valid (registry ):
477- return [False , "input registry name cannot contain '/' characters - valid registry names are of the form <host>:<port> where :<port> is optional" ]
481+ return [False ,
482+ "input registry name cannot contain '/' characters - valid registry names are of the form <host>:<port> where :<port> is optional" ]
478483
479484 payload = {
480485 'registry' : registry ,
@@ -502,7 +507,8 @@ def delete_registry(self, registry):
502507 '''
503508 # do some input string checking
504509 if re .match (".*\\ /.*" , registry ):
505- return [False , "input registry name cannot contain '/' characters - valid registry names are of the form <host>:<port> where :<port> is optional" ]
510+ return [False ,
511+ "input registry name cannot contain '/' characters - valid registry names are of the form <host>:<port> where :<port> is optional" ]
506512
507513 url = self .url + "/api/scanning/v1/anchore/registries/" + registry
508514 res = requests .delete (url , headers = self .hdrs , verify = self .ssl_verify )
@@ -539,7 +545,8 @@ def get_registry(self, registry):
539545 A JSON object representing the registry.
540546 '''
541547 if self ._registry_string_is_valid (registry ):
542- return [False , "input registry name cannot contain '/' characters - valid registry names are of the form <host>:<port> where :<port> is optional" ]
548+ return [False ,
549+ "input registry name cannot contain '/' characters - valid registry names are of the form <host>:<port> where :<port> is optional" ]
543550
544551 url = self .url + "/api/scanning/v1/anchore/registries/" + registry
545552 res = requests .get (url , headers = self .hdrs , verify = self .ssl_verify )
@@ -1059,4 +1066,117 @@ def get_vulnerability_details(self, id):
10591066 if "vulnerabilities" not in json_res or not json_res ["vulnerabilities" ]:
10601067 return [False , f"Vulnerability { id } was not found" ]
10611068
1062- return [True , json_res ["vulnerabilities" ][0 ]]
1069+ return [True , json_res ["vulnerabilities" ][0 ]]
1070+
1071+ def add_vulnerability_exception_bundle (self , name , comment = "" ):
1072+ if not name :
1073+ return [False , "A name is required for the exception bundle" ]
1074+
1075+ url = self .url + f"/api/scanning/v1/vulnexceptions"
1076+ params = {
1077+ "version" : "1_0" ,
1078+ "name" : name ,
1079+ "comment" : comment ,
1080+ }
1081+
1082+ data = json .dumps (params )
1083+ res = requests .post (url , headers = self .hdrs , data = data , verify = self .ssl_verify )
1084+ if not self ._checkResponse (res ):
1085+ return [False , self .lasterr ]
1086+
1087+ return [True , res .json ()]
1088+
1089+ def delete_vulnerability_exception_bundle (self , id ):
1090+
1091+ url = self .url + f"/api/scanning/v1/vulnexceptions/{ id } "
1092+
1093+ res = requests .delete (url , headers = self .hdrs , verify = self .ssl_verify )
1094+ if not self ._checkResponse (res ):
1095+ return [False , self .lasterr ]
1096+
1097+ return [True , None ]
1098+
1099+ def list_vulnerability_exception_bundles (self ):
1100+ url = self .url + f"/api/scanning/v1/vulnexceptions"
1101+
1102+ params = {
1103+ "bundleId" : "default" ,
1104+ }
1105+
1106+ res = requests .get (url , params = params , headers = self .hdrs , verify = self .ssl_verify )
1107+ if not self ._checkResponse (res ):
1108+ return [False , self .lasterr ]
1109+
1110+ return [True , res .json ()]
1111+
1112+ def get_vulnerability_exception_bundle (self , bundle ):
1113+ url = f"{ self .url } /api/scanning/v1/vulnexceptions/{ bundle } "
1114+
1115+ params = {
1116+ "bundleId" : "default" ,
1117+ }
1118+
1119+ res = requests .get (url , params = params , headers = self .hdrs , verify = self .ssl_verify )
1120+ if not self ._checkResponse (res ):
1121+ return [False , self .lasterr ]
1122+
1123+ res_json = res .json ()
1124+ for item in res_json ["items" ]:
1125+ item ["trigger_id" ] = str (item ["trigger_id" ]).rstrip ("+*" )
1126+ return [True , res_json ]
1127+
1128+ def add_vulnerability_exception (self , bundle , cve , note = None , expiration_date = None ):
1129+ url = f"{ self .url } /api/scanning/v1/vulnexceptions/{ bundle } /vulnerabilities"
1130+
1131+ params = {
1132+ "gate" : "vulnerabilities" ,
1133+ "is_busy" : False ,
1134+ "trigger_id" : f"{ cve } +*" ,
1135+ "expiration_date" : int (expiration_date ) if expiration_date else None ,
1136+ "notes" : note ,
1137+ }
1138+
1139+ data = json .dumps (params )
1140+ res = requests .post (url , headers = self .hdrs , data = data , verify = self .ssl_verify )
1141+ if not self ._checkResponse (res ):
1142+ return [False , self .lasterr ]
1143+
1144+ res_json = res .json ()
1145+ res_json ["trigger_id" ] = str (res_json ["trigger_id" ]).rstrip ("+*" )
1146+ return [True , res_json ]
1147+
1148+ def delete_vulnerability_exception (self , bundle , id ):
1149+ url = f"{ self .url } /api/scanning/v1/vulnexceptions/{ bundle } /vulnerabilities/{ id } "
1150+
1151+ params = {
1152+ "bundleId" : "default" ,
1153+ }
1154+
1155+ res = requests .delete (url , params = params , headers = self .hdrs , verify = self .ssl_verify )
1156+ if not self ._checkResponse (res ):
1157+ return [False , self .lasterr ]
1158+
1159+ return [True , None ]
1160+
1161+ def update_vulnerability_exception (self , bundle , id , cve , enabled , note , expiration_date ):
1162+ url = f"{ self .url } /api/scanning/v1/vulnexceptions/{ bundle } /vulnerabilities/{ id } "
1163+
1164+ data = {
1165+ "id" : id ,
1166+ "gate" : "vulnerabilities" ,
1167+ "trigger_id" : f"{ cve } +*" ,
1168+ "enabled" : enabled ,
1169+ "notes" : note ,
1170+ "expiration_date" : int (expiration_date ) if expiration_date else None ,
1171+ }
1172+ params = {
1173+ "bundleId" : "default" ,
1174+ }
1175+
1176+ res = requests .put (url , data = json .dumps (data ), params = params , headers = self .hdrs , verify = self .ssl_verify )
1177+ if not self ._checkResponse (res ):
1178+ return [False , self .lasterr ]
1179+
1180+ res_json = res .json ()
1181+ res_json ["trigger_id" ] = str (res_json ["trigger_id" ]).rstrip ("+*" )
1182+ return [True , res_json ]
0 commit comments