Skip to content

Commit 918e695

Browse files
committed
updated oci image support
1 parent 4b7358f commit 918e695

File tree

3 files changed

+101
-33
lines changed

3 files changed

+101
-33
lines changed

examples/client/multi-image/generate-clone.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ ls -l $SPECFILE
1515

1616
COMMAND="python3 examples/client/multi-image/manage_project_structure.py"
1717

18-
$COMMAND -u $BD_URL -t token -nv -pg "Test Group" -p P3 -pv 2.4 -sp $SUBPROJECTS --clone-from 2.3 $@
19-
# $COMMAND -u $BD_URL -t token -nv -pg "Test Group" -p P3 -pv 2.4 -ssf $SPECFILE --clone-from 2.3 $@
18+
# $COMMAND -u $BD_URL -t <(echo $API_TOKEN) -nv -pg "Test Group" -p P3 -pv 2.4 -sp $SUBPROJECTS --clone-from 2.3 $@
19+
$COMMAND -u $BD_URL -t <(echo $API_TOKEN) -nv -pg "Test Group" -p P3 -pv 2.4 -ssf $SPECFILE --clone-from 2.3 $@
2020

examples/client/multi-image/manage_project_structure.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,12 @@ def scan_container_images(scan_params, hub):
360360
params['project'],
361361
params['version'],
362362
detect_options,
363-
hub=hub
363+
hub=hub,
364+
binary=False
364365
)
365366
except Exception:
367+
import traceback
368+
traceback.print_exc()
366369
logging.error(f"Scanning of {params['image']} failed, skipping")
367370
skipped_scans.append(params)
368371

examples/client/multi-image/scan_docker_image_lite.py

Lines changed: 95 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
from pprint import pprint
9595
from sys import argv
9696
import json
97+
import logging
9798
import os
9899
import requests
99100
import shutil
@@ -136,7 +137,6 @@ def locate_docker(self):
136137
proc = subprocess.Popen(['which','docker'], stdout=subprocess.PIPE)
137138
out, err = proc.communicate()
138139
lines = out.decode().split('\n')
139-
print(lines)
140140
if 'docker' in lines[0]:
141141
return lines[0]
142142
else:
@@ -147,7 +147,7 @@ def pull_container_image(self, image_name):
147147
args.append(self.docker_path)
148148
args.append('pull')
149149
args.append(image_name)
150-
return subprocess.run(args)
150+
return subprocess.run(args, capture_output=True)
151151

152152
def get_container_image_history(self, image_name):
153153
args = []
@@ -173,7 +173,7 @@ def unravel_container(self):
173173
args.append(self.imagefile)
174174
args.append('-C')
175175
args.append(self.imagedir)
176-
return subprocess.run(args)
176+
return subprocess.run(args, capture_output=True)
177177

178178
def read_manifest(self):
179179
filename = self.imagedir + "/manifest.json"
@@ -222,7 +222,7 @@ def detect_run(self, options=['--help']):
222222
cmd.append('--blackduck.api.token=' + self.token)
223223
cmd.append('--blackduck.trust.cert=true')
224224
cmd.extend(options)
225-
subprocess.run(cmd)
225+
return subprocess.run(cmd, capture_output=True)
226226

227227
class ContainerImageScanner():
228228

