diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dfadee6..1d049aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,12 @@ repos: - repo: https://github.com/phantomcyber/dev-cicd-tools - rev: v1.16 + rev: v1.24 hooks: - id: org-hook - id: package-app-dependencies + - id: readme-check - repo: https://github.com/Yelp/detect-secrets - rev: v1.4.0 + rev: v1.5.0 hooks: - id: detect-secrets - args: ['--no-verify', '--exclude-files', '^awsinspector.json$'] + args: ['--no-verify', '--exclude-files', '^(awsinspector.json|README.md)$'] diff --git a/LICENSE b/LICENSE index 94b040f..64cc283 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/NOTICE b/NOTICE index 6f4d80e..99fad61 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Splunk SOAR AWS Inspector -Copyright (c) 2019-2023 Splunk Inc. +Copyright (c) 2019-2024 Splunk Inc. Third-party Software Attributions: diff --git a/README.md b/README.md index 2074bac..2cab158 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,12 @@ Connector Version: 2.2.11 Product Vendor: AWS Product Name: Inspector Product Version Supported (regex): ".\*" -Minimum Product Version: 5.2.0 +Minimum Product Version: 6.3.0 This app integrates with AWS Inspector to perform security assessment actions [comment]: # " File: README.md" -[comment]: # " Copyright (c) 2019-2022 Splunk Inc." +[comment]: # " Copyright (c) 2019-2024 Splunk Inc." [comment]: # "" [comment]: # "Licensed under the Apache License, Version 2.0 (the 'License');" [comment]: # "you may not use this file except in compliance with the License." diff --git a/__init__.py b/__init__.py index 44bae34..a21cc51 100644 --- a/__init__.py +++ b/__init__.py @@ -1,6 +1,6 @@ # File: __init__.py # -# Copyright (c) 2019-2023 Splunk Inc. +# Copyright (c) 2019-2024 Splunk Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/awsinspector.json b/awsinspector.json index 49996cd..600b905 100644 --- a/awsinspector.json +++ b/awsinspector.json @@ -9,12 +9,12 @@ "product_name": "Inspector", "product_version_regex": ".*", "publisher": "Splunk", - "license": "Copyright (c) 2019-2023 Splunk Inc.", + "license": "Copyright (c) 2019-2024 Splunk Inc.", "app_version": "2.2.11", "utctime_updated": "2022-01-07T21:56:46.000000Z", "package_name": "phantom_awsinspector", "main_module": "awsinspector_connector.py", - "min_phantom_version": "5.2.0", + "min_phantom_version": "6.3.0", "fips_compliant": true, "latest_tested_versions": [ "Cloud tested on July 29th, 2021" @@ -35,21 +35,9 @@ "module": "jmespath", "input_file": "wheels/shared/jmespath-0.10.0-py2.py3-none-any.whl" }, - { - "module": "python_dateutil", - "input_file": "wheels/shared/python_dateutil-2.8.1-py2.py3-none-any.whl" - }, { "module": "s3transfer", "input_file": "wheels/shared/s3transfer-0.3.7-py2.py3-none-any.whl" - }, - { - "module": "six", - "input_file": "wheels/shared/six-1.16.0-py2.py3-none-any.whl" - }, - { - "module": "urllib3", - "input_file": "wheels/shared/urllib3-1.26.18-py2.py3-none-any.whl" } ] }, diff --git a/awsinspector_connector.py b/awsinspector_connector.py index 8e119fc..34fa8a5 100644 --- a/awsinspector_connector.py +++ b/awsinspector_connector.py @@ -1,6 +1,6 @@ # File: awsinspector_connector.py # -# Copyright (c) 2019-2023 Splunk Inc. +# Copyright (c) 2019-2024 Splunk Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import sys import phantom.app as phantom + # Usage of the consts file is recommended import requests from boto3 import Session, client @@ -60,7 +61,7 @@ def __init__(self): self._base_url = None def initialize(self): - """ This is an optional function that can be implemented by the AppConnector derived class. Since the + """This is an optional function that can be implemented by the AppConnector derived class. Since the configuration dictionary is already validated by the time this function is called, it's a good place to do any extra initialization of any internal modules. This function MUST return a value of either phantom.APP_SUCCESS. """ @@ -72,13 +73,13 @@ def initialize(self): self._region = AWSINSPECTOR_REGION_DICT.get(config[AWSINSPECTOR_JSON_REGION]) self._proxy = {} - env_vars = config.get('_reserved_environment_variables', {}) - if 'HTTP_PROXY' in env_vars: - self._proxy['http'] = env_vars['HTTP_PROXY']['value'] - if 'HTTPS_PROXY' in env_vars: - self._proxy['https'] = env_vars['HTTPS_PROXY']['value'] + env_vars = config.get("_reserved_environment_variables", {}) + if "HTTP_PROXY" in env_vars: + self._proxy["http"] = env_vars["HTTP_PROXY"]["value"] + if "HTTPS_PROXY" in env_vars: + self._proxy["https"] = env_vars["HTTPS_PROXY"]["value"] - if config.get('use_role'): + if config.get("use_role"): credentials = self._handle_get_ec2_role() if not credentials: return self.set_status(phantom.APP_ERROR, AWSINSPECTOR_ROLE_CREDENTIALS_FAILURE_MSG) @@ -88,8 +89,8 @@ def initialize(self): return phantom.APP_SUCCESS - self._access_key = config.get('access_key') - self._secret_key = config.get('secret_key') + self._access_key = config.get("access_key") + self._secret_key = config.get("secret_key") if not (self._access_key and self._secret_key): return self.set_status(phantom.APP_ERROR, AWSINSPECTOR_BAD_ASSET_CONFIG_MSG) @@ -97,7 +98,7 @@ def initialize(self): return phantom.APP_SUCCESS def finalize(self): - """ This function gets called once all the param dictionary elements are looped over and no more handle_action + """This function gets called once all the param dictionary elements are looped over and no more handle_action calls are left to be made. It gives the AppConnector a chance to loop through all the results that were accumulated by multiple handle_action function calls and create any summary if required. Another usage is cleanup, disconnect from remote devices etc. @@ -127,12 +128,12 @@ def _create_client(self, action_result, param=None): # Try getting and using temporary assume role credentials from parameters temp_credentials = dict() - if param and 'credentials' in param: + if param and "credentials" in param: try: - temp_credentials = ast.literal_eval(param['credentials']) - self._access_key = temp_credentials.get('AccessKeyId', '') - self._secret_key = temp_credentials.get('SecretAccessKey', '') - self._session_token = temp_credentials.get('SessionToken', '') + temp_credentials = ast.literal_eval(param["credentials"]) + self._access_key = temp_credentials.get("AccessKeyId", "") + self._secret_key = temp_credentials.get("SecretAccessKey", "") + self._session_token = temp_credentials.get("SessionToken", "") self.save_progress("Using temporary assume role credentials for action") except Exception as e: @@ -143,18 +144,16 @@ def _create_client(self, action_result, param=None): self.debug_print("Creating boto3 client with API keys") self._client = client( - 'inspector', - region_name=self._region, - aws_access_key_id=self._access_key, - aws_secret_access_key=self._secret_key, - aws_session_token=self._session_token, - config=boto_config) + "inspector", + region_name=self._region, + aws_access_key_id=self._access_key, + aws_secret_access_key=self._secret_key, + aws_session_token=self._session_token, + config=boto_config, + ) else: self.debug_print("Creating boto3 client without API keys") - self._client = client( - 'inspector', - region_name=self._region, - config=boto_config) + self._client = client("inspector", region_name=self._region, config=boto_config) except Exception as e: return action_result.set_status(phantom.APP_ERROR, "Could not create boto3 client: {0}".format(e)) @@ -177,12 +176,12 @@ def _make_boto_call(self, action_result, method, **kwargs): try: resp_json = boto_func(**kwargs) except Exception as e: - return RetVal(action_result.set_status(phantom.APP_ERROR, 'boto3 call to Inspector failed', e), None) + return RetVal(action_result.set_status(phantom.APP_ERROR, "boto3 call to Inspector failed", e), None) return phantom.APP_SUCCESS, resp_json def _handle_test_connectivity(self, param): - """ This function is used to handle the test connectivity action. + """This function is used to handle the test connectivity action. :param param: Dictionary of input parameters :return: Status(phantom.APP_SUCCESS/phantom.APP_ERROR) @@ -194,7 +193,7 @@ def _handle_test_connectivity(self, param): if phantom.is_fail(self._create_client(action_result, param)): return action_result.get_status() - ret_val, response = self._make_boto_call(action_result, 'list_assessment_targets', maxResults=1) + ret_val, response = self._make_boto_call(action_result, "list_assessment_targets", maxResults=1) if phantom.is_fail(ret_val): self.save_progress("Test Connectivity Failed") @@ -204,7 +203,7 @@ def _handle_test_connectivity(self, param): return action_result.set_status(phantom.APP_SUCCESS) def _handle_list_targets(self, param): - """ This function is used to fetch targets of specific AWS account. + """This function is used to fetch targets of specific AWS account. :param param: Dictionary of input parameters :return: list of targets and their info for the specific AWS account @@ -217,40 +216,42 @@ def _handle_list_targets(self, param): if phantom.is_fail(self._create_client(action_result, param)): return action_result.get_status() - target_name = param.get('target_name') - limit = param.get('limit') + target_name = param.get("target_name") + limit = param.get("limit") if (limit and not str(limit).isdigit()) or limit == 0: return action_result.set_status(phantom.APP_ERROR, AWSINSPECTOR_INVALID_LIMIT) filter = {} if target_name: - filter['assessmentTargetNamePattern'] = target_name + filter["assessmentTargetNamePattern"] = target_name kwargs = {} - kwargs['filter'] = filter + kwargs["filter"] = filter - list_targets = self._paginator('list_assessment_targets', limit, action_result, **kwargs) + list_targets = self._paginator("list_assessment_targets", limit, action_result, **kwargs) if list_targets is None: - return action_result.get_status() + return action_result.get_status() tz = tzlocal() self.debug_print(tz) for target in list_targets: - ret_val, res = self._make_boto_call(action_result, 'describe_assessment_targets', assessmentTargetArns=[target]) + ret_val, res = self._make_boto_call(action_result, "describe_assessment_targets", assessmentTargetArns=[target]) if phantom.is_fail(ret_val): return action_result.get_status() - if res.get('assessmentTargets'): - assessment_target = res.get('assessmentTargets')[0] + if res.get("assessmentTargets"): + assessment_target = res.get("assessmentTargets")[0] else: - failure_code = res.get('failedItems', {}).get(target, {}).get('failureCode') - error_message = failure_code if failure_code else 'Unknown error' - return action_result.set_status(phantom.APP_ERROR, - 'Error occurred while fetching the details of the assessment target: {0}. Error: {1}'.format(target, error_message)) + failure_code = res.get("failedItems", {}).get(target, {}).get("failureCode") + error_message = failure_code if failure_code else "Unknown error" + return action_result.set_status( + phantom.APP_ERROR, + "Error occurred while fetching the details of the assessment target: {0}. Error: {1}".format(target, error_message), + ) for key, value in list(assessment_target.items()): if isinstance(value, datetime.datetime): @@ -259,12 +260,12 @@ def _handle_list_targets(self, param): action_result.add_data(assessment_target) summary = action_result.update_summary({}) - summary['total_targets'] = action_result.get_data_size() + summary["total_targets"] = action_result.get_data_size() return action_result.set_status(phantom.APP_SUCCESS) def _handle_list_templates(self, param): - """ This function is used to fetch templates that correspond to the assessment targets. + """This function is used to fetch templates that correspond to the assessment targets. :param param: Dictionary of input parameters :return: list of templates and their info for the specific AWS account @@ -277,13 +278,13 @@ def _handle_list_templates(self, param): if phantom.is_fail(self._create_client(action_result, param)): return action_result.get_status() - target_arns = param.get('target_arns') - if param.get('target_arns'): - target_arns = [target_arn.strip() for target_arn in target_arns.split(',')] - target_arns = ' '.join(target_arns).split() + target_arns = param.get("target_arns") + if param.get("target_arns"): + target_arns = [target_arn.strip() for target_arn in target_arns.split(",")] + target_arns = " ".join(target_arns).split() - template_name = param.get('template_name') - limit = param.get('limit') + template_name = param.get("template_name") + limit = param.get("limit") if (limit and not str(limit).isdigit()) or limit == 0: return action_result.set_status(phantom.APP_ERROR, AWSINSPECTOR_INVALID_LIMIT) @@ -292,27 +293,29 @@ def _handle_list_templates(self, param): kwargs = {} if target_arns: - kwargs['assessmentTargetArns'] = target_arns + kwargs["assessmentTargetArns"] = target_arns if template_name: - filter['namePattern'] = template_name - kwargs['filter'] = filter + filter["namePattern"] = template_name + kwargs["filter"] = filter - list_templates = self._paginator('list_assessment_templates', limit, action_result, **kwargs) + list_templates = self._paginator("list_assessment_templates", limit, action_result, **kwargs) if list_templates is None: - return action_result.get_status() + return action_result.get_status() for template in list_templates: - ret_val, res = self._make_boto_call(action_result, 'describe_assessment_templates', assessmentTemplateArns=[template]) + ret_val, res = self._make_boto_call(action_result, "describe_assessment_templates", assessmentTemplateArns=[template]) - if res.get('assessmentTemplates'): - assessment_template = res.get('assessmentTemplates')[0] + if res.get("assessmentTemplates"): + assessment_template = res.get("assessmentTemplates")[0] else: - failure_code = res.get('failedItems', {}).get(template, {}).get('failureCode') - error_message = failure_code if failure_code else 'Unknown error' - return action_result.set_status(phantom.APP_ERROR, - 'Error occurred while fetching the details of the assessment template: {0}. Error: {1}'.format(template, error_message)) + failure_code = res.get("failedItems", {}).get(template, {}).get("failureCode") + error_message = failure_code if failure_code else "Unknown error" + return action_result.set_status( + phantom.APP_ERROR, + "Error occurred while fetching the details of the assessment template: {0}. Error: {1}".format(template, error_message), + ) for key, value in list(assessment_template.items()): if isinstance(value, datetime.datetime): @@ -324,13 +327,13 @@ def _handle_list_templates(self, param): action_result.add_data(assessment_template) summary = action_result.update_summary({}) - summary['total_templates'] = action_result.get_data_size() + summary["total_templates"] = action_result.get_data_size() self.save_progress("Handle list templates succeeded") return action_result.set_status(phantom.APP_SUCCESS) def _handle_add_target(self, param): - """ This function is used to create a new assessment target to the specific AWS account. + """This function is used to create a new assessment target to the specific AWS account. :param param: Dictionary of input parameters :return: ARN of the assessment target that is created by this action. @@ -343,35 +346,35 @@ def _handle_add_target(self, param): if phantom.is_fail(self._create_client(action_result, param)): return action_result.get_status() - target_name = param['target_name'] - resource_group_arn = param.get('resource_group_arn') + target_name = param["target_name"] + resource_group_arn = param.get("resource_group_arn") kwargs = {} - kwargs['assessmentTargetName'] = target_name + kwargs["assessmentTargetName"] = target_name - if param.get('resource_group_arn'): - kwargs['resourceGroupArn'] = resource_group_arn + if param.get("resource_group_arn"): + kwargs["resourceGroupArn"] = resource_group_arn - ret_val, response = self._make_boto_call(action_result, 'create_assessment_target', **kwargs) + ret_val, response = self._make_boto_call(action_result, "create_assessment_target", **kwargs) if phantom.is_fail(ret_val): return action_result.get_status() try: - del response['ResponseMetadata'] + del response["ResponseMetadata"] except: pass action_result.add_data(response) summary = action_result.update_summary({}) - summary['total_target_arn'] = action_result.get_data_size() + summary["total_target_arn"] = action_result.get_data_size() self.save_progress("Handle add target succeeded") return action_result.set_status(phantom.APP_SUCCESS, "Target successfully added") def _handle_run_assessment(self, param): - """ This function is used to start the assessment run specified by the ARN of the assessment template. + """This function is used to start the assessment run specified by the ARN of the assessment template. :param param: Dictionary of input parameters :return: ARN of the assessment run and their information @@ -384,37 +387,39 @@ def _handle_run_assessment(self, param): if phantom.is_fail(self._create_client(action_result, param)): return action_result.get_status() - template_arn = param['template_arn'] - assessment_run_name = param.get('assessment_run_name') + template_arn = param["template_arn"] + assessment_run_name = param.get("assessment_run_name") kwargs = {} - kwargs['assessmentTemplateArn'] = template_arn + kwargs["assessmentTemplateArn"] = template_arn - if param.get('assessment_run_name'): - kwargs['assessmentRunName'] = assessment_run_name + if param.get("assessment_run_name"): + kwargs["assessmentRunName"] = assessment_run_name - ret, assessment_run = self._make_boto_call(action_result, 'start_assessment_run', **kwargs) + ret, assessment_run = self._make_boto_call(action_result, "start_assessment_run", **kwargs) if phantom.is_fail(ret): return action_result.get_status() - assessment_run_arn = assessment_run.get('assessmentRunArn') + assessment_run_arn = assessment_run.get("assessmentRunArn") if not assessment_run_arn: - return action_result.get_status() + return action_result.get_status() # for arn in assessment_run_arns: - ret_val, res = self._make_boto_call(action_result, 'describe_assessment_runs', assessmentRunArns=[assessment_run_arn]) + ret_val, res = self._make_boto_call(action_result, "describe_assessment_runs", assessmentRunArns=[assessment_run_arn]) if phantom.is_fail(ret_val): return action_result.get_status() - if res.get('assessmentRuns'): - assessment_run = res.get('assessmentRuns')[0] + if res.get("assessmentRuns"): + assessment_run = res.get("assessmentRuns")[0] else: - failure_code = res.get('failedItems', {}).get(assessment_run_arn, {}).get('failureCode') - error_message = failure_code if failure_code else 'Unknown error' - return action_result.set_status(phantom.APP_ERROR, - 'Error occurred while fetching the details of the assessment run: {0}. Error: {1}'.format(assessment_run_arn, error_message)) + failure_code = res.get("failedItems", {}).get(assessment_run_arn, {}).get("failureCode") + error_message = failure_code if failure_code else "Unknown error" + return action_result.set_status( + phantom.APP_ERROR, + "Error occurred while fetching the details of the assessment run: {0}. Error: {1}".format(assessment_run_arn, error_message), + ) for key, value in list(assessment_run.items()): if isinstance(value, datetime.datetime): @@ -427,20 +432,20 @@ def _handle_run_assessment(self, param): val[k1] = str(v1) try: - del res['ResponseMetadata'] + del res["ResponseMetadata"] except: pass action_result.add_data(assessment_run) summary = action_result.update_summary({}) - summary['assessment_run_arn'] = assessment_run_arn + summary["assessment_run_arn"] = assessment_run_arn self.save_progress("Handle run assessment succeeded") return action_result.set_status(phantom.APP_SUCCESS) def _handle_get_findings(self, param): - """ This function is used to fetch findings that are generated by the assessment runs. + """This function is used to fetch findings that are generated by the assessment runs. :param param: Dictionary of input parameters :return: list of findings and their information @@ -455,64 +460,64 @@ def _handle_get_findings(self, param): filter = {} - assessment_run_arns = param.get('assessment_run_arns') + assessment_run_arns = param.get("assessment_run_arns") - if param.get('assessment_run_arns'): - assessment_run_arns = [assessment_run_arn.strip() for assessment_run_arn in assessment_run_arns.split(',')] - assessment_run_arns = ' '.join(assessment_run_arns).split() + if param.get("assessment_run_arns"): + assessment_run_arns = [assessment_run_arn.strip() for assessment_run_arn in assessment_run_arns.split(",")] + assessment_run_arns = " ".join(assessment_run_arns).split() - severities = param.get('severities') - if param.get('severities'): - severities = [severity.strip() for severity in severities.split(',')] - severities = ' '.join(severities).split() - filter.update({ - 'severities': severities - }) + severities = param.get("severities") + if param.get("severities"): + severities = [severity.strip() for severity in severities.split(",")] + severities = " ".join(severities).split() + filter.update({"severities": severities}) - limit = param.get('limit') + limit = param.get("limit") if (limit and not str(limit).isdigit()) or limit == 0: return action_result.set_status(phantom.APP_ERROR, AWSINSPECTOR_INVALID_LIMIT) kwargs = {} - if param.get('assessment_run_arns'): - kwargs['assessmentRunArns'] = assessment_run_arns - kwargs['filter'] = filter + if param.get("assessment_run_arns"): + kwargs["assessmentRunArns"] = assessment_run_arns + kwargs["filter"] = filter - list_findings = self._paginator('list_findings', limit, action_result, **kwargs) + list_findings = self._paginator("list_findings", limit, action_result, **kwargs) if list_findings is None: - return action_result.get_status() + return action_result.get_status() while list_findings: - ret_val, res = self._make_boto_call(action_result, 'describe_findings', findingArns=list_findings[:min(10, len(list_findings))]) + ret_val, res = self._make_boto_call(action_result, "describe_findings", findingArns=list_findings[: min(10, len(list_findings))]) if phantom.is_fail(ret_val): return action_result.get_status() - findings = res.get('findings') + findings = res.get("findings") if findings: for finding in findings: for key, value in list(finding.items()): if isinstance(value, datetime.datetime): finding[key] = str(value) else: - return action_result.set_status(phantom.APP_ERROR, - 'Error occurred while fetching the details of the findings: {0}'.format(str(list_findings[:min(10, len(list_findings))]))) + return action_result.set_status( + phantom.APP_ERROR, + "Error occurred while fetching the details of the findings: {0}".format(str(list_findings[: min(10, len(list_findings))])), + ) for finding_detail in findings: action_result.add_data(finding_detail) - del list_findings[:min(10, len(list_findings))] + del list_findings[: min(10, len(list_findings))] summary = action_result.update_summary({}) - summary['total_findings'] = action_result.get_data_size() + summary["total_findings"] = action_result.get_data_size() self.save_progress("Handle get findings succeeded") return action_result.set_status(phantom.APP_SUCCESS) def _handle_delete_target(self, param): - """ This function is used to delete the existing assessment target from the specific AWS account. + """This function is used to delete the existing assessment target from the specific AWS account. :param param: Dictionary of input parameters :return: Status (phantom.APP_ERROR/phantom.APP_SUCCESS), target is deleted successfully @@ -525,18 +530,18 @@ def _handle_delete_target(self, param): if phantom.is_fail(self._create_client(action_result, param)): return action_result.get_status() - target_arn = param['target_arn'] + target_arn = param["target_arn"] kwargs = {} - kwargs['assessmentTargetArn'] = target_arn + kwargs["assessmentTargetArn"] = target_arn - ret_val, response = self._make_boto_call(action_result, 'delete_assessment_target', **kwargs) + ret_val, response = self._make_boto_call(action_result, "delete_assessment_target", **kwargs) if phantom.is_fail(ret_val): return action_result.get_status() try: - del response['ResponseMetadata'] + del response["ResponseMetadata"] except: pass @@ -557,21 +562,19 @@ def _paginator(self, method_name, limit, action_result, **kwargs): list_items = list() next_token = None dic_map = { - 'list_targets': 'assessmentTargetArns', - 'list_templates': 'assessmentTemplateArns', - 'get_findings': 'findingArns', - 'delete_target': 'assessmentTargetArns' + "list_targets": "assessmentTargetArns", + "list_templates": "assessmentTemplateArns", + "get_findings": "findingArns", + "delete_target": "assessmentTargetArns", } set_name = dic_map.get(self.get_action_identifier()) while True: if next_token: - ret_val, response = self._make_boto_call(action_result, - method_name, - nextToken=next_token, - maxResults=AWSINSPECTOR_MAX_PER_PAGE_LIMIT, - **kwargs) + ret_val, response = self._make_boto_call( + action_result, method_name, nextToken=next_token, maxResults=AWSINSPECTOR_MAX_PER_PAGE_LIMIT, **kwargs + ) else: ret_val, response = self._make_boto_call(action_result, method_name, maxResults=AWSINSPECTOR_MAX_PER_PAGE_LIMIT, **kwargs) @@ -584,14 +587,14 @@ def _paginator(self, method_name, limit, action_result, **kwargs): if limit and len(list_items) >= limit: return list_items[:limit] - next_token = response.get('nextToken') + next_token = response.get("nextToken") if not next_token: break return list_items def handle_action(self, param): - """ This function gets current action identifier and calls member function of its own to handle the action. + """This function gets current action identifier and calls member function of its own to handle the action. :param param: Dictionary which contains information about the actions to be executed :return: Status(phantom.APP_SUCCESS/phantom.APP_ERROR) @@ -599,13 +602,13 @@ def handle_action(self, param): self.debug_print("action_id", self.get_action_identifier()) # Dictionary mapping each action with its corresponding actions action_mapping = { - 'test_connectivity': self._handle_test_connectivity, - 'list_targets': self._handle_list_targets, - 'list_templates': self._handle_list_templates, - 'add_target': self._handle_add_target, - 'delete_target': self._handle_delete_target, - 'run_assessment': self._handle_run_assessment, - 'get_findings': self._handle_get_findings + "test_connectivity": self._handle_test_connectivity, + "list_targets": self._handle_list_targets, + "list_templates": self._handle_list_templates, + "add_target": self._handle_add_target, + "delete_target": self._handle_delete_target, + "run_assessment": self._handle_run_assessment, + "get_findings": self._handle_get_findings, } action = self.get_action_identifier() @@ -618,7 +621,7 @@ def handle_action(self, param): return action_execution_status -if __name__ == '__main__': +if __name__ == "__main__": import argparse @@ -628,10 +631,10 @@ def handle_action(self, param): argparser = argparse.ArgumentParser() - argparser.add_argument('input_test_json', help='Input Test JSON file') - argparser.add_argument('-u', '--username', help='username', required=False) - argparser.add_argument('-p', '--password', help='password', required=False) - argparser.add_argument('-v', '--verify', action='store_true', help='verify', required=False, default=False) + argparser.add_argument("input_test_json", help="Input Test JSON file") + argparser.add_argument("-u", "--username", help="username", required=False) + argparser.add_argument("-p", "--password", help="password", required=False) + argparser.add_argument("-v", "--verify", action="store_true", help="verify", required=False, default=False) args = argparser.parse_args() session_id = None @@ -640,31 +643,32 @@ def handle_action(self, param): password = args.password verify = args.verify - if (username is not None and password is None): + if username is not None and password is None: # User specified a username but not a password, so ask import getpass + password = getpass.getpass("Password: ") - if (username and password): + if username and password: login_url = BaseConnector._get_phantom_base_url() + "login" try: print("Accessing the Login page") response = requests.get(login_url, verify=verify, timeout=AWSINSPECTOR_DEFAULT_TIMEOUT) - csrftoken = response.cookies['csrftoken'] + csrftoken = response.cookies["csrftoken"] data = dict() - data['username'] = username - data['password'] = password - data['csrfmiddlewaretoken'] = csrftoken + data["username"] = username + data["password"] = password + data["csrfmiddlewaretoken"] = csrftoken headers = dict() - headers['Cookie'] = 'csrftoken={0}'.format(csrftoken) - headers['Referer'] = login_url + headers["Cookie"] = "csrftoken={0}".format(csrftoken) + headers["Referer"] = login_url print("Logging into Platform to get the session id") r2 = requests.post(login_url, verify=verify, data=data, headers=headers, timeout=AWSINSPECTOR_DEFAULT_TIMEOUT) - session_id = r2.cookies['sessionid'] + session_id = r2.cookies["sessionid"] except Exception as e: print("Unable to get session id from the platform. Error: {0}".format(str(e))) sys.exit(1) @@ -677,9 +681,9 @@ def handle_action(self, param): connector = AwsInspectorConnector() connector.print_progress_message = True - if (session_id is not None): - in_json['user_session_token'] = session_id - connector._set_csrf_info(csrftoken, headers['Referer']) + if session_id is not None: + in_json["user_session_token"] = session_id + connector._set_csrf_info(csrftoken, headers["Referer"]) ret_val = connector._handle_action(json.dumps(in_json), None) print(json.dumps(json.loads(ret_val), indent=4)) diff --git a/awsinspector_consts.py b/awsinspector_consts.py index 6dc0022..38973a0 100644 --- a/awsinspector_consts.py +++ b/awsinspector_consts.py @@ -1,6 +1,6 @@ # File: awsinspector_consts.py # -# Copyright (c) 2019-2023 Splunk Inc. +# Copyright (c) 2019-2024 Splunk Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,23 +15,23 @@ # # # Define your constants here -AWSINSPECTOR_INVALID_LIMIT = 'Please provide non-zero positive integer in limit' +AWSINSPECTOR_INVALID_LIMIT = "Please provide non-zero positive integer in limit" AWSINSPECTOR_MAX_PER_PAGE_LIMIT = 500 AWSINSPECTOR_JSON_REGION = "region" AWSINSPECTOR_REGION_DICT = { - "US East (N. Virginia)": "us-east-1", - "US East (Ohio)": "us-east-2", - "US West (N. California)": "us-west-1", - "US West (Oregon)": "us-west-2", - "Asia Pacific (Mumbai)": "ap-south-1", - "Asia Pacific (Seoul)": "ap-northeast-2", - "Asia Pacific (Singapore)": "ap-southeast-1", - "Asia Pacific (Sydney)": "ap-southeast-2", - "Asia Pacific (Tokyo)": "ap-northeast-1", - "EU (Frankfurt)": "eu-central-1", - "EU (Ireland)": "eu-west-1", - "EU (London)": "eu-west-2", - } -AWSINSPECTOR_BAD_ASSET_CONFIG_MSG = 'Please provide access keys or select assume role check box in asset configuration' -AWSINSPECTOR_ROLE_CREDENTIALS_FAILURE_MSG = 'Failed to retrieve EC2 role credentials from instance' + "US East (N. Virginia)": "us-east-1", + "US East (Ohio)": "us-east-2", + "US West (N. California)": "us-west-1", + "US West (Oregon)": "us-west-2", + "Asia Pacific (Mumbai)": "ap-south-1", + "Asia Pacific (Seoul)": "ap-northeast-2", + "Asia Pacific (Singapore)": "ap-southeast-1", + "Asia Pacific (Sydney)": "ap-southeast-2", + "Asia Pacific (Tokyo)": "ap-northeast-1", + "EU (Frankfurt)": "eu-central-1", + "EU (Ireland)": "eu-west-1", + "EU (London)": "eu-west-2", +} +AWSINSPECTOR_BAD_ASSET_CONFIG_MSG = "Please provide access keys or select assume role check box in asset configuration" +AWSINSPECTOR_ROLE_CREDENTIALS_FAILURE_MSG = "Failed to retrieve EC2 role credentials from instance" AWSINSPECTOR_DEFAULT_TIMEOUT = 30 diff --git a/awsinspector_get_findings.html b/awsinspector_get_findings.html index f92bc8f..12ab695 100644 --- a/awsinspector_get_findings.html +++ b/awsinspector_get_findings.html @@ -10,7 +10,7 @@ {% block widget_content %}