G Suite Security Alert Center

G Suite Security Alert Center allows users to fetch different alert types such as Suspicious login, Device compromised, Leaked password, and more. Users can delete or recover a single alert or a batch of alerts and retrieve the alert's metadata. This integration allows users to provide feedback for alerts and fetch existing feedback for a particular alert.

Data Enrichment & Threat Intelligence · G Suite Security Alert Center

Details

IDG Suite Security Alert Center
ProviderGoogle
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/googleapi-python3:1.0.0.10182333
Supported ModulesAgentix XSIAM

README

G Suite Security Alert Center allows users to fetch different alert types such as Suspicious login, Device compromised, Leaked password, and more. Users can delete or recover a single alert or a batch of alerts and retrieve the alert’s metadata.
This integration allows users to provide feedback for alerts and fetch existing feedback for a particular alert.
This integration was integrated and tested with version v1beta1 dated Oct 15, 2018 of Google Workspace Alert Center.
Supported Cortex XSOAR versions: 5.0.0 and later.

Configure Service Account for G Suite Security Alert Center

  1. Go to https://console.developers.google.com.

  2. Select or create a project:

    Setup Account

  3. Enable the G Suite Alert Center API:

    1. Under the main menu, select APIs & Services > Library:
      Setup Account

    2. Search for Google Workspace Alert Center API in Search for APIs & Services:
      Setup Account

    3. Enable Google Workspace Alert Center API:
      Setup Account

    4. Select the project and click OPEN:
      Setup Account

  4. Create a service account with the correct restricted permissions:

    1. Under the main menu, select IAM & Admin > Service Accounts:
      Setup Account

    2. In the top bar, select CREATE SERVICE ACCOUNT:
      Setup Account

    3. Under the main menu, select IAM & Admin > Service Accounts and open the account you just created.

    4. Generate new credentials:

      1. On the Service account details page, under Keys, click ADD KEY and select Create new key:
        Setup Account

      2. Under Key type, select JSON, and click CREATE.
        Save the credentials in a secure location. You will need the generated credentials.json file when you configure the instance in Cortex XSOAR:
        Setup Account

      3. The service account JSON will be automatically downloaded.

Add Scopes in G Suite Security Alert Center

  1. Go to https://admin.google.com

  2. Click Menu and select Security > API Controls:
    Setup Account

  3. Under the Domain wide delegation tab, Click MANAGE DOMAIN WIDE DELEGATION:
    Setup Account

  4. Click Add new. Enter the Client ID from your credentials.json file and the required scopes from the integration tips.
    Setup Account

  5. Click AUTHORIZE.

Configure G Suite Security Alert Center on Cortex XSOAR

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for G Suite Security Alert Center.
  3. Click Add instance to create and configure a new integration instance.

    Parameter Description Required
    Fetch incidents   False
    Incident type   False
    Service Account JSON User’s Service Account JSON key. True
    Admin Email G Suite domain administrator’s email ID that acts on behalf of the end-user True
    Maximum number of incidents per fetch The maximum allowed value is 1000. True
    First fetch time interval The time range to consider for the initial data fetch. Formats accepted: YYYY-MM-dd, YYYY-MM-ddTHH:mm:ss, 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years. Accepted timezone: UTC. False
    Alert Type Add a new type or choose existing multiple alert types. Fetches all types of alerts if left empty.  
    Note: If type is present in the Filter parameter this value will be overwritten. False  
    Filter An advanced filter to fetch the list of alerts. For example: source:”Google” AND type=”Suspicious login”. Note: This value takes precedence over any filter arguments. To fetch alerts using createTime, use the first fetch time interval parameter. False
    Fetch feedback Fetches the latest type of feedback for each alert. False
    Trust any certificate (not secure)   False
    Use system proxy settings   False
  4. Click Test to validate the URLs, token, and connection.

Supported query filters

  • To see supported query filters, refer to the link mentioned here.
  • To see the syntax of the filters, refer to the link mentioned here.

Commands

You can execute these commands from the Cortex XSOAR 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.

gsac-alert-list


Lists the alerts. Use the filter to filter the list by various alert types.
Note: The alerts that have been marked for deletion in the past 30 days will still be visible.

Base Command

gsac-alert-list

Input

Argument Name Description Required
page_size The requested page size. Optional
page_token A token identifying a page of results the server should return. Optional
filter A query string for filtering alert results. For example: createTime >= “2020-10-28T20:43:34.381Z” AND type=”Suspicious login”. Optional
order_by The sort order of the list results. Possible values are: createTime asc, createTime desc, updateTime asc, updateTime desc. Optional

Context Output

