AWS - GuardDuty

Amazon Web Services Guard Duty Service (gd).

IT Services · AWS - GuardDuty

Details

IDAWS - GuardDuty
ProviderAmazon
CategoryIT Services
From Version5.0.0
Docker Imagedemisto/boto3py3:1.0.0.10221838
Supported ModulesAgentix XSIAM

README

Amazon Web Services Guard Duty Service (gd)
Use this integration to detect and manage threats to your AWS system. We recommend that you use roles that have the following
built-in AWS policies:

  • AmazonGuardDutyFullAccess
  • AmazonGuardDutyReadOnlyAccess

For detailed instructions about setting up authentication, see: AWS Integrations - Authentication.

Prerequisites

It is important that you familiarize yourself with and complete all steps detailed in
the Amazon AWS Integrations Configuration Guide

Some changes have been made that might affect your existing content.
If you are upgrading from a previous of this integration, see Breaking Changes.

Configure AWS - GuardDuty in Cortex

Parameter Description Required
AWS Default Region The AWS Region for this instance of the integration. For example, us-west-2 True
Role Arn The Amazon Resource Name (ARN) role used for EC2 instance authentication. If this is used, an access key and secret key are not required. False
Fetch incidents   False
Incident type Incident type False
Role Session Name A descriptive name for the assumed role session. For example, xsiam-IAM.integration-Role_SESSION False
Role Session Duration The maximum length of each session in seconds. Default: 900 seconds. The Cortex XSOAR integration will have the permissions assigned only when the session is initiated and for the defined duration. False
Access Key The access key ID used for authentication, that was configured during IAM user configuration. If this is used, Role ARN is not required. False
Secret Key The secret key used for authentication, that was configured during IAM user configuration. If this is used, Role ARN is not required. False
Timeout The time in seconds until a timeout exception is reached. You can specify just the read timeout (for example 60) or also the connect timeout followed after a comma (for example 60,10). If a connect timeout is not specified, a default of 10 second will be used. False
Retries The maximum number of retry attempts when connection or throttling errors are encountered. Set to 0 to disable retries. The default value is 5 and the limit is 10. Note: Increasing the number of retries will increase the execution time. False
How many incidents to fetch each time Default 10 False
First fetch timestamp First fetch query <number> <time unit>, e.g., 7 days. Default 3 days False
Guard Duty Severity level The severity level or higher of findings to be fetched: Low, Medium, or High. For example, if you set the severity level to Medium, only findings with severity level Medium or High will be fetched. False
Archive findings After Fetch You can set whether findings that are fetched will be moved to the GuardDuty archive. False
Incidents Fetch Interval Time interval for fetching incidents. False
Trust any certificate (not secure)   False
Use system proxy settings   False

Fetched Incidents Data

  • The integration fetches newly created Guard DutyFindings.
    Each integration instance can fetch findings from a single AWS Region.
  • Each region can have a maximum of 1,000 member accounts that are linked to a guard duty master account. For more information see
    the Amazon GuardDuty documentation.
  • You can set the severity level of the findings to be fetched. “Low”, “Medium”, “High”. For example, if you set the severity
    level to “Medium”, the integration will only fetch findings with severity level of Medium and higher.

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook. After you successfully
execute a command, a DBot message appears in the War Room with the command details.

aws-gd-create-detector


Create an AWS Guard Duty Detector on the integration instance specified aws account.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:CreateDetector

Base Command

aws-gd-create-detector

Input

Argument Name Description Required
enabled A boolean value that specifies whether the detector is to be enabled. Possible values are: True, False. Default is True. Required
enableS3Logs The status of S3 data event logs as a data source. Possible values are: True, False. Optional
enableKubernetesLogs The status of Kubernetes audit logs as a data source. Possible values are: True, False. Optional
ebsVolumesMalwareProtection Describes the configuration for scanning EBS volumes as a data source. Possible values are: True, False. Optional
findingFrequency Specifies how frequently updated findings are exported. Possible values are: Fifteen Minutes, One Hour, Six Hours. Optional
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

Path Type Description
AWS.GuardDuty.Detectors.DetectorId string The unique ID of the created detector.

Command Example

!aws-gd-create-detector enabled=True region=eu-west-2

aws-gd-delete-detector


Deletes a Amazon GuardDuty detector specified by the detector ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:DeleteDetector

Base Command

aws-gd-delete-detector

Input

Argument Name Description Required
detectorId The unique ID that specifies the detector that you want to delete. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

There is no context output for this command.

Command Example

!aws-gd-delete-detector detectorId=38b1235ed3fe245279cd0c8e235db0715ac5561eb

aws-gd-get-detector


Retrieves an Amazon GuardDuty detector specified by the detectorId.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:GetDetector

Base Command

aws-gd-get-detector

Input

Argument Name Description Required
detectorId The unique ID of the detector that you want to retrieve. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

Path Type Description
AWS.GuardDuty.Detectors.DetectorId string The unique ID of the created detector.
AWS.GuardDuty.Detectors.CreatedAt string The first time a resource was created.
AWS.GuardDuty.Detectors.ServiceRole string Customer serviceRole name or ARN for accessing customer resources.
AWS.GuardDuty.Detectors.Status string The status of detector.
AWS.GuardDuty.Detectors.UpdatedAt string The time a resource was last updated.
AWS.GuardDuty.Detectors.CloudTrailStatus string Describes whether CloudTrail is enabled as a data source for the detector.
AWS.GuardDuty.Detectors.DNSLogsStatus string Denotes whether DNS logs are enabled as a data source.
AWS.GuardDuty.Detectors.FlowLogsStatus string Denotes whether VPC flow logs are enabled as a data source.
AWS.GuardDuty.Detectors.S3LogsStatus string Describes whether S3 data event logs are automatically enabled for new members of the organization.
AWS.GuardDuty.Detectors.KubernetesAuditLogsStatus string Describes whether Kubernetes audit logs are enabled as a data source.
AWS.GuardDuty.Detectors.MalwareProtectionStatus string Describes whether scanning EBS volumes is enabled as a data source.
AWS.GuardDuty.Detectors.MalwareProtectionReason string Specifies the reason why scanning EBS volumes (Malware Protection) was not enabled as a data source.
AWS.GuardDuty.Detectors.Tags string The tags of the detector resource.

Command Example

!aws-gd-get-detector detectorId=38b1ed3fe279fdascd0c8edb071dsf5ac5561eb region=eu-west-2

aws-gd-update-detector


Updates an Amazon GuardDuty detector specified by the detectorId.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:UpdateDetector

Base Command

aws-gd-update-detector

Input

Argument Name Description Required
detectorId The unique ID of the detector that you want to update. Required
enable Updated boolean value for the detector that specifies whether the detector is enabled. Possible values are: True, False. Default is True. Required
enableS3Logs The status of S3 data event logs as a data source. Possible values are: True, False. Optional
enableKubernetesLogs The status of Kubernetes audit logs as a data source. Possible values are: True, False. Optional
ebsVolumesMalwareProtection Describes the configuration for scanning EBS volumes as data source. Possible values are: True, False. Optional
findingFrequency A value that specifies how frequently updated findings are exported. Possible values are: Fifteen Minutes, One Hour, Six Hours. Optional
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

There is no context output for this command.

Command Example

aws-gd-update-detector detectorId=38b1ed3fe279fdascd0c8edb071dsf5ac5561eb enable=True

aws-gd-create-ip-set


A list of trusted IP addresses on allow list for secure communication with AWS infrastructure and applications.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:CreateIPSet

Base Command

aws-gd-create-ip-set

Input

