def validate_object_id(object_id): """Validates object ID (server_id, policy_id, etc...) This function validates Object IDs with the intent of guarding against \ URL traversal. Args: object_id (str or list): Object ID to be validated Returns: (bool) True if valid, throws an exception otherwise. """ rex = re.compile('^[A-Za-z0-9]+$') if isinstance(object_id, (str, unicode)): if not rex.match(object_id): error_message = "Object ID failed validation: %s" % object_id raise CloudPassageValidation(error_message) else: return True elif isinstance(object_id, list): for individual in object_id: if not rex.match(individual): error_message = "Object ID failed validation: %s" % object_id raise CloudPassageValidation(error_message) return True else: error_message = "Wrong type for object ID: %s" % str(type(object_id)) raise TypeError(error_message)
def verify_and_build_module_params(self, module_raw): """Verifies module params and data types""" if isinstance(module_raw, list): for module in module_raw: if self.scan_type_supported(module) is not True: exception_message = "%s is not supported" % module raise CloudPassageValidation(exception_message) else: if self.scan_type_supported(module_raw) is False: error_message = "Unsupported module: %s" % module_raw raise CloudPassageValidation(error_message) return module_raw
def verify_and_build_status_params(self, status_raw): """Verifies status params and data types.""" if isinstance(status_raw, list): for status in status_raw: if self.scan_status_supported(status) is not True: exception_message = "%s is not supported" % status raise CloudPassageValidation(exception_message) else: if self.scan_status_supported(status_raw) is False: error_message = "Unsupported status: %s" % status_raw raise CloudPassageValidation(error_message) return status_raw
def last_scan_results(self, server_id, scan_type): """Get the results of scan_type performed on server_id. Args: server_id (str): ID of server scan_type (str): Type of scan to filter results for Valid scan types: sca - Configuration scan csm - Configuration scan (same as sca) svm - Software vulnerability scan sva - Software vulnerability scan (same as svm) fim - File integrity monitoring scan Returns: dict: Dictionary object describing last scan results """ if self.scan_history_supported(scan_type) is False: exception_message = "Unsupported scan type: %s" % scan_type raise CloudPassageValidation(exception_message) else: scan_type_normalized = self.supported_scans[scan_type] endpoint = "/v1/servers/%s/%s" % (server_id, scan_type_normalized) request = HttpHelper(self.session) response = request.get(endpoint) return response
def initiate_scan(self, server_id, scan_type): """Initiate a scan on a specific server. Args: server_id (str): ID of server to be scanned scan_type (str): Type of scan to be run. Valid scan types: sca - Configuration scan csm - Configuration scan (same as sca) svm - Software vulnerability scan sva - Software vulnerability scan (same as svm) sam - Server access management scan fim - File integrity monitoring scan sv - Agent self-verifiation scan Returns: dict: Dictionary describing command created as a result of this \ call Failure throws an exception. """ sanity.validate_object_id(server_id) if self.scan_type_supported(scan_type) is False: exception_message = "Unsupported scan type: %s" % scan_type raise CloudPassageValidation(exception_message) else: scan_type_normalized = self.supported_scans[scan_type] request_body = {"scan": {"module": scan_type_normalized}} endpoint = "/v1/servers/%s/scans" % server_id request = HttpHelper(self.session) response = request.post(endpoint, request_body) command_info = response["command"] return command_info
def build_endpoint_prefix(self): """This constructs everything to the left of the file path in the URL. """ if not sanity.validate_api_hostname(self.api_host): error_message = "Bad API hostname: %s" % self.api_host raise CloudPassageValidation(error_message) prefix = "https://" + self.api_host + ":" + str(self.api_port) return prefix
def parse_status(url, resp_code, resp_text): """Parse status from HTTP response""" success = True exc = None if resp_code not in [200, 201, 202, 204]: success = False bad_statuses = { 400: CloudPassageValidation(resp_text, code=400), 401: CloudPassageAuthentication(resp_text, code=401), 404: CloudPassageResourceExistence(resp_text, code=404, url=url), 403: CloudPassageAuthorization(resp_text, code=403), 422: CloudPassageValidation(resp_text, code=422) } if resp_code in bad_statuses: return (success, bad_statuses[resp_code]) else: return (success, CloudPassageGeneral(resp_text, code=resp_code)) return success, exc
def get_paginated(self, endpoint, key, max_pages, **kwargs): """This method returns a concatenated list of objects from the Halo API. It's really a wrapper for the get() method. Pass in the path as with the get() method, and a maxpages number. Maxpages is expected to be an integer between 2 and 100 Args: endpoint (str): Path for initial query key (str): The key in the response containing the objects \ of interest. For instance, the /v1/events endpoint will \ have the "events" key, which contains a list of dictionary \ objects representing Halo events. maxpages (int): This is a number from 2-100. More than 100 pages \ can take quite a while to return, so beyond that you should \ consider using this SDK as a component in a multi-threaded tool. Keyword Args: params (list of dict): This is a list of dictionary objects, \ represented like this: [{"k1": "two,too"}] \ which goes into the URL looking like this: ?k1=two,too . \ If you use a list as the value in a dictionary here, you'll get \ two k/v pairs represented in the URL and the CloudPassage API \ doesn't operate like that. Only the last instance of that \ variable will be considered, and your results may be confusing. \ So don't do it. Dictionaries should be {str:str}. """ max_pages_valid, pages_invalid_msg = utility.verify_pages(max_pages) if not max_pages_valid: raise CloudPassageValidation(pages_invalid_msg) more_pages = False response_accumulator = [] if "params" in kwargs and kwargs["params"] is not {}: initial_page = self.get(endpoint, params=kwargs["params"]) else: initial_page = self.get(endpoint) response, next_page = self.process_page(initial_page, key) response_accumulator.extend(response) pages_parsed = 1 if next_page is not None: more_pages = True while more_pages: page = self.get(next_page) response, next_page = self.process_page(page, key) response_accumulator.extend(response) pages_parsed += 1 if next_page is None: more_pages = False if pages_parsed >= max_pages: more_pages = False return response_accumulator
def process_page(cls, page, key): """Page goes in, list data comes out.""" response_accumulator = [] next_page = None if key not in page: fail_msg = ("Requested key %s not found in page" % key) raise CloudPassageValidation(fail_msg) for k in page[key]: response_accumulator.append(k) if "pagination" in page: if "next" in page["pagination"]: nextpage = page["pagination"]["next"] endpoint = str( urlparse.urlsplit(nextpage)[2] + "?" + urlparse.urlsplit(nextpage)[3]) next_page = endpoint return response_accumulator, next_page
def raise_validation(self, msg): raise CloudPassageValidation(msg)