Path Type Description
GSuiteSecurityAlert.PageToken.Alert.nextPageToken String The token for the next page.
GSuiteSecurityAlert.PageToken.Alert.name String Command name.
GSuiteSecurityAlert.Alert.customerId String The unique identifier of the Google account of the customer.
GSuiteSecurityAlert.Alert.alertId String The unique identifier for the alert.
GSuiteSecurityAlert.Alert.createTime Date The time the alert was created.
GSuiteSecurityAlert.Alert.startTime Date The time the event that caused the alert was started or detected.
GSuiteSecurityAlert.Alert.endTime Date The time the event that caused the alert ceased being active.
GSuiteSecurityAlert.Alert.type String The type of the alert.
GSuiteSecurityAlert.Alert.source String A unique identifier for the system that reported the alert.
GSuiteSecurityAlert.Alert.deleted Boolean True if the alert is marked for deletion.
GSuiteSecurityAlert.Alert.securityInvestigationToolLink String An optional Security Investigation Tool query for the alert.
GSuiteSecurityAlert.Alert.updateTime Date The time the alert was last updated.
GSuiteSecurityAlert.Alert.etag String ETag is used for optimistic concurrency control as a way to help prevent simultaneous updates of an alert from overwriting each other.
GSuiteSecurityAlert.Alert.metadata.customerId String The unique identifier of the Google account of the customer.
GSuiteSecurityAlert.Alert.metadata.alertId String The unique identifier of the alert.
GSuiteSecurityAlert.Alert.metadata.status String The current status of the alert.
GSuiteSecurityAlert.Alert.metadata.assignee String The email address of the user assigned to the alert.
GSuiteSecurityAlert.Alert.metadata.updateTime String The time the metadata was last updated.
GSuiteSecurityAlert.Alert.metadata.severity String The severity value of the alert.
GSuiteSecurityAlert.Alert.metadata.etag String ETag is used for optimistic concurrency control as a way to help prevent simultaneous updates of an alert metadata from overwriting each other.
GSuiteSecurityAlert.Alert.data.@type String Alert data of the alert types.
GSuiteSecurityAlert.Alert.data.takeoutRequestId String The takeout request ID.
GSuiteSecurityAlert.Alert.data.email String The email of the associated event. Domain wide takeout initiated applies to the email of the admin who initiated the takeout. Google identity applies to the email of the user who the event belongs to. Mobile device management/State Sponsored Attack applies to the email of the user the alert was created for.
GSuiteSecurityAlert.Alert.data.domainId.customerPrimaryDomain String The primary domain for the customer.
GSuiteSecurityAlert.Alert.data.maliciousEntity.entity.emailAddress String Email address of the user.
GSuiteSecurityAlert.Alert.data.maliciousEntity.entity.displayName String Display name of the user.
GSuiteSecurityAlert.Alert.data.maliciousEntity.fromHeader String The sender email address.
GSuiteSecurityAlert.Alert.data.maliciousEntity.displayName String The header from the display name.
GSuiteSecurityAlert.Alert.data.messages.messageId String The message ID.
GSuiteSecurityAlert.Alert.data.messages.md5HashMessageBody String The hash of the message body text.
GSuiteSecurityAlert.Alert.data.messages.messageBodySnippet String The snippet of the message body text (only available for reported emails).
GSuiteSecurityAlert.Alert.data.messages.md5HashSubject String The MD5 hash of the email’s subject (only available for reported emails).
GSuiteSecurityAlert.Alert.data.messages.subjectText String The email subject text (only available for reported emails).
GSuiteSecurityAlert.Alert.data.messages.attachmentsSha256Hash Unknown The list of SHA256 hashes of the email’s attachment and all MIME parts.
GSuiteSecurityAlert.Alert.data.messages.recipient String The recipient of the email.
GSuiteSecurityAlert.Alert.data.messages.date Date The date the malicious email was sent.
GSuiteSecurityAlert.Alert.data.isInternal Boolean If true, the email originated from within the organization.
GSuiteSecurityAlert.Alert.data.systemActionType String System actions on the messages.
GSuiteSecurityAlert.Alert.data.sourceIp String The source IP address of the malicious email, for example, 127.0.0.1.
GSuiteSecurityAlert.Alert.data.loginDetails.loginTime Date The successful login time that is associated with the warning event. This is not present for blocked login attempts.
GSuiteSecurityAlert.Alert.data.loginDetails.ipAddress String The human-readable IP address (for example, 11.22.33.44) that is associated with the warning event.
GSuiteSecurityAlert.Alert.data.title String A one-line incident description.
GSuiteSecurityAlert.Alert.data.description String Description of the associated alert type.
GSuiteSecurityAlert.Alert.data.affectedUserEmails Unknown The list of emails that correspond to the users directly affected by the incident.
GSuiteSecurityAlert.Alert.data.attachmentData.csv.headers Unknown The list of headers for the data columns in a CSV file.
GSuiteSecurityAlert.Alert.data.attachmentData.csv.dataRows.entries Unknown The data entries in a CSV file row, as a string array rather than a single comma-separated string.
GSuiteSecurityAlert.Alert.data.events.deviceId String The device ID.
GSuiteSecurityAlert.Alert.data.events.serialNumber String The serial number of the device.
GSuiteSecurityAlert.Alert.data.events.deviceType String The type of the device.
GSuiteSecurityAlert.Alert.data.events.deviceModel String The model of the device.
GSuiteSecurityAlert.Alert.data.events.resourceId String The device resource ID.
GSuiteSecurityAlert.Alert.data.events.iosVendorId String iOS vendor ID. Required for iOS, empty for others.
GSuiteSecurityAlert.Alert.data.events.deviceCompromisedState String The device compromised state. Possible values are “Compromised” or “Not Compromised”.
GSuiteSecurityAlert.Alert.data.events.deviceProperty String The device property that was changed.
GSuiteSecurityAlert.Alert.data.events.oldValue String The old value of the device property before the change.
GSuiteSecurityAlert.Alert.data.events.newValue String The new value of the device property after the change.
GSuiteSecurityAlert.Alert.data.requestInfo.appKey String The application that requires the SQL setup.
GSuiteSecurityAlert.Alert.data.requestInfo.appDeveloperEmail Unknown List of app developers who triggered notifications for the application.
GSuiteSecurityAlert.Alert.data.requestInfo.numberOfRequests String Number of requests sent for the application to set up default SQL instances.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.ruleInfo.resourceName String Resource name that uniquely identifies the rule.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.ruleInfo.displayName String User provided name of the rule.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.dataSource String Source of the data.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.trigger String Trigger of the rule.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.triggeringUserEmail String Email of the user who caused the violation.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.recipients Unknown Resource recipients.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.resourceInfo.resourceTitle String Title of the resource, for example email subject, or document title.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.resourceInfo.documentId String Drive file ID.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.matchInfo.userDefinedDetector.resourceName String Resource name that uniquely identifies the detector.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.matchInfo.userDefinedDetector.displayName String Display name of the detector.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.matchInfo.predefinedDetector.detectorName String Name that uniquely identifies the detector.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.triggeredActionTypes Unknown Actions applied as a consequence of the rule being triggered.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.suppressedActionTypes Unknown Actions suppressed due to other actions with higher priority.
GSuiteSecurityAlert.Alert.data.name String Rule name.
GSuiteSecurityAlert.Alert.data.displayName String Alert display name.
GSuiteSecurityAlert.Alert.data.windowSize String Rule window size. Possible values are 1 hour or 24 hours.
GSuiteSecurityAlert.Alert.data.threshold String Alert threshold, for example “COUNT > 5”.
GSuiteSecurityAlert.Alert.data.createTime Date The timestamp indicating when the rule was created.
GSuiteSecurityAlert.Alert.data.updateTime Date The timestamp indicating when the rule was last updated.
GSuiteSecurityAlert.Alert.data.triggerSource String The trigger sources for the rule.
GSuiteSecurityAlert.Alert.data.supersededAlerts Unknown List of alert IDs superseded by the alert.
GSuiteSecurityAlert.Alert.data.supersedingAlerts String Alert ID superseding the alert.
GSuiteSecurityAlert.Alert.data.actionNames Unknown List of action names associated with the rule threshold.
GSuiteSecurityAlert.Alert.data.query String Query that is used to get the data from the associated source.

Command Example

!gsac-alert-list page_size=2

Context Example

{
    "GSuiteSecurityAlert": {
        "Alert": [
            {
                "alertId": "dummy_alert_id1",
                "createTime": "2021-01-21T13:49:06.315483Z",
                "customerId": "dummy_customer_id",
                "data": {
                    "@type": "type.googleapis.com/google.apps.alertcenter.type.MailPhishing",
                    "domainId": {
                        "customerPrimaryDomain": "abc.io"
                    },
                    "maliciousEntity": {
                        "fromHeader": "dummyemail@xyz.com"
                    },
                    "messages": [
                        {
                            "attachmentsSha256Hash": [
                                "74bb828bcab5aedba8264369f791613cf01f5d42eb76d1bfe620fa4ca1d371c1",
                                "7eb70257593da06f682a3ddda54a9d260d4fc514f645237f5ca74b08f8da61a6",
                                "fcad4ae0f38b600395ab04055b273f14b6cdc504837ed0b69da962268e6fa268"
                            ],
                            "date": "2021-01-21T13:08:58.759929Z",
                            "md5HashSubject": "16258756be9e9c233f60b4dad7e4f4ae",
                            "messageId": "dummy1@mail.gmail.com",
                            "recipient": "dummy@abc.io",
                            "subjectText": "Token file"
                        },
                        {
                            "attachmentsSha256Hash": [
                                "e7df42ca778bc244404bf53c4898d970cc5e79f92d2323b03cd58623b165b73c",
                                "80e3e740713f373fcb66787e960bb47d040b1c3d20ea6dd78522988660bcee89"
                            ],
                            "date": "2021-01-21T13:11:11.615314Z",
                            "md5HashSubject": "6ac92bbad6c6204e0427a71cc5f008a8",
                            "messageBodySnippet": " 8c06b05c 91c2 45d8 8b83 a9cf6b5dad3a",
                            "messageId": "dummy2@mail.gmail.com",
                            "recipient": "dummy@abc.io",
                            "subjectText": "Phishing mail"
                        }
                    ]
                },
                "endTime": "2021-01-21T13:48:44.235359Z",
                "etag": "eVE19krfYNU=",
                "metadata": {
                    "alertId": "dummy_alert_id1",
                    "customerId": "dummy_customer_id",
                    "etag": "eVE19krfYNU=",
                    "severity": "HIGH",
                    "status": "NOT_STARTED",
                    "updateTime": "2021-01-21T13:49:06.315483Z"
                },
                "source": "Gmail phishing",
                "startTime": "2021-01-21T13:08:58.759929Z",
                "type": "User reported phishing",
                "updateTime": "2021-01-21T13:49:06.315483Z"
            },
            {
                "alertId": "dummy_alert_id2",
                "createTime": "2020-12-30T09:50:22.824822Z",
                "customerId": "dummy_customer_id",
                "data": {
                    "@type": "type.googleapis.com/google.apps.alertcenter.type.MailPhishing",
                    "domainId": {
                        "customerPrimaryDomain": "abc.io"
                    },
                    "maliciousEntity": {
                        "fromHeader": "dummyemail2@xyz.com"
                    },
                    "messages": [
                        {
                            "attachmentsSha256Hash": [
                                "32531ceff787967cc8ce01a7a0f4e4726f08d6bd1fb908ab5e3a3ce75eaddcef",
                                "3d1ee332859dae7253fbfa3030c396e8a14aaf08d418b50ee9b0dd07bc4a5f0b",
                                "552e99644f06425564b80bef0113b458d56a8ec2146e611be2333def6b176627"
                            ],
                            "date": "2020-12-30T09:12:03.571179Z",
                            "md5HashSubject": "bfa0c70e989f15ec0da8b52e5430371c",
                            "messageBodySnippet": "dummy message snippet",
                            "messageId": "dummy3@mail.gmail.com",
                            "recipient": "dummy@abc.io",
                            "subjectText": "Fwd: Script"
                        }
                    ]
                },
                "endTime": "2020-12-30T09:47:32.789447Z",
                "etag": "2Bgl3kK2ozA=",
                "metadata": {
                    "alertId": "dummy_alert_id2",
                    "customerId": "dummy_customer_id",
                    "etag": "2Bgl3kK2ozA=",
                    "severity": "HIGH",
                    "status": "NOT_STARTED",
                    "updateTime": "2020-12-30T09:50:22.824822Z"
                },
                "source": "Gmail phishing",
                "startTime": "2020-12-30T09:12:03.571179Z",
                "type": "User reported phishing",
                "updateTime": "2020-12-30T09:50:22.824822Z"
            }
        ],
        "PageToken": {
            "Alert": {
                "name": "gsac-alert-list",
                "nextPageToken": "ChAKDmIMCIKp9IAGEIjg2ABCDxNaWNJbmNpZGVudHMiADoIMDNwdWVraGQ"
            }
        }
    }
}

