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
| ID | G Suite Security Alert Center |
|---|---|
| Provider | |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.0.0 |
| Docker Image | demisto/googleapi-python3:1.0.0.10182333 |
| Supported Modules | Agentix 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
-
Select or create a project:

-
Enable the G Suite Alert Center API:
-
Under the main menu, select APIs & Services > Library:

-
Search for Google Workspace Alert Center API in Search for APIs & Services:

-
Enable Google Workspace Alert Center API:

-
Select the project and click OPEN:

-
-
Create a service account with the correct restricted permissions:
-
Under the main menu, select IAM & Admin > Service Accounts:

-
In the top bar, select CREATE SERVICE ACCOUNT:

-
Under the main menu, select IAM & Admin > Service Accounts and open the account you just created.
-
Generate new credentials:
-
On the Service account details page, under Keys, click ADD KEY and select Create new key:

-
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:

-
The service account JSON will be automatically downloaded.
-
-
Add Scopes in G Suite Security Alert Center
-
Go to https://admin.google.com
-
Click Menu and select Security > API Controls:

-
Under the Domain wide delegation tab, Click MANAGE DOMAIN WIDE DELEGATION:

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

-
Click AUTHORIZE.
Configure G Suite Security Alert Center on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for G Suite Security Alert Center.
-
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 - 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 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 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 incidentsincidentType— Incident typeincidentFetchInterval— Incidents Fetch Intervaluser_service_account_json— Service Account JSONadmin_email_creds— Admin Emailadmin_email— Admin Emailmax_fetch— Maximum number of incidents per fetch (required)first_fetch— First fetch time intervalalert_type— Alert Typefilter— Filterfetch_feedback— Fetch feedbackinsecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (6)
-
gsac-alert-deletePerforms 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-createCreates new feedback for an alert.
-
gsac-alert-feedback-listLists all the feedback for an alert.
-
gsac-alert-getGets the specified alert. Note: To get the alert_id, execute the gsac-alert-list command.
-
gsac-alert-listLists 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-recoverPerforms 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()