Skip to content

Commit

Permalink
fix: Improve the SHARR notification messages to include more relevant…
Browse files Browse the repository at this point in the history
… info for administrators receiving the events aws-solutions#185
  • Loading branch information
thesuavehog committed Dec 13, 2023
1 parent 5244c64 commit b42c4ca
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 7 deletions.
26 changes: 22 additions & 4 deletions source/LambdaLayers/sechub_findings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
Expand All @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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:
Expand Down
35 changes: 33 additions & 2 deletions source/Orchestrator/send_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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()
7 changes: 6 additions & 1 deletion source/lib/solution_deploy-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ export class SolutionDeployStack extends cdk.Stack {
resources: ['*'],
}),
new PolicyStatement({
actions: ['securityhub:BatchUpdateFindings'],
actions: ['securityhub:BatchUpdateFindings', 'securityhub:GetFindings'],
resources: ['*'],
}),
new PolicyStatement({
Expand All @@ -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: ['*'],
}),
],
});

Expand Down

0 comments on commit b42c4ca

Please sign in to comment.