Human Readable Output

Alerts

Alert Id Alert Type Source Severity Status Create Time Update Time
dummy_alert_id1 User reported phishing Gmail phishing HIGH NOT_STARTED 2021-01-21T13:49:06.315483Z 2021-01-21T13:49:06.315483Z
dummy_alert_id2 User reported phishing Gmail phishing HIGH NOT_STARTED 2020-12-30T09:50:22.824822Z 2020-12-30T09:50:22.824822Z

Next Page Token

ChAKDmIMCIKp9IAGEIjg2ABCDxNaWNJbmNpZGVudHMiADoIMDNwdWVraGQ

gsac-alert-get


Gets the specified alert.
Note: To get the alert_id, execute the gsac-alert-list command.

Base Command

gsac-alert-get

Input

Argument Name Description Required
alert_id The identifier of the alert to retrieve. Required

Context Output

Path Type Description
GSuiteSecurityAlert.Alert.customerId String The unique identifier of the Google account of the customer.
GSuiteSecurityAlert.Alert.alertId String The unique identifier for the alert.
GSuiteSecurityAlert.Alert.createTime Date The time the alert was created.
GSuiteSecurityAlert.Alert.startTime Date The time the event that caused the alert was started or detected.
GSuiteSecurityAlert.Alert.endTime Date The time the event that caused the alert ceased being active.
GSuiteSecurityAlert.Alert.type String The type of the alert.
GSuiteSecurityAlert.Alert.source String A unique identifier for the system that reported the alert.
GSuiteSecurityAlert.Alert.deleted Boolean True if the alert is marked for deletion.
GSuiteSecurityAlert.Alert.securityInvestigationToolLink String An optional Security Investigation Tool query for the alert.
GSuiteSecurityAlert.Alert.updateTime Date The time the alert was last updated.
GSuiteSecurityAlert.Alert.etag String ETag is used for optimistic concurrency control as a way to help prevent simultaneous updates of an alert from overwriting each other.
GSuiteSecurityAlert.Alert.metadata.customerId String The unique identifier of the Google account of the customer.
GSuiteSecurityAlert.Alert.metadata.alertId String The unique identifier of the alert.
GSuiteSecurityAlert.Alert.metadata.status String The current status of the alert.
GSuiteSecurityAlert.Alert.metadata.assignee String The email address of the user assigned to the alert.
GSuiteSecurityAlert.Alert.metadata.updateTime String The time the metadata was last updated.
GSuiteSecurityAlert.Alert.metadata.severity String The severity value of the alert.
GSuiteSecurityAlert.Alert.metadata.etag String ETag is used for optimistic concurrency control as a way to help prevent simultaneous updates of an alert metadata from overwriting each other.
GSuiteSecurityAlert.Alert.data.@type String Alert data of the alert types.
GSuiteSecurityAlert.Alert.data.takeoutRequestId String The takeout request ID.
GSuiteSecurityAlert.Alert.data.email String The email of the associated event. Domain wide takeout initiated applies to the email of the admin who initiated the takeout. Google identity applies to the email of the user who the event belongs to. Mobile device management/State Sponsored Attack applies to the email of the user the alert was created for.
GSuiteSecurityAlert.Alert.data.domainId.customerPrimaryDomain String The primary domain for the customer.
GSuiteSecurityAlert.Alert.data.maliciousEntity.entity.emailAddress String Email address of the user.
GSuiteSecurityAlert.Alert.data.maliciousEntity.entity.displayName String Display name of the user.
GSuiteSecurityAlert.Alert.data.maliciousEntity.fromHeader String The sender email address.
GSuiteSecurityAlert.Alert.data.maliciousEntity.displayName String The header from the display name.
GSuiteSecurityAlert.Alert.data.messages.messageId String The message ID.
GSuiteSecurityAlert.Alert.data.messages.md5HashMessageBody String The hash of the message body text.
GSuiteSecurityAlert.Alert.data.messages.messageBodySnippet String The snippet of the message body text (only available for reported emails).
GSuiteSecurityAlert.Alert.data.messages.md5HashSubject String The MD5 hash of the email’s subject (only available for reported emails).
GSuiteSecurityAlert.Alert.data.messages.subjectText String The email subject text (only available for reported emails).
GSuiteSecurityAlert.Alert.data.messages.attachmentsSha256Hash Unknown The list of SHA256 hashes of the email’s attachment and all MIME parts.
GSuiteSecurityAlert.Alert.data.messages.recipient String The recipient of the email.
GSuiteSecurityAlert.Alert.data.messages.date Date The date the malicious email was sent.
GSuiteSecurityAlert.Alert.data.isInternal Boolean If true, the email originated from within the organization.
GSuiteSecurityAlert.Alert.data.systemActionType String System actions on the messages.
GSuiteSecurityAlert.Alert.data.sourceIp String The source IP address of the malicious email, for example, 127.0.0.1.
GSuiteSecurityAlert.Alert.data.loginDetails.loginTime Date The successful login time that is associated with the warning event. This is not present for blocked login attempts.
GSuiteSecurityAlert.Alert.data.loginDetails.ipAddress String The human-readable IP address (for example, 11.22.33.44) that is associated with the warning event.
GSuiteSecurityAlert.Alert.data.title String A one-line incident description.
GSuiteSecurityAlert.Alert.data.description String Description of the associated alert type.
GSuiteSecurityAlert.Alert.data.affectedUserEmails Unknown The list of emails that correspond to the users directly affected by the incident.
GSuiteSecurityAlert.Alert.data.attachmentData.csv.headers Unknown The list of headers for the data columns in a CSV file.
GSuiteSecurityAlert.Alert.data.attachmentData.csv.dataRows.entries Unknown The data entries in a CSV file row, as a string array rather than a single comma-separated string.
GSuiteSecurityAlert.Alert.data.events.deviceId String The device ID.
GSuiteSecurityAlert.Alert.data.events.serialNumber String The serial number of the device.
GSuiteSecurityAlert.Alert.data.events.deviceType String The type of the device.
GSuiteSecurityAlert.Alert.data.events.deviceModel String The model of the device.
GSuiteSecurityAlert.Alert.data.events.resourceId String The device resource ID.
GSuiteSecurityAlert.Alert.data.events.iosVendorId String iOS vendor ID. Required for iOS, empty for others.
GSuiteSecurityAlert.Alert.data.events.deviceCompromisedState String The device compromised state. Possible values are “Compromised” or “Not Compromised”.
GSuiteSecurityAlert.Alert.data.events.deviceProperty String The device property that was changed.
GSuiteSecurityAlert.Alert.data.events.oldValue String The old value of the device property before the change.
GSuiteSecurityAlert.Alert.data.events.newValue String The new value of the device property after the change.
GSuiteSecurityAlert.Alert.data.requestInfo.appKey String The application that requires the SQL setup.
GSuiteSecurityAlert.Alert.data.requestInfo.appDeveloperEmail Unknown List of app developers who triggered notifications for the application.
GSuiteSecurityAlert.Alert.data.requestInfo.numberOfRequests String Number of requests sent for the application to set up default SQL instances.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.ruleInfo.resourceName String Resource name that uniquely identifies the rule.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.ruleInfo.displayName String User provided name of the rule.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.dataSource String Source of the data.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.trigger String Trigger of the rule.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.triggeringUserEmail String Email of the user who caused the violation.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.recipients Unknown Resource recipients.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.resourceInfo.resourceTitle String Title of the resource, for example email subject, or document title.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.resourceInfo.documentId String Drive file ID.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.matchInfo.userDefinedDetector.resourceName String Resource name that uniquely identifies the detector.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.matchInfo.userDefinedDetector.displayName String Display name of the detector.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.matchInfo.predefinedDetector.detectorName String Name that uniquely identifies the detector.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.triggeredActionTypes Unknown Actions applied as a consequence of the rule being triggered.
GSuiteSecurityAlert.Alert.data.ruleViolationInfo.suppressedActionTypes Unknown Actions suppressed due to other actions with higher priority.
GSuiteSecurityAlert.Alert.data.name String Rule name.
GSuiteSecurityAlert.Alert.data.displayName String Alert display name.
GSuiteSecurityAlert.Alert.data.windowSize String Rule window size. Possible values are 1 hour or 24 hours.
GSuiteSecurityAlert.Alert.data.threshold String Alert threshold, for example “COUNT > 5”.
GSuiteSecurityAlert.Alert.data.createTime Date The timestamp indicating when the rule was created.
GSuiteSecurityAlert.Alert.data.updateTime Date The timestamp indicating when the rule was last updated.
GSuiteSecurityAlert.Alert.data.triggerSource String The trigger sources for the rule.
GSuiteSecurityAlert.Alert.data.supersededAlerts Unknown List of alert IDs superseded by the alert.
GSuiteSecurityAlert.Alert.data.supersedingAlerts String Alert ID superseding the alert.
GSuiteSecurityAlert.Alert.data.actionNames Unknown List of action names associated with the rule threshold.
GSuiteSecurityAlert.Alert.data.query String Query that is used to get the data from the associated source.