@@ -249,7 +249,6 @@ def __init__(
249249
self.extra_options = []
250250
if detect_options:
251251
self.extra_options = detect_options.split(" ")
252-
print ("<--{}-->".format(self.grouping))
253252
self.binary = False
254253

255254
def prepare_container_image(self):
@@ -272,12 +271,11 @@ def prepare_container_image(self):
272271
history_grouping += str(layer_count) + ":" + found
273272
if len(history_grouping) and self.grouping == '1024:everything':
274273
self.grouping = history_grouping
274+
self.oci_layout = self.docker.read_oci_layout()
275275

276276
def process_container_image_by_user_defined_groups(self):
277277
self.manifest = self.docker.read_manifest()
278-
print(self.manifest)
279278
self.config = self.docker.read_config()
280-
print (json.dumps(self.config, indent=4))
281279

282280
if self.grouping:
283281
self.groups = dict(x.split(":") for x in self.grouping.split(","))
@@ -309,14 +307,12 @@ def process_container_image_by_user_defined_groups(self):
309307
layer['shaid'] = self.config['rootfs']['diff_ids'][num - 1]
310308
self.layers.append(layer)
311309
num = num + 1
312-
print (json.dumps(self.layers, indent=4))
310+
# print (json.dumps(self.layers, indent=4))
313311

314312
def process_container_image_by_base_image_info(self):
315313
self.manifest = self.docker.read_manifest()
316-
print(self.manifest)
317314
self.config = self.docker.read_config()
318-
print (json.dumps(self.config, indent=4))
319-
315+
320316
self.layers = []
321317
num = 1
322318
offset = 0
@@ -342,27 +338,92 @@ def process_container_image_by_base_image_info(self):
342338
layer['name'] = self.project_name + "_" + self.project_version + "_layer_" + str(num)
343339
self.layers.append(layer)
344340
num = num + 1
345-
print (json.dumps(self.layers, indent=4))
341+
# print (json.dumps(self.layers, indent=4))
342+
343+
def process_oci_container_image_by_user_defined_groups(self):
344+
self.manifest = self.docker.read_manifest()
345+
self.config = self.docker.read_config()
346+
347+
self.layers = self.config['history']
348+
tagged_layers = [x for x in self.layers if '_group_end' in x.get('created_by')]
349+
groups = {re.search('echo (.+?)_group_end', str(x['created_by'])).group(1): self.layers.index(x) for x in tagged_layers}
350+
logging.debug(f"Container configuration defines following groups {groups}")
351+
layer_paths = self.manifest[0]['Layers'].copy()
352+
empty_layers = [x for x in self.layers if x.get('empty_layer', False)]
353+
logging.debug(f"Total layers: {len(self.layers)} total paths: {len(layer_paths)} empty layers: {len(empty_layers)}")
354+
355+
assert len(self.layers) == len(layer_paths) + len(empty_layers), "Something is wrong with this image, Layer math does not add up."
356+
357+
for layer in self.layers:
358+
layer['index'] = self.layers.index(layer)
359+
if self.grouping:
360+
layer['group_name'] = self.get_group_name(groups, layer['index'])
361+
layer['project_name'] = "{}_{}".format(self.project_name,layer['group_name'])
362+
layer['project_version'] = self.project_version
363+
layer['name'] = "{}_{}_{}_layer_{}".format(self.project_name,self.project_version,layer['group_name'],str(layer['index']))
364+
else:
365+
layer['project_name'] = self.project_name
366+
layer['project_version'] = self.project_version
367+
layer['name'] = self.project_name + "_" + self.project_version + "_layer_" + str(layer['index'])
368+
if not layer.get('empty_layer', False):
369+
layer['path'] = layer_paths.pop(0)
370+
# print (json.dumps(self.layers, indent=4))
371+
372+
def get_group_name(self, groups, index):
373+
group_name = 'undefined'
374+
for group, value in groups.items():
375+
if index <= value:
376+
group_name = group
377+
break
378+
return group_name
379+
380+
def process_oci_container_image_by_base_image_info(self):
381+
print ("Processing by BAse Image not supported for OCI images")
382+
sys.exit(1)
383+
pass
346384

347385
def process_container_image(self):
386+
if self.oci_layout:
387+
self.process_oci_container_image()
388+
else:
389+
self.process_docker_container_image()
390+
391+
def process_docker_container_image(self):
348392
if self.grouping:
349393
self.process_container_image_by_user_defined_groups()
350394
else:
351395
self.process_container_image_by_base_image_info()
352396

397+
def process_oci_container_image(self):
398+
if self.grouping:
399+
self.process_oci_container_image_by_user_defined_groups()
400+
else:
401+
self.process_oci_container_image_by_base_image_info()
402+
353403
def submit_layer_scans(self):
354404
for layer in self.layers:
355-
options = []
356-
options.append('--detect.project.name={}'.format(layer['project_name']))
357-
options.append('--detect.project.version.name="{}"'.format(layer['project_version']))
358-
# options.append('--detect.blackduck.signature.scanner.disabled=false')
359-
options.append('--detect.code.location.name={}_{}_code_{}'.format(layer['name'],self.image_version,layer['path']))
360-
options.append('--detect.source.path={}/{}'.format(self.docker.imagedir, layer['path'].split('/')[0]))
361-
if self.base_image or self.grouping or self.dockerfile:
362-
options.extend(self.adorn_extra_options(layer))
363-
else:
364-
options.extend(self.extra_options)
365-
self.hub_detect.detect_run(options)
405+
if not layer.get('empty_layer', False):
406+
options = []
407+
options.append('--detect.project.name={}'.format(layer['project_name']))
408+
options.append('--detect.project.version.name="{}"'.format(layer['project_version']))
409+
options.append('--detect.code.location.name={}_{}_code_{}'.format(layer['name'],self.image_version,layer['path']))
410+
if self.binary:
411+
options.append('--detect.tools=BINARY_SCAN')
412+
options.append('--detect.binary.scan.file.path={}/{}'.format(self.docker.imagedir, layer['path']))
413+
else:
414+
options.append('--detect.tools=SIGNATURE_SCAN')
415+
if self.oci_layout:
416+
options.append('--detect.source.path={}/{}'.format(self.docker.imagedir, layer['path']))
417+
else:
418+
options.append('--detect.source.path={}/{}'.format(self.docker.imagedir, layer['path'].split('/')[0]))
419+
if self.base_image or self.grouping or self.dockerfile:
420+
options.extend(self.adorn_extra_options(layer))
421+
else:
422+
options.extend(self.extra_options)
423+
logging.info(f"Submitting scan for {layer['name']}")
424+
completed = self.hub_detect.detect_run(options)
425+
logging.info(f"Detect run for {layer['name']} completed with returncode {completed.returncode}")
426+
366427

367428
def adorn_extra_options(self, layer):
368429
result = list()
@@ -396,24 +457,24 @@ def get_base_layers(self):
396457
if self.base_image:
397458
imagelist.append(self.base_image)
398459

399-
print (imagelist)
460+
# print (imagelist)
400461
base_layers = []
401462
for image in imagelist:
402463
self.docker.initdir()
403464
self.docker.pull_container_image(image)
404465
self.docker.save_container_image(image)
405466
self.docker.unravel_container()
406467
manifest = self.docker.read_manifest()
407-
print(manifest)
468+
# print(manifest)
408469
config = self.docker.read_config()
409-
print(config)
470+
# print(config)
410471
base_layers.extend(config['rootfs']['diff_ids'])
411472
return base_layers
412473

413474

414475
def scan_container_image(
415476
imagespec, grouping=None, base_image=None, dockerfile=None,
416-
project_name=None, project_version=None, detect_options=None, hub=None):
477+
project_name=None, project_version=None, detect_options=None, hub=None, binary=False):
417478

