Skip to content

Commit bd36209

Browse files
committed
added OCI format to scan_docker_image_lite
1 parent 4f4428a commit bd36209

File tree

1 file changed

+99
-20
lines changed

1 file changed

+99
-20
lines changed

examples/scan_docker_image_lite.py

Lines changed: 99 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
Alternative 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
99
Each layer will be scanned as a separate scan with a signature scan.
1010
1111
Layers in the container images could be grouped into groups of contiguous layers.
@@ -50,16 +50,16 @@
5050
5151
optional 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
6565
Using --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

192202
class 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

402477
def 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

465544
if __name__ == "__main__":

0 commit comments

Comments
 (0)