def stop_scan(self, scan_guid): api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.stop_scan(scan_guid) if response.response_code == 401: Logger.app.critical("An Authorization Error occured.") sys.exit(ExitStatus.failure) return response.success
def _download_proxy(self, webmacro, setting, server): api = WebInspectApi(server, verify_ssl=False, username=self.username, password=self.password) if webmacro: response = api.download_proxy_webmacro(self.proxy_name) extension = 'webmacro' elif setting: response = api.download_proxy_setting(self.proxy_name) extension = 'xml' else: Logger.app.error("Please enter a file type to download.") sys.exit(ExitStatus.failure) APIHelper().check_for_response_errors(response) try: with open('{0}-proxy.{1}'.format(self.proxy_name, extension), 'wb') as f: Logger.app.info( 'Scan results file is available: {0}-proxy.{1}'.format( self.proxy_name, extension)) f.write(response.data) except UnboundLocalError as e: Logger.app.error('Error saving file locally {}'.format(e)) sys.exit(ExitStatus.failure)
def create_scan(self): """ Launches and monitors a scan :return: If scan was able to launch, scan_id. Otherwise none. """ try: Logger.app.debug("Creating Scan in webinspect client") overrides = json.dumps(webinspectjson.formatted_settings_payload(self.settings, self.scan_name, self.runenv, self.scan_mode, self.scan_scope, self.login_macro, self.scan_policy, self.scan_start, self.start_urls, self.workflow_macros, self.allowed_hosts)) api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.create_scan(overrides) if response.response_code == 401: Logger.app.critical("An Authorization Error occured.") sys.exit(ExitStatus.failure) logger_response = json.dumps(response, default=lambda o: o.__dict__, sort_keys=True) Logger.app.debug("Request sent to {0}:\n{1}".format(self.url, overrides)) Logger.app.debug("Response from {0}:\n{1}\n".format(self.url, logger_response)) if response.success: scan_id = response.data['ScanId'] sys.stdout.write(str('WebInspect scan launched on {0} your scan id: {1}\n'.format(self.url, scan_id))) else: Logger.app.error("No scan was launched!\n {}".format(response.message)) sys.exit(ExitStatus.failure) return scan_id except (ValueError, UnboundLocalError) as e: Logger.app.error("Creating the WebInspect scan failed! {}".format(e))
def export_scan_results(self, scan_id, extension): """ Save scan results to file :param scan_id: :param extension: :return: """ # Export scan as a xml for Threadfix or other Vuln Management System Logger.app.info('Exporting scan: {} as {}'.format(scan_id, extension)) detail_type = 'Full' if extension == 'xml' else None api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.export_scan_format(scan_id, extension, detail_type) if response.response_code == 401: Logger.app.critical( "An authentication error occurred, exporting scan results!") sys.exit(ExitStatus.failure) if response.success: try: with open('{0}.{1}'.format(self.scan_name, extension), 'wb') as f: Logger.app.debug( str('Scan results file is available: {0}.{1}\n'.format( self.scan_name, extension))) f.write(response.data) print( str('Scan results file is available: {0}.{1}\n'.format( self.scan_name, extension))) except UnboundLocalError as e: Logger.app.error('Error saving file locally! {}'.format(e)) else: Logger.app.error('Unable to retrieve scan results! {} '.format( response.message))
def policy_exists(self, policy_guid): # true if policy exists api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.get_policy_by_guid(policy_guid) if response.response_code == 401: Logger.app.critical("An Authorization Error occured.") sys.exit(ExitStatus.failure) return response.success
def _verify_proxy_server(self, server): api = WebInspectApi(server, verify_ssl=False, username=self.username, password=self.password) response = api.get_proxy_information(self.proxy_name) APIHelper().check_for_response_errors(response) return response.data
class WebInspectWiswag: def __init__(self, url, wiswag_name, username=None, password=None, server=None): auth_config = WebInspectAuth() self.wiswag_name = wiswag_name self.host = server self.username, self.password = auth_config.authenticate(username, password) if self.host is None: self.host = self.get_endpoint() self.api = WebInspectApi(self.host, verify_ssl=False, username=self.username, password=self.password) self.wiswag(url) def wiswag(self, url): if self.wiswag_name is None: self._generate_random_wiswag_name() self.api.create_wiswag(url, self.wiswag_name) # go through the settings list 3 times (~15 secs before timeout) for i in range(3): response = self.api.list_settings() APIHelper().check_for_response_errors(response) if response.data is not None: for settingListName in response.data: if settingListName == self.wiswag_name: Logger.app.info("Found wiswag_name: {}".format(self.wiswag_name)) self._download_wiswag(self.wiswag_name) sys.exit(ExitStatus.success) else: Logger.app.info("Fail to find wiswag_name {}. Retrying".format(self.wiswag_name)) sleep(5) Logger.app.error("Timeout error: Can not find wiswag_name {}".format(self.wiswag_name)) def _download_wiswag(self, wiswag_name): response = self.api.download_settings(wiswag_name) extension = 'xml' try: with open('{0}.{1}'.format(wiswag_name, extension), 'wb') as f: f.write(response.data) except UnboundLocalError as e: Logger.app.error('Error saving file locally {}'.format(e)) sys.exit(ExitStatus.failure) def _generate_random_wiswag_name(self): self.wiswag_name = "wiswag" + "-" + "".join( random.choice(string.ascii_uppercase + string.digits) for _ in range(5)) def get_endpoint(self): jit_scheduler = WebInspectJitScheduler(username=self.username, password=self.password) Logger.app.info("Querying WebInspect scan engines for availability.") endpoint = jit_scheduler.get_endpoint() return endpoint
def get_policy_by_name(self, policy_name): api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.get_policy_by_name(policy_name) if response.response_code == 401: Logger.app.critical("An authentication error occured, retrieving the policy name!") sys.exit(ExitStatus.failure) if response.success: return response.data else: return None
def _list_proxy(self, server): if self.proxy_name is None: self._generate_random_proxy_name() api = WebInspectApi(server, verify_ssl=False, username=self.username, password=self.password) response = api.list_proxies() APIHelper().check_for_response_errors(response) return response.data
def __init__(self, url, wiswag_name, username=None, password=None, server=None): auth_config = WebInspectAuth() self.wiswag_name = wiswag_name self.host = server self.username, self.password = auth_config.authenticate(username, password) if self.host is None: self.host = self.get_endpoint() self.api = WebInspectApi(self.host, verify_ssl=False, username=self.username, password=self.password) self.wiswag(url)
def _delete_proxy(self, server): api = WebInspectApi(server, verify_ssl=False, username=self.username, password=self.password) response = api.delete_proxy(self.proxy_name) APIHelper().check_for_response_errors(response) Logger.app.info("Proxy: '{0}' deleted from '{1}'".format( self.proxy_name, server))
def get_scan_status(self, scan_guid): api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) try: response = api.get_current_status(scan_guid) if response.response_code == 401: Logger.app.critical("An Authorization Error occured.") sys.exit(ExitStatus.failure) status = json.loads(response.data_json())['ScanStatus'] return status except (ValueError, UnboundLocalError, TypeError) as e: Logger.app.error("get_scan_status failed: {}".format(e)) return "Unknown"
def list_webmacros(self): try: api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.list_webmacros() if response.response_code == 401: Logger.app.critical("An Authorization Error occured.") sys.exit(ExitStatus.failure) if response.success: for webmacro in response.data: Logger.console.info("{}".format(webmacro)) else: Logger.app.error("{}".format(response.message)) except (ValueError, UnboundLocalError) as e: Logger.app.error("list_webmacros failed: {}".format(e))
def _upload_proxy(self, upload_file, server): try: api = WebInspectApi(server, verify_ssl=False, username=self.username, password=self.password) response = api.upload_webmacro_proxy(self.proxy_name, upload_file) APIHelper().check_for_response_errors(response) Logger.app.info("Uploaded '{0}' to '{1}' on: {2}.".format( upload_file, self.proxy_name, server)) except (ValueError, UnboundLocalError) as e: Logger.app.error("Error uploading policy {}".format(e)) sys.exit(ExitStatus.failure)
def upload_webmacros(self): try: for webmacro in self.webinspect_upload_webmacros: api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.upload_webmacro(webmacro) if response.response_code == 401: Logger.app.critical("An Authorization Error occured.") sys.exit(ExitStatus.failure) if response.success: Logger.console.debug("Uploaded webmacro {} to server.".format(webmacro)) else: Logger.app.error("Error uploading webmacro {0}. {1}".format(webmacro, response.message)) except (ValueError, UnboundLocalError) as e: logexceptionhelper.LogErrorUploading("webmacro", e) logexceptionhelper.LogNoWebInspectServerFound(e)
def __settings_exists__(self): try: api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.list_settings() if response.response_code == 401: Logger.app.critical("An Authorization Error occured.") sys.exit(ExitStatus.failure) if response.success: for setting in response.data: if setting in self.settings: return True except (ValueError, UnboundLocalError) as e: Logger.app.error("Unable to determine if setting file exists, scan will continue without setting!" "Error: {}".format(e)) return False
def wait_for_scan_status_change(self, scan_id): """ Blocking call, will remain in this method until status of scan changes :param scan_id: :return: """ # WebInspect Scan has started, wait here until it's done api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.wait_for_status_change(scan_id) # this line is the blocker if response.response_code == 401: Logger.app.critical("An Authorization Error occured.") sys.exit(ExitStatus.failure) if response.success: Logger.console.debug('Scan status {}'.format(response.data)) else: Logger.app.debug('Scan status not known because: {}'.format(response.message))
def _get_proxy_certificate(self, server): path = Config().cert api = WebInspectApi(server, verify_ssl=False, username=self.username, password=self.password) response = api.cert_proxy() APIHelper().check_for_response_errors(response) try: with open(path, 'wb') as f: f.write(response.data) Logger.app.info( 'Certificate has downloaded to\t:\t{}'.format(path)) except UnboundLocalError as e: Logger.app.error('Error saving certificate locally {}'.format(e))
def upload_policy(self): # if a policy of the same name already exists, delete it prior to upload try: api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) # bit of ugliness here. I'd like to just have the policy name at this point but I don't # so find it in the full path # TODO: Verify split here response = api.get_policy_by_name(ntpath.basename(self.webinspect_upload_policy).split('.')[0]) if response.response_code == 401: Logger.app.critical("An Authorization Error occured.") sys.exit(ExitStatus.failure) if response.success and response.response_code == 200: # the policy exists on the server already api = webinspectapi.WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.delete_policy(response.data['uniqueId']) if response.success: Logger.app.debug("Deleted policy {} from server".format( ntpath.basename(self.webinspect_upload_policy).split('.')[0])) except (ValueError, UnboundLocalError, TypeError) as e: Logger.app.error("Verify if deletion of existing policy failed: {}".format(e)) try: api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.upload_policy(self.webinspect_upload_policy) if response.response_code == 401: Logger.app.critical("An Authorization Error occured.") sys.exit(ExitStatus.failure) if response.success: Logger.console.debug("Uploaded policy {} to server.".format(self.webinspect_upload_policy)) else: Logger.app.error("Error uploading policy {0}. {1}".format(self.webinspect_upload_policy, response.message)) except (ValueError, UnboundLocalError, TypeError, NameError) as e: logexceptionhelper.LogErrorUploading("policy", e) logexceptionhelper.LogNoWebInspectServerFound(e)
def get_scan_issues(self, scan_name=None, scan_guid=None, pretty=False): try: if scan_name: api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.get_scan_by_name(scan_name) if response.response_code == 401: Logger.app.critical( "An authentication error occurred, retrieving scan issues!" ) sys.exit(ExitStatus.failure) if response.success: scan_guid = response.data[0]['ID'] else: Logger.app.error(response.message) return None api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.get_scan_issues(scan_guid) if response.response_code == 401: Logger.app.critical("An authentication error occurred!") sys.exit(ExitStatus.failure) if response.success: return response.data_json(pretty=True) else: return None except (ValueError, UnboundLocalError) as e: Logger.app.error( "There was an error getting scan issues: {}".format(e))
def get_scan_log(self, scan_name=None, scan_guid=None): try: if scan_name: api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.get_scan_by_name(scan_name) if response.response_code == 401: Logger.app.critical("An an authentication error occurred while retrieving scan name!") sys.exit(ExitStatus.failure) if response.success: scan_guid = response.data[0]['ID'] else: Logger.app.error(response.message) return None api = WebInspectApi(self.url, verify_ssl=False, username=self.username, password=self.password) response = api.get_scan_log(scan_guid) if response.response_code == 401: Logger.app.critical("An authentication error occurred while reading the scan log!") sys.exit(ExitStatus.failure) if response.success: return response.data_json() else: return None except (ValueError, UnboundLocalError) as e: Logger.app.error("get_scan_log failed: {}".format(e))
def _set_api(self): self.api = WebInspectApi(self.host, verify_ssl=False, username=self.username, password=self.password)
class WebInspectAPIHelper(object): def __init__(self, host=None, username=None, password=None, webinspect_setting_overrides=None, silent=False): # If a host is passed then we are doing a download or a list, otherwise a scan. self.host = host self.username = username self.password = password # if - we are running a webinspect scan if webinspect_setting_overrides is not None: self.setting_overrides = webinspect_setting_overrides # set the host to be the available endpoint self.host = self.setting_overrides.endpoint else: self.setting_overrides = None self._set_api() self.silent = silent if self.silent is False: # want to be able to hide this output for multithreading webinspect_logexceptionhelp.log_info_using_webinspect_server( self.host) def _set_api(self): self.api = WebInspectApi(self.host, verify_ssl=False, username=self.username, password=self.password) @CircuitBreaker(fail_max=5, reset_timeout=60) def create_scan(self): """ Launches and monitors a scan :return: If scan was able to launch, scan_id. Otherwise none. """ try: Logger.app.debug("Creating Scan in webinspect client") overrides = json.dumps( webinspectjson.formatted_settings_payload( self.setting_overrides.settings, self.setting_overrides.scan_name, self.setting_overrides.runenv, self.setting_overrides.scan_mode, self.setting_overrides.scan_scope, self.setting_overrides.login_macro, self.setting_overrides.scan_policy, self.setting_overrides.scan_start, self.setting_overrides.start_urls, self.setting_overrides.workflow_macros, self.setting_overrides.allowed_hosts)) response = self.api.create_scan(overrides) logger_response = json.dumps(response, default=lambda o: o.__dict__, sort_keys=True) Logger.app.debug("Request sent to {0}:\n{1}".format( self.setting_overrides.endpoint, overrides)) Logger.app.debug("Response from {0}:\n{1}\n".format( self.setting_overrides.endpoint, logger_response)) scan_id = response.data['ScanId'] webinspect_logexceptionhelp.log_info_scan_start( self.setting_overrides.endpoint, scan_id) return scan_id except (ValueError, UnboundLocalError) as e: webinspect_logexceptionhelp.log_error_scan_start_failed(e) sys.exit(ExitStatus.failure) @CircuitBreaker(fail_max=5, reset_timeout=60) def export_scan_results(self, scan_id, extension, scan_name=None): """ Save scan results to file, if you pass a scan name it will have that name, otherwise it will read from the overrides for the scan name :param scan_id: :param extension: :param scan_name: name of scan to be saved locally :return: """ # Export scan as a xml for Threadfix or other Vuln Management System Logger.app.info('Exporting scan: {} as {}'.format(scan_id, extension)) detail_type = 'Full' if extension == 'xml' else None response = self.api.export_scan_format(scan_id, extension, detail_type) #APIHelper().check_for_response_errors(response) # setting_overrides is on a webinspect scan if scan_name == None: scan_name = self.setting_overrides.scan_name try: with open('{0}.{1}'.format(scan_name, extension), 'wb') as f: f.write(response.data) webinspect_logexceptionhelp.log_info_successful_scan_export( scan_name, extension) except (UnboundLocalError, IOError) as e: webinspect_logexceptionhelp.log_error_failed_scan_export(e) @CircuitBreaker(fail_max=5, reset_timeout=60) def get_policy_by_guid(self, policy_guid): response = self.api.get_policy_by_guid(policy_guid) #APIHelper().check_for_response_errors(response) return response.data @CircuitBreaker(fail_max=5, reset_timeout=60) def get_policy_by_name(self, policy_name): response = self.api.get_policy_by_name(policy_name) #APIHelper().check_for_response_errors(response) return response.data @CircuitBreaker(fail_max=5, reset_timeout=60) def get_scan_by_name(self, scan_name): """ Search Webinspect server for a scan matching scan_name :param scan_name: :return: List of search results """ # scan_name = self._trim_ext(scan_name) response = self.api.get_scan_by_name(scan_name) #APIHelper().check_for_response_errors(response) return response.data @CircuitBreaker(fail_max=5, reset_timeout=60) def get_scan_status(self, scan_guid): """ Get scan status from the Webinspect server :param scan_guid: :return: Current status of scan """ try: response = self.api.get_current_status(scan_guid) #APIHelper().check_for_response_errors(response) status = json.loads(response.data_json())['ScanStatus'] return status except (ValueError, TypeError, UnboundLocalError) as e: webinspect_logexceptionhelp.log_error_get_scan_status(e) return None @CircuitBreaker(fail_max=5, reset_timeout=60) def list_scans(self): """ List all scans found on host :return: response.data from the Webinspect server """ try: response = self.api.list_scans() #APIHelper().check_for_response_errors(response) return response.data except (ValueError, UnboundLocalError, NameError) as e: webinspect_logexceptionhelp.log_error_list_scans(e) @CircuitBreaker(fail_max=5, reset_timeout=60) def list_running_scans(self): """ List all running scans on host :return: """ response = self.api.list_running_scans() return response @CircuitBreaker(fail_max=5, reset_timeout=60) def policy_exists(self, policy_guid): # true if policy exists response = self.api.get_policy_by_guid(policy_guid) #APIHelper().check_for_response_errors(response) return response.success @CircuitBreaker(fail_max=5, reset_timeout=60) def stop_scan(self, scan_guid): response = self.api.stop_scan(scan_guid) #APIHelper().check_for_response_errors(response) return response.success @CircuitBreaker(fail_max=5, reset_timeout=60) def upload_policy(self): # if a policy of the same name already exists, delete it prior to upload try: # bit of ugliness here. I'd like to just have the policy name at this point but I don't # so find it in the full path # TODO: Verify split here response = self.api.get_policy_by_name( ntpath.basename( self.setting_overrides.webinspect_upload_policy).split('.') [0]) #APIHelper().check_for_response_errors(response) if response.success and response.response_code == 200: # the policy exists on the server already response = self.api.delete_policy(response.data['uniqueId']) #APIHelper().check_for_response_errors(response) Logger.app.debug("Deleted policy {} from server".format( ntpath.basename( self.setting_overrides.webinspect_upload_policy).split( '.')[0])) except (ValueError, UnboundLocalError, TypeError) as e: webinspect_logexceptionhelp.log_error_policy_deletion(e) try: response = self.api.upload_policy( self.setting_overrides.webinspect_upload_policy) #APIHelper().check_for_response_errors(response) Logger.console.debug("Uploaded policy {} to server.".format( self.setting_overrides.webinspect_upload_policy)) except (ValueError, UnboundLocalError, TypeError, NameError) as e: webinspect_logexceptionhelp.log_error_uploading("policy", e) webinspect_logexceptionhelp.log_no_webinspect_server_found(e) @CircuitBreaker(fail_max=5, reset_timeout=60) def upload_settings(self): try: response = self.api.upload_settings( self.setting_overrides.webinspect_upload_settings) #APIHelper().check_for_response_errors(response) Logger.console.debug("Uploaded settings {} to server.".format( self.setting_overrides.webinspect_upload_settings)) except (ValueError, UnboundLocalError, NameError) as e: webinspect_logexceptionhelp.log_error_uploading("settings", e) webinspect_logexceptionhelp.log_no_webinspect_server_found(e) @CircuitBreaker(fail_max=5, reset_timeout=60) def upload_webmacros(self): try: for webmacro in self.setting_overrides.webinspect_upload_webmacros: response = self.api.upload_webmacro(webmacro) #APIHelper().check_for_response_errors(response) Logger.console.debug( "Uploaded webmacro {} to server.".format(webmacro)) except (ValueError, UnboundLocalError) as e: webinspect_logexceptionhelp.log_error_uploading("webmacro", e) webinspect_logexceptionhelp.log_no_webinspect_server_found(e) @CircuitBreaker(fail_max=5, reset_timeout=60) def verify_scan_policy(self, config): try: if self.setting_overrides.scan_policy: # two happy paths: either the provided policy refers to an existing builtin policy, or it refers to # a local policy we need to first upload and then use. built_in = self._check_if_built_in(config) if built_in: idx = self._get_index(config) policy_guid = config.mapped_policies[idx[0]][1] Logger.app.info( "scan_policy {} with policyID {} has been selected.". format(self.setting_overrides.scan_policy, policy_guid)) Logger.app.info( "Checking to make sure a policy with that ID exists in WebInspect." ) if not self.policy_exists(policy_guid): Logger.app.error( "Scan policy {} cannot be located on the WebInspect server. Stopping" .format(self.setting_overrides.scan_policy)) sys.exit(ExitStatus.failure) else: Logger.app.info( "Found policy {} in WebInspect.".format( policy_guid)) else: # Not a builtin. Assume that caller wants the provided policy to be uploaded Logger.app.info( "Provided scan policy is not built-in, so will assume it needs to be uploaded." ) self.upload_policy() policy = self.get_policy_by_name( self.setting_overrides.scan_policy) if policy: policy_guid = policy['uniqueId'] else: Logger.app.error( "The policy name is either incorrect or not available in {}." .format('.webbreaker/etc/webinspect/policies')) sys.exit(ExitStatus.failure) # Change the provided policy name into the corresponding policy id for scan creation. policy_id = self.get_policy_by_guid(policy_guid)['id'] self.setting_overrides.scan_policy = policy_id Logger.app.debug("New scan policy has been set") else: Logger.app.debug( "No WebInspect Scan Override Policy was selected: {}!". format(self.setting_overrides.scan_policy)) except (UnboundLocalError, NameError) as e: webinspect_logexceptionhelp.log_no_webinspect_server_found(e) def _check_if_built_in(self, config): """ check if scan policy is a built in, this is abstracted from verify_scan_policy so it can be mocked. :param config: :return: """ if str(self.setting_overrides.scan_policy).lower() in [ str(x[0]).lower() for x in config.mapped_policies ]: return True else: return False def _get_index(self, config): """ # wow what a list comprehension. not a single comment - this was added posthumously. this was abstracted from verify_scan_policy so mocking could occur. :param config: :return: """ index = [ x for x, y in enumerate(config.mapped_policies) if y[0] == str(self.setting_overrides.scan_policy).lower() ] return index