@@ -76,6 +76,20 @@ def get_service_url(region: str, service: str) -> str:
7676 raise
7777
7878
79+ def get_s3_object_url (region : str , bucket : str , key : str ) -> str :
80+ """Get the appropriate S3 object URL for the region
81+
82+ :param region: name of the AWS region
83+ :param bucket: name of the S3 bucket
84+ :param key: key of the relevant S3 object
85+ :returns: AWS console url formatted for the region and bucket + key provided
86+ """
87+ if region .startswith ("us-gov-" ):
88+ return f"https://console.amazonaws-us-gov.com/s3/object/{ bucket } ?region={ region } &prefix={ key } "
89+ else :
90+ return f"https://console.aws.amazon.com/s3/object/{ bucket } ?region={ region } &prefix={ key } "
91+
92+
7993class CloudWatchAlarmState (Enum ):
8094 """Maps CloudWatch notification state to Slack message format color"""
8195
@@ -342,6 +356,59 @@ def format_guardduty_finding(message: Dict[str, Any], region: str) -> Dict[str,
342356 }
343357
344358
359+ def format_guardduty_malware_protection_object_scan_result (message : Dict [str , Any ], region : str ) -> Dict [str , Any ]:
360+ """
361+ Format GuardDuty Malware Protection Object Scan Result into Slack message format
362+
363+ :params message: SNS message body containing GuardDuty Malware Protection Object Scan Result
364+ :params region: AWS region where the event originated from
365+ :returns: formatted Slack message payload
366+ """
367+
368+ detail = message ["detail" ]
369+ scanResultDetails = detail .get ("scanResultDetails" )
370+ scanResultStatus = scanResultDetails .get ("scanResultStatus" )
371+
372+ s3ObjectDetails = detail .get ("s3ObjectDetails" )
373+ s3_url = get_s3_object_url (region = region , bucket = s3ObjectDetails ["bucketName" ], key = s3ObjectDetails ["objectKey" ])
374+
375+ severity = "High"
376+
377+ if scanResultStatus == "NO_THREATS_FOUND" :
378+ severity = "Low"
379+ elif scanResultStatus == "THREATS_FOUND" :
380+ severity = "High"
381+ elif scanResultStatus == "UNSUPPORTED" :
382+ severity = "Medium"
383+ elif scanResultStatus == "ACCESS_DENIED" :
384+ severity = "Medium"
385+ elif scanResultStatus == "FAILED" :
386+ severity = "Medium"
387+
388+ return {
389+ "color" : GuardDutyFindingSeverity [severity ].value ,
390+ "fallback" : f"GuardDuty Malware Scan Result: { scanResultStatus } " ,
391+ "fields" : [
392+ {
393+ "title" : "S3 Bucket" ,
394+ "value" : f"`{ detail ['s3ObjectDetails' ]['bucketName' ]} `" ,
395+ "short" : False ,
396+ },
397+ {
398+ "title" : "S3 Object" ,
399+ "value" : f"`{ detail ['s3ObjectDetails' ]['objectKey' ]} `" ,
400+ "short" : False ,
401+ },
402+ {
403+ "title" : "Link to S3 object" ,
404+ "value" : f"{ s3_url } " ,
405+ "short" : False ,
406+ },
407+ ],
408+ "text" : f"AWS GuardDuty Malware Scan Result - { scanResultStatus } " ,
409+ }
410+
411+
345412class AwsHealthCategory (Enum ):
346413 """Maps AWS Health eventTypeCategory to Slack message format color
347414
@@ -520,6 +587,8 @@ def parse_notification(message: Dict[str, Any], subject: Optional[str], region:
520587 return format_cloudwatch_alarm (message = message , region = region )
521588 if isinstance (message , Dict ) and message .get ("detail-type" ) == "GuardDuty Finding" :
522589 return format_guardduty_finding (message = message , region = message ["region" ])
590+ if isinstance (message , Dict ) and message .get ("detail-type" ) == "GuardDuty Malware Protection Object Scan Result" :
591+ return format_guardduty_malware_protection_object_scan_result (message = message , region = message ["region" ])
523592 if isinstance (message , Dict ) and message .get ("detail-type" ) == "Security Hub Findings - Imported" :
524593 return format_aws_security_hub (message = message , region = message ["region" ])
525594 if isinstance (message , Dict ) and message .get ("detail-type" ) == "AWS Health Event" :
0 commit comments