Command Example

!gsac-alert-get alert_id=dummy_alert_id4

Context Example

{
    "GSuiteSecurityAlert": {
        "Alert": {
            "alertId": "dummy_alert_id4",
            "createTime": "2020-10-28T20:43:34.381Z",
            "customerId": "dummy_customer_id",
            "data": {
                "@type": "type.googleapis.com/google.apps.alertcenter.type.AccountWarning",
                "email": "dummy@abc.io",
                "loginDetails": {
                    "ipAddress": "2570:ab56:c548:85a0:2567:7025:3963:183"
                }
            },
            "endTime": "2020-10-28T20:13:33Z",
            "etag": "dummyetag=",
            "metadata": {
                "alertId": "dummy_alert_id4",
                "customerId": "dummy_customer_id",
                "etag": "dummyetag=",
                "severity": "LOW",
                "status": "NOT_STARTED",
                "updateTime": "2020-10-28T20:43:34.381Z"
            },
            "source": "Google identity",
            "startTime": "2020-10-28T20:13:33Z",
            "type": "Suspicious login",
            "updateTime": "2020-10-28T20:43:34.381Z"
        }
    }
}

Human Readable Output

Alert

Alert Id Alert Type Source Severity Status Create Time Update Time
dummy_alert_id4 Suspicious login Google identity LOW NOT_STARTED 2020-10-28T20:43:34.381Z 2020-10-28T20:43:34.381Z

gsac-alert-feedback-create


Creates new feedback for an alert.

Base Command

gsac-alert-feedback-create

Input

Argument Name Description Required
alert_id The identifier of the alert the feedback belongs to. Required
feedback_type The type of alert feedback. Possible values are: ALERT_FEEDBACK_TYPE_UNSPECIFIED, NOT_USEFUL, SOMEWHAT_USEFUL, VERY_USEFUL. Required

Context Output

Path Type Description
GSuiteSecurityAlert.Feedback.customerId String The unique identifier of the Google account of the customer.
GSuiteSecurityAlert.Feedback.alertId String The alert identifier.
GSuiteSecurityAlert.Feedback.feedbackId String The unique identifier for the feedback.
GSuiteSecurityAlert.Feedback.createTime String The time the feedback was created.
GSuiteSecurityAlert.Feedback.type String The type of the feedback.
GSuiteSecurityAlert.Feedback.email String The email of the user who provided the feedback.

Command Example

!gsac-alert-feedback-create alert_id=dummy_alert_id4 feedback_type=VERY_USEFUL

Context Example

{
    "GSuiteSecurityAlert": {
        "Feedback": {
            "alertId": "dummy_alert_id4",
            "createTime": "2021-02-01T11:56:27.455114Z",
            "customerId": "dummy_customer_id",
            "email": "dummy@abc.io",
            "feedbackId": "dummy_feedback_id",
            "type": "VERY_USEFUL"
        }
    }
}

Human Readable Output

Feedback details

Feedback ID Alert ID Create Time Feedback Type Email
dummy_feedback_id dummy_alert_id4 2021-02-01T11:56:27.455114Z VERY_USEFUL dummy@abc.io

gsac-alert-feedback-list


Lists all the feedback for an alert.

Base Command

gsac-alert-feedback-list

Input

Argument Name Description Required
alert_id The identifier of the alert the feedbacks belong to. The “-“ wildcard can be used to represent all alerts. Required
filter A query string for filtering alert results. For example: alert_id = alertId01 AND feedback_id = feedbackId01. Optional
page_size The requested page size. Default is 50. Optional

Context Output

Path Type Description
GSuiteSecurityAlert.Feedback.customerId String The unique identifier of the Google account of the customer.
GSuiteSecurityAlert.Feedback.alertId String The alert identifier.
GSuiteSecurityAlert.Feedback.feedbackId String The unique identifier for the feedback.
GSuiteSecurityAlert.Feedback.createTime String The time the feedback was created.
GSuiteSecurityAlert.Feedback.type String The type of the feedback.
GSuiteSecurityAlert.Feedback.email String The email of the user who provided the feedback.

Command Example

!gsac-alert-feedback-list alert_id=dummy_alert_id5

Context Example

