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..271de55 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - 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/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..26b3bac 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,16 @@ # AWS Inspector Publisher: Splunk -Connector Version: 2.2.11 +Connector Version: 2.2.12 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." @@ -46,8 +46,8 @@ actions within a playbook. For more information, please see the [AWS Identity an documentation](https://docs.aws.amazon.com/iam/index.html) . -### Configuration Variables -The below configuration variables are required for this Connector to operate. These variables are specified when configuring a Inspector asset in SOAR. +### Configuration variables +This table lists the configuration variables required to operate AWS Inspector. These variables are specified when configuring a Inspector asset in Splunk SOAR. VARIABLE | REQUIRED | TYPE | DESCRIPTION -------- | -------- | ---- | ----------- 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..c32132a 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.", - "app_version": "2.2.11", - "utctime_updated": "2022-01-07T21:56:46.000000Z", + "license": "Copyright (c) 2019-2024 Splunk Inc.", + "app_version": "2.2.12", + "utctime_updated": "2024-12-31T10:39:07.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" } ] }, @@ -1271,4 +1259,4 @@ "versions": "EQ(*)" } ] -} +} \ No newline at end of file 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 %}