def get_dockerfile_packages(): '''Given a Dockerfile return an approximate image object. This is mosty guess work and shouldn't be relied on for accurate information. Add Notice messages indicating as such: 1. Create an image with a placeholder repotag 2. For each RUN command, create a package list 3. Create layer objects with incremental integers and add the package list to that layer with a Notice about parsing 4. Return stub image''' stub_image = Image('easteregg:cookie') layer_count = 0 for cmd in dhelper.docker_commands: if cmd['instruction'] == 'RUN': layer_count = layer_count + 1 layer = ImageLayer(layer_count) install_commands, msg = \ common.filter_install_commands(cmd['value']) if msg: layer.origins.add_notice_to_origins(cmd['value'], Notice(msg, 'info')) pkg_names = [] for command in install_commands: pkg_names.append(common.get_installed_package_names(command)) for pkg_name in pkg_names: pkg = Package(pkg_name) # shell parser does not parse version pins yet # when that is enabled, Notices for no versions need to be # added here layer.add_package(pkg) return stub_image
def execute_live(args): """Execute inventory at container build time We assume a mounted working directory is ready to inventory""" logger.debug('Starting analysis...') # create the working directory setup() # create a layer object to bundle package metadata into layer = ImageLayer("") # see if there is an os-release file at the mount point layer.os_guess = single_layer.find_os_release(args.live) # create a Prereqs object to store requirements to inventory prereqs = core.Prereqs() prereqs.host_path = os.path.abspath(args.live) # Find a shell that may exist in the layer prereqs.fs_shell = dcom.find_shell(prereqs.host_path) # Find the host's shell prereqs.host_shell = host.check_shell() # collect metadata into the layer object fill_packages(layer, prereqs) # resolve unique packages for this run with reports from previous runs if args.with_context: # get a list of previous layers based on plugin type context_layers = get_context_layers(args.with_context, args.report_format) # resolve the packages for each of the layers context_layers.append(layer) resolve_context_packages(context_layers) final_layer = context_layers.pop() else: final_layer = layer # report out the packages logger.debug("Preparing report") report.report_layer(final_layer, args)
def load_image(self): l1 = ImageLayer('123abc', 'path/to/tar') self.name = 'testimage' self.tag = 'testtag' l1.add_package(Package('p1')) l1.add_package(Package('p2')) self._layers.append(l1)
def create_image_layer(report): """Given a report file, create an ImageLayer object with the metadata""" # expect a json input, raise an error if it is not content = {} try: with open(os.path.abspath(report), encoding='utf-8') as f: content = json.load(f) except OSError as err: logger.critical("Cannot access file %s: %s", report, err) raise ConsumerError(f"Error with given report file: {report}") from err except json.JSONDecodeError as err: logger.critical("Cannot parse JSON in file %s: %s", report, err) raise ConsumerError(f"Error with given report file: {report}") from err # we should have some content but it may be empty if not content: raise ConsumerError("No content consumed from given report file") # instantiate a layer and fill it layer = ImageLayer("") # if there are license refs, make a dictionary with license refs to # extracted content refs_license = get_license_refs_dict( content.get('hasExtractedLicensingInfos', [])) try: # we ignore the document level information and go straight # to the packages for pkg in content['packages']: pkg_obj = get_package_from_dict(pkg) pkg_obj.pkg_license = refs_license.get(pkg['licenseDeclared']) layer.add_package(pkg_obj) return layer except ValueError as err: logger.critical("Cannot find required data in report: %s", err) return None
def load_image(self): """Load metadata from an extracted docker image. This assumes the image has already been downloaded and extracted into the working directory""" try: self._manifest = self.get_image_manifest() self.__repotags = self.get_image_repotags(self._manifest) self._config = self.get_image_config(self._manifest) self.__history = self.get_image_history(self._config) layer_paths = self.get_image_layers(self._manifest) layer_diffs = self.get_diff_ids(self._config) checksum_type = self.get_diff_checksum_type(self._config) layer_count = 1 while layer_diffs and layer_paths: layer = ImageLayer(layer_diffs.pop(0), layer_paths.pop(0)) layer.set_checksum(checksum_type, layer.diff_id) layer.gen_fs_hash() layer.layer_index = layer_count layer_count = layer_count + 1 self._layers.append(layer) self.set_layer_created_by() except NameError as e: raise NameError(e) except subprocess.CalledProcessError as e: raise subprocess.CalledProcessError(e.returncode, cmd=e.cmd, output=e.output, stderr=e.stderr) except IOError as e: raise IOError(e)
def create_image_layer(report): """Given a report file, create an ImageLayer object with the metadata""" # expect a json input, raise an error if it is not content = {} try: with open(os.path.abspath(report), encoding='utf-8') as f: content = json.load(f) except OSError as err: logger.critical("Cannot access file %s: %s", report, err) raise ConsumerError(f"Error with given report file: {report}") from err except json.JSONDecodeError as err: logger.critical("Cannot parse JSON in file %s: %s", report, err) raise ConsumerError(f"Error with given report file: {report}") from err # we should have some content but it may be empty if not content: raise ConsumerError("No content consumed from given report file") # instantiate a layer and fill it layer = ImageLayer("") try: layer.os_guess = content['os_guess'] for pkg in content['packages']: pkg_obj = Package(pkg['name']) pkg_obj.fill(pkg) layer.add_package(pkg_obj) for filedict in content['files']: file_obj = FileData(filedict['name'], filedict['path']) file_obj.fill(filedict) layer.add_file(file_obj) return layer except ValueError as err: logger.critical("Cannot find required data in report: %s", err) return None
def load_image(self): '''Load image metadata using docker commands''' try: container.extract_image_metadata(self.repotag) self._manifest = self.get_image_manifest() self.__repotags = self.get_image_repotags(self._manifest) self._config = self.get_image_config(self._manifest) self.__history = self.get_image_history(self._config) layer_paths = self.get_image_layers(self._manifest) layer_diffs = self.get_diff_ids(self._config) checksum_type = self.get_diff_checksum_type(self._config) layer_count = 1 while layer_diffs and layer_paths: layer = ImageLayer(layer_diffs.pop(0), layer_paths.pop(0)) layer.set_checksum(checksum_type, layer.diff_id) layer.gen_fs_hash() layer.layer_index = layer_count layer_count = layer_count + 1 self._layers.append(layer) self.set_layer_created_by() except NameError as e: raise NameError(e) except subprocess.CalledProcessError as e: raise subprocess.CalledProcessError( e.returncode, cmd=e.cmd, output=e.output, stderr=e.stderr) except IOError as e: raise IOError(e)
def execute_live(args): """Execute inventory at container build time We assume a mounted working directory is ready to inventory""" logger.debug('Starting analysis...') # create the working directory setup() # create a layer object to bundle package metadata into layer = ImageLayer("") # see if there is an os-release file at the mount point layer.os_guess = single_layer.find_os_release(args.live) # create a Prereqs object to store requirements to inventory prereqs = core.Prereqs() prereqs.host_path = os.path.abspath(args.live) # Find a shell that may exist in the layer prereqs.fs_shell = dcom.find_shell(prereqs.host_path) # Find the host's shell prereqs.host_shell = host.check_shell() # collect metadata into the layer object fill_packages(layer, prereqs) # report out the packages report.report_layer(layer, args)
def load_image(self): '''Load image metadata using docker commands''' try: option = self.get_image_option() container.extract_image_metadata(option) self._manifest = self.get_image_manifest() self._image_id = self.get_image_id(self._manifest) self.__repotags = self.get_image_repotags(self._manifest) self._config = self.get_image_config(self._manifest) self.__history = self.get_image_history(self._config) layer_paths = self.get_image_layers(self._manifest) layer_diffs = self.get_diff_ids(self._config) checksum_type = self.get_diff_checksum_type(self._config) while layer_diffs and layer_paths: layer = ImageLayer(layer_diffs.pop(0), layer_paths.pop(0)) layer.set_checksum(checksum_type, layer.diff_id) layer.gen_fs_hash() self._layers.append(layer) self.set_layer_created_by() except NameError: # pylint: disable=try-except-raise raise except subprocess.CalledProcessError: # pylint: disable=try-except-raise raise except IOError: # pylint: disable=try-except-raise raise
def load_image(self, load_until_layer=0): """Load metadata from an extracted docker image. This assumes the image has already been downloaded and extracted into the working directory""" if load_until_layer > 0: self._load_until_layer = load_until_layer # else defaults to 0 - handles negative load_until_layer try: self._manifest = self.get_image_manifest() self.__repotags = self.get_image_repotags(self._manifest) self._config = self.get_image_config(self._manifest) self.__history = self.get_image_history(self._config) layer_paths = self.get_image_layers(self._manifest) layer_diffs = self.get_diff_ids(self._config) checksum_type = self.get_diff_checksum_type(self._config) layer_count = 1 while layer_diffs and layer_paths: layer = ImageLayer(layer_diffs.pop(0), layer_paths.pop(0)) # Only load metadata for the layers we need to report on according to the --layers command line option # If --layers option is not present, load all the layers if self.load_until_layer >= layer_count or self.load_until_layer == 0: layer.set_checksum(checksum_type, layer.diff_id) layer.gen_fs_hash() layer.layer_index = layer_count self._layers.append(layer) layer_count = layer_count + 1 self._total_layers = layer_count - 1 if self.load_until_layer > self.total_layers: # if user asked to analyze more layers than image has # turn off the load_until_layer feature self._load_until_layer = 0 self.set_layer_created_by() except NameError as e: raise NameError(e) except subprocess.CalledProcessError as e: raise subprocess.CalledProcessError(e.returncode, cmd=e.cmd, output=e.output, stderr=e.stderr) except IOError as e: raise IOError(e)
def setUp(self): self.layer = ImageLayer('123abc', 'path/to/tar')
class TestClassImageLayer(unittest.TestCase): def setUp(self): self.layer = ImageLayer('123abc', 'path/to/tar') def tearDown(self): del self.layer def testInstance(self): self.assertEqual(self.layer.diff_id, '123abc') self.assertEqual(self.layer.tar_file, 'path/to/tar') self.assertFalse(self.layer.packages) self.assertFalse(self.layer.created_by) self.assertRaises(AttributeError, setattr, self.layer, 'diff_id', '456def') self.assertRaises(AttributeError, setattr, self.layer, 'tar_file', 'some/other/path') self.layer.created_by = 'some string' self.assertEqual(self.layer.created_by, 'some string') self.layer.pkg_format = 'rpm' self.assertEqual(self.layer.pkg_format, 'rpm') self.layer.os_guess = 'operating system' self.assertEqual(self.layer.os_guess, 'operating system') self.assertFalse(self.layer.files_analyzed) self.layer.files_analyzed = True self.assertTrue(self.layer.files_analyzed) self.assertRaises(ValueError, setattr, self.layer, 'files_analyzed', 'some string') self.assertEqual("", self.layer.analyzed_output) self.layer.analyzed_output = 'some string' self.assertEqual(self.layer.analyzed_output, 'some string') self.assertRaises(ValueError, setattr, self.layer, 'analyzed_output', 123) def testAddPackage(self): err = "Object type String, should be Package" p1 = Package('x') self.layer.add_package(p1) self.assertEqual(len(self.layer.packages), 1) with self.assertRaises(TypeError, msg=err): self.layer.add_package("not_a_package") def testRemovePackage(self): p1 = Package('x') p2 = Package('y') self.layer.add_package(p1) self.layer.add_package(p2) self.assertTrue(self.layer.remove_package('y')) self.assertFalse(self.layer.remove_package('y')) def testAddFile(self): err = "Object type String, should be FileData" file1 = FileData('file1', 'path/to/file1') self.layer.add_file(file1) self.assertEqual(len(self.layer.files), 1) with self.assertRaises(TypeError, msg=err): self.layer.add_file("afile") def testRemoveFile(self): file1 = FileData('file1', 'path/to/file1') self.layer.add_file(file1) self.assertFalse(self.layer.remove_file('file1')) self.assertTrue(self.layer.remove_file('path/to/file1')) self.assertFalse(self.layer.remove_file('path/to/file1')) def testToDict(self): p1 = Package('x') f1 = FileData('file1', 'path/to/file1') self.layer.add_package(p1) self.layer.add_file(f1) a_dict = self.layer.to_dict() self.assertEqual(a_dict['diff_id'], '123abc') self.assertEqual(len(a_dict['packages']), 1) self.assertEqual(a_dict['packages'][0]['name'], 'x') self.assertEqual(len(a_dict['files']), 1) self.assertEqual(a_dict['files'][0]['name'], 'file1') self.assertEqual(a_dict['files'][0]['path'], 'path/to/file1') self.assertEqual(a_dict['tar_file'], 'path/to/tar') def testToDictTemplate(self): template1 = TestTemplate1() template2 = TestTemplate2() p1 = Package('x') self.layer.add_package(p1) f1 = FileData('file1', 'path/to/file1') self.layer.add_file(f1) dict1 = self.layer.to_dict(template1) dict2 = self.layer.to_dict(template2) self.assertEqual(len(dict1.keys()), 4) self.assertEqual(dict1['layer.diff'], '123abc') self.assertEqual(dict1['layer.tarfile'], 'path/to/tar') self.assertEqual(len(dict1['layer.packages']), 1) self.assertEqual(len(dict1['layer.files']), 1) self.assertEqual(len(dict2.keys()), 5) self.assertFalse(dict2['notes']) self.assertFalse(dict2['layer.packages'][0]['notes']) self.assertFalse(dict2['layer.files'][0]['notes']) def testGetPackageNames(self): p1 = Package('x') self.layer.add_package(p1) pkgs = self.layer.get_package_names() self.assertEqual(pkgs[0], 'x') def testGetFilePaths(self): f1 = FileData('file1', 'path/to/file1') f2 = FileData('file2', 'path/to/file2') self.layer.add_file(f1) self.layer.add_file(f2) file_paths = self.layer.get_file_paths() self.assertEqual(file_paths, ['path/to/file1', 'path/to/file2'])
class TestClassImageLayer(unittest.TestCase): def setUp(self): self.layer = ImageLayer('123abc', 'path/to/tar') def tearDown(self): del self.layer def testInstance(self): self.assertEqual(self.layer.diff_id, '123abc') self.assertEqual(self.layer.tar_file, 'path/to/tar') self.assertFalse(self.layer.packages) self.assertFalse(self.layer.created_by) self.assertRaises(AttributeError, setattr, self.layer, 'diff_id', '456def') self.assertRaises(AttributeError, setattr, self.layer, 'tar_file', 'some/other/path') self.layer.created_by = 'some string' self.assertEqual(self.layer.created_by, 'some string') def testAddPackage(self): err = "Object type String, should be Package" p1 = Package('x') self.layer.add_package(p1) self.assertEqual(len(self.layer.packages), 1) with self.assertRaises(TypeError, msg=err): self.layer.add_package("not_a_package") def testRemovePackage(self): p1 = Package('x') p2 = Package('y') self.layer.add_package(p1) self.layer.add_package(p2) self.assertTrue(self.layer.remove_package('y')) self.assertFalse(self.layer.remove_package('y')) def testToDict(self): p1 = Package('x') self.layer.add_package(p1) a_dict = self.layer.to_dict() self.assertTrue(a_dict['']) self.assertEqual(len(a_dict['']['packages']), 1) self.assertEqual(a_dict['']['packages'][0]['name'], 'x') self.assertEqual(a_dict['']['tar_file'], 'path/to/tar') def testGetPackageNames(self): p1 = Package('x') self.layer.add_package(p1) pkgs = self.layer.get_package_names() self.assertEqual(pkgs[0], 'x')
def setUp(self): self.layer = ImageLayer('123abc', 'path/to/tar') rootfs.set_working_dir()
class TestClassImageLayer(unittest.TestCase): def setUp(self): self.layer = ImageLayer('123abc', 'path/to/tar') rootfs.set_working_dir() def tearDown(self): del self.layer def testInstance(self): self.assertEqual(self.layer.diff_id, '123abc') self.assertEqual(self.layer.tar_file, 'path/to/tar') self.assertFalse(self.layer.packages) self.assertFalse(self.layer.created_by) self.assertRaises(AttributeError, setattr, self.layer, 'diff_id', '456def') self.assertRaises(AttributeError, setattr, self.layer, 'tar_file', 'some/other/path') self.layer.created_by = 'some string' self.assertEqual(self.layer.created_by, 'some string') self.layer.pkg_format = 'rpm' self.assertEqual(self.layer.pkg_format, 'rpm') self.layer.os_guess = 'operating system' self.assertEqual(self.layer.os_guess, 'operating system') self.assertFalse(self.layer.files_analyzed) self.layer.files_analyzed = True self.assertTrue(self.layer.files_analyzed) self.assertRaises(ValueError, setattr, self.layer, 'files_analyzed', 'some string') self.assertEqual("", self.layer.analyzed_output) self.layer.analyzed_output = 'some string' self.assertEqual(self.layer.analyzed_output, 'some string') self.assertRaises(ValueError, setattr, self.layer, 'analyzed_output', 123) def testAddPackage(self): err = "Object type String, should be Package" p1 = Package('x') self.layer.add_package(p1) self.assertEqual(len(self.layer.packages), 1) with self.assertRaises(TypeError, msg=err): self.layer.add_package("not_a_package") def testRemovePackage(self): p1 = Package('x') p2 = Package('y') self.layer.add_package(p1) self.layer.add_package(p2) self.assertTrue(self.layer.remove_package('y')) self.assertFalse(self.layer.remove_package('y')) def testAddFile(self): err = "Object type String, should be FileData" file1 = FileData('file1', 'path/to/file1') self.layer.add_file(file1) self.assertEqual(len(self.layer.files), 1) with self.assertRaises(TypeError, msg=err): self.layer.add_file("afile") def testRemoveFile(self): file1 = FileData('file1', 'path/to/file1') self.layer.add_file(file1) self.assertFalse(self.layer.remove_file('file1')) self.assertTrue(self.layer.remove_file('path/to/file1')) self.assertFalse(self.layer.remove_file('path/to/file1')) def testToDict(self): p1 = Package('x') f1 = FileData('file1', 'path/to/file1') self.layer.add_package(p1) self.layer.add_file(f1) a_dict = self.layer.to_dict() self.assertEqual(a_dict['diff_id'], '123abc') self.assertEqual(len(a_dict['packages']), 1) self.assertEqual(a_dict['packages'][0]['name'], 'x') self.assertEqual(len(a_dict['files']), 1) self.assertEqual(a_dict['files'][0]['name'], 'file1') self.assertEqual(a_dict['files'][0]['path'], 'path/to/file1') self.assertEqual(a_dict['tar_file'], 'path/to/tar') def testToDictTemplate(self): template1 = TestTemplate1() template2 = TestTemplate2() p1 = Package('x') self.layer.add_package(p1) f1 = FileData('file1', 'path/to/file1') self.layer.add_file(f1) dict1 = self.layer.to_dict(template1) dict2 = self.layer.to_dict(template2) self.assertEqual(len(dict1.keys()), 4) self.assertEqual(dict1['layer.diff'], '123abc') self.assertEqual(dict1['layer.tarfile'], 'path/to/tar') self.assertEqual(len(dict1['layer.packages']), 1) self.assertEqual(len(dict1['layer.files']), 1) self.assertEqual(len(dict2.keys()), 5) self.assertFalse(dict2['notes']) self.assertFalse(dict2['layer.packages'][0]['notes']) self.assertFalse(dict2['layer.files'][0]['notes']) def testGetPackageNames(self): p1 = Package('x') self.layer.add_package(p1) pkgs = self.layer.get_package_names() self.assertEqual(pkgs[0], 'x') def testGetFilePaths(self): f1 = FileData('file1', 'path/to/file1') f2 = FileData('file2', 'path/to/file2') self.layer.add_file(f1) self.layer.add_file(f2) file_paths = self.layer.get_file_paths() self.assertEqual(file_paths, ['path/to/file1', 'path/to/file2']) def testSetChecksum(self): self.layer.set_checksum('sha256', '12345abcde') self.assertEqual(self.layer.checksum_type, 'sha256') self.assertEqual(self.layer.checksum, '12345abcde') def testAddChecksums(self): self.layer.add_checksums({ 'SHA1': '12345abcde', 'MD5': '1ff38cc592c4c5d0c8e3ca38be8f1eb1' }) self.assertEqual(self.layer.checksums, { 'sha1': '12345abcde', 'md5': '1ff38cc592c4c5d0c8e3ca38be8f1eb1' }) def testSetExtensionInfo(self): self.layer.extension_info = {"header": set({"Test Header"})} self.assertIsInstance(self.layer.extension_info, dict) self.assertIsNotNone(self.layer.extension_info.get("header", None), None) self.assertIsInstance(self.layer.extension_info.get("header", None), set) header = self.layer.extension_info.get("header").pop() self.assertEqual(header, "Test Header") def testGetUntarDir(self): self.layer.image_layout = "oci" self.assertEqual(self.layer.image_layout, "oci") self.layer.image_layout = "docker" self.assertEqual(self.layer.image_layout, "docker") self.layer.image_layout = "" self.assertEqual(self.layer.image_layout, "oci") self.layer.layer_index = 1 self.assertEqual(self.layer.layer_index, "1") expected_path = os.path.join(rootfs.get_working_dir(), '1/contents') self.assertEqual(self.layer.get_untar_dir(), expected_path) self.layer.image_layout = "docker" expected_path = os.path.join(rootfs.get_working_dir(), 'path/to/contents') self.assertEqual(self.layer.get_untar_dir(), expected_path)
def load_image(self): l1 = ImageLayer('123abc', 'path/to/tar') l1.add_package(Package('p1')) l1.add_package(Package('p2')) self._layers.append(l1)