{
    "GSuiteSecurityAlert": {
        "Feedback": [
            {
                "alertId": "dummy_alert_id5",
                "createTime": "2021-01-21T12:06:22.350408Z",
                "customerId": "dummy_customer_id",
                "email": "dummy@abc.io",
                "feedbackId": "dummy_feedback_id1",
                "type": "NOT_USEFUL"
            },
            {
                "alertId": "dummy_alert_id5",
                "createTime": "2021-01-21T11:56:53.489471Z",
                "customerId": "dummy_customer_id",
                "email": "dummy@abc.io",
                "feedbackId": "dummy_feedback_id2",
                "type": "NOT_USEFUL"
            },
            {
                "alertId": "dummy_alert_id5",
                "createTime": "2021-01-20T20:11:20.343391Z",
                "customerId": "dummy_customer_id",
                "email": "dummy@abc.io",
                "feedbackId": "dummy_feedback_id3",
                "type": "VERY_USEFUL"
            }
        ]
    }
}

Human Readable Output

Feedback details

Feedback ID Alert ID Create Time Feedback Type Email
dummy_feedback_id1 dummy_alert_id5 2021-01-21T12:06:22.350408Z NOT_USEFUL dummy@abc.io
dummy_feedback_id2 dummy_alert_id5 2021-01-21T11:56:53.489471Z NOT_USEFUL dummy@abc.io
dummy_feedback_id3 dummy_alert_id5 2021-01-20T20:11:20.343391Z VERY_USEFUL dummy@abc.io

gsac-alert-delete


Performs delete operation on alerts.
Note: The alerts that are marked for deletion will be removed from the Alert Center after 30 days.

Base Command

gsac-alert-delete

Input

Argument Name Description Required
alert_id Comma-separated list of alert IDs to be marked for deletion. Required

Context Output

Path Type Description
GSuiteSecurityAlert.Delete.successAlerts.id Unknown The list of alert IDs successfully marked for deletion.
GSuiteSecurityAlert.Delete.failedAlerts.id Unknown The list of alert IDs failed to be marked for deletion.
GSuiteSecurityAlert.Delete.failedAlerts.code Number The status code of the failed alerts.
GSuiteSecurityAlert.Delete.failedAlerts.message String A developer-facing error message.
GSuiteSecurityAlert.Delete.failedAlerts.status String Status of the failed alert deletion.
GSuiteSecurityAlert.Delete.successAlerts.status String Status of the successful alert deletion.

Note: The gsac-alert-delete command returns a custom context that contains the status key that shows the status of the alert ID marked for deletion.

Command Example

!gsac-alert-delete alert_id="dummy_alert_id1,dummy_alert_id2"

Context Example

{
    "GSuiteSecurityAlert": {
        "Delete": {
            "failedAlerts": {
                "code": 5,
                "id": "dummy_alert_id2",
                "message": "NOT_FOUND",
                "status": "Fail"
            },
            "successAlerts": {
                "id": "dummy_alert_id1",
                "status": "Success"
            }
        }
    }
}

Human Readable Output

Delete Alerts

Alert ID Status
dummy_alert_id1 Success
dummy_alert_id2 Fail (NOT_FOUND)

gsac-alert-recover


Performs a recover operation on alerts.
Note: Recovers the alerts that were marked for deletion within the past 30 days.

Base Command

gsac-alert-recover

Input

Argument Name Description Required
alert_id Comma-separated list of alert IDs to be recovered. Required

Context Output

Path Type Description
GSuiteSecurityAlert.Recover.successAlerts.id Unknown The list of alert IDs successfully marked for recovery.
GSuiteSecurityAlert.Recover.failedAlerts.id Unknown The list of alert IDs failed to be marked for recovery.
GSuiteSecurityAlert.Recover.failedAlerts.code Number The status code of the failed alerts to be recovered.
GSuiteSecurityAlert.Recover.failedAlerts.message String A developer-facing error message for the failed alerts.
GSuiteSecurityAlert.Recover.successAlerts.status String Status of the successful alert recovery.
GSuiteSecurityAlert.Recover.failedAlerts.status String Status of the failed alert recovery.

Note: The gsac-alert-recover command returns a custom context that contains the status key that shows the status of the alert ID marked for recovery.

Command Example

!gsac-alert-recover alert_id="dummy_alert_id1,dummy_alert_id2"

Context Example

{
    "GSuiteSecurityAlert": {
        "Recover": {
            "failedAlerts": {
                "code": 5,
                "id": "dummy_alert_id2",
                "message": "NOT_FOUND",
                "status": "Fail"
            },
            "successAlerts": {
                "id": "dummy_alert_id1",
                "status": "Success"
            }
        }
    }
}

Human Readable Output

Recover Alerts

Alert ID Status
dummy_alert_id1 Success
dummy_alert_id2 Fail (NOT_FOUND)

Configuration parameters

  • isFetch — Fetch incidents
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • user_service_account_json — Service Account JSON
  • admin_email_creds — Admin Email
  • admin_email — Admin Email
  • max_fetch — Maximum number of incidents per fetch (required)
  • first_fetch — First fetch time interval
  • alert_type — Alert Type
  • filter — Filter
  • fetch_feedback — Fetch feedback
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (6)

  • gsac-alert-delete

    Performs delete operation on alerts. Note: The alerts that are marked for deletion will be removed from the Alert Center after 30 days.

  • gsac-alert-feedback-create

    Creates new feedback for an alert.

  • gsac-alert-feedback-list

    Lists all the feedback for an alert.

  • gsac-alert-get

    Gets the specified alert. Note: To get the alert_id, execute the gsac-alert-list command.

  • gsac-alert-list

    Lists the alerts. Use the filter to filter the list by various alert types. Note: The alerts that have been marked for deletion in the past 30 days will still be visible.

  • gsac-alert-recover

    Performs a recover operation on alerts. Note: Recovers the alerts that were marked for deletion within the past 30 days.

from CommonServerPython import *

from CommonServerUserPython import *

""" IMPORTS """

import traceback
from collections.abc import Callable
from datetime import datetime
from typing import Any

import urllib3

# Disable insecure warnings
urllib3.disable_warnings()

""" CONSTANTS """

DEFAULT_FIRST_FETCH = "3 days"
DEFAULT_MAX_FETCH = 15
BASE_URL = "https://alertcenter.googleapis.com/"
NEXT_PAGE_TOKEN = "### Next Page Token:\n{}\n"
LIST_FEEDBACK_PAGE_SIZE = 50

URL_SUFFIX: dict[str, str] = {
    "LIST_ALERTS": "v1beta1/alerts",
    "FEEDBACK": "v1beta1/alerts/{0}/feedback",
    "GET_ALERT": "v1beta1/alerts/{}",
    "BATCH_DELETE": "v1beta1/alerts:batchDelete",
    "BATCH_RECOVER": "v1beta1/alerts:batchUndelete",
}

OUTPUT_PATHS = {
    "ALERT": "GSuiteSecurityAlert.Alert(val.alertId == obj.alertId)",
    "TOKEN": "GSuiteSecurityAlert.PageToken.Alert(val.name == val.name)",
    "FEEDBACK": "GSuiteSecurityAlert.Feedback",
    "BATCH_DELETE_SUCCESS": "GSuiteSecurityAlert.Delete.successAlerts(val.id && val.id == obj.id)",
    "BATCH_DELETE_FAILED": "GSuiteSecurityAlert.Delete.failedAlerts(val.id && val.id == obj.id)",
    "BATCH_RECOVER_SUCCESS": "GSuiteSecurityAlert.Recover.successAlerts(val.id && val.id == obj.id)",
    "BATCH_RECOVER_FAILED": "GSuiteSecurityAlert.Recover.failedAlerts(val.id && val.id == obj.id)",
}

