SumoLogicSEC
Freeing the analyst with autonomous decisions.
Analytics & SIEM · Sumo Logic Cloud SIEM
Details
| ID | SumoLogicSEC |
|---|---|
| Provider | Francisco Partners |
| Category | Analytics & SIEM |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Freeing the analyst with autonomous decisions.
This integration was integrated and tested with version 6.1.0 of SumoLogicSEC.
Prerequisites
Only use this integration if your Cloud SIEM portal url ends with .sumologic.com - this can be verified via the url in your browser when logged into Cloud SIEM.
You’ll need an access key in order to complete the instance setup. Instructions on how to generate access keys can be found here.
Configure SumoLogicSEC in Cortex
| Parameter | Description | Required |
|---|---|---|
| Sumo Logic API Endpoint | https://api.<deployment>.sumologic.com/api | True |
| Sumo Logic Instance Endpoint | https://<yoursubdomain>.<deployment>.sumologic.com | False |
| Fetch incidents | False | |
| Incident type | False | |
| Access ID | True | |
| Access Key | True | |
| Incidents Fetch Interval | False | |
| Fetch Limit | Fetch limit must not be greater than 200 | False |
| Override default fetch query | Default fetch query is status:in(“new”, “inprogress”) | False |
| First fetch time | False | |
| Pull associated Sumo Logic Signals as Incidents | Pull Sumo Logic signals associated with detected Sumo Logic Insights as XSOAR incidents | False |
| Incident Mirroring Direction | Choose the direction to mirror the incident: Incoming (from Sumo Logic SIEM to Cortex XSOAR), Outgoing (from Cortex XSOAR to Sumo Logic SIEM), or Incoming and Outgoing (from/to Cortex XSOAR and Sumo Logic SIEM). Cortex XSOAR only parameter. | False |
| Close Mirrored XSOAR Incident | When selected, closing the Sumo Logic SIEM Insight is mirrored in Cortex XSOAR. Cortex XSOAR only parameter. | False |
| Close Mirrored Sumo Logic Insight | When selected, closing the Cortex XSOAR incident is mirrored in Sumo Logic SIEM. | False |
| Override Record Summary Fields | Record Summary Fields included when fetching Insights | False |
API documentation and query examples
For commands with query parameter input the available fields and operators are documented in API docs. These docs are useful when executing queries using the following commands:
sumologic-sec-insight-searchsumologic-sec-signal-searchsumologic-sec-entity-search
To access the API documentation, select the link for your deployment from here. Add sec to the end of the url to access Cloud SIEM API docs - e.g. https://api.us2.sumologic.com/docs/sec/.
Example: Insight search query ‘q’ parameter:
The search query string in our custom DSL that is used to filter the results.
Operators:
exampleField:"bar": The value of the field is equal to “bar”.exampleField:in("bar", "baz", "qux"): The value of the field > is equal to either “bar”, “baz”, or “qux”.exampleTextField:contains("foo bar"): The value of the field > contains the phrase “foo bar”.exampleNumField:>5: The value of the field is greater than 5. There are similar<,<=, and>=operators.exampleNumField:5..10: The value of the field is between 5 and 10 (inclusive).exampleDateField:>2019-02-01T05:00:00+00:00: The value of the date field is after 5 a.m. UTC time on February 2, 2019.exampleDateField:2019-02-01T05:00:00+00:00..2019-02-01T08:00:00+00:00: The value of the date field is between 5 a.m. and 8 a.m. UTC time on February 2, 2019.Fields:
idreadableIdstatusnameinsightIddescriptioncreatedtimestampclosedassigneeentity.ipentity.hostnameentity.usernameentity.typeenrichmenttagseverityresolutionruleIdrecords
Migrating from JASK content pack
The table below shows differences between this integration and the legacy JASK integration:
| JASK (legacy) | Sumo Logic Cloud SIEM | Notes |
|---|---|---|
| jask-get-insight-details | sumologic-sec-insight-get-details | |
| jask-get-insight-comments | sumologic-sec-insight-get-comments | |
| jask-get-signal-details | sumologic-sec-signal-get-details | |
| jask-get-entity-details | sumologic-sec-entity-get-details | |
| jask-get-related-entities | Deprecated | |
| jask-get-whitelisted-entities | Deprecated - use command sumologic-sec-entity-search with filter whitelisted:"true" |
|
| jask-search-insights | sumologic-sec-insight-search | |
| jask-search-entities | sumologic-sec-entity-search | |
| jask-search-signals | sumologic-sec-signal-search |
New commands introduced in Sumo Logic Cloud SIEM pack
sumologic-sec-insight-set-statussumologic-sec-match-list-getsumologic-sec-match-list-updatesumologic-sec-threat-intel-search-indicatorssumologic-sec-threat-intel-get-sourcessumologic-sec-threat-intel-update-source
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
sumologic-sec-insight-get-details
Get Insight details for a specific Insight ID.
Base Command
sumologic-sec-insight-get-details
Input
| Argument Name | Description | Required |
|---|---|---|
| insight_id | The insight to retrieve details for. | Required |
| record_summary_fields | Record Summary Fields to include in the output (override default fields). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.Insight.Assignee | string | User or team assigned to the Insight |
| SumoLogicSec.Insight.Closed | Date | Closed date |
| SumoLogicSec.Insight.ClosedBy | String | Closed by user |
| SumoLogicSec.Insight.Created | Date | Created date |
| SumoLogicSec.Insight.Description | String | Description of the Insight |
| SumoLogicSec.Insight.Entity | String | Entity name associated with the Insight |
| SumoLogicSec.Insight.Id | String | The ID of the Insight |
| SumoLogicSec.Insight.LastUpdated | Date | The time the Insight was last updated |
| SumoLogicSec.Insight.LastUpdatedBy | string | The last user to update the Insight |
| SumoLogicSec.Insight.Name | String | The name of the Insight |
| SumoLogicSec.Insight.ReadableId | String | The ID of the Insight in readable form |
| SumoLogicSec.InsightList.RecordSummaryFields | Array | Record Summary Fields associated with the Insight |
| SumoLogicSec.Insight.Resolution | String | Resolution for closed Insight |
| SumoLogicSec.Insight.Severity | String | The severity of the Insight |
| SumoLogicSec.Insight.Signals.contentType | String | Type of content that triggered the Signal |
| SumoLogicSec.Insight.Signals.description | String | Description of the Signal |
| SumoLogicSec.Insight.Signals.id | String | The ID of the Signal |
| SumoLogicSec.Insight.Signals.name | String | The name of the Signal |
| SumoLogicSec.Insight.Signals.recordCount | Number | Number of records associated with the Signal |
| SumoLogicSec.Insight.Signals.ruleId | String | Rule ID associated with the Signal |
| SumoLogicSec.Insight.Signals.severity | Number | The severity of the Signal |
| SumoLogicSec.Insight.Signals.stage | String | The stage of the Signal |
| SumoLogicSec.Insight.Signals.timestamp | Date | Signal timestamp |
| SumoLogicSec.Insight.Source | String | The source of the Insight |
| SumoLogicSec.Insight.Status | String | The status of the Insight |
| SumoLogicSec.Insight.TimeToDetection | Number | Insight time to detection |
| SumoLogicSec.Insight.TimeToRemediation | Number | Insight time to remediation |
| SumoLogicSec.Insight.TimeToResponse | Number | Insight time to response |
| SumoLogicSec.Insight.Timestamp | Date | Insight timestamp |
Command Example
!sumologic-sec-insight-get-details insight-id=INSIGHT-116
Human Readable Output
Insight Details:
| Id | Readable Id | Name | Action | Status | Assignee | Description | Last Updated | Last Updated By | Severity | Closed | Closed By | Timestamp | Entity | Resolution |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| c6c97d84-983d-303e-a03b-86f53d657fc8 | INSIGHT-116 | Lateral Movement with Discovery and Credential Access | Closed | Initial Access, Lateral Movement, Discovery, Initial Access, Credential Access | 2021-05-10T23:48:10.016204 | HIGH | 2021-05-10T23:48:09.961023 | obfuscated@email.com | 2021-02-18T22:04:08.330000 | 1.2.3.4 | No Action |
sumologic-sec-insight-get-comments
Get comments for a specific Insight ID. (Users can post and update comments on the Sumo Logic Cloud SIEM portal for any Insight ID.)
Base Command
sumologic-sec-insight-add-comment
Input
| Argument Name | Description | Required |
|---|---|---|
| insight_id | The insight ID for which to add a comment. | Required |
| comment | The comment to be added. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.InsightComments.Id | String | ID of comment |
| SumoLogicSec.InsightComments.Body | String | Comment contents |
| SumoLogicSec.InsightComments.Author | String | User that created the comment |
| SumoLogicSec.InsightComments.Timestamp | Date | Comment created timestamp |
| SumoLogicSec.InsightComments.InsightId | String | The ID of the Insight |
Command Example
!sumologic-sec-insight-add-comment insight-id=INSIGHT-116 comment="This is an example comment"
Human Readable Output
Insight Comment:
| Id | Insight Id | Author | Body | Last Updated | Timestamp |
|---|---|---|---|---|---|
| 2 | INSIGHT-116 | obfuscated@email.com | This is an example comment | 2021-04-23T00:38:43.977543 |
sumologic-sec-insight-get-comments
Get comments for a specific Insight ID. (Users can post and update comments on the Sumo Logic Cloud SIEM portal for any Insight ID.)
Base Command
sumologic-sec-insight-get-comments
Input
| Argument Name | Description | Required |
|---|---|---|
| insight_id | The insight ID for which to retrieve comments. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.InsightComments.Id | String | ID of comment |
| SumoLogicSec.InsightComments.Body | String | Comment contents |
| SumoLogicSec.InsightComments.Author | String | User that created the comment |
| SumoLogicSec.InsightComments.Timestamp | Date | Comment created timestamp |
| SumoLogicSec.InsightComments.InsightId | String | The ID of the Insight |
Command Example
!sumologic-sec-insight-get-comments insight-id=INSIGHT-116
Human Readable Output
Insight Comments:
| Id | Insight Id | Author | Body | Last Updated | Timestamp |
|---|---|---|---|---|---|
| 2 | INSIGHT-116 | obfuscated@email.com | This is an example comment | 2021-04-23T00:38:43.977543 |
sumologic-sec-signal-get-details
Get Signal details for a specific Signal ID. Signal details command references signals in Sumo Logic Cloud SIEM which are created when records exhibit suspicious properties and mate with patterns or other detection logic.
Base Command
sumologic-sec-signal-get-details
Input
| Argument Name | Description | Required |
|---|---|---|
| signal_id | The signal to retrieve details for. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.Signal.ContentType | String | Type of content that triggered the Signal |
| SumoLogicSec.Signal.Description | String | Description of the Signal |
| SumoLogicSec.Signal.Entity | String | Entity name associated with the Signal |
| SumoLogicSec.Signal.Id | String | The ID of the Signal |
| SumoLogicSec.Signal.Name | String | The name of the Signal |
| SumoLogicSec.Signal.RecordCount | Number | Number of records associated with the Signal |
| SumoLogicSec.Signal.RuleId | String | Rule ID associated with the Signal |
| SumoLogicSec.Signal.Severity | Number | The severity of the Signal |
| SumoLogicSec.Signal.Stage | String | The stage of the Signal |
| SumoLogicSec.Signal.Suppressed | Boolean | Whether or not the Signal was suppressed |
| SumoLogicSec.Signal.Timestamp | Date | Signal timestamp |
Command Example
!sumologic-sec-signal-get-details signal-id=e0e7096b-2f91-5b72-b1a2-db48ce882dfc
Human Readable Output
Signal Details:
| Id | Name | Rule Id | Description | Severity | Content Type | Timestamp | Entity |
|---|---|---|---|---|---|---|---|
| e0e7096b-2f91-5b72-b1a2-db48ce882dfc | Potential malicious JVM download | LEGACY-S00062 | A document was downloaded and opened followed by a file download using a Java user-agent. | 4 | RULE | 2021-02-18T22:04:08.230000 | 1.2.3.4 |
sumologic-sec-entity-get-details
Get entity details for a specific entity ID
Base Command
sumologic-sec-entity-get-details
Input
| Argument Name | Description | Required |
|---|---|---|
| entity-id | The entity to retrieve details for. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.Entity.ActivityScore | Number | Entity Activity Score |
| SumoLogicSec.Entity.FirstSeen | Date | When the Entity was first seen |
| SumoLogicSec.Entity.Hostname | String | Entity hostname |
| SumoLogicSec.Entity.Id | String | Entity ID |
| SumoLogicSec.Entity.IsWhitelisted | Boolean | Whether or not the Entity is on allow list |
| SumoLogicSec.Entity.LastSeen | Date | When the Entity was last seen |
| SumoLogicSec.Entity.Name | String | The Entity name |
| SumoLogicSec.Entity.OperatingSystem | String | Entity Operating System (observed or from inventory) |
| SumoLogicSec.Entity.InventoryData | Boolean | Whether or not this Entity was ingested from inventory e.g. Active Directory |
Command Example
!sumologic-sec-entity-get-details entity-id=_hostname-win10--admin.obfuscated
Human Readable Output
Entity Details
| Id | Name | First Seen | Last Seen | Activity Score | Is Whitelisted | Operating System | Inventory Data |
|---|---|---|---|---|---|---|---|
| _hostname-win10–admin.obfuscated | win10-admin.obfuscated | 2021-04-21T14:43:38.526000 | 9 | false | Windows 10 Enterprise | true |
sumologic-sec-insight-search
Search insights using available filters
Base Command
sumologic-sec-insight-search
Input
| Argument Name | Description | Required |
|---|---|---|
| query | Use a query string to search, see API documentation for more details. | Optional |
| created | When the insight was created. Defaults to ‘All time’ if no time arguments are specified. Possible values are: All time, Last week, Last 48 hours, Last 24 hours. | Optional |
| status | Comma separated list of values from the options: new,inprogress,closed. | Optional |
| asignee | User assigned to Insights. | Optional |
| offset | The number of items to skip before starting to collect the result set. Default is 0. | Optional |
| limit | The maximum number of items to return. Default is 10. | Optional |
| record_summary_fields | Record Summary Fields to include in the output (override default fields). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.InsightList.Assignee | String | User or team assigned to the Insight |
| SumoLogicSec.InsightList.Closed | Date | Closed date |
| SumoLogicSec.InsightList.ClosedBy | String | Closed by user |
| SumoLogicSec.InsightList.Created | Date | Created date |
| SumoLogicSec.InsightList.Description | String | Description of the Insight |
| SumoLogicSec.InsightList.Entity | String | Entity name associated with the Insight |
| SumoLogicSec.InsightList.Id | String | The ID of the Insight |
| SumoLogicSec.InsightList.LastUpdated | Date | The time the Insight was last updated |
| SumoLogicSec.InsightList.LastUpdatedBy | String | The last user to update the Insight |
| SumoLogicSec.InsightList.Name | String | The name of the Insight |
| SumoLogicSec.InsightList.ReadableId | String | The ID of the Insight in readable form |
| SumoLogicSec.InsightList.RecordSummaryFields | Array | Record Summary Fields associated with the Insight |
| SumoLogicSec.InsightList.Resolution | String | Resolution for closed Insight |
| SumoLogicSec.InsightList.Severity | String | The severity of the Insight |
| SumoLogicSec.InsightList.Signals.contentType | String | Type of content that triggered the Signal |
| SumoLogicSec.InsightList.Signals.description | String | Description of the Signal |
| SumoLogicSec.InsightList.Signals.id | String | The ID of the Signal |
| SumoLogicSec.InsightList.Signals.name | String | The name of the Signal |
| SumoLogicSec.InsightList.Signals.recordCount | Number | Number of records associated with the Signal |
| SumoLogicSec.InsightList.Signals.ruleId | String | Rule ID associated with the Signal |
| SumoLogicSec.InsightList.Signals.severity | Number | The severity of the Signal |
| SumoLogicSec.InsightList.Signals.stage | String | The stage of the Signal |
| SumoLogicSec.InsightList.Signals.timestamp | Date | Signal timestamp |
| SumoLogicSec.InsightList.Source | String | The source of the Insight |
| SumoLogicSec.InsightList.Status | String | The status of the Insight |
| SumoLogicSec.InsightList.TimeToDetection | Number | Insight time to detection |
| SumoLogicSec.InsightList.TimeToRemediation | Number | Insight time to remediation |
| SumoLogicSec.InsightList.TimeToResponse | Number | Insight time to response |
| SumoLogicSec.InsightList.Timestamp | Date | Insight timestamp |
Command Example
!sumologic-sec-insight-search query="timestamp:>\"2021-02-01T05:00:00+00:00\" status:\"closed\" AND severity:>\"MEDIUM\"" limit=3
Human Readable Output
Insights:
| Id | Readable Id | Name | Action | Status | Assignee | Description | Last Updated | Last Updated By | Severity | Closed | Closed By | Timestamp | Entity | Resolution |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 00853cdd-763e-3e31-a2e4-f74277922f9f | INSIGHT-220 | Command and Control with Defense Evasion and Execution | Closed | Initial Access, Command and Control, Defense Evasion, Execution | 2021-03-23T20:06:51.565599 | HIGH | 2021-03-23T20:06:51.511505 | obfuscated@email.com | 2021-02-22T16:27:51 | testcomputer.somedomain.net | No Action | |||
| eefdff8d-7447-3b47-83e0-66a0b210d618 | INSIGHT-219 | Discovery with Credential Access and Execution | Closed | Initial Access, Credential Access, Initial Access, Execution, Discovery, Credential Access | 2021-03-23T21:21:55.029798 | HIGH | 2021-03-23T21:21:54.914061 | obfuscated@email.com | 2021-02-22T16:24:07.959000 | 1.2.3.4 | No Action | |||
| 8a77d12e-5905-3401-ae7c-2e17b1fd3060 | INSIGHT-221 | Privilege Escalation with Persistence and Execution | Closed | obfuscated@email.com | Execution, Privilege Escalation, Persistence, Execution | 2021-05-12T21:47:08.297222 | HIGH | 2021-05-12T21:47:08.132251 | obfuscated@email.com | 2021-02-22T16:24:07.959000 | 5.6.7.8 | No Action |
sumologic-sec-signal-search
Search signals using available filters
Base Command
sumologic-sec-signal-search
Input
| Argument Name | Description | Required |
|---|---|---|
| query | Use a query string to search, see API documentation for more details. | Optional |
| created | When the Signal was created. Defaults to ‘All time’ if no time arguments are specified. Possible values are: All time, Last week, Last 48 hours, Last 24 hours. Default is All time. | Optional |
| contentType | Content type associated with the signals. Options: ANOMALY, DEFAULT, THREATINTEL, RULE. Possible values are: ANOMALY, DEFAULT, THREATINTEL, RULE. | Optional |
| offset | The number of items to skip before starting to collect the result set. Default is 0. | Optional |
| limit | The maximum number of items to return. Default is 10. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.SignalList.ContentType | String | Type of content that triggered the Signal |
| SumoLogicSec.SignalList.Description | String | Description of the Signal |
| SumoLogicSec.SignalList.Entity | String | Entity name associated with the Signal |
| SumoLogicSec.SignalList.Id | String | The ID of the Signal |
| SumoLogicSec.SignalList.Name | String | The name of the Signal |
| SumoLogicSec.SignalList.RecordCount | Number | Number of records associated with the Signal |
| SumoLogicSec.SignalList.RuleId | String | Rule ID associated with the Signal |
| SumoLogicSec.SignalList.Severity | Number | The severity of the Signal |
| SumoLogicSec.SignalList.Stage | String | The stage of the Signal |
| SumoLogicSec.SignalList.Suppressed | Boolean | Whether or not the Signal was suppressed |
| SumoLogicSec.SignalList.Timestamp | Date | Signal timestamp |
Command Example
!sumologic-sec-signal-search query="timestamp:NOW-7D.NOW name:contains(\"Internal\")"
Human Readable Output
Signals:
| Id | Name | Entity | Rule Id | Description | Severity | Stage | Timestamp | Content Type | Tags |
|---|---|---|---|---|---|---|---|---|---|
| b50fd570-341b-576d-85b5-8b5cd17c0aee | IP Address Scan - Internal | 1.2.3.4 | LEGACY-S00050 | A scan of IP addresses | 3 | Discovery | 2021-04-22T04:08:13.514000 | RULE | _mitreAttackTactic:TA0007, _mitreAttackTactic:TA0043, _mitreAttackTechnique:T1046, _mitreAttackTechnique:T1595 |
sumologic-sec-entity-search
Search entities using the available filters
Base Command
sumologic-sec-entity-search
Input
| Argument Name | Description | Required |
|---|---|---|
| query | Use a query string to search, see API documentation for more details. | Optional |
| ip | IP Address to search for e.g. 1.2.3.4. | Optional |
| hostname | Hostname to search for e.g. host.example.com. | Optional |
| username | Username to search for e.g. admin. | Optional |
| type | Entity type to search for. Options: username, hostname, ip, mac. Possible values are: username, hostname, ip, mac. | Optional |
| whitelisted | Is the Entity whitelisted? true/false. Possible values are: true, false. | Optional |
| tag | Tag contains value. | Optional |
| offset | The number of items to skip before starting to collect the result set. Default is 0. | Optional |
| limit | The maximum number of items to return. Default is 10. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.EntityList.ActivityScore | Number | Entity Activity Score |
| SumoLogicSec.EntityList.FirstSeen | Date | When the Entity was first seen |
| SumoLogicSec.EntityList.Id | String | Entity ID |
| SumoLogicSec.EntityList.IpHostname | String | Hostname associated with IP Entity |
| SumoLogicSec.EntityList.IsWhitelisted | Boolean | Whether or not the Entity is on allow list |
| SumoLogicSec.EntityList.LastSeen | Date | When the Entity was last seen |
| SumoLogicSec.EntityList.Name | String | The Entity name |
| SumoLogicSec.EntityList.OperatingSystem | String | Entity Operating System (observed or from inventory) |
| SumoLogicSec.EntityList.InventoryData | Boolean | Whether or not this Entity was ingested from inventory e.g. Active Directory |
| SumoLogicSec.EntityList.Hostname | String | Entity hostname |
| SumoLogicSec.EntityList.Department | String | Username Entity department |
| SumoLogicSec.EntityList.EmployeeId | String | Username Entity employee ID |
Command Example
!sumologic-sec-entity-search query="type:\"ip\" activityScore:>=3"
Human Readable Output
Entities:
| Id | Name | First Seen | Last Seen | Activity Score | Is Whitelisted | Operating System | Inventory Data |
|---|---|---|---|---|---|---|---|
| _ip-specops_analysis_lab-1.2.3.4 | 1.2.3.4 | 2021-04-22T04:08:13.514000 | 3 | false | false |
sumologic-sec-insight-set-status
Change status of Insight
Base Command
sumologic-sec-insight-set-status
Input
| Argument Name | Description | Required |
|---|---|---|
| insight_id | The insight to change status for. | Required |
| status | The desired Insight status. Possible values are: new, inprogress, closed. Default is in-progress. | Optional |
| resolution | Resolution for closing Insight. Valid values are: “Resolved”, “False Positive”, “No Action”, “Duplicate”. Possible values are: Resolved, False Positive, No Action, Duplicate. Default is Resolved. | Optional |
| sub_resolution | Custom sub resolution for closing Insight. If populated, it will override the resolution field. Please make sure the resolution matches exactly your Sumo Resolutions | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.Insight.Assignee | String | User or team assigned to the Insight |
| SumoLogicSec.Insight.Closed | Date | Closed date |
| SumoLogicSec.Insight.ClosedBy | String | Closed by user |
| SumoLogicSec.Insight.Created | Date | Created date |
| SumoLogicSec.Insight.Description | String | Description of the Insight |
| SumoLogicSec.Insight.Entity | String | Entity name associated with the Insight |
| SumoLogicSec.Insight.Id | String | The ID of the Insight |
| SumoLogicSec.Insight.LastUpdated | Date | The time the Insight was last updated |
| SumoLogicSec.Insight.LastUpdatedBy | String | The last user to update the Insight |
| SumoLogicSec.Insight.Name | String | The name of the Insight |
| SumoLogicSec.Insight.ReadableId | String | The ID of the Insight in readable form |
| SumoLogicSec.Insight.Resolution | String | Resolution for closed Insight |
| SumoLogicSec.Insight.Severity | String | The severity of the Insight |
| SumoLogicSec.Insight.Signals.contentType | String | Type of content that triggered the Signal |
| SumoLogicSec.Insight.Signals.description | String | Description of the Signal |
| SumoLogicSec.Insight.Signals.id | String | The ID of the Signal |
| SumoLogicSec.Insight.Signals.name | String | The name of the Signal |
| SumoLogicSec.Insight.Signals.recordCount | Number | Number of records associated with the Signal |
| SumoLogicSec.Insight.Signals.ruleId | String | Rule ID associated with the Signal |
| SumoLogicSec.Insight.Signals.severity | Number | The severity of the Signal |
| SumoLogicSec.Insight.Signals.stage | String | The stage of the Signal |
| SumoLogicSec.Insight.Signals.timestamp | Date | Signal timestamp |
| SumoLogicSec.Insight.Source | String | The source of the Insight |
| SumoLogicSec.Insight.Status | String | The status of the Insight |
| SumoLogicSec.Insight.TimeToDetection | Number | Insight time to detection |
| SumoLogicSec.Insight.TimeToRemediation | Number | Insight time to remediation |
| SumoLogicSec.Insight.TimeToResponse | Number | Insight time to response |
| SumoLogicSec.Insight.Timestamp | Date | Insight timestamp |
Command Example
!sumologic-sec-insight-set-status insight-id=INSIGHT-116 status=closed resolution="No Action"
Human Readable Output
Insight Details:
| Id | Readable Id | Name | Action | Status | Assignee | Description | Last Updated | Last Updated By | Severity | Closed | Closed By | Timestamp | Entity | Resolution |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| c6c97d84-983d-303e-a03b-86f53d657fc8 | INSIGHT-116 | Lateral Movement with Discovery and Credential Access | Closed | Initial Access, Lateral Movement, Discovery, Initial Access, Credential Access | 2021-05-13T01:28:32.648352 | HIGH | 2021-05-13T01:28:32.580039 | obfuscated@email.com | 2021-02-18T22:04:08.330000 | 1.2.3.4 | No Action |
sumologic-sec-match-list-get
Get match lists
Base Command
sumologic-sec-match-list-get
Input
| Argument Name | Description | Required |
|---|---|---|
| offset | The number of items to skip before starting to collect the result set. Default is 0. | Optional |
| limit | Number of match lists returned. Default is 10. | Optional |
| sort | Sort expression. Default is name. | Optional |
| sortDir | Sort direction. Possible values are: ASC, DESC. Default is ASC. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.MatchLists.Created | String | When the Match List was created |
| SumoLogicSec.MatchLists.CreatedBy | String | User that created the Match List |
| SumoLogicSec.MatchLists.DefaultTtl | Number | Default TTL for entries in the Match List |
| SumoLogicSec.MatchLists.Description | String | Description of the Match List |
| SumoLogicSec.MatchLists.Id | String | ID of the Match List |
| SumoLogicSec.MatchLists.LastUpdated | String | When the Match List was last updated |
| SumoLogicSec.MatchLists.LastUpdatedBy | String | The last user to update the Match List |
| SumoLogicSec.MatchLists.Name | String | Name of Match List |
| SumoLogicSec.MatchLists.TargetColumn | String | Match List Target Column |
Command Example
!sumologic-sec-match-list-get limit=3
Human Readable Output
Match lists:
| Id | Name | Target Column | Default Ttl |
|---|---|---|---|
| 173 | admin_ips | SrcIp | 0 |
| 24 | auth_servers | Ip | |
| 162 | auth_servers_dst | DstIp |
sumologic-sec-match-list-update
Add item to match list
Base Command
sumologic-sec-match-list-update
Input
| Argument Name | Description | Required |
|---|---|---|
| match_list_id | ID of match list. | Required |
| active | Item active or disabled. | Required |
| description | Description of match list item. | Required |
| expiration | Expiration of match list item, e.g. “2021-03-25T23:52:23.508Z”. | Required |
| value | Value of match list item. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.UpdateResult.Result | String | Result (Success or Failed) |
| SumoLogicSec.UpdateResult.Server response | Boolean | Server response (True or False) |
Command Example
!sumologic-sec-match-list-update match-list-id=166 description="My description" expiration=2021-04-25T22:36:10.925Z value="10.20.30.40" active=true
Human Readable Output
Result:
| Result | Server Response |
|---|---|
| Success | true |
sumologic-sec-threat-intel-search-indicators
Search Threat Intel Indicators
Base Command
sumologic-sec-threat-intel-search-indicators
Input
| Argument Name | Description | Required |
|---|---|---|
| q | Use a query string to search, see API documentation for more details. | Optional |
| value | The value to search for. | Required |
| offset | The number of items to skip before starting to collect the result set. Default is 0. | Optional |
| limit | The numbers of items to return. Default is 10. | Optional |
| sourceIds | Comma separated list of threat intelligence source IDs to search, e.g. 1,2,3. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.ThreatIntelIndicators.Active | Boolean | Whether or not the Threat Intel Indicator is Active |
| SumoLogicSec.ThreatIntelIndicators.Expiration | Date | Date and time the Threat Intel Indicator is set to expire |
| SumoLogicSec.ThreatIntelIndicators.Id | String | ID of Threat Intel Indicator |
| SumoLogicSec.ThreatIntelIndicators.Meta.created.username | String | User that created the Threat Intel Indicator |
| SumoLogicSec.ThreatIntelIndicators.Meta.created.when | Date | When the Threat Intel Indicator was created |
| SumoLogicSec.ThreatIntelIndicators.Meta.description | String | Description of Threat Intel Indicator |
| SumoLogicSec.ThreatIntelIndicators.Meta.updated | Date | When the Threat Intel Indicator was last updated |
| SumoLogicSec.ThreatIntelIndicators.Value | String | Value of Threat Intel Indicator |
Command Example
!sumologic-sec-threat-intel-search-indicators value=1.2.3.4 sourceIds=54
Human Readable Output
Threat Intel Indicators:
| Id | Value | Active | Expiration |
|---|---|---|---|
| f396ae69aa223c049ff639b3649ba1dd6465ec74397c3126916786bbcd6d76017468726561745f49705f44656d6973746f5f54657374 | 1.2.3.4 | true | 2021-04-29T00:00:00 |
sumologic-sec-threat-intel-get-sources
Get Threat Intel Sources
Base Command
sumologic-sec-threat-intel-get-sources
Input
| Argument Name | Description | Required |
|---|---|---|
| offset | The number of items to skip before starting to collect the result set. Default is 0. | Optional |
| limit | The numbers of items to return. Default is 10. | Optional |
| sort | Sort expression. Default is name. | Optional |
| sortDir | Sort direction. Possible values are: ASC, DESC. Default is ASC. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.ThreatIntelSources.Created | String | When the Threat Intel Source was created |
| SumoLogicSec.ThreatIntelSources.CreatedBy | String | User that created the Threat Intel Source |
| SumoLogicSec.ThreatIntelSources.Description | String | Description of Threat Intel Source |
| SumoLogicSec.ThreatIntelSources.Id | String | ID of Threat Intel Source |
| SumoLogicSec.ThreatIntelSources.LastUpdated | String | When the Threat Intel Source was last updated |
| SumoLogicSec.ThreatIntelSources.LastUpdatedBy | String | User that last updated the Threat Intel Source |
| SumoLogicSec.ThreatIntelSources.Name | String | Name of Threat Intel Source |
| SumoLogicSec.ThreatIntelSources.SourceType | String | Source type of Threat Intel Source |
Command Example
!sumologic-sec-threat-intel-get-sources limit=3
Human Readable Output
Threat intel sources:
| Id | Name | Description | Source Type |
|---|---|---|---|
| 35 | abuse.ch | CUSTOM | |
| 25 | Alienvault OTX | Alienvault | TAXII |
| 24 | Anomali | TAXII |
sumologic-sec-threat-intel-update-source
Add Threat Intel Indicator to Threat Intel Source
Base Command
sumologic-sec-threat-intel-update-source
Input
| Argument Name | Description | Required |
|---|---|---|
| threat-intel-source-id | ID of Threat Intel Source. | Required |
| active | Indicator active or disabled. Default is true. | Required |
| description | Description of indicator. | Required |
| expiration | Expiration of match list item, e.g. “2021-03-25T23:52:23.508Z”. | Required |
| value | Indicator value. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| SumoLogicSec.UpdateResult.Result | String | Result (Success or Failed) |
| SumoLogicSec.UpdateResult.Server response | Boolean | Server response (True or False) |
Command Example
!sumologic-sec-threat-intel-update-source threat-intel-source-id=54 active=true value=1.2.3.4 description=test expiration=2021-04-29T00:00:00.000Z
Human Readable Output
Result:
| Result | Server Response |
|---|---|
| Success | true |
Configuration parameters
api_endpoint— Sumo Logic API Endpoint (required)instance_endpoint— Sumo Logic Instance EndpointisFetch— Fetch incidentsincidentType— Incident typeaccess_id— Access ID (required)access_key— Access Key (required)incidentFetchInterval— Incidents Fetch Intervalmax_fetch— Fetch Limitfetch_query— Override default fetch queryfirst_fetch— First fetch timepull_signals— Pull associated Sumo Logic Signals as Incidentsmirror_direction— Incident Mirroring Directionclose_incident— Close Mirrored XSOAR Incidentclose_insight— Close Mirrored Sumo Logic Insightrecord_summary_fields— Override Record Summary Fields
Commands (14)
-
sumologic-sec-entity-get-detailsGet entity details for a specific entity ID.
-
sumologic-sec-entity-searchSearch entities using the available filters.
-
sumologic-sec-insight-add-commentAdd a comment for a specific Insight ID (Users can post and update comments on the Sumo Logic Cloud SIEM portal for any Insight ID).
-
sumologic-sec-insight-get-commentsGet comments for a specific Insight ID (Users can post and update comments on the Sumo Logic Cloud SIEM portal for any Insight ID).
-
sumologic-sec-insight-get-detailsGet Insight details for a specific Insight ID.
-
sumologic-sec-insight-searchSearch insights using available filters.
-
sumologic-sec-insight-set-statusChange the status of an Insight.
-
sumologic-sec-match-list-getGet match lists.
-
sumologic-sec-match-list-updateAdd an item to a match list.
-
sumologic-sec-signal-get-detailsGet Signal details for a specific Signal ID. Signal details command references signals in Sumo Logic Cloud SIEM which are created when records exhibit suspicious properties and mate with patterns or other detection logic.
-
sumologic-sec-signal-searchSearch signals using available filters.
-
sumologic-sec-threat-intel-get-sourcesGet Threat Intel Sources.
-
sumologic-sec-threat-intel-search-indicatorsSearch Threat Intel Indicators.
-
sumologic-sec-threat-intel-update-sourceAdd a Threat Intel Indicator to an Threat Intel Source.
"""Integration for Sumo Logic Cloud SIEM - Unit Tests file More information about Unit Tests in Cortex XSOAR: https://xsoar.pan.dev/docs/integrations/unit-testing """ import json from datetime import UTC, datetime from CommonServerPython import * from CommonServerUserPython import * MOCK_URL = "https://test.com/api" RECORD_SUMMARY_FIELDS_DEFAULT = ( "action,description,device_hostname,device_ip,dstDevice_hostname,dstDevice_ip," "email_sender,file_basename,file_hash_md5,file_hash_sha1,file_hash_sha256,srcDevice_hostname," "srcDevice_ip,threat_name,threat_category,threat_identifier,user_username,threat_url,listMatches" ) def util_load_json(path): with open(path, encoding="utf-8") as f: return json.loads(f.read()) def test_insight_get_details(requests_mock): """Tests sumologic-sec-insight-get-details command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, insight_get_details, insight_signal_to_readable mock_response = util_load_json("test_data/insight_details.json") insight_id = "INSIGHT-220" insight = insight_signal_to_readable(mock_response.get("data")) requests_mock.get( f"{MOCK_URL}/sec/v1/insights/{insight_id}?exclude=signals.allRecords&recordSummaryFields=action%2C" "description%2Cdevice_hostname%2Cdevice_ip%2CdstDevice_hostname%2CdstDevice_ip%2Cemail_sender%2C" "file_basename%2Cfile_hash_md5%2Cfile_hash_sha1%2Cfile_hash_sha256%2CsrcDevice_hostname%2C" "srcDevice_ip%2Cthreat_name%2Cthreat_category%2Cthreat_identifier%2Cuser_username%2Cthreat_url%2ClistMatches", json=mock_response, ) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) args = {"insight_id": insight_id, "record_summary_fields": RECORD_SUMMARY_FIELDS_DEFAULT} response = insight_get_details(client, args) assert response.outputs_prefix == "SumoLogicSec.Insight" assert response.outputs_key_field == "Id" assert response.outputs == insight assert response.readable_output == tableToMarkdown( "Insight Details:", [insight], [ "Id", "ReadableId", "Name", "Action", "Status", "Assignee", "Description", "LastUpdated", "LastUpdatedBy", "Severity", "Closed", "ClosedBy", "Timestamp", "Entity", "Resolution", "SumoUrl", ], headerTransform=pascalToSpace, ) def test_insight_get_comments(requests_mock): """Tests sumologic-sec-insight-get-comments command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, insight_get_comments mock_response = util_load_json("test_data/insight_comments.json") insight_id = "INSIGHT-116" comments = mock_response["data"]["comments"] requests_mock.get(f"{MOCK_URL}/sec/v1/insights/{insight_id}/comments", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = {"insight_id": insight_id} response = insight_get_comments(client, args) assert response.outputs_prefix == "SumoLogicSec.InsightComments" assert response.outputs_key_field == "Id" assert response.outputs[0]["Id"] == comments[0]["id"] == "2" assert response.outputs[0]["Author"] == comments[0]["author"]["username"] == "obfuscated@email.com" assert response.outputs[0]["Body"] == comments[0]["body"] == "This is an example comment" def test_insight_add_comment(requests_mock): """Tests sumologic-sec-insight-add-comment command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, insight_add_comment mock_response = util_load_json("test_data/insight_add_comment.json") insight_id = "INSIGHT-116" comment = mock_response["data"] requests_mock.post(f"{MOCK_URL}/sec/v1/insights/{insight_id}/comments", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = {"insight_id": insight_id} response = insight_add_comment(client, args) assert response.outputs_prefix == "SumoLogicSec.InsightComments" assert response.outputs_key_field == "Id" assert response.outputs[0]["Id"] == comment["id"] == "32" assert response.outputs[0]["Author"] == comment["author"]["username"] == "obfuscated@email.com" assert response.outputs[0]["Body"] == comment["body"] == "Test adding comment via API" def test_signal_get_details(requests_mock): """Tests sumologic-sec-signal-get-details command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, insight_signal_to_readable, signal_get_details mock_response = util_load_json("test_data/signal_details.json") signal_id = "2b449e56-f6e8-5306-980a-447a8c026b77" signal = mock_response.get("data") del signal["allRecords"] signal = insight_signal_to_readable(signal) requests_mock.get(f"{MOCK_URL}/sec/v1/signals/{signal_id}", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) args = {"signal_id": signal_id} response = signal_get_details(client, args) assert response.outputs_prefix == "SumoLogicSec.Signal" assert response.outputs_key_field == "Id" assert response.outputs == signal def test_entity_get_details(requests_mock): """Tests sumologic-sec-entity-get-details command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, entity_get_details, entity_to_readable mock_response = util_load_json("test_data/entity_details.json") entity_id = "_hostname-win10--admin.b.test.com" entity = entity_to_readable(mock_response.get("data")) requests_mock.get(f"{MOCK_URL}/sec/v1/entities/{entity_id}", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = {"entity_id": entity_id} response = entity_get_details(client, args) assert response.outputs_prefix == "SumoLogicSec.Entity" assert response.outputs_key_field == "Id" assert response.outputs == entity def test_insight_search(requests_mock): """Tests sumologic-sec-insight-search command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, insight_search, insight_signal_to_readable mock_response = util_load_json("test_data/insight_list.json") insights = [] for insight in mock_response["data"]["objects"]: insights.append(insight_signal_to_readable(insight)) requests_mock.get(f"{MOCK_URL}/sec/v1/insights?limit=2", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = {"limit": "2"} response = insight_search(client, args) assert response.outputs_prefix == "SumoLogicSec.InsightList" assert response.outputs_key_field == "Id" assert response.outputs == insights def test_entity_search(requests_mock): """Tests sumologic-sec-entity-search command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, entity_search, entity_to_readable mock_response = util_load_json("test_data/entity_list.json") entities = [] for entity in mock_response["data"]["objects"]: entities.append(entity_to_readable(entity)) requests_mock.get(f'{MOCK_URL}/sec/v1/entities?q=hostname:matchesWildcard("*test*")&limit=2', json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = {"query": 'hostname:matchesWildcard("*test*")', "limit": "2"} response = entity_search(client, args) assert response.outputs_prefix == "SumoLogicSec.EntityList" assert response.outputs_key_field == "Id" assert response.outputs == entities def test_signal_search(requests_mock): """Tests sumologic-sec-signal-search command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, insight_signal_to_readable, signal_search mock_response = util_load_json("test_data/signal_list.json") signals = [] for signal in mock_response["data"]["objects"]: del signal["allRecords"] signals.append(insight_signal_to_readable(signal)) requests_mock.get(f'{MOCK_URL}/sec/v1/signals?q=contentType:"ANOMALY"&limit=2', json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = {"created": "All time", "contentType": "ANOMALY", "limit": "2"} response = signal_search(client, args) assert response.outputs_prefix == "SumoLogicSec.SignalList" assert response.outputs_key_field == "Id" assert response.outputs == signals def test_insight_set_status(requests_mock): """Tests sumologic-sec-insight-set-status command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, insight_set_status, insight_signal_to_readable mock_response = util_load_json("test_data/insight_status.json") insight_id = "INSIGHT-221" for signal in mock_response["data"]["signals"]: del signal["allRecords"] insight = insight_signal_to_readable(mock_response.get("data")) requests_mock.put(f"{MOCK_URL}/sec/v1/insights/{insight_id}/status", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = {"insight_id": insight_id, "status": "closed", "resolution": "Resolved"} response = insight_set_status(client, args) assert response.outputs_prefix == "SumoLogicSec.Insight" assert response.outputs_key_field == "Id" assert response.outputs == insight def test_match_list_get(requests_mock): """Tests sumologic-sec-match-list-get command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, match_list_get mock_response = util_load_json("test_data/match_lists.json") match_lists = [] for match_list in mock_response["data"]["objects"]: match_lists.append({(k[0].capitalize() + k[1:]): v for k, v in match_list.items()}) requests_mock.get(f"{MOCK_URL}/sec/v1/match-lists?limit=5", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = {"limit": "5"} response = match_list_get(client, args) assert response.outputs_prefix == "SumoLogicSec.MatchLists" assert response.outputs_key_field == "Id" assert response.outputs == match_lists def test_match_list_update(requests_mock): """Tests sumologic-sec-match-list-update command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, get_update_result, match_list_update mock_response = util_load_json("test_data/update_result.json") match_list_id = "166" requests_mock.post(f"{MOCK_URL}/sec/v1/match-lists/{match_list_id}/items", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = { "match_list_id": match_list_id, "description": "My description", "expiration": "2021-05-30T22:36:10.925Z", "value": "10.20.30.40", "active": "true", } response = match_list_update(client, args) assert response.outputs_prefix == "SumoLogicSec.UpdateResult" assert response.outputs == get_update_result(mock_response.get("data")) def test_threat_intel_search_indicators(requests_mock): """Tests sumologic-sec-threat-intel-search-indicators command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, threat_intel_search_indicators mock_response = util_load_json("test_data/threat_intel_indicators.json") threat_intel_indicators = [] for threat_intel_indicator in mock_response["data"]["objects"]: threat_intel_indicators.append({(k[0].capitalize() + k[1:]): v for k, v in threat_intel_indicator.items()}) requests_mock.get(f"{MOCK_URL}/sec/v1/threat-intel-indicators?value=11.22.33.44&sourceIds=54", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = { "value": "11.22.33.44", "sourceIds": "54", } response = threat_intel_search_indicators(client, args) assert response.outputs_prefix == "SumoLogicSec.ThreatIntelIndicators" assert response.outputs_key_field == "Id" assert response.outputs == threat_intel_indicators def test_threat_intel_get_sources(requests_mock): """Tests sumologic-sec-threat-intel-get-sources command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, threat_intel_get_sources mock_response = util_load_json("test_data/threat_intel_sources.json") threat_intel_sources = [] for threat_intel_source in mock_response["data"]["objects"]: threat_intel_sources.append({(k[0].capitalize() + k[1:]): v for k, v in threat_intel_source.items()}) requests_mock.get(f"{MOCK_URL}/sec/v1/threat-intel-sources?limit=5", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = {"limit": "5"} response = threat_intel_get_sources(client, args) assert response.outputs_prefix == "SumoLogicSec.ThreatIntelSources" assert response.outputs_key_field == "Id" assert response.outputs == threat_intel_sources def test_threat_intel_update_source(requests_mock): """Tests sumologic-sec-threat-intel-update-source command function.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, get_update_result, threat_intel_update_source mock_response = util_load_json("test_data/update_result.json") threat_intel_source_id = "54" requests_mock.post(f"{MOCK_URL}/sec/v1/threat-intel-sources/{threat_intel_source_id}/items", json=mock_response) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) args = { "threat_intel_source_id": threat_intel_source_id, "description": "My description", "expiration": "2021-05-30T22:36:10.925Z", "value": "10.20.30.40", "active": "true", } response = threat_intel_update_source(client, args) assert response.outputs_prefix == "SumoLogicSec.UpdateResult" assert response.outputs == get_update_result(mock_response.get("data")) def test_fetch_incidents(requests_mock): """Tests fetch incidents.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, fetch_incidents mock_response1 = util_load_json("test_data/insight_list_page1.json") requests_mock.get( f"{MOCK_URL}/sec/v1/insights?q=created%3A%3E%3D2021-05-18T00%3A00%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29" "&limit=20&recordSummaryFields=action%2Cdescription%2Cdevice_hostname%2Cdevice_ip%2CdstDevice_hostname" "%2CdstDevice_ip%2Cemail_sender%2Cfile_basename%2Cfile_hash_md5%2Cfile_hash_sha1%2Cfile_hash_sha256" "%2CsrcDevice_hostname%2CsrcDevice_ip%2Cthreat_name%2Cthreat_category%2Cthreat_identifier%2Cuser_username" "%2Cthreat_url%2ClistMatches", json=mock_response1, ) mock_response2 = util_load_json("test_data/insight_list_page2.json") requests_mock.get( f"{MOCK_URL}/sec/v1/insights?q=created%3A%3E%3D2021-05-18T00%3A00%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29" "&limit=20&recordSummaryFields=action%2Cdescription%2Cdevice_hostname%2Cdevice_ip%2CdstDevice_hostname" "%2CdstDevice_ip%2Cemail_sender%2Cfile_basename%2Cfile_hash_md5%2Cfile_hash_sha1%2Cfile_hash_sha256" "%2CsrcDevice_hostname%2CsrcDevice_ip%2Cthreat_name%2Cthreat_category%2Cthreat_identifier%2Cuser_username" "%2Cthreat_url%2ClistMatches&offset=1", json=mock_response2, ) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) next_run, incidents = fetch_incidents( client=client, max_results=20, last_run={}, first_fetch_time=1621296000, fetch_query=None, pull_signals=False, record_summary_fields=RECORD_SUMMARY_FIELDS_DEFAULT, other_args=None, ) assert incidents[0].get("name") == "Defense Evasion with Persistence - INSIGHT-231" assert incidents[0].get("occurred") == "2021-05-18T14:46:46.000Z" assert incidents[1].get("name") == "Defense Evasion with Persistence - INSIGHT-232" assert incidents[1].get("occurred") == "2021-05-18T14:46:47.000Z" latest_created_time = datetime.strptime(incidents[1].get("occurred"), "%Y-%m-%dT%H:%M:%S.%fZ") assert next_run.get("last_fetch") == int(latest_created_time.replace(tzinfo=UTC).timestamp()) def test_fetch_incidents_lookback_window(requests_mock): """ Test that an insight created within the lookback window is captured. Scenario: - Run 1: Fetch initial insight at 00:10:00 - Run 2: With lookback (queries from 00:05:00), catches insight at 00:07:00 """ from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, fetch_incidents # First run: Establish baseline mock_response_initial = { "data": { "objects": [ { "id": "INSIGHT-INITIAL", "readableId": "INSIGHT-INITIAL", "name": "Initial Insight", "created": "2026-02-11T00:10:00.000000", "description": "Initial insight.", "severity": "MEDIUM", "signals": [], } ], "total": 1, "hasNextPage": False, }, "errors": [], } requests_mock.get( f"{MOCK_URL}/sec/v1/insights?sort=CREATED&sortDir=ASC&q=created%3A%3E%3D2026-02-11T00%3A00%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29&limit=20&offset=0", json=mock_response_initial, ) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) first_fetch_time = 1770768000 # 2026-02-11T00:00:00Z next_run, incidents = fetch_incidents( client=client, max_results=20, last_run={}, first_fetch_time=first_fetch_time, fetch_query=None, pull_signals=False, record_summary_fields=None, other_args=None, ) assert len(incidents) == 1, f"First run: Expected 1 incident, got {len(incidents)}" # Second run: Lookback catches insight at 00:07:00 mock_response_lookback = { "data": { "objects": [ { "id": "INSIGHT-INITIAL", "readableId": "INSIGHT-INITIAL", "name": "Initial Insight", "created": "2026-02-11T00:10:00.000000", "description": "Initial insight.", "severity": "MEDIUM", "signals": [], }, { "id": "INSIGHT-LOOKBACK", "readableId": "INSIGHT-LOOKBACK", "name": "Lookback Window Insight", "created": "2026-02-11T00:07:00.000000", "description": "Insight within lookback window.", "severity": "HIGH", "signals": [], }, ], "total": 2, "hasNextPage": False, }, "errors": [], } requests_mock.get( f"{MOCK_URL}/sec/v1/insights?sort=CREATED&sortDir=ASC&q=created%3A%3E%3D2026-02-11T00%3A05%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29&limit=20&offset=0", json=mock_response_lookback, ) last_run = {"last_fetch": next_run["last_fetch"], "last_fetch_ids": next_run["last_fetch_ids"]} next_run, incidents = fetch_incidents( client=client, max_results=20, last_run=last_run, first_fetch_time=None, fetch_query=None, pull_signals=False, record_summary_fields=None, other_args=None, ) assert len(incidents) == 1, f"Second run: Expected 1 incident, got {len(incidents)}" assert incidents[0]["name"].startswith("Lookback Window Insight") def test_fetch_incidents_delayed_insight_captured(requests_mock): """ Test that a delayed insight is captured in the second run. Scenario: - Run 1: Fetch 2 initial insights - Run 2: Delayed insight appears, initial ones are deduplicated """ from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, fetch_incidents mock_response_first = util_load_json("test_data/insight_list_first_run.json") requests_mock.get( f"{MOCK_URL}/sec/v1/insights?sort=CREATED&sortDir=ASC&q=created%3A%3E%3D2026-02-11T00%3A00%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29&limit=20&offset=0", json=mock_response_first, ) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) first_fetch_time = 1770768000 # 2026-02-11T00:00:00Z next_run, incidents = fetch_incidents( client=client, max_results=20, last_run={}, first_fetch_time=first_fetch_time, fetch_query=None, pull_signals=False, record_summary_fields=None, other_args=None, ) assert len(incidents) == 2, f"First run: Expected 2 incidents, got {len(incidents)}" assert any(i["name"].startswith("Initial Insight 1") for i in incidents) assert any(i["name"].startswith("Initial Insight 2") for i in incidents) mock_response_second = util_load_json("test_data/insight_list_delayed_run.json") requests_mock.get( f"{MOCK_URL}/sec/v1/insights?sort=CREATED&sortDir=ASC&q=created%3A%3E%3D2026-02-10T23%3A55%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29&limit=20&offset=0", json=mock_response_second, ) last_run = {"last_fetch": next_run["last_fetch"], "last_fetch_ids": next_run["last_fetch_ids"]} next_run, incidents = fetch_incidents( client=client, max_results=20, last_run=last_run, first_fetch_time=None, fetch_query=None, pull_signals=False, record_summary_fields=None, other_args=None, ) assert len(incidents) == 1, f"Second run: Expected 1 incident, got {len(incidents)}" assert incidents[0]["name"].startswith("Delayed Insight 1") def test_fetch_incidents_no_duplicates(requests_mock): """ Test that duplicate insights are not created due to lookback overlap. Scenario: - Run 1: Fetch 3 insights - Run 2: Same 3 insights returned (lookback), all deduplicated """ from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, fetch_incidents mock_response_once = util_load_json("test_data/insight_list_delayed_run.json") requests_mock.get( f"{MOCK_URL}/sec/v1/insights?sort=CREATED&sortDir=ASC&q=created%3A%3E%3D2026-02-11T00%3A01%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29&limit=20&offset=0", json=mock_response_once, ) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) first_fetch_time = 1770768060 # 2026-02-11T00:01:00Z next_run, incidents = fetch_incidents( client=client, max_results=20, last_run={}, first_fetch_time=first_fetch_time, fetch_query=None, pull_signals=False, record_summary_fields=None, other_args=None, ) assert len(incidents) == 3, f"First fetch: Expected 3 incidents, got {len(incidents)}" ids = [i["name"] for i in incidents] assert any("Initial Insight 1" in n for n in ids) assert any("Initial Insight 2" in n for n in ids) assert any("Delayed Insight 1" in n for n in ids) requests_mock.get( f"{MOCK_URL}/sec/v1/insights?sort=CREATED&sortDir=ASC&q=created%3A%3E%3D2026-02-10T23%3A56%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29&limit=20&offset=0", json=mock_response_once, ) last_run = {"last_fetch": next_run["last_fetch"], "last_fetch_ids": next_run["last_fetch_ids"]} next_run, incidents = fetch_incidents( client=client, max_results=20, last_run=last_run, first_fetch_time=None, fetch_query=None, pull_signals=False, record_summary_fields=None, other_args=None, ) assert len(incidents) == 0, f"Second fetch: Expected 0 incidents, got {len(incidents)}" def test_fetch_incidents_no_missed_insight_in_6_runs(requests_mock): """ Test that running fetch every minute for 6 runs does not miss any insight. Scenario: - 20 insights all at same timestamp (00:00:00) - Run 6 times, each with lookback - All 20 should be captured without missing any """ from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, fetch_incidents mock_response_many = util_load_json("test_data/insight_list_many.json") # Run 1: no lookback requests_mock.get( f"{MOCK_URL}/sec/v1/insights?sort=CREATED&sortDir=ASC&q=created%3A%3E%3D2026-02-11T00%3A00%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29&limit=20&offset=0", json=mock_response_many, ) # Runs 2-6: with lookback requests_mock.get( f"{MOCK_URL}/sec/v1/insights?sort=CREATED&sortDir=ASC&q=created%3A%3E%3D2026-02-10T23%3A55%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29&limit=20&offset=0", json=mock_response_many, ) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) first_fetch_time = 1770768000 # 2026-02-11T00:00:00Z last_run = {} seen_ids = set() for i in range(6): next_run, incidents = fetch_incidents( client=client, max_results=20, last_run=last_run, first_fetch_time=first_fetch_time if i == 0 else None, fetch_query=None, pull_signals=False, record_summary_fields=None, other_args=None, ) for inc in incidents: seen_ids.add(inc["name"]) last_run = next_run assert len(seen_ids) == 20, f"Expected 20 unique insights, got {len(seen_ids)}" def test_fetch_incidents_5_insights_per_minute_for_6_runs(requests_mock): """ Test continuous insight creation with 5 new insights every minute. With the cap, all runs query from first_fetch_time (00:00:00). """ from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, fetch_incidents client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) # All insights with proper timestamps insights_data = [] for minute in range(6): timestamp = f"2026-02-11T00:0{minute}:00.000000" for i in range(5): insight_num = minute * 5 + i + 1 insights_data.append( { "id": f"INSIGHT-{insight_num}", "readableId": f"INSIGHT-{insight_num}", "name": f"Insight {insight_num}", "created": timestamp, "description": f"Insight {insight_num} created at minute {minute}", "severity": "MEDIUM", "signals": [], } ) first_fetch_time = 1770768000 # 2026-02-11T00:00:00Z last_run = {} all_fetched = [] # The base URL that will be queried every time (due to cap) base_query = ( f"{MOCK_URL}/sec/v1/insights?sort=CREATED&sortDir=ASC" f"&q=created%3A%3E%3D2026-02-11T00%3A00%3A00.000000" "+status%3Ain%28%22new%22%2C+%22inprogress%22%29&limit=20" ) for run_num in range(1, 7): # How many insights exist at this run (5 per minute) total_insights = run_num * 5 current_insights = insights_data[:total_insights] # Mock page 1 (offset=0) - return first 20 page1_data = current_insights[:20] requests_mock.get( f"{base_query}&offset=0", json={"data": {"objects": page1_data, "total": total_insights, "hasNextPage": total_insights > 20}, "errors": []}, ) # Mock page 2 (offset=20) - if needed if total_insights > 20: page2_data = current_insights[20:] requests_mock.get( f"{base_query}&offset=20", json={"data": {"objects": page2_data, "total": total_insights, "hasNextPage": False}, "errors": []}, ) # Fetch incidents next_run, incidents = fetch_incidents( client=client, max_results=20, last_run=last_run, first_fetch_time=first_fetch_time, # Always pass it fetch_query=None, pull_signals=False, record_summary_fields=None, other_args=None, ) all_fetched.extend(incidents) last_run = next_run # Each run should fetch exactly 5 new insights assert len(incidents) == 5, f"Run {run_num}: Expected 5 insights, got {len(incidents)}" # Verify totals assert len(all_fetched) == 30, f"Expected 30 total insights, got {len(all_fetched)}" # Verify no duplicates incident_names = [inc["name"] for inc in all_fetched] assert len(set(incident_names)) == 30, "Found duplicate incidents" def test_fetch_incidents_with_signals(requests_mock): """Tests fetch incidents.""" from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, fetch_incidents mock_response1 = util_load_json("test_data/insight_list_page1.json") requests_mock.get( f"{MOCK_URL}/sec/v1/insights?q=created%3A%3E%3D2021-05-18T00%3A00%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29" "&limit=20&recordSummaryFields=action%2Cdescription%2Cdevice_hostname%2Cdevice_ip%2CdstDevice_hostname" "%2CdstDevice_ip%2Cemail_sender%2Cfile_basename%2Cfile_hash_md5%2Cfile_hash_sha1%2Cfile_hash_sha256" "%2CsrcDevice_hostname%2CsrcDevice_ip%2Cthreat_name%2Cthreat_category%2Cthreat_identifier%2Cuser_username" "%2Cthreat_url%2ClistMatches", json=mock_response1, ) mock_response2 = util_load_json("test_data/insight_list_page2.json") requests_mock.get( f"{MOCK_URL}/sec/v1/insights?q=created%3A%3E%3D2021-05-18T00%3A00%3A00.000000+status%3Ain%28%22new%22%2C+%22inprogress%22%29" "&limit=20&recordSummaryFields=action%2Cdescription%2Cdevice_hostname%2Cdevice_ip%2CdstDevice_hostname" "%2CdstDevice_ip%2Cemail_sender%2Cfile_basename%2Cfile_hash_md5%2Cfile_hash_sha1%2Cfile_hash_sha256" "%2CsrcDevice_hostname%2CsrcDevice_ip%2Cthreat_name%2Cthreat_category%2Cthreat_identifier%2Cuser_username" "%2Cthreat_url%2ClistMatches&offset=1", json=mock_response2, ) mock_response3 = util_load_json("test_data/insight_signal_list1.json") url_combo = ( f'{MOCK_URL}/sec/v1/signals?q=id:in("b4709a3c-c886-5cf0-9c98-88d976161edd","38c4bfea-23b5-5e4f-998c-5433dd093834' + '","0f335c0d-5ffe-5637-a868-146e7f401775","3bea8bf1-430d-5f68-b13c-81cdcf614e20","ab1313bf-de71-539c-bae4-922475561a45' + '","d1607da7-368b-5341-8aeb-927135c8f307","bc7fb3ac-a86e-57ab-bf34-3f98789bb16f","f845dfc7-dd64-58d9-a4d1-238da768ffd1' + '","bb96f1eb-8794-5c69-9e20-f21c43be87aa","8fbe5ecf-c83f-5ed8-9d6f-e5488e34d49c")' ) requests_mock.get(url_combo, json=mock_response3) mock_response4 = util_load_json("test_data/insight_signal_list2.json") requests_mock.get( f'{MOCK_URL}/sec/v1/signals?q=id:in("2e4f64c2-e8c5-53ae-b0aa-01e5005155a7",' + '"ead54992-94f6-5aa3-b902-ec0fb5318dbc")', json=mock_response4, ) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) next_run, incidents = fetch_incidents( client=client, max_results=20, last_run={}, first_fetch_time=1621296000, fetch_query=None, pull_signals=True, record_summary_fields=RECORD_SUMMARY_FIELDS_DEFAULT, other_args=None, ) # Signal incidents should be created first assert incidents[0].get("name") == "GCP API Permission Denied - b4709a3c-c886-5cf0-9c98-88d976161edd" assert incidents[0].get("occurred") == "2021-05-17T20:02:03.000Z" assert incidents[1].get("name") == "GCP Audit Logging Sink Modified - 38c4bfea-23b5-5e4f-998c-5433dd093834" assert incidents[1].get("occurred") == "2021-05-18T14:30:00.000Z" assert incidents[2].get("name") == "GCP Audit GCE Network Route Created or Modified - 0f335c0d-5ffe-5637-a868-146e7f401775" assert incidents[2].get("occurred") == "2021-05-17T16:24:23.000Z" assert incidents[11].get("name") == "GCP Audit Pub/Sub Topic Deleted - ead54992-94f6-5aa3-b902-ec0fb5318dbc" assert incidents[11].get("occurred") == "2021-05-18T14:45:51.000Z" # Followed by Insight incidents assert incidents[12].get("name") == "Defense Evasion with Persistence - INSIGHT-231" assert incidents[12].get("occurred") == "2021-05-18T14:46:46.000Z" assert incidents[13].get("name") == "Defense Evasion with Persistence - INSIGHT-232" assert incidents[13].get("occurred") == "2021-05-18T14:46:47.000Z" latest_created_time = datetime.strptime(incidents[13].get("occurred"), "%Y-%m-%dT%H:%M:%S.%fZ") assert next_run.get("last_fetch") == int(latest_created_time.replace(tzinfo=UTC).timestamp()) DEMISTO_ARGS = {"api_endpoint": MOCK_URL, "access_id": "SUMO_ACCESS_ID", "access_key": "SUMO_ACCESS_KEY"} def get_demisto_arg(name): if name in DEMISTO_ARGS: return DEMISTO_ARGS[name] raise Exception(f"Test setup did not specify a Demisto argument named {name}.") def test_get_remote_data_command(requests_mock, mocker): from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, get_remote_data_command mocker.patch.object(demisto, "getParam", get_demisto_arg) client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) args = { "id": "INSIGHT-220", "lastUpdate": 1621296000, } mock_response = util_load_json("test_data/insight_details.json") requests_mock.get(f"{MOCK_URL}/sec/v1/insights/INSIGHT-220?exclude=signals.allRecords", json=mock_response) response = get_remote_data_command(client, args, True) assert isinstance(response, GetRemoteDataResponse) assert response.entries[0]["Type"] == EntryType.NOTE assert response.entries[0]["Contents"]["dbotIncidentClose"] is True assert response.entries[0]["Contents"]["closeReason"] == "Other" assert response.entries[0]["Contents"]["closeNotes"].startswith( "Insight INSIGHT-220 was closed on Sumo Logic SIEM with resolution" ) assert demisto.getParam("api_endpoint") == DEMISTO_ARGS["api_endpoint"] def test_update_remote_system_command(requests_mock, mocker): from SumoLogicCloudSIEM import DEFAULT_HEADERS, Client, update_remote_system_command client = Client( base_url=MOCK_URL, verify=False, headers=DEFAULT_HEADERS, proxy=False, auth=("access_id", "access_key"), ok_codes=[200] ) client.set_extra_params({"instance_endpoint": "https://test.us2.sumologic.com"}) args = { "data": {"id": "8196"}, "entries": [], "incidentChanged": True, "remoteId": "INSIGHT-220", "status": IncidentStatus.DONE, "delta": {"closeNotes": "Closing incident", "closeReason": "For UT", "closingUserId": "admin", "runStatus": "completed"}, } params = {"close_insight": True} mock_response = util_load_json("test_data/insight_add_comment.json") requests_mock.post(f"{MOCK_URL}/sec/v1/insights/INSIGHT-220/comments", json=mock_response) mock_response = util_load_json("test_data/insight_status.json") requests_mock.put(f"{MOCK_URL}/sec/v1/insights/INSIGHT-220/status", json=mock_response) response = update_remote_system_command(client, args, params) # This Insight ID comes from insight_status.json so it's different assert response == "INSIGHT-221" def test_arg_time_to_q(requests_mock, mocker): from SumoLogicCloudSIEM import arg_time_query_to_q assert arg_time_query_to_q("query>", "Last week", "ts") == "query> ts:NOW-7D..NOW" assert arg_time_query_to_q("query>", "Last 48 hours", "ts") == "query> ts:NOW-48h..NOW" assert arg_time_query_to_q("query>", "Last 24 hours", "ts") == "query> ts:NOW-24h..NOW" # python2 uses __builtin__ python3 uses builtins if __name__ == "__builtin__" or __name__ == "builtins": main()