Skip to content

Commit 1013c35

Browse files
authored
feat: Add support for GuardDuty Malware Protection Object Scan Result events (#262)
1 parent a3b0392 commit 1013c35

File tree

4 files changed

+122
-0
lines changed

4 files changed

+122
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Doing serverless with Terraform? Check out [serverless.tf framework](https://ser
1717
- AWS CloudWatch Alarms
1818
- AWS CloudWatch LogMetrics Alarms
1919
- AWS GuardDuty Findings
20+
- AWS GuardDuty Malware Protection Object Scan Results
2021

2122
## Usage
2223

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"Records": [{
3+
"EventSource": "aws:sns",
4+
"EventVersion": "1.0",
5+
"EventSubscriptionArn": "arn:aws:sns:us-gov-east-1::ExampleTopic",
6+
"Sns": {
7+
"Type": "Notification",
8+
"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
9+
"TopicArn": "arn:aws:sns:us-gov-east-1:123456789012:ExampleTopic",
10+
"Subject": "GuardDuty Malware Protection Object Scan Result",
11+
"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)\"}]}}}",
12+
"Timestamp": "1970-01-01T00:00:00.000Z",
13+
"SignatureVersion": "1",
14+
"Signature": "EXAMPLE",
15+
"SigningCertUrl": "EXAMPLE",
16+
"UnsubscribeUrl": "EXAMPLE",
17+
"MessageAttributes": {}
18+
}
19+
}]
20+
}

functions/notify_slack.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
7993
class 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+
345412
class 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":

functions/snapshots/snap_notify_slack_test.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,38 @@
602602
}
603603
]
604604

605+
snapshots['test_sns_get_slack_message_payload_snapshots message_guardduty_malware_protection_object_scan_result.json'] = [
606+
{
607+
'attachments': [
608+
{
609+
'color': 'danger',
610+
'fallback': 'GuardDuty Malware Scan Result: THREATS_FOUND',
611+
'fields': [
612+
{
613+
'short': False,
614+
'title': 'S3 Bucket',
615+
'value': '`amzn-s3-demo-bucket`'
616+
},
617+
{
618+
'short': False,
619+
'title': 'S3 Object',
620+
'value': '`APKAEIBAERJR2EXAMPLE`'
621+
},
622+
{
623+
'short': False,
624+
'title': 'Link to S3 object',
625+
'value': 'https://console.aws.amazon.com/s3/object/amzn-s3-demo-bucket?region=us-east-1&prefix=APKAEIBAERJR2EXAMPLE'
626+
}
627+
],
628+
'text': 'AWS GuardDuty Malware Scan Result - THREATS_FOUND'
629+
}
630+
],
631+
'channel': 'slack_testing_sandbox',
632+
'icon_emoji': ':aws:',
633+
'username': 'notify_slack_test'
634+
}
635+
]
636+
605637
snapshots['test_sns_get_slack_message_payload_snapshots message_text_message.json'] = [
606638
{
607639
'attachments': [

0 commit comments

Comments
 (0)