MESSAGES: dict[str, str] = {
    "TEST_CONNECTIVITY_FAILED_ERROR": "Test connectivity failed. Check the configuration parameters provided.",
    "INTEGER_ERROR": "The argument {} must be a positive integer.",
    "MAX_INCIDENT_ERROR": "Value of maximum number of incidents to fetch every time must be a positive integer "
    "between 1 and 1000.",
    "INVALID_FEEDBACK_TYPE_ERROR": "The given value for feedback type is invalid. Valid feedback types: "
    "ALERT_FEEDBACK_TYPE_UNSPECIFIED, NOT_USEFUL, SOMEWHAT_USEFUL, VERY_USEFUL.",
    "NO_RECORDS_FOUND": "No {} were found for the given argument(s).",
    "MISSING_REQUIRED_ARGUMENTS_ERROR": "Required argument(s): {}.",
    "INVALID_FILTER": "Invalid createTime parameter in Filter. To fetch alerts using createTime, use the first fetch "
    "time interval parameter.",
    "INVALID_PARAM_VALUE_ERROR": "The given value for {0} parameter is invalid.",
}

DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
SCOPES: dict[str, list[str]] = {"ALERT": ["https://www.googleapis.com/auth/apps.alerts"]}
ALERT_FEEDBACK_TYPES = ["alert_feedback_type_unspecified", "not_useful", "somewhat_useful", "very_useful"]


def validate_date(first_fetch) -> str:
    """
    Check whether the date provided is valid.

    :type first_fetch: string
    :param first_fetch: date

    :return: raise ValueError if validation fails, else return parsed date.
    :rtype: str
    """
    date_time = arg_to_datetime(first_fetch, is_utc=True, settings={"STRICT_PARSING": True})
    if date_time:
        create_time = date_time.strftime(DATE_FORMAT)
    else:
        raise ValueError(MESSAGES["INVALID_PARAM_VALUE_ERROR"].format("first fetch time interval"))

    current_time = datetime.utcnow()
    date_time_obj = datetime.strptime(create_time, DATE_FORMAT)

    if date_time_obj > current_time:
        raise ValueError(MESSAGES["INVALID_PARAM_VALUE_ERROR"].format("first fetch time interval"))

    return create_time


def validate_params_for_fetch_incidents(params: dict[str, Any], last_run: dict) -> tuple[dict[str, Any], str]:
    """
    Validates parameters for fetch-incidents command.

    :param params: parameters dictionary.
    :param last_run: A dict with a key containing the latest incident created time we got
        from last fetch

    :return: A tuple containing two elements:
            params (``Dict[str, int]``): fetch incident parameters.
            last_fetch (``str``): updated last_fetch.
    """
    params = GSuiteClient.remove_empty_entities(params)

    # get user provided fetch_first interval
    first_fetch = params.get("first_fetch")
    first_fetch = first_fetch if first_fetch else DEFAULT_FIRST_FETCH

    # Validate the first_fetch value
    create_time = validate_date(first_fetch)

    # get the last_fetch value
    last_fetch = last_run["last_fetch"] if last_run.get("last_fetch") else create_time

    # get the user provided max_fetch value
    max_fetch = params.get("max_fetch", DEFAULT_MAX_FETCH)
    max_fetch = GSuiteClient.validate_get_int(max_fetch, limit=1000, message=MESSAGES["MAX_INCIDENT_ERROR"])

    next_page_token = last_run.get("next_page_token", "")

    # set the filter with last_fetch value
    alert_filter = f'createTime>"{last_fetch}"'

    # get user provided filter value and validate it
    advance_filter = params.get("filter", "").replace("'", '"')
    if "createTime" in advance_filter or "create_time" in advance_filter:
        raise ValueError(MESSAGES["INVALID_FILTER"])

    if "type" not in advance_filter:
        alert_types = params.get("alert_type", [])

        for index in range(len(alert_types)):
            alert_types[index] = alert_types[index].replace('"', "").replace("'", "").strip()
            if index == 0:
                alert_filter += f' AND (type="{alert_types[index]}"'
            else:
                alert_filter += f' OR type="{alert_types[index]}"'

        if "(" in alert_filter:
            alert_filter += ")"

    if advance_filter != "":
        alert_filter += f" AND {advance_filter}"

    # If next_page_token is present replace filter with filter present in last_run as API does not support
    # change in filter.
    if next_page_token:
        alert_filter = last_run.get("alert_filter", "")

    updated_params = {"filter": alert_filter, "pageSize": max_fetch, "pageToken": next_page_token, "orderBy": "createTime asc"}
    return GSuiteClient.remove_empty_entities(updated_params), last_fetch


def validate_params_for_list_alerts(args: dict[str, str]) -> dict[str, Any]:
    """
    Prepares arguments for list alerts.

    :param args: Command arguments.
    :return: Prepared params.
    :raises ValueError: If there any invalid value of argument.
    """
    page_size = args.get("page_size", "")
    page_size = (
        int(page_size)
        if page_size == "0"
        else GSuiteClient.validate_get_int(page_size, message=MESSAGES["INTEGER_ERROR"].format("page_size"))
    )

    alert_filter = args.get("filter", "")
    if alert_filter:
        alert_filter = alert_filter.replace("'", '"')

    params = {
        "pageToken": args.get("page_token", ""),
        "pageSize": page_size,
        "filter": alert_filter,
        "orderBy": args.get("order_by", ""),
    }

    return GSuiteClient.remove_empty_entities(params)


def prepare_hr_for_alerts(alerts: list[dict[str, Any]], header: str) -> str:
    """
    Prepare the Human readable info for alerts command.

    :param alerts: The alerts data.
    :param header: Header of the hr table.
    :return: Human readable.
    """

    hr_list = []
    for record in alerts:
        hr_record = {
            "Alert ID": record.get("alertId", ""),
            "Create Time": record.get("createTime", ""),
            "Update Time": record.get("updateTime", ""),
            "Alert Type": record.get("type", ""),
            "Source": record.get("source", ""),
            "Severity": record.get("metadata", {}).get("severity", ""),
            "Status": record.get("metadata", {}).get("status", ""),
        }
        hr_list.append(hr_record)

    return tableToMarkdown(
        header, hr_list, ["Alert ID", "Alert Type", "Source", "Severity", "Status", "Create Time", "Update Time"], removeNull=True
    )


def prepare_hr_for_alert_feedback(feedbacks: list[dict[str, Any]]) -> str:
    """
    Prepare the Human readable info for create alert feedback command.

    :param feedbacks: The created feedback data.
    :return: Human readable.
    """

    hr_table: list[dict[str, Any]] = []
    for feedback in feedbacks:
        hr_table.append(
            {
                "Feedback ID": feedback.get("feedbackId", ""),
                "Alert ID": feedback.get("alertId", ""),
                "Create Time": feedback.get("createTime", ""),
                "Feedback Type": feedback.get("type", ""),
                "Email": feedback.get("email", ""),
            }
        )
    return tableToMarkdown(
        "Feedback Details", hr_table, ["Feedback ID", "Alert ID", "Create Time", "Feedback Type", "Email"], removeNull=True
    )


def prepare_hr_for_batch_command(response: dict[str, Any], method: str) -> str:
    """
    Prepare the Human readable info for batch delete and recover alerts command.

    :param response: The delete and recover alerts data.
    :param method: To delete or recover alerts.
    :return: Human readable.
    """

    hr_list: list[dict[str, Any]] = []
    for each_success_id in response.get("successAlertIds", []):
        hr_record = {"Alert ID": each_success_id, "Status": "Success"}
        hr_list.append(hr_record)

    for each_fail_key, val in response.get("failedAlertStatus", {}).items():
        hr_record = {"Alert ID": each_fail_key, "Status": f'Fail ({val.get("message")})'}
        hr_list.append(hr_record)

    return tableToMarkdown(name=method, t=hr_list, headers=["Alert ID", "Status"], removeNull=True)


