5
5
6
6
Alternative version if Docker image layer by layer scan.
7
7
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
9
9
Each layer will be scanned as a separate scan with a signature scan.
10
10
11
11
Layers in the container images could be grouped into groups of contiguous layers.
50
50
51
51
optional arguments:
52
52
-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)
54
54
--base-image BASE_IMAGE
55
55
Use base image spec to determine base image/layers (can't be used with --grouping or
56
56
--dockerfile)
57
57
--dockerfile DOCKERFILE
58
58
Use Dockerfile to determine base image/layers (can't be used with --grouping or ---base-image)
59
59
--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)
61
61
--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
63
63
64
64
65
65
Using --detect-options
@@ -188,12 +188,24 @@ def read_config(self):
188
188
with open (configFile ) as fp :
189
189
data = json .load (fp )
190
190
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
+
191
201
192
202
class Detector ():
193
203
def __init__ (self , hub ):
194
204
# self.detecturl = 'https://blackducksoftware.github.io/hub-detect/hub-detect.sh'
195
205
# 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'
197
209
self .baseurl = hub .config ['baseurl' ]
198
210
self .filename = '/tmp/hub-detect.sh'
199
211
self .token = hub .config ['api_token' ]
@@ -240,6 +252,7 @@ def __init__(
240
252
if detect_options :
241
253
self .extra_options = detect_options .split (" " )
242
254
print ("<--{}-->" .format (self .grouping ))
255
+ self .binary = False
243
256
244
257
def prepare_container_image (self ):
245
258
self .docker .initdir ()
@@ -262,6 +275,7 @@ def prepare_container_image(self):
262
275
self .grouping = history_grouping
263
276
self .docker .save_container_image (self .container_image_name )
264
277
self .docker .unravel_container ()
278
+ self .oci_layout = self .docker .read_oci_layout ()
265
279
266
280
def process_container_image_by_user_defined_groups (self ):
267
281
self .manifest = self .docker .read_manifest ()
@@ -333,25 +347,86 @@ def process_container_image_by_base_image_info(self):
333
347
num = num + 1
334
348
print (json .dumps (self .layers , indent = 4 ))
335
349
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
+
336
391
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 ):
337
398
if self .grouping :
338
399
self .process_container_image_by_user_defined_groups ()
339
400
else :
340
401
self .process_container_image_by_base_image_info ()
341
402
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
+
342
409
def submit_layer_scans (self ):
343
410
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 )
355
430
356
431
def adorn_extra_options (self , layer ):
357
432
result = list ()
@@ -401,7 +476,7 @@ def get_base_layers(self):
401
476
402
477
def scan_container_image (
403
478
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 ):
405
480
406
481
hub = HubInstance ()
407
482
scanner = ContainerImageScanner (
@@ -416,6 +491,8 @@ def scan_container_image(
416
491
scanner .grouping = '1024:everything'
417
492
else :
418
493
scanner .base_layers = scanner .get_base_layers ()
494
+ if binary :
495
+ scanner .binary = True
419
496
scanner .prepare_container_image ()
420
497
scanner .process_container_image ()
421
498
scanner .submit_layer_scans ()
@@ -429,12 +506,13 @@ def main(argv=None):
429
506
430
507
parser = ArgumentParser ()
431
508
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)" )
433
510
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)" )
434
511
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)" )
435
512
parser .add_argument ('--project-name' ,default = None , type = str , help = "Specify project name (default is container image spec)" )
436
513
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" )
438
516
439
517
args = parser .parse_args ()
440
518
@@ -459,7 +537,8 @@ def main(argv=None):
459
537
args .dockerfile ,
460
538
args .project_name ,
461
539
args .project_version ,
462
- args .detect_options )
540
+ args .detect_options ,
541
+ args .binary )
463
542
464
543
465
544
if __name__ == "__main__" :
0 commit comments