Argument Name Description Required
activate A boolean value that indicates whether GuardDuty is to start using the uploaded IPSet. Possible values are: True, False. Default is True. Optional
detectorId The unique ID of the detector that you want to update. Required
format The format of the file that contains the IPSet. Possible values are: TXT, STIX, OTX_CSV, ALIEN_VAULT, PROOF_POINT, FIRE_EYE. Required
location The URI of the file that contains the IPSet. For example (https://s3.us-west-2.amazonaws.com/my-bucket/my-object-key). Optional
name The user friendly name to identify the IPSet. This name is displayed in all findings that are triggered by activity that involves IP addresses included in this IPSet. Optional
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

Path Type Description
AWS.GuardDuty.Detectors.IPSet.IpSetId unknown The unique identifier for an IP Set.

Command Example

!aws-gd-create-ip-set format=TXT location=https://s3.eu-central-1.amazonaws.com/test/ipset.txt activate=True detectorId=38b1ed3fe279czvasdd0c8edb0715azdsfc5561eb name=test region=eu-west-2

aws-gd-delete-ip-set


Deletes the IPSet specified by the IPSet ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:DeleteIPSet

Base Command

aws-gd-delete-ip-set

Input

Argument Name Description Required
detectorId The detectorID that specifies the GuardDuty service whose IPSet you want to delete. Required
ipSetId The unique ID that specifies the IPSet that you want to delete. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

There is no context output for this command.

Command Example

!aws-gd-delete-ip-set detectorId=38b1ed3fe279cd0c8edb0715ac5561eb ipSetId=7eb1f440be5931f168280b574a26d44d region=eu-west-2

aws-gd-list-detectors


Lists detectorIds of all the existing Amazon GuardDuty detector resources.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:ListDetectors

Base Command

aws-gd-list-detectors

Input

Argument Name Description Required
limit Number of total results to query. Default is 50. Optional
page Specific page to query. Optional
page_size Number of total results in each page. Default is 50. Optional
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

Path Type Description
AWS.GuardDuty.Detectors.DetectorId string The unique identifier for a detector.

Command Example

!aws-gd-list-detectors region=eu-west-2

aws-gd-update-ip-set


Updates the IPSet specified by the IPSet ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:UpdateIPSet

Base Command

aws-gd-update-ip-set

Input

Argument Name Description Required
activate The updated boolean value that specifies whether the IPSet is active or not. Possible values are: True, False. Optional
detectorId The detectorID that specifies the GuardDuty service whose IPSet you want to update. Required
ipSetId The unique ID that specifies the IPSet that you want to update. Required
location The updated URI of the file that contains the IPSet. For example (https://s3.us-west-2.amazonaws.com/my-bucket/my-object-key). Optional
name The user friendly name to identify the IPSet. Optional
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

There is no context output for this command.

Command Example

!aws-gd-update-ip-set detectorId=38b1ed3fe279cd0c8edb0715ac5561eb ipSetId=7eb1f440be5931f168280b574a26d44d activate=False region=eu-west-2

aws-gd-get-ip-set


Retrieves the IPSet specified by the IPSet ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:GetIPSet

Base Command

aws-gd-get-ip-set

Input

Argument Name Description Required
detectorId The detectorID that specifies the GuardDuty service whose IPSet you want to retrieve. Required
ipSetId The unique ID that specifies the IPSet that you want to describe. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

Path Type Description
AWS.GuardDuty.Detectors.IPSet.IpSetId string The unique ID for the IPSet.
AWS.GuardDuty.Detectors.IPSet.Format string The format of the file that contains the IPSet.
AWS.GuardDuty.Detectors.IPSet.Location string The URI of the file that contains the IPSet.
AWS.GuardDuty.Detectors.IPSet.Name string he user friendly name to identify the IPSet.
AWS.GuardDuty.Detectors.IPSet.Status string The status of ipSet file uploaded.

Command Example

!aws-gd-get-ip-set detectorId=38b1ed3fesdf279cd0c8edbdsf071sdgfac5561eb ipSetId=7eb1sdff440be5931f1682adf80b574a26d44d region=eu-west-2

aws-gd-list-ip-sets


Lists the IPSets of the GuardDuty service specified by the detector ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:ListIPSet

Base Command

aws-gd-list-ip-sets

Input

Argument Name Description Required
detectorId The unique ID of the detector that you want to retrieve. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional
limit Number of total results to query. Default is 50. Optional
page Specific page to query. Optional
page_size Number of total results in each page. Default is 50. Optional

Context Output

Path Type Description
AWS.GuardDuty.Detectors.IPSet.IpSetId unknown The unique identifier for an IP Set

Command Example

!aws-gd-list-ip-sets detectorId=38b1ed3fesdf279cd0c8edbdsf071sdgfac5561eb region=eu-west-2

aws-gd-create-threatintel-set


Create a new ThreatIntelSet. ThreatIntelSets consist of known malicious IP addresses. GuardDuty generates findings based on
ThreatIntelSets.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:CreateThreatIntelSet

Base Command

aws-gd-create-threatintel-set

Input

Argument Name Description Required
activate A boolean value that indicates whether GuardDuty is to start using the uploaded ThreatIntelSet. Possible values are: True, False. Default is True. Required
detectorId The unique ID of the detector that you want to update. Required
format The format of the file that contains the ThreatIntelSet. Possible values are: TXT, STIX, OTX_CSV, ALIEN_VAULT, PROOF_POINT, FIRE_EYE. Required
location The URI of the file that contains the ThreatIntelSet. For example (https://s3.us-west-2.amazonaws.com/my-bucket/my-object-key). Required
name A user-friendly ThreatIntelSet name that is displayed in all finding generated by activity that involves IP addresses included in this ThreatIntelSet. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

Path Type Description
AWS.GuardDuty.Detectors.ThreatIntelSet.ThreatIntelSetId string The unique identifier for an threat intel set.

Command Example

!aws-gd-create-threatintel-set format=TXT location=https://s3.eu-central-1.amazonaws.com/test/threatintel.txt activate=True detectorId=38b1ed3fe279czvasdd0c8edb0715azdsfc5561eb name=test region=eu-west-2

aws-gd-delete-threatintel-set


Deletes ThreatIntelSet specified by the ThreatIntelSet ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:DeleteThreatIntelSet

Base Command

aws-gd-delete-threatintel-set

Input

Argument Name Description Required
detectorId The detectorID that specifies the GuardDuty service whose ThreatIntelSet you want to delete. Required
threatIntelSetId The unique ID that specifies the ThreatIntelSet that you want to delete. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

There is no context output for this command.

Command Example

!aws-gd-delete-threatintel-set detectorId=38b1ed3fe279cd0c8edb0715ac5561eb threatIntelSetId=7eb1f440be5931f168280b574a26d44d region=eu-west-2

aws-gd-get-threatintel-set


Retrieves the ThreatIntelSet that is specified by the ThreatIntelSet ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:GetThreatIntelSet

Base Command

aws-gd-get-threatintel-set

Input

Argument Name Description Required
detectorId The detectorID that specifies the GuardDuty service whose ThreatIntelSet you want to describe. Required
threatIntelSetId The unique ID that specifies the ThreatIntelSet that you want to describe. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

Path Type Description
AWS.GuardDuty.Detectors.ThreatIntelSet.ThreatIntelSetId string The unique ID that specifies the ThreatIntelSet.
AWS.GuardDuty.Detectors.ThreatIntelSet.Format string The format of the threatIntelSet.
AWS.GuardDuty.Detectors.ThreatIntelSet.Location string The URI of the file that contains the ThreatIntelSet.
AWS.GuardDuty.Detectors.ThreatIntelSet.Name string A user-friendly ThreatIntelSet name.
AWS.GuardDuty.Detectors.ThreatIntelSet.Status string The status of threatIntelSet file uploaded.

Command Example

!aws-gd-get-threatintel-set detectorId=38b1ed3fe279cd0c8edb0715ac5561eb threatIntelSetId=7eb1f440be5931f168280b574a26d44d region=eu-west-2

aws-gd-list-threatintel-sets


Lists the ThreatIntelSets of the GuardDuty service specified by the detector ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:ListThreatIntelSet

Base Command

aws-gd-list-threatintel-sets

Input

Argument Name Description Required
detectorId The detectorID that specifies the GuardDuty service whose ThreatIntelSets you want to list. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional
limit Number of total results to query. Default is 50. Optional
page Specific page to query. Optional
page_size Number of total results in each page. Default is 50. Optional

Context Output

Path Type Description
AWS.GuardDuty.Detectors.ThreatIntelSet.ThreatIntelSetId string The unique identifier for an threat intel set

Command Example

!aws-gd-list-threatintel-sets detectorId=38b1ed3fe279cd0c8edb0715ac5561eb region=eu-west-2

aws-gd-update-threatintel-set


Updates the ThreatIntelSet specified by ThreatIntelSet ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:UpdateThreatIntelSet

Base Command

aws-gd-update-threatintel-set

Input

Argument Name Description Required
detectorId The detectorID that specifies the GuardDuty service whose ThreatIntelSet you want to update. Required
threatIntelSetId The unique ID that specifies the ThreatIntelSet that you want to update. Optional
activate The updated boolean value that specifies whether the ThreatIntelSet is active or not. Possible values are: True, False. Optional
location The updated URI of the file that contains the ThreatIntelSet. For example (https://s3.us-west-2.amazonaws.com/my-bucket/my-object-key). Optional
name The user-friendly ThreatIntelSet name. Optional
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

There is no context output for this command.

Command Example

!aws-gd-update-threatintel-set detectorId=38b1ed3fe279cd0c8edb0715ac5561eb threatIntelSetId=7eb1f440be5931f168280b574a26d44d activate=False region=eu-west-2

aws-gd-list-findings


Lists Amazon GuardDuty findings for the specified detector ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:ListFindings

Base Command

aws-gd-list-findings

Input

Argument Name Description Required
detectorId The ID of the detector that specifies the GuardDuty service whose findings you want to list. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional
limit Number of total results to query. Default is 50. Optional
page Specific page to query. Optional
page_size Number of total results in each page. Default is 50. Optional

Context Output

Path Type Description
AWS.GuardDuty.Findings.FindingId string The unique identifier for the Finding

Command Example

!aws-gd-list-findings detectorId=38b1ed3fe279cd0c8edb0715ac5561eb region=eu-west-2

aws-gd-get-findings


Describes Amazon GuardDuty findings specified by finding IDs.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:GetFindings

Base Command

aws-gd-get-findings

Input

Argument Name Description Required
detectorId The ID of the detector that specifies the GuardDuty service whose findings you want to retrieve. Required
findingIds IDs of the findings that you want to retrieve. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional
returnRawResponse Select ‘true’ to save all fields from the response to the context. Otherwise, complex fields will be stored in JSON format. Default value is False. Optional

Context Output

Path Type Description
AWS.GuardDuty.Findings.AccountId string The ID of the account in which the finding was generated.
AWS.GuardDuty.Findings.CreatedAt string The time and date when the finding was created.
AWS.GuardDuty.Findings.Description string The description of the finding.
AWS.GuardDuty.Findings.Region string The region where the finding was generated.
AWS.GuardDuty.Findings.Id string The ID of the finding.
AWS.GuardDuty.Findings.Title string The title of the finding.
AWS.GuardDuty.Findings.Severity string The severity of the finding.
AWS.GuardDuty.Findings.Type string The type of finding.
AWS.GuardDuty.Findings.UpdatedAt string The time and date when the finding was last updated.
AWS.GuardDuty.Findings.Arn string The ARN of the finding.
AWS.GuardDuty.Findings.Confidence string The confidence score for the finding.
AWS.GuardDuty.Findings.Partition string The partition associated with the finding.
AWS.GuardDuty.Findings.ResourceType string The type of Amazon Web Services resource.
AWS.GuardDuty.Findings.SchemaVersion string The version of the schema used for the finding.
AWS.GuardDuty.Findings.Service string Contains additional information about the generated finding.
AWS.GuardDuty.Findings.Resource.AccessKeyDetails string The IAM access key details (IAM user information) of a user that engaged in the activity that prompted GuardDuty to generate a finding.
AWS.GuardDuty.Findings.Resource.InstanceDetails string The information about the EC2 instance associated with the activity that prompted GuardDuty to generate a finding.
AWS.GuardDuty.Findings.Resource.EksClusterDetails string Details about the EKS cluster involved in a Kubernetes finding.
AWS.GuardDuty.Findings.Resource.KubernetesDetails string Details about the Kubernetes user and workload involved in a Kubernetes finding.
AWS.GuardDuty.Findings.Resource.EbsVolumeDetails string Contains a list of scanned and skipped EBS volumes with details.
AWS.GuardDuty.Findings.Resource.EcsClusterDetails string Contains information about the details of the ECS Cluster.
AWS.GuardDuty.Findings.Resource.ContainerDetails string Details of a container.
AWS.GuardDuty.Findings.Resource.S3BucketDetails string Contains information on the S3 bucket.

Command Example

!aws-gd-get-findings detectorIds=4f1fc7cd7dsg26sdf4328d8dc813 findingIds=96b1ac608sdf00e5183c3dds115c36aac328b,0ab180f5801sdg954418f3806c2a45282c9

aws-gd-create-sample-findings


Generates example findings of types specified by the list of finding types. If ‘NULL’ is specified for findingTypes, the API
generates example findings of all supported finding types.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:CreateSampleFindings

Base Command

aws-gd-create-sample-findings

Input

Argument Name Description Required
detectorId The ID of the detector to create sample findings for. Required
findingTypes Types of sample findings that you want to generate. Separated by comma. Optional
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

There is no context output for this command.

Command Example

!aws-gd-create-sample-findings detectorId=4f1fc7cd7dsg2adf6sdf4328d8dc813 findingTypes=NULL region=eu-central-1

aws-gd-archive-findings


Archives Amazon GuardDuty findings specified by the list of finding IDs.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:ArchiveFindings

Base Command

aws-gd-archive-findings

Input

Argument Name Description Required
detectorId The ID of the detector that specifies the GuardDuty service whose findings you want to archive. Required
findingIds IDs of the findings that you want to archive. Separated by comma. Optional
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

There is no context output for this command.

Command Example

!aws-gd-archive-findings detectorIds=4f1fc7cd7dsg26sdf4328d8dc813 findingIds=96b1ac608sdf00e5183c3dds115c36aac328b,0ab180f5801sdg954418f3806c2a45282c9

aws-gd-unarchive-findings


Unarchives Amazon GuardDuty findings specified by the list of finding IDs.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:UnarchiveFindings

Base Command

aws-gd-unarchive-findings

Input

Argument Name Description Required
detectorId The ID of the detector that specifies the GuardDuty service whose findings you want to unarchive. Required
findingIds IDs of the findings that you want to unarchive. Optional

Context Output

There is no context output for this command.

Command Example

!aws-gd-unarchive-findings detectorIds=4f1fc7cd7dsg26sdf4328d8dc813 findingIds=96b1ac608sdf00e5183c3dds115c36aac328b,0ab180f5801sdg954418f3806c2a45282c9

aws-gd-update-findings-feedback


Marks specified Amazon GuardDuty findings as useful or not useful.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:UpdateFindingsFeedback

Base Command

aws-gd-update-findings-feedback

Input

Argument Name Description Required
detectorId The ID of the detector that specifies the GuardDuty service whose findings you want to mark as useful or not useful. Required
findingIds IDs of the findings that you want to mark as useful or not useful. Optional
comments Additional feedback about the GuardDuty findings. Optional
feedback Specify whether the finding was useful or not. Possible values are: USEFUL, NOT_USEFUL. Optional
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

There is no context output for this command.

Command Example

!aws-gd-update-findings-feedback detectorIds=4f1fc7cd7dsg26sdf4328d8dc813 findingIds=96b1ac608sdf00e5183c3dds115c36aac328b comments=Good Job feedback=USEFUL

aws-gd-list-members


Describes Amazon GuardDuty members for the specified detector ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:ListMembers

Base Command

aws-gd-list-members

Input

Argument Name Description Required
detectorId The ID of the detector that specifies the GuardDuty service whose members you want to retrieve. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional
limit Number of total results to query. Default is 50. Optional
page Specific page to query. Optional
page_size Number of total results in each page. Default is 50. Optional

Context Output

Path Type Description
AWS.GuardDuty.Members.AccountId string The unique account ID of the member.
AWS.GuardDuty.Members.DetectorId string The unique detector ID of the member.
AWS.GuardDuty.Members.MasterId string The unique detector ID of the master.
AWS.GuardDuty.Members.Email string The email of the member.
AWS.GuardDuty.Members.RelationshipStatus string The relationship status of member.
AWS.GuardDuty.Members.InvitedAt string The first time a member was invited.
AWS.GuardDuty.Members.UpdatedAt string The time a member was last updated.

Command Example

!aws-gd-list-members detectorIds=4f1fc7cd7dsg26sdf4328d8dc813

aws-gd-get-members


Describes Amazon GuardDuty members for the specified detector ID & account ID.

AWS IAM Policy Permission

Effect: Allow

Action: guardduty:GetMembers

Base Command

aws-gd-get-members

Input

Argument Name Description Required
detectorId The ID of the detector that specifies the GuardDuty service whose members you want to retrieve. Required
accountIds The ID of the account that specifies the GuardDuty service whose details you want to retrieve. Required
region The AWS Region, if not specified the default region will be used. Optional
roleArn The Amazon Resource Name (ARN) of the role to assume. Optional
roleSessionName An identifier for the assumed role session. Optional
roleSessionDuration The duration, in seconds, of the role session. The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role. Optional

Context Output

Path Type Description
AWS.GuardDuty.Members.AccountId string The unique account ID of the member.
AWS.GuardDuty.Members.DetectorId string The unique detector ID of the member.
AWS.GuardDuty.Members.MasterId string The unique detector ID of the master.
AWS.GuardDuty.Members.Email string The email of the member.
AWS.GuardDuty.Members.RelationshipStatus string The relationship status of member.
AWS.GuardDuty.Members.InvitedAt string The first time a member was invited.
AWS.GuardDuty.Members.UpdatedAt string The time a member was last updated.

Command Example

!aws-gd-get-members detectorIds=4f1fc7cd7dsg26sdf4328d8dc813 accountIds=1f3fc2cd1dag26sdf4338d8aa813

Breaking changes from the previous version of this integration - AWS-GuardDuty

The following sections list the changes in this version.

Commands

Fetch incidents command - Findings that are fetched are no longer moved automatically to the GuardDuty archive.

Parameters

The following parameters were added in this version:

  • How many incidents to fetch each time
  • First fetch timestamp
  • Archive findings After Fetch

Configuration parameters

  • defaultRegion — AWS Default Region (required)
  • roleArn — Role Arn
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • roleSessionName — Role Session Name
  • sessionDuration — Role Session Duration
  • credentials — Access Key
  • access_key — Access Key
  • secret_key — Secret Key
  • timeout — Timeout
  • retries — Retries
  • endpoint_url — PrivateLink service URL.
  • sts_endpoint_url — STS PrivateLink URL.
  • sts_regional_endpoint — AWS STS Regional Endpoints
  • fetch_limit — How many incidents to fetch each time
  • first_fetch_time — First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)
  • gs_severity — Guard Duty Severity level
  • is_archive — Archive findings After Fetch
  • incidentFetchInterval — Incidents Fetch Interval
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (23)

  • aws-gd-archive-findings

    Archives Amazon GuardDuty findings specified by the list of finding IDs.

  • aws-gd-create-detector

    Create an AWS Guard Duty Detector on the integration instance specified aws account.

  • aws-gd-create-ip-set

    A list of trusted IP addresses on allow list for secure communication with AWS infrastructure and applications.

  • aws-gd-create-sample-findings

    Generates example findings of types specified by the list of finding types. If 'NULL' is specified for findingTypes, the API generates example findings of all supported finding types.

  • aws-gd-create-threatintel-set

    Create a new ThreatIntelSet. ThreatIntelSets consist of known malicious IP addresses. GuardDuty generates findings based on ThreatIntelSets.

  • aws-gd-delete-detector

    Deletes a Amazon GuardDuty detector specified by the detector ID.

  • aws-gd-delete-ip-set

    Deletes the IPSet specified by the IPSet ID.

  • aws-gd-delete-threatintel-set

    Deletes ThreatIntelSet specified by the ThreatIntelSet ID.

  • aws-gd-get-detector

    Retrieves an Amazon GuardDuty detector specified by the detectorId.

  • aws-gd-get-findings

    Describes Amazon GuardDuty findings specified by finding IDs.

  • aws-gd-get-ip-set

    Retrieves the IPSet specified by the IPSet ID.

  • aws-gd-get-members

    Describes Amazon GuardDuty members for the specified detector ID & account ID.

  • aws-gd-get-threatintel-set

    Retrieves the ThreatIntelSet that is specified by the ThreatIntelSet ID.

  • aws-gd-list-detectors

    Lists detectorIds of all the existing Amazon GuardDuty detector resources.

  • aws-gd-list-findings

    Lists Amazon GuardDuty findings for the specified detector ID.

  • aws-gd-list-ip-sets

    Lists the IPSets of the GuardDuty service specified by the detector ID.

  • aws-gd-list-members

    Describes Amazon GuardDuty members for the specified detector ID.

  • aws-gd-list-threatintel-sets

    Lists the ThreatIntelSets of the GuardDuty service specified by the detector ID.

  • aws-gd-unarchive-findings

    Unarchives Amazon GuardDuty findings specified by the list of finding IDs.

  • aws-gd-update-detector

    Updates an Amazon GuardDuty detector specified by the detectorId.

  • aws-gd-update-findings-feedback

    Marks specified Amazon GuardDuty findings as useful or not useful.

  • aws-gd-update-ip-set

    Updates the IPSet specified by the IPSet ID.

  • aws-gd-update-threatintel-set

    Updates the ThreatIntelSet specified by ThreatIntelSet ID.

import json
from collections import defaultdict
from datetime import date, datetime
from typing import TYPE_CHECKING

import demistomock as demisto  # noqa: F401
from AWSApiModule import *  # noqa: E402
from CommonServerPython import *  # noqa: F401

# The following import are used only for type hints and autocomplete.
# It is not used at runtime, and not exist in the docker image.
if TYPE_CHECKING:
    from mypy_boto3_guardduty import GuardDutyClient
    from mypy_boto3_guardduty.type_defs import ConditionTypeDef, FindingTypeDef


""" CONSTANTS """

SERVICE = "guardduty"

FINDING_FREQUENCY = {"Fifteen Minutes": "FIFTEEN_MINUTES", "One Hour": "ONE_HOUR", "Six Hours": "SIX_HOURS"}

DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
MAX_RESULTS_RESPONSE = 50


class DatetimeEncoder(json.JSONEncoder):
    def default(self, obj: Any):  # pylint: disable=E0202
        if isinstance(obj, datetime):
            return obj.strftime("%Y-%m-%dT%H:%M:%S")
        elif isinstance(obj, date):  # pylint: disable=E0602
            return obj.strftime("%Y-%m-%d")
        # Let the base class default method raise the TypeError
        return json.JSONEncoder.default(self, obj)


def create_detector(client: "GuardDutyClient", args: dict) -> CommandResults:
    """
    Creates a single Amazon GuardDuty detector.
    """
    kwargs = {"Enable": argToBoolean(args.get("enabled", False))}

    if args.get("findingFrequency"):
        kwargs["FindingPublishingFrequency"] = FINDING_FREQUENCY[args["findingFrequency"]]
    get_dataSources = {}
    if args.get("enableKubernetesLogs"):
        get_dataSources.update({"Kubernetes": {"AuditLogs": {"Enable": argToBoolean(args["enableKubernetesLogs"])}}})
    if args.get("ebsVolumesMalwareProtection"):
        get_dataSources.update(
            {
                "MalwareProtection": {
                    "ScanEc2InstanceWithFindings": {"EbsVolumes": argToBoolean(args["ebsVolumesMalwareProtection"])}
                }
            }
        )
    if args.get("enableS3Logs"):
        get_dataSources.update({"S3Logs": {"Enable": argToBoolean(args["enableS3Logs"])}})
    if get_dataSources:
        kwargs["DataSources"] = get_dataSources

    response = client.create_detector(**kwargs)
    data = {"DetectorId": response["DetectorId"]}

    readable_output = (
        tableToMarkdown("AWS GuardDuty Detectors - detector created successfully", data) if data else "No result were found"
    )
    return CommandResults(
        readable_output=readable_output, outputs=data, outputs_prefix="AWS.GuardDuty.Detectors", outputs_key_field="DetectorId"
    )


def delete_detector(client: "GuardDutyClient", args: dict):
    response = client.delete_detector(DetectorId=args.get("detectorId", ""))
    if response == {} or response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 200:
        return f"The Detector {args.get('detectorId')} has been deleted"
    else:
        raise Exception(f"The Detector {args.get('detectorId')} failed to delete.")


def get_detector(client: "GuardDutyClient", args: dict) -> CommandResults:
    """
    Retrieves an Amazon GuardDuty detector specified by the detectorId.
    """
    response = client.get_detector(DetectorId=args.get("detectorId", ""))
    data = {
        "DetectorId": args.get("detectorId"),
        "CreatedAt": response.get("CreatedAt"),
        "ServiceRole": response.get("ServiceRole"),
        "Status": response.get("Status"),
        "UpdatedAt": response.get("UpdatedAt"),
        "CloudTrailStatus": demisto.get(response, "DataSources.CloudTrail.Status"),
        "DNSLogsStatus": demisto.get(response, "DataSources.DNSLogs.Status"),
        "FlowLogsStatus": demisto.get(response, "DataSources.FlowLogs.Status"),
        "S3LogsStatus": demisto.get(response, "DataSources.S3Logs.Status"),
        "KubernetesAuditLogsStatus": demisto.get(response, "DataSources.Kubernetes.AuditLogs.Status"),
        "MalwareProtectionStatus": demisto.get(
            response, "DataSources.MalwareProtection.ScanEc2InstanceWithFindings.EbsVolumes.Status"
        ),
        "MalwareProtectionReason": demisto.get(
            response, "DataSources.MalwareProtection.ScanEc2InstanceWithFindings.EbsVolumes.Reason"
        ),
        "Tags": response.get("Tags"),
    }
    readable_output = tableToMarkdown("AWS GuardDuty Detectors", data, removeNull=True) if data else "No result were found"
    return CommandResults(
        readable_output=readable_output, outputs=data, outputs_prefix="AWS.GuardDuty.Detectors", outputs_key_field="DetectorId"
    )


def update_detector(client: "GuardDutyClient", args: dict) -> str:
    """
    Updates the Amazon GuardDuty detector specified by the detectorId.
    """
    kwargs = {"Enable": argToBoolean(args.get("enable", False)), "DetectorId": args.get("detectorId")}

    if args.get("findingFrequency"):
        kwargs["FindingPublishingFrequency"] = FINDING_FREQUENCY[args["findingFrequency"]]
    get_dataSources = {}
    if args.get("enableKubernetesLogs"):
        get_dataSources.update({"Kubernetes": {"AuditLogs": {"Enable": argToBoolean(args["enableKubernetesLogs"])}}})
    if args.get("ebsVolumesMalwareProtection"):
        get_dataSources.update(
            {
                "MalwareProtection": {
                    "ScanEc2InstanceWithFindings": {"EbsVolumes": argToBoolean(args["ebsVolumesMalwareProtection"])}
                }
            }
        )
    if args.get("enableS3Logs"):
        get_dataSources.update({"S3Logs": {"Enable": argToBoolean(args["enableS3Logs"])}})
    if get_dataSources:
        kwargs["DataSources"] = get_dataSources

    response = client.update_detector(**kwargs)
    if response == {} or response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 200:
        return f"The Detector {args.get('detectorId')} has been updated successfully"
    else:
        raise Exception(f"Detector {args.get('detectorId')} failed to update. Response was: {response}")


def list_detectors(client: "GuardDutyClient", args: dict) -> CommandResults:
    limit, page_size, page = get_pagination_args(args)

    paginator = client.get_paginator("list_detectors")
    response_iterator = paginator.paginate(
        PaginationConfig={
            "MaxItems": limit,
            "PageSize": page_size,
        }
    )

    data = []
    for i, page_response in enumerate(response_iterator):
        if page is None or (page - 1) == i:
            for detector in page_response["DetectorIds"]:
                data.append({"DetectorId": detector})
            if page:
                break

    readable_output = tableToMarkdown("AWS GuardDuty Detectors", data) if data else "No result were found"
    return CommandResults(
        readable_output=readable_output, outputs=data, outputs_prefix="AWS.GuardDuty.Detectors", outputs_key_field="DetectorId"
    )


def create_ip_set(client: "GuardDutyClient", args: dict):
    kwargs: dict[str, Any] = {"DetectorId": args.get("detectorId")}
    if args.get("activate") is not None:
        kwargs.update({"Activate": args.get("activate") == "True"})
    if args.get("format") is not None:
        kwargs.update({"Format": args.get("format")})
    if args.get("location") is not None:
        kwargs.update({"Location": args.get("location")})
    if args.get("name") is not None:
        kwargs.update({"Name": args.get("name")})

    response = client.create_ip_set(**kwargs)

    data = {"DetectorId": args.get("detectorId"), "IpSetId": response["IpSetId"]}
    readable_output = tableToMarkdown("AWS GuardDuty IPSets", data) if data else "No result were found"
    return CommandResults(
        readable_output=readable_output, outputs=data, outputs_prefix="AWS.GuardDuty.Detectors.IPSet", outputs_key_field="IpSetId"
    )


def delete_ip_set(client: "GuardDutyClient", args: dict):
    response = client.delete_ip_set(DetectorId=args.get("detectorId", ""), IpSetId=args.get("ipSetId", ""))
    if response == {} or response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 200:
        return f"The IPSet {args.get('ipSetId')} has been deleted from Detector {args.get('detectorId')}"

    else:
        raise Exception(f"Failed to delete ip set {args.get('ipSetId')} . Response was: {response}")


def update_ip_set(client: "GuardDutyClient", args: dict):
    kwargs: dict[str, Any] = {"DetectorId": args.get("detectorId"), "IpSetId": args.get("ipSetId")}
    if args.get("activate"):
        kwargs.update({"Activate": args.get("activate") == "True"})
    if args.get("location"):
        kwargs.update({"Location": args.get("location")})
    if args.get("name"):
        kwargs.update({"Name": args.get("name")})

    response = client.update_ip_set(**kwargs)

    if response == {} or response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 200:
        return f"The IPSet {args.get('ipSetId')} has been Updated"

    else:
        raise Exception(f"Failed updating ip set {args.get('ipSetId')} . Response was: {response}")


def get_ip_set(client: "GuardDutyClient", args: dict):
    response = client.get_ip_set(DetectorId=args.get("detectorId", ""), IpSetId=args.get("ipSetId", ""))
    data = {
        "DetectorId": args.get("detectorId"),
        "IpSetId": args.get("ipSetId"),
        "Format": response["Format"],
        "Location": response["Location"],
        "Name": response["Name"],
        "Status": response["Status"],
    }

    readable_output = tableToMarkdown("AWS GuardDuty IPSets", data) if data else "No result were found"
    return CommandResults(
        readable_output=readable_output, outputs=data, outputs_prefix="AWS.GuardDuty.Detectors.IPSet", outputs_key_field="IpSetId"
    )


def list_ip_sets(client: "GuardDutyClient", args: dict) -> CommandResults:
    limit, page_size, page = get_pagination_args(args)

    paginator = client.get_paginator("list_ip_sets")
    response_iterator = paginator.paginate(
        DetectorId=args.get("detectorId", ""),
        PaginationConfig={
            "MaxItems": limit,
            "PageSize": page_size,
        },
    )

    data = []
    data.append({"DetectorId": args.get("detectorId")})
    for i, page_response in enumerate(response_iterator):
        if page is None or (page - 1) == i:
            for ipSet in page_response["IpSetIds"]:
                data.append({"IpSetId": ipSet})
            if page:
                break

    readable_output = tableToMarkdown("AWS GuardDuty IPSets", data) if data else "No result were found"
    return CommandResults(
        readable_output=readable_output, outputs=data, outputs_prefix="AWS.GuardDuty.Detectors.IPSet", outputs_key_field="IpSetId"
    )


def create_threat_intel_set(client: "GuardDutyClient", args: dict):
    kwargs: dict[str, Any] = {"DetectorId": args.get("detectorId")}
    if args.get("activate") is not None:
        kwargs.update({"Activate": args.get("activate") == "True"})
    if args.get("format") is not None:
        kwargs.update({"Format": args.get("format")})
    if args.get("location") is not None:
        kwargs.update({"Location": args.get("location")})
    if args.get("name") is not None:
        kwargs.update({"Name": args.get("name")})

    response = client.create_threat_intel_set(**kwargs)

    data = {"DetectorId": args.get("detectorId"), "ThreatIntelSetId": response["ThreatIntelSetId"]}

    readable_output = tableToMarkdown("AWS GuardDuty ThreatIntel Set", data) if data else "No result were found"
    return CommandResults(
        readable_output=readable_output,
        outputs=data,
        outputs_prefix="AWS.GuardDuty.Detectors.ThreatIntelSet",
        outputs_key_field="ThreatIntelSetId",
    )


def delete_threat_intel_set(client: "GuardDutyClient", args: dict):
    response = client.delete_threat_intel_set(
        DetectorId=args.get("detectorId", ""), ThreatIntelSetId=args.get("threatIntelSetId", "")
    )
    if response == {} or response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 200:
        return f"The ThreatIntel Set {args.get('threatIntelSetId')} has been deleted from Detector {args.get('detectorId')}"
    else:
        raise Exception(f"Failed to delete ThreatIntel set {args.get('threatIntelSetId')} . Response was: {response}")


def get_threat_intel_set(client: "GuardDutyClient", args: dict):
    response = client.get_threat_intel_set(
        DetectorId=args.get("detectorId", ""), ThreatIntelSetId=args.get("threatIntelSetId", "")
    )
    data = {
        "DetectorId": args.get("detectorId"),
        "ThreatIntelSetId": args.get("threatIntelSetId"),
        "Format": response["Format"],
        "Location": response["Location"],
        "Name": response["Name"],
        "Status": response["Status"],
    }

    readable_output = tableToMarkdown("AWS GuardDuty ThreatIntel Set", data) if data else "No result were found"
    return CommandResults(
        readable_output=readable_output,
        outputs=data,
        outputs_prefix="AWS.GuardDuty.Detectors.ThreatIntelSet",
        outputs_key_field="ThreatIntelSetId",
    )


def list_threat_intel_sets(client: "GuardDutyClient", args: dict) -> CommandResults:
    limit, page_size, page = get_pagination_args(args)

    paginator = client.get_paginator("list_threat_intel_sets")
    response_iterator = paginator.paginate(
        DetectorId=args.get("detectorId", ""),
        PaginationConfig={
            "MaxItems": limit,
            "PageSize": page_size,
        },
    )

    data = []
    data.append({"DetectorId": args.get("detectorId")})
    for i, page_response in enumerate(response_iterator):
        if page is None or (page - 1) == i:
            for threatIntelSet in page_response["ThreatIntelSetIds"]:
                data.append({"ThreatIntelSetId": threatIntelSet})
            if page:
                break

    readable_output = tableToMarkdown("AWS GuardDuty ThreatIntel Sets", data) if data else "No result were found"
    return CommandResults(
        readable_output=readable_output,
        outputs=data,
        outputs_prefix="AWS.GuardDuty.Detectors.ThreatIntelSet",
        outputs_key_field="ThreatIntelSetId",
    )


def update_threat_intel_set(client: "GuardDutyClient", args: dict):
    kwargs: dict[str, Any] = {"DetectorId": args.get("detectorId"), "ThreatIntelSetId": args.get("threatIntelSetId")}
    if args.get("activate"):
        kwargs.update({"Activate": args.get("activate") == "True"})
    if args.get("location"):
        kwargs.update({"Location": args.get("location")})
    if args.get("name"):
        kwargs.update({"Name": args.get("name")})
    response = client.update_threat_intel_set(**kwargs)

    if response == {} or response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 200:
        return f"The ThreatIntel set {args.get('threatIntelSetId')} has been updated"
    else:
        raise Exception(f"Failed updating ThreatIntel set {args.get('threatIntelSetId')}. Response was: {response}")


def severity_mapping(severity: Optional[float]) -> Optional[int]:
    demisto_severity = None
    if severity:
        if severity <= 3.9:
            demisto_severity = 1
        elif 4 <= severity <= 6.9:
            demisto_severity = 2
        elif 7 <= severity <= 8.9:
            demisto_severity = 3
        else:
            demisto_severity = 0
    return demisto_severity


def gd_severity_mapping(severity_list: List[str]):
    if "Low" in severity_list:
        gd_severity = 1
    elif "Medium" in severity_list:
        gd_severity = 4
    elif "High" in severity_list:
        gd_severity = 7
    else:
        gd_severity = 1

    return gd_severity


def list_findings(client: "GuardDutyClient", args: dict) -> CommandResults:
    limit, page_size, page = get_pagination_args(args)

    paginator = client.get_paginator("list_findings")
    response_iterator = paginator.paginate(
        DetectorId=args.get("detectorId", ""),
        PaginationConfig={
            "MaxItems": limit,
            "PageSize": page_size,
        },
    )

    data = []
    for i, page_response in enumerate(response_iterator):
        if page is None or (page - 1) == i:
            for finding in page_response["FindingIds"]:
                data.append({"FindingId": finding})
            if page:
                break

    readable_output = tableToMarkdown("AWS GuardDuty Findings", data) if data else "No result were found"
    return CommandResults(
        readable_output=readable_output, outputs=data, outputs_prefix="AWS.GuardDuty.Findings", outputs_key_field=""
    )


def get_pagination_args(args: dict) -> tuple[int, int, Optional[int]]:
    """
    Gets and validates pagination arguments.
    :param args: The command arguments (page, page_size or limit)
    :return: limit, page_size, page after validation and convert
    """

    # Automatic Pagination
    limit = arg_to_number(args.get("limit")) or MAX_RESULTS_RESPONSE

    # Manual Pagination
    page = arg_to_number(args.get("page"))
    if page is not None and page <= 0:
        raise DemistoException("page argument must be greater than 0")

    page_size = arg_to_number(args.get("page_size")) or MAX_RESULTS_RESPONSE
    if not 0 < page_size <= MAX_RESULTS_RESPONSE:
        raise DemistoException(f"page_size argument must be between 1 to {MAX_RESULTS_RESPONSE}")

    if page:
        limit = page * page_size

    return limit, page_size, page


def parse_finding(finding: "FindingTypeDef") -> Dict[str, Any]:
    """
    Parse the finding data to output, context format
    :param finding: Contains information about the finding,
    which is generated when abnormal or suspicious activity is detected.
    :return: parsed_finding
    """
    parsed_finding: dict = {}
    parsed_finding["AccountId"] = finding.get("AccountId")
    parsed_finding["CreatedAt"] = finding.get("CreatedAt")
    parsed_finding["Description"] = finding.get("Description")
    parsed_finding["Region"] = finding.get("Region")
    parsed_finding["Id"] = finding.get("Id")
    parsed_finding["Title"] = finding.get("Title")
    parsed_finding["Type"] = finding.get("Type")
    parsed_finding["Severity"] = severity_mapping(finding.get("Severity"))
    parsed_finding["UpdatedAt"] = finding.get("UpdatedAt")

    parsed_finding["Arn"] = finding.get("Arn")
    parsed_finding["Confidence"] = finding.get("Confidence")
    parsed_finding["Partition"] = finding.get("Partition")
    parsed_finding["SchemaVersion"] = finding.get("SchemaVersion")
    parsed_finding["Service"] = json.dumps(finding.get("Service"), cls=DatetimeEncoder)
    parsed_finding["ResourceType"] = demisto.get(finding, "Resource.ResourceType")

    get_resource = finding.get("Resource")
    if get_resource:
        parsed_finding["Resource"] = {
            k: json.dumps(v, cls=DatetimeEncoder) for k, v in get_resource.items() if k != "ResourceType"
        }
    return parsed_finding


def get_findings(client: "GuardDutyClient", args: dict) -> dict:
    return_raw_response = argToBoolean(args.get("returnRawResponse", "false"))

    response = client.get_findings(DetectorId=args.get("detectorId", ""), FindingIds=argToList(args.get("findingIds")))

    data = []
    for finding in response["Findings"]:
        data.append(parse_finding(finding))

    output = json.dumps(response["Findings"], cls=DatetimeEncoder)
    raw = json.loads(output)

    headers = ["Id", "Title", "Description", "Type", "ResourceType", "CreatedAt", "AccountId", "Arn"]
    readable_output = (
        tableToMarkdown("AWS GuardDuty Findings", data, removeNull=True, headers=headers) if data else "No result were found"
    )

    return {
        "ContentsFormat": formats["json"],
        "Type": entryTypes["note"],
        "Contents": raw if raw else data,
        "ReadableContentsFormat": formats["markdown"],
        "HumanReadable": readable_output,
        "EntryContext": {"AWS.GuardDuty.Findings(val.FindingId === obj.Id)": raw if return_raw_response else data},
    }


def parse_incident_from_finding(finding: "FindingTypeDef") -> Dict[str, Any]:
    incident: dict = {}
    incident["name"] = finding.get("Title")
    incident["details"] = finding.get("Description")
    incident["occurred"] = finding.get("CreatedAt")
    incident["severity"] = severity_mapping(finding.get("Severity"))
    incident["rawJSON"] = json.dumps(finding, default=str)
    return incident


def time_to_unix_epoch(date_time: datetime) -> int:
    """
    :param date_time: The time and date in '%Y-%m-%dT%H:%M:%S.%fZ' format
    :return: Timestamp in Unix Epoch millisecond format
    example: date_time = dateparser.parse('2017-02-10 00:09:35.000000+00:00') to 1486685375000
    """
    return int(date_time.timestamp() * 1000)


def fetch_incidents(
    client: "GuardDutyClient",
    aws_gd_severity: List[str],
    last_run: dict,
    fetch_limit: int,
    first_fetch_time: str,
    is_archive: bool,
) -> tuple[Dict[str, Any], List[dict]]:
    """
    This function will execute each interval (default is 1 minute).

    Args:
        client (Client): "GuardDutyClient" client
        aws_gd_severity (List[str]): Guard Duty Severity level
        last_run (dict): {'latest_created_time' (string): The greatest incident created_time we fetched from last fetch,
                          'latest_updated_time' (string): The greatest incident updated_time we fetched from last fetch,
                          'last_incidents_ids' (list): The last incidents ids of the latest_created_time,
                          'last_next_token' (string): The value of NextToken from the previous response to continue listing data.}
        fetch_limit (int): Maximum numbers of incidents per fetch
        first_fetch_time (str): If last_fetch is None then fetch all incidents since first_fetch_time
        is_archive (bool): Archive findings After Fetch
    Returns:
        next_run: This will be last_run in the next fetch-incidents
        incidents: Incidents that will be created in Demisto
    """

    # Get the latest_created_time, latest_updated_time, last_incidents_ids, and last_next_token if exist
    latest_created_time = last_run.get("latest_created_time")
    last_incidents_ids = last_run.get("last_incidents_ids", [])
    last_next_token = last_run.get("last_next_token", "")
    if latest_updated_time := last_run.get("latest_updated_time", ""):
        latest_updated_time = dateparser.parse(latest_updated_time)

    # Handle first time fetch
    if latest_created_time is None:
        latest_created_time = dateparser.parse(dateparser.parse(first_fetch_time).strftime(DATE_FORMAT))  # type: ignore[union-attr]
    else:
        latest_created_time = dateparser.parse(latest_created_time)

    response = client.list_detectors()
    detector = response["DetectorIds"]

    created_time_to_ids = defaultdict(list)
    created_time_to_ids[latest_created_time] = last_incidents_ids

    # Represents the criteria to be used in the filter for querying findings.
    criterion_conditions: Dict[str, ConditionTypeDef] = {}
    criterion_conditions["severity"] = {"Gte": gd_severity_mapping(aws_gd_severity)}
    if is_archive:
        demisto.debug("Fetching Amazon GuardDuty with Archive")
        criterion_conditions["service.archived"] = {"Eq": ["false"]}
    if not last_next_token and latest_updated_time:
        criterion_conditions["updatedAt"] = {"Gte": time_to_unix_epoch(latest_updated_time)}
    if last_incidents_ids:
        criterion_conditions["id"] = {"Neq": last_incidents_ids[:]}

    demisto.info(f"Fetching Amazon GuardDuty findings for the {detector[0]} since: {latest_created_time!s}")

    incidents: list[dict] = []
    while True:
        left_to_fetch = fetch_limit - len(incidents)
        max_results = min(MAX_RESULTS_RESPONSE, left_to_fetch)

        list_findings_res = client.list_findings(
            DetectorId=detector[0],
            FindingCriteria={"Criterion": criterion_conditions},
            SortCriteria={"AttributeName": "createdAt", "OrderBy": "ASC"},
            MaxResults=max_results,
            NextToken=last_next_token,
        )
        last_next_token = list_findings_res.get("NextToken", "")
        finding_ids = list_findings_res.get("FindingIds", [])
        get_findings_res = client.get_findings(
            DetectorId=detector[0], FindingIds=finding_ids, SortCriteria={"AttributeName": "createdAt", "OrderBy": "ASC"}
        )

        for finding in get_findings_res["Findings"]:
            incident_created_time = dateparser.parse(finding.get("CreatedAt", ""))
            incident_updated_time = dateparser.parse(finding.get("UpdatedAt", ""))
            incident_id = finding.get("Id")

            # Update the latest_updated_time
            if not latest_updated_time or (incident_updated_time and incident_updated_time > latest_updated_time):
                latest_updated_time = incident_updated_time

            # Update last run (latest_created_time) and add incident if the incident is newer than last fetch
            if (incident_created_time and latest_created_time) and incident_created_time >= latest_created_time:
                demisto.debug(
                    f"Added Incident with ID {incident_id}, occured: {incident_created_time!s}, "
                    f"updated: {incident_updated_time!s}"
                )

                latest_created_time = incident_created_time
                created_time_to_ids[latest_created_time].append(incident_id)

                incident = parse_incident_from_finding(finding)
                incidents.append(incident)

        if incidents and is_archive:
            # Archive findings
            demisto.debug(f"Archived {len(finding_ids)} findings.")
            client.archive_findings(DetectorId=detector[0], FindingIds=finding_ids)

        # if there is no next_token, or we have reached the fetch_limit -> break
        if not last_next_token or fetch_limit - len(incidents) == 0:
            demisto.debug("fetch_limit has been reached or there is no next token")
            break

    next_run = {
        "latest_created_time": latest_created_time.strftime(DATE_FORMAT) if latest_created_time else None,
        "latest_updated_time": latest_updated_time.strftime(DATE_FORMAT) if latest_updated_time else None,
        "last_incidents_ids": created_time_to_ids[latest_created_time],
        "last_next_token": last_next_token,
    }

    demisto.debug(f"{next_run=}")
    demisto.debug(f"fetched {len(incidents)} incidents")
    return next_run, incidents


def create_sample_findings(client: "GuardDutyClient", args: dict):
    kwargs: dict[str, Any] = {"DetectorId": args.get("detectorId")}
    if args.get("findingTypes") is not None:
        kwargs.update({"FindingTypes": argToList(args.get("findingTypes"))})

    response = client.create_sample_findings(**kwargs)

    if response == {} or response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 200:
        return "Sample Findings were generated"
    else:
        raise Exception(f"Failed to generate findings. Response was: {response}")


def archive_findings(client: "GuardDutyClient", args: dict):
    kwargs: dict[str, Any] = {"DetectorId": args.get("detectorId")}
    if args.get("findingIds") is not None:
        kwargs.update({"FindingIds": argToList(args.get("findingIds"))})

    response = client.archive_findings(**kwargs)

    if response == {} or response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 200:
        return "Findings were archived"
    else:
        raise Exception(f"Failed to archive findings. Response was: {response}")


def unarchive_findings(client: "GuardDutyClient", args: dict):
    kwargs: dict = {"DetectorId": args.get("detectorId")}
    if args.get("findingIds") is not None:
        kwargs.update({"FindingIds": argToList(args.get("findingIds"))})

    response = client.unarchive_findings(**kwargs)

    if response == {} or response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 200:
        return "Findings were unarchived"
    else:
        raise Exception(f"Failed to archive findings. Response was: {response}")


def update_findings_feedback(client: "GuardDutyClient", args: dict):
    kwargs: dict[str, Any] = {"DetectorId": args.get("detectorId")}
    if args.get("findingIds") is not None:
        kwargs.update({"FindingIds": argToList(args.get("findingIds"))})
    if args.get("comments") is not None:
        kwargs.update({"Comments": args.get("comments")})
    if args.get("feedback") is not None:
        kwargs.update({"Feedback": args.get("feedback")})

    response = client.update_findings_feedback(**kwargs)
    if response == {} or response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 200:
        return "Findings Feedback sent!"
    else:
        raise Exception(f"Failed to send findings feedback. Response was: {response}")


def list_members(client: "GuardDutyClient", args: dict) -> CommandResults:
    limit, page_size, page = get_pagination_args(args)

    paginator = client.get_paginator("list_members")
    response_iterator = paginator.paginate(
        DetectorId=args.get("detectorId", ""),
        PaginationConfig={
            "MaxItems": limit,
            "PageSize": page_size,
        },
    )

    data = []
    for i, page_response in enumerate(response_iterator):
        if page is None or (page - 1) == i:
            for member in page_response["Members"]:
                data.append({"Member": member})
            if page:
                break

    readable_output = tableToMarkdown("AWS GuardDuty Members", data) if data else "No result were found"
    return CommandResults(
        readable_output=readable_output, outputs=data, outputs_prefix="AWS.GuardDuty.Members", outputs_key_field="AccountId"
    )


def get_members(client: "GuardDutyClient", args: dict):
    accountId_list = []
    accountId_list.append(args.get("accountIds", ""))

    response = client.get_members(DetectorId=args.get("detectorId", ""), AccountIds=accountId_list)

    members_response = response.get("Members", [])
    filtered_members = [member for member in members_response if member]

    readable_output = tableToMarkdown("AWS GuardDuty Members", filtered_members) if filtered_members else "No result were found"
    return CommandResults(
        readable_output=readable_output,
        outputs=filtered_members,
        outputs_prefix="AWS.GuardDuty.Members",
        outputs_key_field="AccountId",
    )


def connection_test(client: "GuardDutyClient"):
    response = client.list_detectors()
    if response.get("DetectorIds"):
        return "ok"
    else:
        raise Exception(f"Error listing detectors. Response was {response}")


def main():  # pragma: no cover
    params = demisto.params()
    aws_default_region = params.get("defaultRegion")
    aws_role_arn = params.get("roleArn")
    aws_role_session_name = params.get("roleSessionName")
    aws_role_session_duration = params.get("sessionDuration")
    aws_role_policy = None
    aws_access_key_id = params.get("credentials", {}).get("identifier") or params.get("access_key")
    aws_secret_access_key = params.get("credentials", {}).get("password") or params.get("secret_key")
    aws_gd_severity = params.get("gs_severity", [])
    verify_certificate = not params.get("insecure", True)
    timeout = params.get("timeout") or 1
    retries = params.get("retries") or 5
    first_fetch_time = params.get("first_fetch_time", "10 minutes").strip()
    fetch_limit = arg_to_number(params.get("fetch_limit", 10))
    is_archive = argToBoolean(params.get("is_archive", False))
    sts_endpoint_url = params.get("sts_endpoint_url") or None
    endpoint_url = params.get("endpoint_url") or None

    try:
        validate_params(aws_default_region, aws_role_arn, aws_role_session_name, aws_access_key_id, aws_secret_access_key)

        aws_client = AWSClient(
            aws_default_region,
            aws_role_arn,
            aws_role_session_name,
            aws_role_session_duration,
            aws_role_policy,
            aws_access_key_id,
            aws_secret_access_key,
            verify_certificate,
            timeout,
            retries,
            sts_endpoint_url=sts_endpoint_url,
            endpoint_url=endpoint_url,
        )
        args = demisto.args()

        client: GuardDutyClient = aws_client.aws_session(
            service=SERVICE,
            region=args.get("region"),
            role_arn=args.get("roleArn"),
            role_session_name=args.get("roleSessionName"),
            role_session_duration=args.get("roleSessionDuration"),
        )

        # The command demisto.command() holds the command sent from the user.
        if demisto.command() == "test-module":
            # This is the call made when pressing the integration test button.
            result = connection_test(client)

        elif demisto.command() == "aws-gd-create-detector":
            result = create_detector(client, demisto.args())

        elif demisto.command() == "aws-gd-delete-detector":
            result = delete_detector(client, demisto.args())

        elif demisto.command() == "aws-gd-get-detector":
            result = get_detector(client, demisto.args())

        elif demisto.command() == "aws-gd-update-detector":
            result = update_detector(client, demisto.args())

        elif demisto.command() == "aws-gd-create-ip-set":
            result = create_ip_set(client, demisto.args())

        elif demisto.command() == "aws-gd-delete-ip-set":
            result = delete_ip_set(client, demisto.args())

        elif demisto.command() == "aws-gd-list-detectors":
            result = list_detectors(client, demisto.args())

        elif demisto.command() == "aws-gd-update-ip-set":
            result = update_ip_set(client, demisto.args())

        elif demisto.command() == "aws-gd-get-ip-set":
            result = get_ip_set(client, demisto.args())

        elif demisto.command() == "aws-gd-list-ip-sets":
            result = list_ip_sets(client, demisto.args())

        elif demisto.command() == "aws-gd-create-threatintel-set":
            result = create_threat_intel_set(client, demisto.args())

        elif demisto.command() == "aws-gd-delete-threatintel-set":
            result = delete_threat_intel_set(client, demisto.args())

        elif demisto.command() == "aws-gd-get-threatintel-set":
            result = get_threat_intel_set(client, demisto.args())

        elif demisto.command() == "aws-gd-list-threatintel-sets":
            result = list_threat_intel_sets(client, demisto.args())

        elif demisto.command() == "aws-gd-update-threatintel-set":
            result = update_threat_intel_set(client, demisto.args())

        elif demisto.command() == "aws-gd-list-findings":
            result = list_findings(client, demisto.args())

        elif demisto.command() == "aws-gd-get-findings":
            result = get_findings(client, demisto.args())

        elif demisto.command() == "aws-gd-create-sample-findings":
            result = create_sample_findings(client, demisto.args())

        elif demisto.command() == "aws-gd-archive-findings":
            result = archive_findings(client, demisto.args())

        elif demisto.command() == "aws-gd-unarchive-findings":
            result = unarchive_findings(client, demisto.args())

        elif demisto.command() == "aws-gd-update-findings-feedback":
            result = update_findings_feedback(client, demisto.args())

        elif demisto.command() == "aws-gd-list-members":
            result = list_members(client, demisto.args())

        elif demisto.command() == "aws-gd-get-members":
            result = get_members(client, demisto.args())

        elif demisto.command() == "fetch-incidents":
            next_run, incidents = fetch_incidents(
                client=client,
                aws_gd_severity=aws_gd_severity,
                last_run=demisto.getLastRun(),
                fetch_limit=fetch_limit,  # type: ignore[arg-type]
                first_fetch_time=first_fetch_time,
                is_archive=is_archive,
            )
            demisto.setLastRun(next_run)
            demisto.incidents(incidents)
            sys.exit(0)

        else:
            raise NotImplementedError(f"Command {demisto.command()} is not implemented in AWSGuardDuty integration.")

        return_results(result)

    except Exception as e:
        return_error(f"Error has occurred in the AWS GuardDuty Integration: {type(e)}\n {e!s}")


if __name__ in ("__main__", "__builtin__", "builtins"):  # pragma: no cover
    main()