418479
if hub:
419480
hub = hub
@@ -431,6 +492,8 @@ def scan_container_image(
431492
scanner.grouping = '1024:everything'
432493
else:
433494
scanner.base_layers = scanner.get_base_layers()
495+
if binary:
496+
scanner.binary = True
434497
scanner.prepare_container_image()
435498
scanner.process_container_image()
436499
scanner.submit_layer_scans()
@@ -450,10 +513,11 @@ def main(argv=None):
450513
parser.add_argument('--project-name',default=None, type=str, help="Specify project name (default is container image spec)")
451514
parser.add_argument('--project-version',default=None, type=str, help="Specify project version (default is container image tag/version)")
452515
parser.add_argument('--detect-options',default=None, type=str, help="Extra detect options to be passed directly to the detect")
453-
516+
parser.add_argument('--binary', action='store_true', help="Use Binary Scan instead of signature scan")
517+
454518
args = parser.parse_args()
455519

456-
print (args);
520+
logging.debug(args);
457521

458522
if not args.imagespec:
459523
parser.print_help(sys.stdout)
@@ -474,7 +538,8 @@ def main(argv=None):
474538
args.dockerfile,
475539
args.project_name,
476540
args.project_version,
477-
args.detect_options)
541+
args.detect_options,
542+
args.binary)
478543

479544

480545
if __name__ == "__main__":

0 commit comments

Comments
 (0)