def create_custom_context_for_batch_command(response: dict[str, Any]) -> tuple[list, list]:
    """
    Prepare the custom Context Output for batch delete and recover alerts command.

    :param response: The batch delete and recover alerts data.
    :return: Success alerts list and failed alerts list
    """
    success_list: list = []
    failed_list: list = []
    for each_id in response.get("successAlertIds", []):
        success_obj: dict[str, Any] = {"id": each_id, "status": "Success"}
        success_list.append(success_obj)

    for failed_key, value in response.get("failedAlertStatus", {}).items():
        failed_alert_id: dict[str, Any] = {
            "id": failed_key,
            "status": "Fail",
            "code": value.get("code"),
            "message": value.get("message", ""),
        }
        failed_list.append(failed_alert_id)

    return success_list, failed_list


def check_required_arguments(required_arguments: list[str], args: dict[str, Any]):
    """
    Checks if the required arguments after trimming the spaces are non empty

    :param required_arguments: List of required arguments in a command
    :param args: command parameters
    :return: Raises ValueError if any required arguments are missing
    """
    missing_args = []
    for arg in required_arguments:
        if arg not in args:
            missing_args.append(arg)
    if missing_args:
        raise ValueError(MESSAGES["MISSING_REQUIRED_ARGUMENTS_ERROR"].format(", ".join(missing_args)))


""" COMMAND FUNCTIONS """


@logger
def test_module(gsuite_client, last_run: dict, params: dict[str, Any]) -> str:
    """
    Performs test connectivity by valid http response

    :param gsuite_client: client object which is used to get response from api.
    :param last_run: Demisto last run dictionary.
    :param params: configuration parameters.

    :return: raise ValueError if any error occurred during connection
    :raises DemistoException: If there is any other issues while making the http call.
    """
    if params.get("isFetch"):
        fetch_incidents(gsuite_client, last_run, params, is_test=True)
    else:
        list_alerts_params = {
            "pageSize": 1,
        }
        gsuite_client.set_authorized_http(
            scopes=SCOPES["ALERT"], subject=params.get("admin_email_creds", {}).get("identifier") or params.get("admin_email", "")
        )
        gsuite_client.http_request(url_suffix=URL_SUFFIX["LIST_ALERTS"], method="GET", params=list_alerts_params)

        if not gsuite_client.credentials.valid:
            raise DemistoException(MESSAGES["TEST_CONNECTIVITY_FAILED_ERROR"])

    return "ok"


@logger
def gsac_list_alerts_command(client, args: dict[str, str]) -> CommandResults:
    """
    List alerts from G Suite Security Alert center.

    :param client: Client object.
    :param args: Command arguments.

    :return: Command Result.
    """
    # Prepare params
    admin_email = args.get("admin_email")
    params = validate_params_for_list_alerts(args)

    # API Call
    client.set_authorized_http(scopes=SCOPES["ALERT"], subject=admin_email)
    response = client.http_request(url_suffix=URL_SUFFIX["LIST_ALERTS"], method="GET", params=params)

    total_records = response.get("alerts", [])
    if not total_records:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("alert(s)"))

    token_ec = {}

    # Creating human-readable
    readable_hr = prepare_hr_for_alerts(total_records, "Alerts")
    if response.get("nextPageToken"):
        readable_hr += NEXT_PAGE_TOKEN.format(response.get("nextPageToken"))
        token_ec = {"name": "gsac-alert-list", "nextPageToken": response.get("nextPageToken")}

    # Creating entry context
    output = {OUTPUT_PATHS["ALERT"]: total_records, OUTPUT_PATHS["TOKEN"]: token_ec}

    output = GSuiteClient.remove_empty_entities(output)

    return CommandResults(outputs=output, readable_output=readable_hr, raw_response=response)


@logger
def gsac_get_alert_command(client, args: dict[str, str]) -> CommandResults:
    """
    Get a single alert from G Suite Security Alert center.

    :param client: Client object.
    :param args: Command arguments.

    :return: Command Result.
    """
    # Check if required arguments are present
    check_required_arguments(required_arguments=["alert_id"], args=args)

    # Prepare params
    admin_email = args.get("admin_email")
    alert_id = args.get("alert_id", "")

    # API Call
    client.set_authorized_http(scopes=SCOPES["ALERT"], subject=admin_email)
    response = client.http_request(url_suffix=URL_SUFFIX["GET_ALERT"].format(alert_id), method="GET")

    if not response:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("alert"))

    # Creating entry context
    custom_ec_for_alerts = GSuiteClient.remove_empty_entities(response)

    # Creating human-readable
    readable_hr = prepare_hr_for_alerts([response], "Alert")

    return CommandResults(
        outputs_prefix="GSuiteSecurityAlert.Alert",
        outputs_key_field="alertId",
        outputs=custom_ec_for_alerts,
        readable_output=readable_hr,
        raw_response=response,
    )


@logger
def gsac_batch_delete_alerts_command(client, args: dict[str, str]) -> CommandResults:
    """
    Performs batch delete operation on alerts.

    :param client: Client object.
    :param args: Command arguments.

    :return: Command Result.
    """
    # Check if required arguments are present
    check_required_arguments(required_arguments=["alert_id"], args=args)

    # Prepare params
    json_body: dict[str, Any] = {}
    admin_email = args.get("admin_email")

    ids = argToList(args.get("alert_id", []), ",")

    json_body["alertId"] = ids

    # API Call
    client.set_authorized_http(scopes=SCOPES["ALERT"], subject=admin_email)
    batch_delete_response = client.http_request(url_suffix=URL_SUFFIX["BATCH_DELETE"], method="POST", body=json_body)

    # Create entry context
    success_list, failed_list = create_custom_context_for_batch_command(batch_delete_response)
    custom_context: dict[str, Any] = {
        OUTPUT_PATHS["BATCH_DELETE_SUCCESS"]: success_list,
        OUTPUT_PATHS["BATCH_DELETE_FAILED"]: failed_list,
    }

    # Create HR
    hr = prepare_hr_for_batch_command(batch_delete_response, "Delete Alerts")

    return CommandResults(
        outputs=GSuiteClient.remove_empty_entities(custom_context), readable_output=hr, raw_response=batch_delete_response
    )


@logger
def gsac_batch_recover_alerts_command(client, args: dict[str, str]) -> CommandResults:
    """
    Performs batch recover operation on alerts.

    :param client: Client object.
    :param args: Command arguments.

    :return: Command Result.
    """
    # Check if required arguments are present
    check_required_arguments(required_arguments=["alert_id"], args=args)

    # Prepare params
    json_body: dict[str, Any] = {}
    admin_email = args.get("admin_email")

    ids = argToList(args.get("alert_id", []), ",")

    json_body["alertId"] = ids

    # API Call
    client.set_authorized_http(scopes=SCOPES["ALERT"], subject=admin_email)
    batch_recover_response = client.http_request(url_suffix=URL_SUFFIX["BATCH_RECOVER"], method="POST", body=json_body)

    # Create entry context
    success_list, failed_list = create_custom_context_for_batch_command(batch_recover_response)
    custom_context: dict[str, Any] = {
        OUTPUT_PATHS["BATCH_RECOVER_SUCCESS"]: success_list,
        OUTPUT_PATHS["BATCH_RECOVER_FAILED"]: failed_list,
    }

    # Create HR
    hr = prepare_hr_for_batch_command(batch_recover_response, "Recover Alerts")

    return CommandResults(
        outputs=GSuiteClient.remove_empty_entities(custom_context), readable_output=hr, raw_response=batch_recover_response
    )


