diff --git a/README.md b/README.md index 5458ed57..2057a929 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Doing serverless with Terraform? Check out [serverless.tf framework](https://ser - AWS CloudWatch Alarms - AWS CloudWatch LogMetrics Alarms - AWS GuardDuty Findings + - AWS GuardDuty Malware Protection Object Scan Results ## Usage diff --git a/functions/messages/guardduty_malware_protection_object_scan_result.json b/functions/messages/guardduty_malware_protection_object_scan_result.json new file mode 100644 index 00000000..09791b3a --- /dev/null +++ b/functions/messages/guardduty_malware_protection_object_scan_result.json @@ -0,0 +1,20 @@ +{ + "Records": [{ + "EventSource": "aws:sns", + "EventVersion": "1.0", + "EventSubscriptionArn": "arn:aws:sns:us-gov-east-1::ExampleTopic", + "Sns": { + "Type": "Notification", + "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", + "TopicArn": "arn:aws:sns:us-gov-east-1:123456789012:ExampleTopic", + "Subject": "GuardDuty Malware Protection Object Scan Result", + "Message": "{\"version\":\"0\",\"id\":\"72c7d362-737a-6dce-fc78-9e27a0171419\",\"detail-type\":\"GuardDuty Malware Protection Object Scan Result\",\"source\":\"aws.guardduty\",\"account\":\"111122223333\",\"time\":\"2024-02-28T01:01:01Z\",\"region\":\"us-east-1\",\"resources\":[\"arn:aws:guardduty:us-east-1:111122223333:malware-protection-plan/b4c7f464ab3a4EXAMPLE\"],\"detail\":{\"schemaVersion\":\"1.0\",\"scanStatus\":\"COMPLETED\",\"resourceType\":\"S3_OBJECT\",\"s3ObjectDetails\":{\"bucketName\":\"amzn-s3-demo-bucket\",\"objectKey\":\"APKAEIBAERJR2EXAMPLE\",\"eTag\":\"ASIAI44QH8DHBEXAMPLE\",\"versionId\":\"d41d8cd98f00b204e9800998eEXAMPLE\",\"s3Throttled\":false},\"scanResultDetails\":{\"scanResultStatus\":\"THREATS_FOUND\",\"threats\":[{\"name\":\"EICAR-Test-File (not a virus)\"}]}}}", + "Timestamp": "1970-01-01T00:00:00.000Z", + "SignatureVersion": "1", + "Signature": "EXAMPLE", + "SigningCertUrl": "EXAMPLE", + "UnsubscribeUrl": "EXAMPLE", + "MessageAttributes": {} + } + }] +} diff --git a/functions/notify_slack.py b/functions/notify_slack.py index 61595d62..03e6a945 100644 --- a/functions/notify_slack.py +++ b/functions/notify_slack.py @@ -76,6 +76,20 @@ def get_service_url(region: str, service: str) -> str: raise +def get_s3_object_url(region: str, bucket: str, key: str) -> str: + """Get the appropriate S3 object URL for the region + + :param region: name of the AWS region + :param bucket: name of the S3 bucket + :param key: key of the relevant S3 object + :returns: AWS console url formatted for the region and bucket + key provided + """ + if region.startswith("us-gov-"): + return f"https://console.amazonaws-us-gov.com/s3/object/{bucket}?region={region}&prefix={key}" + else: + return f"https://console.aws.amazon.com/s3/object/{bucket}?region={region}&prefix={key}" + + class CloudWatchAlarmState(Enum): """Maps CloudWatch notification state to Slack message format color""" @@ -342,6 +356,59 @@ def format_guardduty_finding(message: Dict[str, Any], region: str) -> Dict[str, } +def format_guardduty_malware_protection_object_scan_result(message: Dict[str, Any], region: str) -> Dict[str, Any]: + """ + Format GuardDuty Malware Protection Object Scan Result into Slack message format + + :params message: SNS message body containing GuardDuty Malware Protection Object Scan Result + :params region: AWS region where the event originated from + :returns: formatted Slack message payload + """ + + detail = message["detail"] + scanResultDetails = detail.get("scanResultDetails") + scanResultStatus = scanResultDetails.get("scanResultStatus") + + s3ObjectDetails = detail.get("s3ObjectDetails") + s3_url = get_s3_object_url(region=region, bucket=s3ObjectDetails["bucketName"], key=s3ObjectDetails["objectKey"]) + + severity = "High" + + if scanResultStatus == "NO_THREATS_FOUND": + severity = "Low" + elif scanResultStatus == "THREATS_FOUND": + severity = "High" + elif scanResultStatus == "UNSUPPORTED": + severity = "Medium" + elif scanResultStatus == "ACCESS_DENIED": + severity = "Medium" + elif scanResultStatus == "FAILED": + severity = "Medium" + + return { + "color": GuardDutyFindingSeverity[severity].value, + "fallback": f"GuardDuty Malware Scan Result: {scanResultStatus}", + "fields": [ + { + "title": "S3 Bucket", + "value": f"`{detail['s3ObjectDetails']['bucketName']}`", + "short": False, + }, + { + "title": "S3 Object", + "value": f"`{detail['s3ObjectDetails']['objectKey']}`", + "short": False, + }, + { + "title": "Link to S3 object", + "value": f"{s3_url}", + "short": False, + }, + ], + "text": f"AWS GuardDuty Malware Scan Result - {scanResultStatus}", + } + + class AwsHealthCategory(Enum): """Maps AWS Health eventTypeCategory to Slack message format color @@ -520,6 +587,8 @@ def parse_notification(message: Dict[str, Any], subject: Optional[str], region: return format_cloudwatch_alarm(message=message, region=region) if isinstance(message, Dict) and message.get("detail-type") == "GuardDuty Finding": return format_guardduty_finding(message=message, region=message["region"]) + if isinstance(message, Dict) and message.get("detail-type") == "GuardDuty Malware Protection Object Scan Result": + return format_guardduty_malware_protection_object_scan_result(message=message, region=message["region"]) if isinstance(message, Dict) and message.get("detail-type") == "Security Hub Findings - Imported": return format_aws_security_hub(message=message, region=message["region"]) if isinstance(message, Dict) and message.get("detail-type") == "AWS Health Event": diff --git a/functions/snapshots/snap_notify_slack_test.py b/functions/snapshots/snap_notify_slack_test.py index a8ec24cf..971a4ff1 100644 --- a/functions/snapshots/snap_notify_slack_test.py +++ b/functions/snapshots/snap_notify_slack_test.py @@ -602,6 +602,38 @@ } ] +snapshots['test_sns_get_slack_message_payload_snapshots message_guardduty_malware_protection_object_scan_result.json'] = [ + { + 'attachments': [ + { + 'color': 'danger', + 'fallback': 'GuardDuty Malware Scan Result: THREATS_FOUND', + 'fields': [ + { + 'short': False, + 'title': 'S3 Bucket', + 'value': '`amzn-s3-demo-bucket`' + }, + { + 'short': False, + 'title': 'S3 Object', + 'value': '`APKAEIBAERJR2EXAMPLE`' + }, + { + 'short': False, + 'title': 'Link to S3 object', + 'value': 'https://console.aws.amazon.com/s3/object/amzn-s3-demo-bucket?region=us-east-1&prefix=APKAEIBAERJR2EXAMPLE' + } + ], + 'text': 'AWS GuardDuty Malware Scan Result - THREATS_FOUND' + } + ], + 'channel': 'slack_testing_sandbox', + 'icon_emoji': ':aws:', + 'username': 'notify_slack_test' + } +] + snapshots['test_sns_get_slack_message_payload_snapshots message_text_message.json'] = [ { 'attachments': [