55
66Alternative version if Docker image layer by layer scan.
77
8- This program will download docker image and scan it into Blackduck server layer by layer
8+ This program will download docker image and scan it into Black Duck server layer by layer
99Each layer will be scanned as a separate scan with a signature scan.
1010
1111Layers in the container images could be grouped into groups of contiguous layers.
5050
5151optional arguments:
5252 -h, --help show this help message and exit
53- --grouping GROUPING Group layers into user defined provect versions (can't be used with --base-image)
53+ --grouping GROUPING Group layers into user defined project versions (can't be used with --base-image)
5454 --base-image BASE_IMAGE
5555 Use base image spec to determine base image/layers (can't be used with --grouping or
5656 --dockerfile)
5757 --dockerfile DOCKERFILE
5858 Use Dockerfile to determine base image/layers (can't be used with --grouping or ---base-image)
5959 --project-name Specify project name (default is container image spec)
60- --project-verson Specify project version (default is container image tag/version)
60+ --project-version Specify project version (default is container image tag/version)
6161 --detect-options DETECT_OPTIONS
62- Extra detect options to be passed directlyto the detect
62+ Extra detect options to be passed directly to the detect
6363
6464
6565Using --detect-options
@@ -188,12 +188,24 @@ def read_config(self):
188188 with open (configFile ) as fp :
189189 data = json .load (fp )
190190 return data
191+
192+ def read_oci_layout (self ):
193+ oci_layout_file = self .imagedir + "/" + 'oci-layout'
194+ if os .path .exists (oci_layout_file ) and os .path .isfile (oci_layout_file ):
195+ with open (oci_layout_file ) as fp :
196+ data = json .load (fp )
197+ return data
198+ else :
199+ return None
200+
191201
192202class Detector ():
193203 def __init__ (self , hub ):
194204 # self.detecturl = 'https://blackducksoftware.github.io/hub-detect/hub-detect.sh'
195205 # self.detecturl = 'https://detect.synopsys.com/detect.sh'
196- self .detecturl = 'https://detect.synopsys.com/detect7.sh'
206+ # self.detecturl = 'https://detect.synopsys.com/detect7.sh'
207+ # self.detecturl = 'https://detect.synopsys.com/detect8.sh'
208+ self .detecturl = 'https://detect.synopsys.com/detect9.sh'
197209 self .baseurl = hub .config ['baseurl' ]
198210 self .filename = '/tmp/hub-detect.sh'
199211 self .token = hub .config ['api_token' ]
@@ -240,6 +252,7 @@ def __init__(
240252 if detect_options :
241253 self .extra_options = detect_options .split (" " )
242254 print ("<--{}-->" .format (self .grouping ))
255+ self .binary = False
243256
244257 def prepare_container_image (self ):
245258 self .docker .initdir ()
@@ -262,6 +275,7 @@ def prepare_container_image(self):
262275 self .grouping = history_grouping
263276 self .docker .save_container_image (self .container_image_name )
264277 self .docker .unravel_container ()
278+ self .oci_layout = self .docker .read_oci_layout ()
265279
266280 def process_container_image_by_user_defined_groups (self ):
267281 self .manifest = self .docker .read_manifest ()
@@ -333,25 +347,86 @@ def process_container_image_by_base_image_info(self):
333347 num = num + 1
334348 print (json .dumps (self .layers , indent = 4 ))
335349
350+ def process_oci_container_image_by_user_defined_groups (self ):
351+ self .manifest = self .docker .read_manifest ()
352+ self .config = self .docker .read_config ()
353+
354+ self .layers = self .config ['history' ]
355+ tagged_layers = [x for x in self .layers if '_group_end' in x .get ('created_by' )]
356+ groups = {re .search ('echo (.+?)_group_end' , str (x ['created_by' ])).group (1 ): self .layers .index (x ) for x in tagged_layers }
357+ print (groups )
358+ layer_paths = self .manifest [0 ]['Layers' ].copy ()
359+ empty_layers = [x for x in self .layers if x .get ('empty_layer' , False )]
360+ print (f"Total layers: { len (self .layers )} total paths: { len (layer_paths )} empty layers: { len (empty_layers )} " )
361+
362+ assert len (self .layers ) == len (layer_paths ) + len (empty_layers ), "Something is wrong with this image, Layer math does not add up."
363+
364+ for layer in self .layers :
365+ layer ['index' ] = self .layers .index (layer )
366+ if self .grouping :
367+ layer ['group_name' ] = self .get_group_name (groups , layer ['index' ])
368+ layer ['project_version' ] = "{}_{}" .format (self .project_version ,layer ['group_name' ])
369+ layer ['name' ] = "{}_{}_{}_layer_{}" .format (self .project_name ,self .project_version ,layer ['group_name' ],str (layer ['index' ]))
370+ else :
371+ layer ['project_version' ] = self .project_version
372+ layer ['name' ] = self .project_name + "_" + self .project_version + "_layer_" + str (layer ['index' ])
373+ layer ['project_name' ] = self .project_name
374+ if not layer .get ('empty_layer' , False ):
375+ layer ['path' ] = layer_paths .pop (0 )
376+ print (json .dumps (self .layers , indent = 4 ))
377+
378+ def get_group_name (self , groups , index ):
379+ group_name = 'undefined'
380+ for group , value in groups .items ():
381+ if index <= value :
382+ group_name = group
383+ break
384+ return group_name
385+
386+ def process_oci_container_image_by_base_image_info (self ):
387+ print ("Processing by BAse Image not supported for OCI images" )
388+ sys .exit (1 )
389+ pass
390+
336391 def process_container_image (self ):
392+ if self .oci_layout :
393+ self .process_oci_container_image ()
394+ else :
395+ self .process_docker_container_image ()
396+
397+ def process_docker_container_image (self ):
337398 if self .grouping :
338399 self .process_container_image_by_user_defined_groups ()
339400 else :
340401 self .process_container_image_by_base_image_info ()
341402
403+ def process_oci_container_image (self ):
404+ if self .grouping :
405+ self .process_oci_container_image_by_user_defined_groups ()
406+ else :
407+ self .process_oci_container_image_by_base_image_info ()
408+
342409 def submit_layer_scans (self ):
343410 for layer in self .layers :
344- options = []
345- options .append ('--detect.project.name={}' .format (layer ['project_name' ]))
346- options .append ('--detect.project.version.name="{}"' .format (layer ['project_version' ]))
347- # options.append('--detect.blackduck.signature.scanner.disabled=false')
348- options .append ('--detect.code.location.name={}_{}_code_{}' .format (layer ['name' ],self .image_version ,layer ['path' ]))
349- options .append ('--detect.source.path={}/{}' .format (self .docker .imagedir , layer ['path' ].split ('/' )[0 ]))
350- if self .base_image or self .grouping or self .dockerfile :
351- options .extend (self .adorn_extra_options (layer ))
352- else :
353- options .extend (self .extra_options )
354- self .hub_detect .detect_run (options )
411+ if not layer .get ('empty_layer' , False ):
412+ options = []
413+ options .append ('--detect.project.name={}' .format (layer ['project_name' ]))
414+ options .append ('--detect.project.version.name="{}"' .format (layer ['project_version' ]))
415+ options .append ('--detect.code.location.name={}_{}_code_{}' .format (layer ['name' ],self .image_version ,layer ['path' ]))
416+ if self .binary :
417+ options .append ('--detect.tools=BINARY_SCAN' )
418+ options .append ('--detect.binary.scan.file.path={}/{}' .format (self .docker .imagedir , layer ['path' ]))
419+ else :
420+ options .append ('--detect.tools=SIGNATURE_SCAN' )
421+ if self .oci_layout :
422+ options .append ('--detect.source.path={}/{}' .format (self .docker .imagedir , layer ['path' ]))
423+ else :
424+ options .append ('--detect.source.path={}/{}' .format (self .docker .imagedir , layer ['path' ].split ('/' )[0 ]))
425+ if self .base_image or self .grouping or self .dockerfile :
426+ options .extend (self .adorn_extra_options (layer ))
427+ else :
428+ options .extend (self .extra_options )
429+ self .hub_detect .detect_run (options )
355430
356431 def adorn_extra_options (self , layer ):
357432 result = list ()
@@ -401,7 +476,7 @@ def get_base_layers(self):
401476
402477def scan_container_image (
403478 imagespec , grouping = None , base_image = None , dockerfile = None ,
404- project_name = None , project_version = None , detect_options = None ):
479+ project_name = None , project_version = None , detect_options = None , binary = False ):
405480
406481 hub = HubInstance ()
407482 scanner = ContainerImageScanner (
@@ -416,6 +491,8 @@ def scan_container_image(
416491 scanner .grouping = '1024:everything'
417492 else :
418493 scanner .base_layers = scanner .get_base_layers ()
494+ if binary :
495+ scanner .binary = True
419496 scanner .prepare_container_image ()
420497 scanner .process_container_image ()
421498 scanner .submit_layer_scans ()
@@ -429,12 +506,13 @@ def main(argv=None):
429506
430507 parser = ArgumentParser ()
431508 parser .add_argument ('imagespec' , help = "Container image tag, e.g. repository/imagename:version" )
432- parser .add_argument ('--grouping' ,default = None , type = str , help = "Group layers into user defined provect versions (can't be used with --base-image)" )
509+ parser .add_argument ('--grouping' ,default = None , type = str , help = "Group layers into user defined project versions (can't be used with --base-image)" )
433510 parser .add_argument ('--base-image' ,default = None , type = str , help = "Use base image spec to determine base image/layers (can't be used with --grouping or --dockerfile)" )
434511 parser .add_argument ('--dockerfile' ,default = None , type = str , help = "Use Dockerfile to determine base image/layers (can't be used with --grouping or ---base-image)" )
435512 parser .add_argument ('--project-name' ,default = None , type = str , help = "Specify project name (default is container image spec)" )
436513 parser .add_argument ('--project-version' ,default = None , type = str , help = "Specify project version (default is container image tag/version)" )
437- parser .add_argument ('--detect-options' ,default = None , type = str , help = "Extra detect options to be passed directlyto the detect" )
514+ parser .add_argument ('--detect-options' ,default = None , type = str , help = "Extra detect options to be passed directly to the detect" )
515+ parser .add_argument ('--binary' , action = 'store_true' , help = "Use Binary Scan instead of signature scan" )
438516
439517 args = parser .parse_args ()
440518
@@ -459,7 +537,8 @@ def main(argv=None):
459537 args .dockerfile ,
460538 args .project_name ,
461539 args .project_version ,
462- args .detect_options )
540+ args .detect_options ,
541+ args .binary )
463542
464543
465544if __name__ == "__main__" :
0 commit comments