@logger
def gsac_create_alert_feedback_command(gsuite_client, args: dict[str, Any]) -> CommandResults:
    """
    Creates new feedback for an alert.

    :param gsuite_client: client object which is used to get response from api.
    :param args: command parameters.

    :return: CommandResults or raise ValueError if any error occurred.
    :raises DemistoException: If there is any other issues while making the http call.
    """

    # Check if required arguments are present
    check_required_arguments(required_arguments=["alert_id", "feedback_type"], args=args)

    # Prepare Params
    json_body: dict[str, Any] = {}
    params: dict[str, Any] = {}
    admin_email = args.get("admin_email")

    if args["feedback_type"].lower() not in ALERT_FEEDBACK_TYPES:
        raise ValueError(MESSAGES["INVALID_FEEDBACK_TYPE_ERROR"])

    json_body["type"] = args["feedback_type"]

    # API call
    gsuite_client.set_authorized_http(scopes=SCOPES["ALERT"], subject=admin_email)
    create_feedback_response = gsuite_client.http_request(
        url_suffix=URL_SUFFIX["FEEDBACK"].format(args["alert_id"]), method="POST", body=json_body, params=params
    )

    # Create HR
    hr = prepare_hr_for_alert_feedback([create_feedback_response])

    # Create entry context
    custom_ec = gsuite_client.remove_empty_entities(create_feedback_response)

    return CommandResults(
        outputs_prefix=OUTPUT_PATHS["FEEDBACK"],
        outputs_key_field="feedbackId",
        outputs=custom_ec,
        readable_output=hr,
        raw_response=create_feedback_response,
    )


@logger
def gsac_list_alert_feedback_command(gsuite_client, args: dict[str, Any]) -> CommandResults:
    """
    Lists all the feedback for an alert.

    :param gsuite_client: client object which is used to get response from api.
    :param args: command parameters.

    :return: CommandResults or raise ValueError if any error occurred.
    :raises DemistoException: If there is any other issues while making the http call.
    """

    # Check if required arguments are present
    check_required_arguments(required_arguments=["alert_id"], args=args)

    # Prepare params
    params: dict[str, Any] = {
        "filter": args.get("filter", "").replace("'", '"'),
    }
    admin_email = args.get("admin_email")
    page_size = args.get("page_size", LIST_FEEDBACK_PAGE_SIZE)
    page_size = GSuiteClient.validate_get_int(page_size, message=MESSAGES["INTEGER_ERROR"].format("page_size"))

    # API call
    gsuite_client.set_authorized_http(scopes=SCOPES["ALERT"], subject=admin_email)
    list_alert_feedback_response = gsuite_client.http_request(
        url_suffix=URL_SUFFIX["FEEDBACK"].format(args["alert_id"]),
        method="GET",
        params=GSuiteClient.remove_empty_entities(params),
    )

    no_records = len(list_alert_feedback_response.get("feedback", [])) == 0
    if no_records:
        return CommandResults(readable_output=MESSAGES["NO_RECORDS_FOUND"].format("feedback(s)"))

    list_alert_feedback_response["feedback"] = list_alert_feedback_response["feedback"][0:page_size]
    # Create HR
    hr = prepare_hr_for_alert_feedback(list_alert_feedback_response["feedback"])

    # Create entry context
    custom_ec = gsuite_client.remove_empty_entities(list_alert_feedback_response["feedback"])

    return CommandResults(
        outputs_prefix=OUTPUT_PATHS["FEEDBACK"],
        outputs_key_field="feedbackId",
        outputs=custom_ec,
        readable_output=hr,
        raw_response=list_alert_feedback_response,
    )


@logger
def fetch_incidents(client, last_run: dict, params: dict, is_test: bool = False) -> tuple[list | None, dict | None]:
    """
    This function is called for fetching incidents.
    This function gets all alerts, then after get latest feedback for each alert.
    This function will execute each interval (default is 1 minute).

    :param client: Client object.
    :param last_run: A dict with a key containing the latest incident created time we got
        from last fetch
    :param params: arguments for fetch-incident.
    :param is_test: True if fetch-incident is called from test-module.

    :return: A tuple containing two elements:
            next_run (``Dict[str, int]``): Contains the timestamp that will be
                    used in ``last_run`` on the next fetch.
            incidents (``List[dict]``): List of incidents that will be created in XSOAR.
    """

    admin_email = params.get("admin_email_creds", {}).get("identifier") or params.get("admin_email")

    fetch_feedback = params.get("fetch_feedback", False)
    # Validate arguments
    params, last_fetch = validate_params_for_fetch_incidents(params, last_run)

    # Fetch Alerts API call
    client.set_authorized_http(scopes=SCOPES["ALERT"], subject=admin_email)
    response = client.http_request(url_suffix=URL_SUFFIX["LIST_ALERTS"], method="GET", params=params)

    alerts = response.get("alerts", [])
    next_page_token = response.get("nextPageToken", "")

    demisto.info(f'[GSAC ALERT]: Request URL: {BASE_URL}{URL_SUFFIX["LIST_ALERTS"]}')
    demisto.info(f"[GSAC ALERT]: Next Token: {next_page_token}")

    incidents: list[dict[str, Any]] = []

    # Prepare incidents data
    for alert in alerts:
        if fetch_feedback:
            # Fetch Alert Feedback API call
            feedback_response = client.http_request(url_suffix=URL_SUFFIX["FEEDBACK"].format(alert.get("alertId")), method="GET")
            feedback_response = feedback_response.get("feedback", [])
            if len(feedback_response) > 0:
                # Fetch latest feedback
                alert["feedback"] = feedback_response[0]
        incident = {
            "name": f'{alert.get("type")} - {alert.get("source")}',
            "occurred": alert.get("createTime"),
            "rawJSON": json.dumps(alert),
        }
        incidents.append(incident)

    # Fetch createTime of latest alert
    if len(alerts) > 0:
        timestamp = alerts[-1]["createTime"]
    else:
        timestamp = last_fetch

    if is_test:
        return None, None
    return incidents, {"last_fetch": timestamp, "next_page_token": next_page_token, "alert_filter": params["filter"]}


def main() -> None:
    """
    PARSE AND VALIDATE INTEGRATION PARAMS
    """

    # Commands dictionary
    commands: dict[str, Callable] = {
        "gsac-alert-list": gsac_list_alerts_command,
        "gsac-alert-feedback-create": gsac_create_alert_feedback_command,
        "gsac-alert-get": gsac_get_alert_command,
        "gsac-alert-delete": gsac_batch_delete_alerts_command,
        "gsac-alert-feedback-list": gsac_list_alert_feedback_command,
        "gsac-alert-recover": gsac_batch_recover_alerts_command,
    }
    command = demisto.command()
    demisto.info(f"Command being called is {command}")

    try:
        params = demisto.params()
        service_account_dict = GSuiteClient.safe_load_non_strict_json(
            params.get("admin_email_creds", {}).get("password") or params.get("user_service_account_json")
        )
        verify_certificate = not params.get("insecure", False)
        proxy = params.get("proxy", False)

        headers = {"Content-Type": "application/json"}

        # prepare client class object
        gsuite_client = GSuiteClient(
            service_account_dict, base_url=BASE_URL, verify=verify_certificate, proxy=proxy, headers=headers
        )

        # Trim the arguments
        args = GSuiteClient.strip_dict(demisto.args())

        # This is the call made when pressing the integration Test button.
        if demisto.command() == "test-module":
            result = test_module(gsuite_client, {}, params)
            return_results(result)
        elif demisto.command() == "fetch-incidents":
            incidents, next_run = fetch_incidents(gsuite_client, last_run=demisto.getLastRun(), params=params)

            demisto.setLastRun(next_run)
            demisto.incidents(incidents)
        elif command in commands:
            args["admin_email"] = params.get("admin_email_creds", {}).get("identifier") or params.get("admin_email", "")
            return_results(commands[command](gsuite_client, args))

    # Log exceptions
    except Exception as e:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f"Failed to execute {demisto.command()} command.\nError:\n{e!s}")


from GSuiteApiModule import *  # noqa: E402

if __name__ in ("__main__", "__builtin__", "builtins"):
    main()