def test_https_bad_uri(self): with TempDirectory() as test_dir: with self.assertRaisesRegex( RuntimeError, r"Error downloading file \(.+\): HTTP Error 404: Not Found" ): download_and_decompress_source_to_destination( source_uri= "https://www.redhat.com/security/data/metrics/ds/v2/RHEL8/does-not-exist.ds.xml.bz2", destination_dir=test_dir.path)
def test_bad_protocol(self): with TempDirectory() as test_dir: with self.assertRaisesRegex( ValueError, r"Unexpected error, should have been caught by step validation." r" Source \(.+\) must start with known protocol \(/|file://\|http://\|https://\)." ): download_and_decompress_source_to_destination( source_uri= "bad://www.redhat.com/security/data/metrics/ds/v2/RHEL8/rhel-8.ds.xml.bz2", destination_dir=test_dir.path)
def test_https_xml(self): with TempDirectory() as test_dir: destination_path = download_and_decompress_source_to_destination( source_uri="https://www.redhat.com/security/data/cvrf/2020/cvrf-rhba-2020-0017.xml", destination_dir=test_dir.path ) self.assertIsNotNone(destination_path) self.assertRegex(destination_path, rf'{test_dir.path}/cvrf-rhba-2020-0017.xml$') with open(destination_path) as downloaded_file: self.assertTrue(downloaded_file.read())
def test_https_bz2(self): with TempDirectory() as test_dir: destination_path = download_and_decompress_source_to_destination( source_uri="https://www.redhat.com/security/data/metrics/ds/v2/RHEL8/rhel-8.ds.xml.bz2", destination_dir=test_dir.path ) self.assertIsNotNone(destination_path) self.assertRegex(destination_path, rf'{test_dir.path}/rhel-8.ds.xml$') with open(destination_path) as downloaded_file: self.assertTrue(downloaded_file.read())
def test_local_file_download_forward_slash_prefix(self): sample_file_path = os.path.join(os.path.dirname(__file__), 'files', 'cvrf-rhba-2020-0017.xml') with TempDirectory() as test_dir: destination_path = download_and_decompress_source_to_destination( source_uri=f"{sample_file_path}", destination_dir=test_dir.path) self.assertIsNotNone(destination_path) self.assertRegex(destination_path, rf'{test_dir.path}/cvrf-rhba-2020-0017.xml$') with open(destination_path) as downloaded_file, open( sample_file_path) as sample_file: downloaded_file_contents = downloaded_file.read() self.assertTrue(downloaded_file_contents) self.assertEqual(downloaded_file_contents, sample_file.read())
def _run_step(self): # pylint: disable=too-many-locals,too-many-statements """Runs the OpenSCAP eval for a given input file against a given container. """ step_result = StepResult.from_step_implementer(self) image_tar_file = self.get_value('image-tar-file') oscap_profile = self.get_value('oscap-profile') oscap_fetch_remote_resources = self.get_value( 'oscap-fetch-remote-resources') # create a container name from the tar file name, step name, and sub step name container_name = os.path.splitext(os.path.basename(image_tar_file))[0] container_name += f"-{self.step_name}-{self.sub_step_name}" try: # import image tar file to vfs file system print(f"\nImport image: {image_tar_file}") OpenSCAPGeneric.__buildah_import_image_from_tar( image_tar_file=image_tar_file, container_name=container_name) print(f"Imported image: {image_tar_file}") # baking `buildah unshare` command to wrap other buildah commands with # so that container does not need to be running in a privileged mode to be able # to function buildah_unshare_command = sh.buildah.bake('unshare') # pylint: disable=no-member # mount the container filesystem and get mount path # # NOTE: run in the context of `buildah unshare` so that container does not # need to be run in a privileged mode print(f"\nMount container: {container_name}") container_mount_path = OpenSCAPGeneric.__buildah_mount_container( buildah_unshare_command=buildah_unshare_command, container_id=container_name) print( f"Mounted container ({container_name}) with mount path: '{container_mount_path}'" ) try: # download the open scap input file oscap_input_definitions_uri = self.get_value( 'oscap-input-definitions-uri') print( f"\nDownload input definitions: {oscap_input_definitions_uri}" ) oscap_input_file = download_and_decompress_source_to_destination( source_url=oscap_input_definitions_uri, destination_dir=self.work_dir_path_step) print(f"Downloaded input definitions to: {oscap_input_file}") except (RuntimeError, AssertionError) as error: raise StepRunnerException( f"Error downloading OpenSCAP input file: {error}" ) from error try: # if specified download oscap tailoring file oscap_tailoring_file = None oscap_tailoring_file_uri = self.get_value( 'oscap-tailoring-uri') if oscap_tailoring_file_uri: print( f"\nDownload oscap tailoring file: {oscap_tailoring_file_uri}" ) oscap_tailoring_file = download_and_decompress_source_to_destination( source_url=oscap_tailoring_file_uri, destination_dir=self.work_dir_path_step) print( f"Download oscap tailoring file to: {oscap_tailoring_file}" ) except (RuntimeError, AssertionError) as error: raise StepRunnerException( f"Error downloading OpenSCAP tailoring file: {error}" ) from error # determine oscap eval type based on document type print( f"\nDetermine OpenSCAP document type of input file: {oscap_input_file}" ) oscap_document_type = OpenSCAPGeneric.__get_oscap_document_type( oscap_input_file=oscap_input_file) print("Determined OpenSCAP document type of input file" f" ({oscap_input_file}): {oscap_document_type}") print( f"\nDetermine OpenSCAP eval type for input file ({oscap_input_file}) " f"of document type: {oscap_document_type}") oscap_eval_type = OpenSCAPGeneric.__get_oscap_eval_type_based_on_document_type( oscap_document_type=oscap_document_type) print("Determined OpenSCAP eval type of input file" f" ({oscap_input_file}): {oscap_eval_type}") # Execute scan in the context of buildah unshare # # NOTE: run in the context of `buildah unshare` so that container does not # need to be run in a privilaged mode oscap_out_file_path = self.write_working_file( f'oscap-{oscap_eval_type}-out') oscap_xml_results_file_path = self.write_working_file( f'oscap-{oscap_eval_type}-results.xml') oscap_html_report_path = self.write_working_file( f'oscap-{oscap_eval_type}-report.html') print("\nRun oscap scan") oscap_eval_success, oscap_eval_fails = OpenSCAPGeneric.__run_oscap_scan( buildah_unshare_command=buildah_unshare_command, oscap_eval_type=oscap_eval_type, oscap_input_file=oscap_input_file, oscap_out_file_path=oscap_out_file_path, oscap_xml_results_file_path=oscap_xml_results_file_path, oscap_html_report_path=oscap_html_report_path, container_mount_path=container_mount_path, oscap_profile=oscap_profile, oscap_tailoring_file=oscap_tailoring_file, oscap_fetch_remote_resources=oscap_fetch_remote_resources) print( f"OpenSCAP scan completed with eval success: {oscap_eval_success}" ) # save scan results step_result.success = oscap_eval_success if not oscap_eval_success: step_result.message = f"OSCAP eval found issues:\n{oscap_eval_fails}" step_result.add_artifact(name='html-report', value=oscap_html_report_path) step_result.add_artifact(name='xml-report', value=oscap_xml_results_file_path) step_result.add_artifact(name='stdout-report', value=oscap_out_file_path) except StepRunnerException as error: step_result.success = False step_result.message = str(error) return step_result
def _run_step(self): # pylint: disable=too-many-locals,too-many-statements """Runs the OpenSCAP eval for a given input file against a given container. """ step_result = StepResult.from_step_implementer(self) # get config image_address = self.get_value([ 'container-image-build-address', 'container-image-push-address', 'container-image-pull-address', 'container-image-address', 'container-image-tag' ]) oscap_profile = self.get_value('oscap-profile') oscap_fetch_remote_resources = self.get_value( 'oscap-fetch-remote-resources') oscap_severity_index = OpenSCAPGeneric.__parse_sev_to_int( oscap_severity=self.get_value('oscap-severity')) pull_repository_type = self.get_value([ 'container-image-pull-registry-type', 'container-image-registry-type' ]) try: # create container from image that can be mounted print(f"\nCreate container from image ({image_address})") container_name = create_container_from_image( image_address=image_address, repository_type=pull_repository_type) print( f"Created container ({container_name}) from image ({image_address})" ) # baking `buildah unshare` command to wrap other buildah commands with # so that container does not need to be running in a privileged mode to be able # to function buildah_unshare_command = sh.buildah.bake('unshare') # pylint: disable=no-member # mount the container filesystem and get mount path # # NOTE: run in the context of `buildah unshare` so that container does not # need to be run in a privileged mode print(f"\nMount container: {container_name}") container_mount_path = mount_container( buildah_unshare_command=buildah_unshare_command, container_id=container_name) print( f"Mounted container ({container_name}) with mount path: '{container_mount_path}'" ) try: # download the open scap input file oscap_input_definitions_uri = self.get_value( 'oscap-input-definitions-uri') print( f"\nDownload input definitions: {oscap_input_definitions_uri}" ) oscap_input_file = download_and_decompress_source_to_destination( source_uri=oscap_input_definitions_uri, destination_dir=self.work_dir_path) print(f"Downloaded input definitions to: {oscap_input_file}") except (RuntimeError, ValueError) as error: raise StepRunnerException( f"Error downloading OpenSCAP input file: {error}" ) from error try: # if specified download oscap tailoring file oscap_tailoring_file = None oscap_tailoring_file_uri = self.get_value( 'oscap-tailoring-uri') if oscap_tailoring_file_uri: print( f"\nDownload oscap tailoring file: {oscap_tailoring_file_uri}" ) oscap_tailoring_file = download_and_decompress_source_to_destination( source_uri=oscap_tailoring_file_uri, destination_dir=self.work_dir_path) print( f"Download oscap tailoring file to: {oscap_tailoring_file}" ) except (RuntimeError, ValueError) as error: raise StepRunnerException( f"Error downloading OpenSCAP tailoring file: {error}" ) from error # determine oscap eval type based on document type print( f"\nDetermine OpenSCAP document type of input file: {oscap_input_file}" ) oscap_document_type = OpenSCAPGeneric.__get_oscap_document_type( oscap_input_file=oscap_input_file) print("Determined OpenSCAP document type of input file" f" ({oscap_input_file}): {oscap_document_type}") print( f"\nDetermine OpenSCAP eval type for input file ({oscap_input_file}) " f"of document type: {oscap_document_type}") oscap_eval_type = OpenSCAPGeneric.__get_oscap_eval_type_based_on_document_type( oscap_document_type=oscap_document_type) print("Determined OpenSCAP eval type of input file" f" ({oscap_input_file}): {oscap_eval_type}") # Execute scan in the context of buildah unshare # # NOTE: run in the context of `buildah unshare` so that container does not # need to be run in a privilaged mode oscap_out_file_path = self.write_working_file( f'oscap-{oscap_eval_type}-out') oscap_xml_results_file_path = self.write_working_file( f'oscap-{oscap_eval_type}-results.xml') oscap_html_report_path = self.write_working_file( f'oscap-{oscap_eval_type}-report.html') print("\nRun oscap scan") oscap_eval_success, \ oscap_eval_fails, \ oscap_failure_met_threshold = OpenSCAPGeneric.__run_oscap_scan( buildah_unshare_command=buildah_unshare_command, oscap_eval_type=oscap_eval_type, oscap_input_file=oscap_input_file, oscap_out_file_path=oscap_out_file_path, oscap_xml_results_file_path=oscap_xml_results_file_path, oscap_html_report_path=oscap_html_report_path, container_mount_path=container_mount_path, oscap_profile=oscap_profile, oscap_tailoring_file=oscap_tailoring_file, oscap_fetch_remote_resources=oscap_fetch_remote_resources, oscap_severity_index=oscap_severity_index ) print( f"OpenSCAP scan completed with eval success: {oscap_eval_success}" ) # save scan results # if there were no failure or threshold was met then pass step_result.success = oscap_eval_success or not oscap_failure_met_threshold # report all issues even if they did not meet threshold if not oscap_eval_success: step_result.message = f"OSCAP eval found issues:\n{oscap_eval_fails}" step_result.add_artifact(name='html-report', value=oscap_html_report_path) step_result.add_artifact(name='xml-report', value=oscap_xml_results_file_path) step_result.add_artifact(name='stdout-report', value=oscap_out_file_path) except (StepRunnerException, RuntimeError) as error: step_result.success = False step_result.message = str(error) return step_result