diff --git a/source/LambdaLayers/sechub_findings.py b/source/LambdaLayers/sechub_findings.py index 629753dc..4e20217c 100644 --- a/source/LambdaLayers/sechub_findings.py +++ b/source/LambdaLayers/sechub_findings.py @@ -8,6 +8,11 @@ from utils import publish_to_sns from awsapi_cached_client import AWSCachedClient from botocore.exceptions import ClientError +from logger import Logger + +# initialise loggers +LOG_LEVEL = os.getenv('log_level', 'info') +LOGGER = Logger(loglevel=LOG_LEVEL) # Get AWS region from Lambda environment. If not present then we're not # running under lambda, so defaulting to us-east-1 @@ -49,6 +54,8 @@ class Finding(object): region = None arn = '' uuid = '' + account_alias = '' + resource = {} def __init__(self, finding_rec): self.region = os.getenv('AWS_DEFAULT_REGION', 'us-east-1') @@ -59,8 +66,8 @@ def __init__(self, finding_rec): self.uuid = self.arn.split ('/finding/')[1] self.generator_id = self.details.get('GeneratorId', 'error') self.account_id = self.details.get('AwsAccountId', 'error') - resource = self.details.get('Resources',[])[0] - self.resource_region = resource.get('Region','error') + self.resource = self.details.get('Resources', [])[0] + self.resource_region = self.resource.get('Region','error') if not self.is_valid_finding_json(): raise InvalidFindingJson @@ -78,7 +85,14 @@ def __init__(self, finding_rec): self.standard_version = '2.0.0' self.standard_name = 'security-control' - + # Lookup the alias for the Account as it's more human readable + try: + accounts = self.aws_api_client.get_connection('organizations').list_accounts()['Accounts'] + self.account_alias = next((x for x in accounts if x['Id'] == self.account_id), self.account_id)['Name'] + except Exception as error: + LOGGER.warning(f'Unable to list account aliases for {self.account_id}') + LOGGER.warning(error) + self.account_alias = self.account_id self._get_security_standard_abbreviation_from_ssm() self._get_control_remap() @@ -231,6 +245,8 @@ class SHARRNotification(object): logdata = [] send_to_sns = False finding_info = {} + state = 'UNKNOWN' + execution_id = None def __init__(self, security_standard, region, controlid=None): """ @@ -268,7 +284,9 @@ def notify(self): sns_notify_json = { 'severity': self.severity, 'message': self.message, - 'finding': self.finding_info + 'finding': self.finding_info, + 'state': self.state, + 'execution_id': self.execution_id } if self.send_to_sns: diff --git a/source/Orchestrator/send_notifications.py b/source/Orchestrator/send_notifications.py index 456db202..40251776 100644 --- a/source/Orchestrator/send_notifications.py +++ b/source/Orchestrator/send_notifications.py @@ -7,6 +7,7 @@ import sechub_findings from logger import Logger from metrics import Metrics +import urllib.parse # Get AWS region from Lambda environment. If not present then we're not # running under lambda, so defaulting to us-east-1 @@ -59,6 +60,8 @@ def lambda_handler(event, _): # SecurityStandard?: string # EventType?: string + LOGGER.info(json.dumps(event)) + message_prefix, message_suffix = set_message_prefix_and_suffix(event) # Get finding status @@ -74,6 +77,25 @@ def lambda_handler(event, _): finding_info = '' if 'Finding' in event: finding = sechub_findings.Finding(event['Finding']) + + resource = (event['Notification'].get('AffectedObject', '').split(' ', 2)[-1] + if 'AffectedObject' in event['Notification'] and isinstance(event['Notification']['AffectedObject'], str) + else finding.resource.get('Id', '').split(':')[-1] + ) + + # Get the Note that the SSM Automation added to the finding + note = None + try: + note = sechub_findings.get_securityhub().get_findings(Filters={'Id':[{'Comparison':'EQUALS','Value':finding.arn}]})['Findings'][0]['Note']['Text'] + except Exception as _error: + LOGGER.info('Unable to retrieve Note from Finding') + note = '' + + # https://us-east-1.console.aws.amazon.com/securityhub/home?region=us-east-1#/findings?search=Id%3D%255Coperator%255C%253AEQUALS%255C%253Aarn%253Aaws%253Asecurityhub%253Aus-east-1%253A166758023262%253Asecurity-control%252FCloudWatch.12%252Ffinding%252F368ddc4f-b603-4076-866a-26d10b2a9e94 + _search = urllib.parse.quote_plus(f'\operator\:EQUALS\:{finding.arn}') + _search = urllib.parse.quote_plus(f'Id={_search}') + link = f'https://console.aws.amazon.com/securityhub/home?region={finding.region}#/findings?search={_search}' + finding_info = { 'finding_id': finding.uuid, 'finding_description': finding.description, @@ -83,7 +105,11 @@ def lambda_handler(event, _): 'title': finding.title, 'region': finding.region, 'account': finding.account_id, - 'finding_arn': finding.arn + 'finding_arn': finding.arn, + 'account_alias': finding.account_alias, + 'link': link, + 'note': note, + 'resource': resource } # Send anonymous metrics @@ -101,6 +127,7 @@ def lambda_handler(event, _): ) notification.severity = 'INFO' notification.send_to_sns = True + notification.state = event['Notification']['State'].upper() elif event['Notification']['State'].upper() == 'FAILED': notification = sechub_findings.SHARRNotification( @@ -110,10 +137,12 @@ def lambda_handler(event, _): ) notification.severity = 'ERROR' notification.send_to_sns = True + notification.state = event['Notification']['State'].upper() elif event['Notification']['State'].upper() in {'WRONGSTANDARD', 'LAMBDAERROR'}: notification = sechub_findings.SHARRNotification('SHARR',AWS_REGION, None) notification.severity = 'ERROR' + notification.state = event['Notification']['State'].upper() else: notification = sechub_findings.SHARRNotification( @@ -125,9 +154,11 @@ def lambda_handler(event, _): if finding: finding.flag(event['Notification']['Message']) - notification.message = message_prefix + event['Notification']['Message'] + message_suffix + notification.execution_id = event['Notification'].get('ExecId', None) + notification.message = event['Notification']['Message'] + message_suffix if 'Details' in event['Notification'] and event['Notification']['Details'] != 'MISSING': notification.logdata = format_details_for_output(event['Notification']['Details']) notification.finding_info = finding_info + LOGGER.info(notification) notification.notify() diff --git a/source/lib/solution_deploy-stack.ts b/source/lib/solution_deploy-stack.ts index 6ce99acd..55da9f1a 100644 --- a/source/lib/solution_deploy-stack.ts +++ b/source/lib/solution_deploy-stack.ts @@ -461,7 +461,7 @@ export class SolutionDeployStack extends cdk.Stack { resources: ['*'], }), new PolicyStatement({ - actions: ['securityhub:BatchUpdateFindings'], + actions: ['securityhub:BatchUpdateFindings', 'securityhub:GetFindings'], resources: ['*'], }), new PolicyStatement({ @@ -476,6 +476,11 @@ export class SolutionDeployStack extends cdk.Stack { actions: ['sns:Publish'], resources: [`arn:${this.partition}:sns:${this.region}:${this.account}:${RESOURCE_PREFIX}-SHARR_Topic`], }), + // Allows conversion of Account Id -> Account Alias (Name) + new PolicyStatement({ + actions: ['organizations:ListAccounts'], + resources: ['*'], + }), ], });