CriblSearch
Cribl Search allows you to query, retrieve, and manage search jobs, datasets, and saved searches across your Cribl Cloud deployment.
IT Services · Cribl
Details
| ID | CriblSearch |
|---|---|
| Provider | Cribl |
| Category | IT Services |
| From Version | 6.10.0 |
| Docker Image | demisto/fastapi:0.125.0.10158186 |
README
Cribl Search is a search solution that allows you to query, retrieve, and manage search jobs, datasets, and saved searches across your Cribl Cloud deployment.
This integration was integrated and tested with version 4.17.0 of Cribl API.
Configure CriblSearch in Cortex
| Parameter | Description | Required |
|---|---|---|
| Base URL | The base URL assigned to your organization: https://${workspaceName}-${organizationId}.cribl.cloud | True |
| Client ID | True | |
| Client Secret | True | |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False |
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.
cribl-search-query
Runs a search query against Cribl Search and returns results.
Base Command
cribl-search-query
Input
| Argument Name | Description | Required |
|---|---|---|
| query_id | The ID of a saved query to execute. | Optional |
| job_id | The ID of an existing search job to retrieve results from. | Optional |
| query | The search query string to execute. | Optional |
| earliest | The start time for the search, in relative time or epoch seconds. | Optional |
| latest | The end time for the search, in relative time or epoch seconds. | Optional |
| sample_rate | The probability (0-1) of including each matching event (for example, 0.1 returns ~10%). If omitted, no sampling is applied. | Optional |
| force | Whether to force execution of a scheduled query. | Optional |
| page | The page number for pagination. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Cribl.SearchQuery.events | Unknown | The list of events returned by the search (parsed from the NDJSON response). May be empty when the job is still queued/running. |
| Cribl.SearchQuery.isFinished | Boolean | Whether the search query has finished executing. |
| Cribl.SearchQuery.job | Object | The search job metadata associated with this query. |
| Cribl.SearchQuery.job.id | String | The unique identifier of the search job that produced these results. |
| Cribl.SearchQuery.job.query | String | The search query string executed by the job. |
| Cribl.SearchQuery.job.status | String | The current status of the search job (for example, queued, running, completed). |
| Cribl.SearchQuery.job.timeCreated | Number | The epoch (ms) when the search job was created. |
| Cribl.SearchQuery.offset | Number | The offset within the result set used for pagination. |
| Cribl.SearchQuery.persistedEventCount | Number | The number of events persisted in the result set. |
| Cribl.SearchQuery.totalEventCount | Number | Total number of events matched by the query. |
Command example
!cribl-search-query query="dataset=\"cribl_search_sample\" | project method, source, status, url | take 5" earliest="-24h" latest="now" limit=3
Context Example
{
"Cribl": {
"SearchQuery": {
"events": [],
"isFinished": false,
"job": {
"earliest": "-24h",
"id": "1777447153600.MgWe3v",
"latest": "now",
"query": "dataset=\"cribl_search_sample\" | project method, source, status, url | take 5",
"status": "queued",
"timeCreated": 1777447153600
},
"limit": 3,
"offset": 0,
"persistedEventCount": 0,
"totalEventCount": 0
}
}
}
Human Readable Output
Search Query - Job Info
Is Finished Job ID Status Query Earliest Latest Total Events false 1777447153600.MgWe3v queued dataset=”cribl_search_sample” | project method, source, status, url | take 5 -24h now 0
cribl-search-status
Retrieves the status of a specific search job.
Base Command
cribl-search-status
Input
| Argument Name | Description | Required |
|---|---|---|
| job_id | The unique identifier of the search job. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Cribl.SearchStatus.pendingComputeNodeStatuses | Object | The counts of pending compute nodes (warm/cold) for the job. |
| Cribl.SearchStatus.status | String | The current status of the search job (for example, queued, running, completed). |
| Cribl.SearchStatus.timeCreated | Number | The epoch (ms) when the search job was created. |
| Cribl.SearchStatus.timeStarted | Number | The epoch (ms) when the search job started executing. This is only set once the job leaves the queued state. |
| Cribl.SearchStatus.timeCompleted | Number | The epoch (ms) when the search job completed. This is only set after the job finishes. |
Command example
!cribl-search-status job_id="1777207943198.pb0ZZ0"
Context Example
{
"Cribl": {
"SearchStatus": {
"cacheStatusesByStageId": {
"root": {
"cribl_search_sample": {
"cacheStatus": "miss",
"computeType": "v1",
"reason": "Not a Lake Dataset",
"usedCache": false
}
}
},
"pendingComputeNodeStatuses": {
"countCold": 0,
"countWarm": 0
},
"status": "completed",
"timeCompleted": 1777207949675,
"timeCreated": 1777207943198,
"timeNow": 1777447157205,
"timeStarted": 1777207943675
}
}
}
Human Readable Output
Search Job 1777207943198.pb0ZZ0 Status
Status Time Started Time Created Time Completed completed 1777207943675 1777207943198 1777207949675
cribl-search-result
Retrieves the results of a completed search job.
Base Command
cribl-search-result
Input
| Argument Name | Description | Required |
|---|---|---|
| job_id | The unique identifier of the search job. | Required |
| lower_bound | The lower time bound for results (inclusive, epoch). | Optional |
| upper_bound | The upper time bound for results (exclusive, epoch). | Optional |
| page | The page number for pagination. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| all_results | Whether to return all results. If true, overrides the limit argument. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Cribl.SearchResult.events | Unknown | The list of events returned by the search (parsed from the NDJSON response). Each element is a free-form event object whose shape depends on the dataset and the query’s projection. |
| Cribl.SearchResult.isFinished | Boolean | Whether the search job has finished executing. |
| Cribl.SearchResult.job | Object | The search job metadata associated with these results. |
| Cribl.SearchResult.job.id | String | The unique identifier of the search job. |
| Cribl.SearchResult.job.query | String | The search query string executed by the job. |
| Cribl.SearchResult.job.status | String | The current status of the search job (for example, queued, running, completed). |
| Cribl.SearchResult.job.timeCreated | Number | The epoch (ms) when the search job was created. |
| Cribl.SearchResult.offset | Number | The offset within the result set used for pagination. |
| Cribl.SearchResult.persistedEventCount | Number | The number of events persisted in the result set. |
| Cribl.SearchResult.totalEventCount | Number | The total number of events matched by the search job. |
Command example
!cribl-search-result job_id="1777207943198.pb0ZZ0" limit=5
Context Example
{
"Cribl": {
"SearchResult": {
"events": [
{
"source": "s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0RRoVn.2.raw.gz"
},
{
"source": "s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0ZBHzD.2.raw.gz"
},
{
"source": "s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-2w9JEP.2.raw.gz"
},
{
"source": "s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0RRoVn.2.raw.gz"
},
{
"source": "s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0ZBHzD.2.raw.gz"
}
],
"isFinished": true,
"job": {
"earliest": "-24h",
"id": "1777207943198.pb0ZZ0",
"latest": "now",
"query": "dataset=\"cribl_search_sample\" | project method, source, status, url | take 5",
"status": "completed",
"timeCompleted": 1777207949675,
"timeCreated": 1777207943198,
"timeStarted": 1777207943675
},
"limit": 5,
"offset": 0,
"persistedEventCount": 5,
"totalEventCount": 5
}
}
}
Human Readable Output
Search Job 1777207943198.pb0ZZ0 Results - Job Info
Is Finished Job ID Status Query Earliest Latest Total Events true 1777207943198.pb0ZZ0 completed dataset=”cribl_search_sample” | project method, source, status, url | take 5 -24h now 5 Search Job 1777207943198.pb0ZZ0 Results - Events
source s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0RRoVn.2.raw.gz s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0ZBHzD.2.raw.gz s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-2w9JEP.2.raw.gz s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0RRoVn.2.raw.gz s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0ZBHzD.2.raw.gz
cribl-search-job-create
Creates a new search job in Cribl Search.
Base Command
cribl-search-job-create
Input
| Argument Name | Description | Required |
|---|---|---|
| query | The search query string. | Required |
| earliest | The start time for the search, in epoch seconds. | Optional |
| latest | The end time for the search, in epoch seconds. | Optional |
| sample_rate | The probability (0-1) of including each matching event (for example, 0.1 returns ~10%). If omitted, no sampling is applied. | Optional |
| num_events_before | The number of events to include before the target event. | Optional |
| num_events_after | The number of events to include after the target event. | Optional |
| target_event_time | The target event time (epoch seconds). | Optional |
| is_private | Whether the search job is private. Default is True. | Optional |
| set_options | A JSON string of additional search options. | Optional |
| expected_output_type | The expected output type for the search. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Cribl.SearchJob.id | String | The unique identifier of the search job. |
| Cribl.SearchJob.user | String | The user identifier (client ID) that created the job. |
| Cribl.SearchJob.displayUsername | String | The display name of the user who created the job. |
| Cribl.SearchJob.group | String | The search group the job belongs to. |
| Cribl.SearchJob.query | String | The search query string executed by the job. |
| Cribl.SearchJob.status | String | The current status of the search job (for example, queued, running, completed). |
| Cribl.SearchJob.timeCreated | Number | The epoch (ms) when the search job was created. |
| Cribl.SearchJob.type | String | The type of search job (for example, standard, dashboard). |
| Cribl.SearchJob.usageGroupId | String | The identifier of the usage group the job is billed against. |
| Cribl.SearchJob.isPrivate | Boolean | Whether the search job is marked private. |
| Cribl.SearchJob.accelerated | Boolean | Whether the search job uses acceleration. |
| Cribl.SearchJob.earliest | String | The start time for the search, in relative time or epoch seconds. |
| Cribl.SearchJob.latest | String | The end time for the search, in relative time or epoch seconds. |
| Cribl.SearchJob.compatibilityChecks | Object | The compatibility check flags evaluated for the job. |
| Cribl.SearchJob.metadata | Object | The metadata about the query (for example, datasets, providers, operators, and functions). |
| Cribl.SearchJob.setOptions | Object | The additional search options provided when creating the job. |
| Cribl.SearchJob.stages | Unknown | The stages of the search job’s execution plan. |
| Cribl.SearchJob.internal | Object | The internal job state (compiled policies, role-derived limits, preprocessed query, etc.). Returned on create; not normally returned by list/update. |
| Cribl.SearchJob.userDetails | Object | The details about the user/credential that created the job. |
| Cribl.SearchJob.userDetails.email | String | The email address of the user who created the job. |
| Cribl.SearchJob.userDetails.username | String | The username of the user (or client ID, for API-credential users) who created the job. |
| Cribl.SearchJob.userDetails.displayUsername | String | The display name of the user who created the job. |
| Cribl.SearchJob.userDetails.type | String | The type of user identity (for example, apiCredential, sso). |
| Cribl.SearchJob.userDetails.roles | Unknown | The roles assigned to the user. |
Command example
!cribl-search-job-create query="dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr"
Context Example
{
"Cribl": {
"SearchJob": {
"accelerated": false,
"compatibilityChecks": {
"datatypes": false
},
"displayUsername": "example.user@example.com",
"earliest": "-1h",
"group": "default_search",
"id": "1777447149939.xPWbOm",
"internal": {
"compiledPolicies": [
{
"actions": [
"*"
],
"object": "*"
},
{
"actions": [
"GET"
],
"object": "/system/users/EXAMPLECLIENTID0000000000000000@clients"
},
{
"actions": [
"PATCH"
],
"object": "/system/users/EXAMPLECLIENTID0000000000000000@clients/info"
}
],
"detectedKeyAccesses": {},
"email": "example.user@example.com",
"maxExecutors": 50,
"maxResultsPerSearch": 50000,
"maxRunningTimeRange": {
"maxSec": 86400
},
"preprocessedQuery": "dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr",
"roles": [
"search_user",
"org_user",
"ws_user"
]
},
"isPrivate": true,
"latest": "now",
"metadata": {
"arguments": {},
"cloudProvider": "aws",
"computeTypes": {
"v1": 1
},
"datasets": {
"cribl_search_sample": 1
},
"functions": {
"count": 1
},
"operators": {
"dataset=\"cribl_search_sample\"": 1,
"summarize": 1
},
"providerTypes": {
"s3": 1
},
"providers": {
"cribl_s3sample_provider": 1
}
},
"query": "dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr",
"setOptions": {},
"stages": [
{
"dependencies": [],
"filter": "(dataset == 'cribl_search_sample')",
"id": "root",
"resolvedDatasetIds": [
"cribl_search_sample"
],
"searchConfig": {
"canComputeMetadataDistributively": false,
"datasets": [
"cribl_search_sample"
],
"hasSendOperator": false,
"logicalPlans": {
"Combined": {
"root:0:2uw2": [
{
"condition": {
"caseSensitive": false,
"lhs": {
"columnPath": [
"dataset"
],
"type": "identifier"
},
"operator": "==",
"rhs": {
"literal": "cribl_search_sample",
"type": "literal"
},
"type": "binaryOperation"
},
"type": "filter"
}
],
"root:1:tTTC": [
{
"aggregates": [
{
"assignee": {
"columnPath": [
"cnt"
],
"type": "identifier"
},
"operation": {
"functionType": "aggregation",
"name": "count",
"parameters": [],
"type": "function"
},
"type": "assign"
}
],
"aggregationType": "summarize",
"canDistributeAggregation": false,
"groupBy": [
{
"columnPath": [
"srcaddr"
],
"type": "identifier"
}
],
"isPreviewableOperation": true,
"location": "coordinated",
"type": "aggregate"
}
],
"root:3:uDgk": [
{
"type": "noop"
}
]
},
"Coordinated": {
...
},
"Federated": {
...
}
},
"orderedFieldNames": [
"srcaddr",
"cnt"
],
"pipelines": {
"Combined": {
"conf": {
"asyncFuncTimeout": 1000,
"description": "Pipeline, generated from Kalipso query",
"functions": [
{
"canFullyPushToFederated": true,
"conf": {},
"description": "dataset=\"cribl_search_sample\"",
"disabled": false,
"filter": "!(dataset == 'cribl_search_sample')",
"final": false,
"functionInstanceId": "root:0:2uw2",
"id": "drop"
},
{
"canFullyPushToFederated": false,
"conf": {
"aggregations": [
"count().as(cnt)"
],
"cumulative": true,
"flushOnInputClose": false,
"groupbys": [
"srcaddr"
],
"metricsMode": false,
"preserveGroupBys": true,
"printUndefineds": true,
"searchAggMode": "Coordinated",
"sufficientStatsOnly": false,
"timeWindow": "1s"
},
"description": "summarize cnt=count() by srcaddr",
"disabled": false,
"filter": "true",
"final": false,
"functionInstanceId": "root:1:tTTC",
"id": "aggregation"
},
{
"canFullyPushToFederated": true,
"conf": {
"keep": [
"cnt",
"cnt.*",
"srcaddr",
"srcaddr.*"
],
"printUndefineds": true,
"remove": [
"*"
]
},
"description": "summarize cnt=count() by srcaddr",
"disabled": false,
"filter": "true",
"final": false,
"functionInstanceId": "root:3:uDgk",
"id": "eval"
}
]
},
"id": "root"
},
"Coordinated": {
...
},
"Federated": {
...
}
},
"referencedColumnPaths": [
[
"cnt"
],
[
"srcaddr"
]
],
"searchTerms": [],
"useFormattedVisualization": true
},
"searchVersionByDatasetId": {},
"status": "new",
"subQueryText": "dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr"
}
],
"status": "queued",
"timeCreated": 1777447149939,
"type": "standard",
"usageGroupId": "default",
"user": "EXAMPLECLIENTID0000000000000000@clients",
"userDetails": {
"apiCredential": {
"clientId": "EXAMPLECLIENTID0000000000000000@clients",
"createdBy": "example.user@example.com",
"name": "example.user@example.com"
},
"displayUsername": "example.user@example.com",
"email": "example.user@example.com",
"roles": [
"search_user",
"org_user",
"ws_user"
],
"ssoGroups": [],
"type": "apiCredential",
"username": "EXAMPLECLIENTID0000000000000000@clients"
}
}
}
}
Human Readable Output
Search Job Created
User ID Is Private Type Status EXAMPLECLIENTID0000000000000000@clients 1777447149939.xPWbOm true standard queued
cribl-search-job-list
Retrieves a list of search jobs or details of a specific search job.
Base Command
cribl-search-job-list
Input
| Argument Name | Description | Required |
|---|---|---|
| job_id | The unique identifier of a specific search job to retrieve. | Optional |
| limit | The maximum number of results to return. Default is 10. | Optional |
| all_results | Whether to return all results. If true, overrides the limit argument. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Cribl.SearchJob.id | String | Unique identifier of the search job. |
| Cribl.SearchJob.user | String | User identifier (client ID) that created the job. |
| Cribl.SearchJob.displayUsername | String | Display name of the user who created the job. |
| Cribl.SearchJob.group | String | Search group the job belongs to. |
| Cribl.SearchJob.query | String | The search query string executed by the job. |
| Cribl.SearchJob.status | String | Current status of the search job (e.g., queued, running, completed). |
| Cribl.SearchJob.timeCreated | Number | Epoch (ms) when the search job was created. |
| Cribl.SearchJob.timeStarted | Number | Epoch (ms) when the search job started executing. |
| Cribl.SearchJob.timeCompleted | Number | Epoch (ms) when the search job completed. |
| Cribl.SearchJob.type | String | Type of search job (e.g., standard, dashboard). |
| Cribl.SearchJob.isPrivate | Boolean | Whether the search job is marked private. |
| Cribl.SearchJob.accelerated | Boolean | Whether the search job uses acceleration. |
| Cribl.SearchJob.earliest | String | Earliest time boundary for the search (relative time string or epoch seconds). |
| Cribl.SearchJob.earliestEpoch | Number | Resolved earliest time boundary in epoch milliseconds. |
| Cribl.SearchJob.latest | String | Latest time boundary for the search (relative time string or epoch seconds). |
| Cribl.SearchJob.latestEpoch | Number | Resolved latest time boundary in epoch milliseconds. |
| Cribl.SearchJob.cpuMetrics | Object | CPU usage metrics for the executed job (billable seconds, per-executor breakdown, totals). |
| Cribl.SearchJob.compatibilityChecks | Object | Compatibility check flags evaluated for the job. |
| Cribl.SearchJob.metadata | Object | Metadata about the query (datasets, providers, operators, functions, etc.). |
| Cribl.SearchJob.setOptions | Object | Additional search options provided when creating the job. |
| Cribl.SearchJob.stages | Unknown | Stages of the search job’s execution plan, including per-stage cache status and search config. |
Command example
!cribl-search-job-list limit=3
Context Example
{
"Cribl": {
"SearchJob": [
{
"accelerated": false,
"compatibilityChecks": {
"datatypes": false
},
"cpuMetrics": {
"billableCPUSeconds": 24.78200000000004,
"executorsCPUSeconds": {
"23ywr3HV": 0.621,
"2C3c5u1h": 0.619,
"2G7pjwPk": 0.6,
"COORDINATOR": 5.817,
...
},
"totalCPUSeconds": 24.78200000000004,
"totalExecCPUSeconds": 24.78200000000004
},
"displayUsername": "example.user@example.com",
"earliest": "-24h",
"earliestEpoch": 1777121543198,
"group": "default_search",
"id": "1777207943198.pb0ZZ0",
"isPrivate": true,
"latest": "now",
"latestEpoch": 1777207943198,
"metadata": {
"arguments": {},
"cloudProvider": "aws",
"computeTypes": {
"v1": 1
},
"datasets": {
"cribl_search_sample": 1
},
"functions": {},
"operators": {
"dataset=\"cribl_search_sample\"": 1,
"project": 1,
"take": 1
},
"providerTypes": {
"s3": 1
},
"providers": {
"cribl_s3sample_provider": 1
}
},
"query": "dataset=\"cribl_search_sample\" | project method, source, status, url | take 5",
"setOptions": {},
"stages": [
{
"cacheStatusByDatasetId": {
"cribl_search_sample": {
"cacheStatus": "miss",
"computeType": "v1",
"reason": "Not a Lake Dataset",
"usedCache": false
}
},
"dependencies": [],
"filter": "(dataset == 'cribl_search_sample')",
"id": "root",
"resolvedDatasetIds": [
"cribl_search_sample"
],
"searchConfig": {
"canComputeMetadataDistributively": false,
"datasets": [
"cribl_search_sample"
],
"hasSendOperator": false,
"logicalPlans": {
"Combined": {
"root:0:HEER": [
{
"condition": {
"caseSensitive": false,
"lhs": {
"columnPath": [
"dataset"
],
"type": "identifier"
},
"operator": "==",
"rhs": {
"literal": "cribl_search_sample",
"type": "literal"
},
"type": "binaryOperation"
},
"type": "filter"
}
],
"root:1:UBPo": [
{
"add": [
{
"columnPath": [
"method"
],
"type": "identifier"
},
{
"columnPath": [
"source"
],
"type": "identifier"
},
{
"columnPath": [
"status"
],
"type": "identifier"
},
{
"columnPath": [
"url"
],
"type": "identifier"
}
],
"removeOthers": true,
"type": "project"
}
],
"root:3:Xm06": [
{
"limit": 5,
"type": "limit"
}
]
},
"Coordinated": {
...
},
"Federated": {
...
}
},
"orderedFieldNames": [
"method",
"source",
"status",
"url"
],
"pipelines": {
"Combined": {
"conf": {
"asyncFuncTimeout": 1000,
"description": "Pipeline, generated from Kalipso query",
"functions": [
{
"canFullyPushToFederated": true,
"conf": {},
"description": "dataset=\"cribl_search_sample\"",
"disabled": false,
"filter": "!(dataset == 'cribl_search_sample')",
"final": false,
"functionInstanceId": "root:0:HEER",
"id": "drop"
},
{
"canFullyPushToFederated": false,
"conf": {
"limit": 5
},
"description": "take 5",
"disabled": false,
"filter": "true",
"final": false,
"functionInstanceId": "root:3:Xm06",
"id": "limit"
},
{
"canFullyPushToFederated": true,
"conf": {
"keep": [
"method",
"method.*",
"source",
"source.*",
"status",
"status.*",
"url",
"url.*"
],
"printUndefineds": true,
"remove": [
"*"
]
},
"description": "project method, source, status, url",
"disabled": false,
"filter": "true",
"final": false,
"functionInstanceId": "root:1:UBPo",
"id": "eval"
}
]
},
"id": "root"
},
"Coordinated": {
...
},
"Federated": {
...
}
},
"referencedColumnPaths": [
[
"method"
],
[
"source"
],
[
"status"
],
[
"url"
]
],
"searchTerms": [],
"useFormattedVisualization": true
},
"searchVersionByDatasetId": {},
"status": "completed",
"subQueryText": "dataset=\"cribl_search_sample\" | project method, source, status, url | take 5"
}
],
"status": "completed",
"timeCompleted": 1777207949675,
"timeCreated": 1777207943198,
"timeStarted": 1777207943675,
"type": "dashboard",
"user": "EXAMPLECLIENTID0000000000000000@clients"
},
...
]
}
}
Human Readable Output
Search Jobs List
User ID Is Private Type Status EXAMPLECLIENTID0000000000000000@clients 1777207943198.pb0ZZ0 true dashboard completed EXAMPLECLIENTID0000000000000000@clients 1777208015306.F0hxMo true dashboard completed EXAMPLECLIENTID0000000000000000@clients 1777208286161.tTkDeJ true dashboard completed
cribl-search-job-update
Updates a search job’s status or privacy setting. At least one of status or is_private must be provided.
Base Command
cribl-search-job-update
Input
| Argument Name | Description | Required |
|---|---|---|
| job_id | The unique identifier of the search job to update. | Required |
| status | The new status for the search job (e.g., completed, canceled). | Optional |
| is_private | Whether the search job should be private. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Cribl.SearchJob.id | String | Unique identifier of the search job. |
| Cribl.SearchJob.user | String | User identifier (client ID) that created the job. |
| Cribl.SearchJob.displayUsername | String | Display name of the user who created the job. |
| Cribl.SearchJob.group | String | Search group the job belongs to. |
| Cribl.SearchJob.query | String | The search query string executed by the job. |
| Cribl.SearchJob.status | String | Current status of the search job (e.g., queued, running, completed). |
| Cribl.SearchJob.timeCreated | Number | Epoch (ms) when the search job was created. |
| Cribl.SearchJob.timeStarted | Number | Epoch (ms) when the search job started executing. |
| Cribl.SearchJob.timeCompleted | Number | Epoch (ms) when the search job completed. |
| Cribl.SearchJob.type | String | Type of search job (e.g., standard, dashboard). |
| Cribl.SearchJob.isPrivate | Boolean | Whether the search job is marked private. |
| Cribl.SearchJob.accelerated | Boolean | Whether the search job uses acceleration. |
| Cribl.SearchJob.earliest | String | Earliest time boundary for the search (relative time string or epoch seconds). |
| Cribl.SearchJob.earliestEpoch | Number | Resolved earliest time boundary in epoch milliseconds. |
| Cribl.SearchJob.latest | String | Latest time boundary for the search (relative time string or epoch seconds). |
| Cribl.SearchJob.latestEpoch | Number | Resolved latest time boundary in epoch milliseconds. |
| Cribl.SearchJob.compatibilityChecks | Object | Compatibility check flags evaluated for the job. |
| Cribl.SearchJob.metadata | Object | Metadata about the query (datasets, providers, operators, functions, etc.). |
| Cribl.SearchJob.setOptions | Object | Additional search options provided when creating the job. |
| Cribl.SearchJob.stages | Unknown | Stages of the search job’s execution plan, including per-stage cache status and search config. |
| Cribl.SearchJob.userDetails | Object | Details about the user/credential that created the job. |
| Cribl.SearchJob.userDetails.email | String | Email address of the user who created the job. |
| Cribl.SearchJob.userDetails.username | String | Username of the user (or client ID, for API-credential users) who created the job. |
| Cribl.SearchJob.userDetails.displayUsername | String | Display name of the user who created the job. |
| Cribl.SearchJob.userDetails.type | String | Type of user identity (e.g., apiCredential, sso). |
| Cribl.SearchJob.userDetails.roles | Unknown | Roles assigned to the user. |
Command example
!cribl-search-job-update job_id="1777446985069.KSZQ5h" is_private=true
Context Example
{
"Cribl": {
"SearchJob": {
"accelerated": false,
"compatibilityChecks": {
"datatypes": false
},
"displayUsername": "example.user@example.com",
"earliest": "-1h",
"earliestEpoch": 1777443385069,
"group": "default_search",
"id": "1777446985069.KSZQ5h",
"isPrivate": true,
"latest": "now",
"latestEpoch": 1777446985069,
"metadata": {
"arguments": {},
"cloudProvider": "aws",
"computeTypes": {
"v1": 1
},
"datasets": {
"cribl_search_sample": 1
},
"functions": {
"count": 1
},
"operators": {
"dataset=\"cribl_search_sample\"": 1,
"summarize": 1
},
"providerTypes": {
"s3": 1
},
"providers": {
"cribl_s3sample_provider": 1
}
},
"query": "dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr",
"setOptions": {},
"stages": [
{
"cacheStatusByDatasetId": {
"cribl_search_sample": {
"cacheStatus": "miss",
"computeType": "v1",
"reason": "Not a Lake Dataset",
"usedCache": false
}
},
"dependencies": [],
"filter": "(dataset == 'cribl_search_sample')",
"id": "root",
"resolvedDatasetIds": [
"cribl_search_sample"
],
"searchConfig": {
"canComputeMetadataDistributively": false,
"datasets": [
"cribl_search_sample"
],
"hasSendOperator": false,
"logicalPlans": {
"Combined": {
"root:0:R25N": [
{
"condition": {
"caseSensitive": false,
"lhs": {
"columnPath": [
"dataset"
],
"type": "identifier"
},
"operator": "==",
"rhs": {
"literal": "cribl_search_sample",
"type": "literal"
},
"type": "binaryOperation"
},
"type": "filter"
}
],
"root:1:TFOw": [
{
"aggregates": [
{
"assignee": {
"columnPath": [
"cnt"
],
"type": "identifier"
},
"operation": {
"functionType": "aggregation",
"name": "count",
"parameters": [],
"type": "function"
},
"type": "assign"
}
],
"aggregationType": "summarize",
"canDistributeAggregation": false,
"groupBy": [
{
"columnPath": [
"srcaddr"
],
"type": "identifier"
}
],
"isPreviewableOperation": true,
"location": "coordinated",
"type": "aggregate"
}
],
"root:3:zikB": [
{
"type": "noop"
}
]
},
"Coordinated": {
...
},
"Federated": {
...
}
},
"orderedFieldNames": [
"srcaddr",
"cnt"
],
"pipelines": {
"Combined": {
"conf": {
"asyncFuncTimeout": 1000,
"description": "Pipeline, generated from Kalipso query",
"functions": [
{
"canFullyPushToFederated": true,
"conf": {},
"description": "dataset=\"cribl_search_sample\"",
"disabled": false,
"filter": "!(dataset == 'cribl_search_sample')",
"final": false,
"functionInstanceId": "root:0:R25N",
"id": "drop"
},
{
"canFullyPushToFederated": false,
"conf": {
"aggregations": [
"count().as(cnt)"
],
"cumulative": true,
"flushOnInputClose": false,
"groupbys": [
"srcaddr"
],
"metricsMode": false,
"preserveGroupBys": true,
"printUndefineds": true,
"searchAggMode": "Coordinated",
"sufficientStatsOnly": false,
"timeWindow": "1s"
},
"description": "summarize cnt=count() by srcaddr",
"disabled": false,
"filter": "true",
"final": false,
"functionInstanceId": "root:1:TFOw",
"id": "aggregation"
},
{
"canFullyPushToFederated": true,
"conf": {
"keep": [
"cnt",
"cnt.*",
"srcaddr",
"srcaddr.*"
],
"printUndefineds": true,
"remove": [
"*"
]
},
"description": "summarize cnt=count() by srcaddr",
"disabled": false,
"filter": "true",
"final": false,
"functionInstanceId": "root:3:zikB",
"id": "eval"
}
]
},
"id": "root"
},
"Coordinated": {
...
},
"Federated": {
...
}
},
"referencedColumnPaths": [
[
"cnt"
],
[
"srcaddr"
]
],
"searchTerms": [],
"useFormattedVisualization": true
},
"searchVersionByDatasetId": {},
"status": "completed",
"subQueryText": "dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr"
}
],
"status": "completed",
"timeCompleted": 1777446992662,
"timeCreated": 1777446985069,
"timeStarted": 1777446985598,
"type": "standard",
"user": "EXAMPLECLIENTID0000000000000000@clients",
"userDetails": {
"apiCredential": {
"clientId": "EXAMPLECLIENTID0000000000000000@clients",
"createdBy": "example.user@example.com",
"name": "example.user@example.com"
},
"displayUsername": "example.user@example.com",
"email": "example.user@example.com",
"roles": [
"search_user",
"org_user",
"ws_user"
],
"ssoGroups": [],
"type": "apiCredential",
"username": "EXAMPLECLIENTID0000000000000000@clients"
}
}
}
}
Human Readable Output
The job 1777446985069.KSZQ5h has been successfully updated.
cribl-search-job-delete
Deletes a specific search job.
Base Command
cribl-search-job-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| job_id | The unique identifier of the search job to delete. | Required |
Context Output
There is no context output for this command.
Command example
!cribl-search-job-delete job_id="1777446985069.KSZQ5h"
Human Readable Output
The job 1777446985069.KSZQ5h has been successfully deleted.
cribl-search-dataset-list
Retrieves a list of available datasets or details of a specific dataset.
Base Command
cribl-search-dataset-list
Input
| Argument Name | Description | Required |
|---|---|---|
| dataset_id | The unique identifier of a specific dataset to retrieve. | Optional |
| limit | The maximum number of results to return. Default is 10. | Optional |
| all_results | Whether to return all results. If true, overrides the limit argument. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Cribl.SearchDataset.id | String | The unique identifier of the dataset. |
| Cribl.SearchDataset.type | String | The dataset provider type (for example, s3, azure_blob, gcs). |
| Cribl.SearchDataset.provider | String | The identifier of the provider configuration backing the dataset. |
| Cribl.SearchDataset.region | String | The cloud region where the dataset’s underlying storage resides (when applicable). |
| Cribl.SearchDataset.bucket | String | The bucket/path template that locates the dataset’s underlying objects. |
| Cribl.SearchDataset.description | String | The human-readable description of the dataset. |
| Cribl.SearchDataset.filter | String | The filter expression applied to events from the dataset; defaults to “true” (passes all events). |
| Cribl.SearchDataset.tags | Unknown | The tags assigned to the dataset (string or array of strings). |
| Cribl.SearchDataset.breakerRulesets | Unknown | The event breaker rulesets associated with the dataset. |
| Cribl.SearchDataset.storageClasses | Unknown | The storage classes the dataset is configured to read from. |
| Cribl.SearchDataset.staleChannelFlushMs | Number | The time in milliseconds after which a stale channel is flushed during ingestion. |
Command example
!cribl-search-dataset-list limit=3
Context Example
{
"Cribl": {
"SearchDataset": {
"breakerRulesets": [
"AWS Datatypes",
"Apache Datatypes",
"Syslog Datatypes",
"Cribl Search",
"Microsoft Windows Datatypes",
"Azure Datatypes",
"Microsoft O365 Datatypes",
"Microsoft Graph API Datatypes"
],
"bucket": "cribl-search-example/data/${dataSource}/${_time:%Y}/${_time:%m}/${_time:%d}/${_time:%H}",
"description": "Search Cribl provided public sample data",
"filter": "true",
"id": "cribl_search_sample",
"provider": "cribl_s3sample_provider",
"region": "us-west-2",
"staleChannelFlushMs": 10000,
"storageClasses": [
"STANDARD",
"INTELLIGEN",
"STANDARD_I",
"ONEZONE_IA",
"GLACIER_IR",
"REDUCED_RE",
"_RESTORED"
],
"tags": "cribl:default",
"type": "s3"
}
}
}
Human Readable Output
Datasets List
ID Provider Type Region cribl_search_sample cribl_s3sample_provider s3 us-west-2
cribl-saved-search-list
Retrieves a list of saved searches or details of a specific saved search.
Base Command
cribl-saved-search-list
Input
| Argument Name | Description | Required |
|---|---|---|
| search_id | The unique identifier of a specific saved search to retrieve. | Optional |
| limit | The maximum number of results to return. Default is 10. | Optional |
| all_results | Whether to return all results. If true, overrides the limit argument. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Cribl.SavedSearch.id | String | The unique identifier of the saved search. |
| Cribl.SavedSearch.name | String | The display name of the saved search. |
| Cribl.SavedSearch.query | String | The search query string defined by the saved search. |
Command example
!cribl-saved-search-list limit=3
Context Example
{
"Cribl": {
"SavedSearch": [
{
"description": "Searches finished in the last 1h",
"earliest": "-1h",
"id": "cribl_search_finished_1h",
"latest": "now",
"lib": "cribl",
"name": "cribl_search_finished_1h",
"query": "cribl dataset=\"cribl_internal_logs\" source=*searches.log message=\"search finished\" | summarize count(), elapsedMS=sum(stats.elapsedMs), eventsFound=sum(stats.eventsFound) by user=coalesce(stats.userDisplayName, stats.user)"
},
{
"description": "Searches started in the last 1h",
"earliest": "-1h",
"id": "cribl_search_started_1h",
"latest": "now",
"lib": "cribl",
"name": "cribl_search_started_1h",
"query": "cribl dataset=\"cribl_internal_logs\" source=*searches.log message=\"search started\" | summarize count() by user=coalesce(stats.userDisplayName, stats.user)"
}
]
}
}
Human Readable Output
Saved Searches List
ID Description Name Query cribl_search_finished_1h Searches finished in the last 1h cribl_search_finished_1h cribl dataset=”cribl_internal_logs” source=*searches.log message=”search finished” | summarize count(), elapsedMS=sum(stats.elapsedMs), eventsFound=sum(stats.eventsFound) by user=coalesce(stats.userDisplayName, stats.user) cribl_search_started_1h Searches started in the last 1h cribl_search_started_1h cribl dataset=”cribl_internal_logs” source=*searches.log message=”search started” | summarize count() by user=coalesce(stats.userDisplayName, stats.user)
Configuration parameters
url— Base URL (required)credentials— Client ID (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (9)
-
cribl-saved-search-listRetrieves a list of saved searches or details of a specific saved search.
-
cribl-search-dataset-listRetrieves a list of available datasets or details of a specific dataset.
-
cribl-search-job-createCreates a new search job in Cribl Search.
-
cribl-search-job-deleteDeletes a specific search job.
-
cribl-search-job-listRetrieves a list of search jobs or details of a specific search job.
-
cribl-search-job-updateUpdates a search job's status or privacy setting. At least one of status or is_private must be provided.
-
cribl-search-queryRuns a search query against Cribl Search and returns results.
-
cribl-search-resultRetrieves the results of a completed search job.
-
cribl-search-statusRetrieves the status of a specific search job.
# ruff: noqa: F403, F405 import demistomock as demisto from CommonServerPython import * from CommonServerUserPython import * import json from typing import Any from pydantic import AnyUrl, Field, SecretStr, root_validator, validator # pylint: disable=no-name-in-module from ContentClientApiModule import * from BaseContentApiModule import * # region Constants BASE_CONTEXT_OUTPUT_PREFIX = "Cribl" CRIBL_TOKEN_URL = "https://login.cribl.cloud/oauth/token" API_PREFIX = "/api/v1/m/default_search" # endregion # region Helpers def _parse_ndjson(response_text: str) -> dict[str, Any]: """ Parses a newline-delimited JSON (ndjson) response. The first line contains metadata (isFinished, job, etc.). Subsequent lines contain the actual event data. Args: response_text (str): The raw ndjson response text. Returns: dict[str, Any]: The parsed metadata dict with an 'events' key containing parsed event lines. """ lines = [line for line in response_text.strip().split("\n") if line.strip()] if not lines: return {} # Parse the first line as the metadata try: metadata: dict[str, Any] = json.loads(lines[0]) except json.JSONDecodeError as e: raise DemistoException(f"Failed to parse Cribl Search response metadata: {e}") from e # Parse remaining lines as events events: list[dict[str, Any]] = [] for line in lines[1:]: try: events.append(json.loads(line)) except json.JSONDecodeError: continue metadata["events"] = events return metadata def truncate_results(results: list[Any], limit: int | None = None, all_results: bool = False) -> list[Any]: """ Truncates a list of results based on a limit or an override flag. Args: results (list[Any]): The list of results to truncate. limit (int | None): The maximum number of results to return. all_results (bool): If True, returns the full list regardless of the limit. Returns: list[Any]: The truncated slice of results. """ if all_results: return results if limit is not None: return results[:limit] return results def validate_json(value): if isinstance(value, str) and value: try: return json.loads(value) except json.JSONDecodeError as e: # not logging json value as it might contain sensitive information demisto.debug(f"[VALIDATION FAILED] Could not parse json from provided value with exception {e.msg}.") return value return value # endregion # region Parameters class Credentials(ContentBaseModel): """Credentials model for API authentication.""" identifier: str password: SecretStr class CriblSearchParams(BaseParams): """Integration parameters for Cribl Search.""" url: AnyUrl credentials: Credentials @property def client_id(self): return self.credentials.identifier @property def client_secret(self): return self.credentials.password # endregion # region Auth & Client class CriblSearchAuthHandler(OAuth2ClientCredentialsHandler): """Custom authentication handler for Cribl Search.""" def __init__(self, client_id: str, client_secret: SecretStr): # Create context store for token persistence context_store = ContentClientContextStore(namespace="CriblSearch") super().__init__( token_url=CRIBL_TOKEN_URL, client_id=client_id, client_secret=client_secret.get_secret_value(), audience="https://api.cribl.cloud", context_store=context_store, ) class CriblSearchClient(ContentClient): """Client for Cribl Search API.""" def __init__(self, params: CriblSearchParams): auth_handler = CriblSearchAuthHandler( params.client_id, params.client_secret, ) super().__init__( base_url=urljoin(str(params.url), API_PREFIX), verify=params.verify, proxy=params.proxy, auth_handler=auth_handler, client_name="CriblSearchClient", ) def search_query( self, query_id: str | None = None, job_id: str | None = None, query: str | None = None, earliest: str | None = None, latest: str | None = None, sample_rate: int | None = None, force: bool | None = None, offset: int | None = None, limit: int | None = None, ) -> dict[str, Any]: """ Executes a search query. Args: query_id (str | None): The query ID. job_id (str | None): The job ID. query (str | None): The search query string. earliest (str | None): The earliest time boundary. latest (str | None): The latest time boundary. sample_rate (int | None): The sample rate. force (bool | None): Whether to force the query. offset (int | None): The offset for pagination. limit (int | None): The maximum number of results. Returns: dict[str, Any]: The search query response. """ url_suffix = "/search/query" params: dict[str, Any] = assign_params( queryId=query_id, jobId=job_id, query=query, earliest=earliest, latest=latest, sampleRate=sample_rate, force=force, offset=offset, limit=limit, ) demisto.debug(f"Sending a GET Request to {url_suffix}.") response_text = self.get( url_suffix=url_suffix, params=params, resp_type="text", ) return _parse_ndjson(response_text) def search_job_status(self, job_id: str) -> dict[str, Any]: """ Gets the status of a search job. Args: job_id (str): The ID of the search job. Returns: dict[str, Any]: The search job status. """ url_suffix = f"/search/jobs/{job_id}/status" demisto.debug(f"Sending a GET Request to {url_suffix}.") response = self.get( url_suffix=url_suffix, resp_type="json", ) items = response.get("items", []) return items[0] if items else {} def search_job_results( self, job_id: str, lower_bound: int | None = None, upper_bound: int | None = None, offset: int | None = None, limit: int | None = None, ) -> dict[str, Any]: """ Gets the results of a search job. Args: job_id (str): The ID of the search job. lower_bound (int | None): The lower bound for results. upper_bound (int | None): The upper bound for results. offset (int | None): The offset for pagination. limit (int | None): The maximum number of results. Returns: dict[str, Any]: The search job results. """ url_suffix = f"/search/jobs/{job_id}/results" params: dict[str, Any] = assign_params( lowerBound=lower_bound, upperBound=upper_bound, offset=offset, limit=limit, ) demisto.debug(f"Sending a GET Request to {url_suffix}.") response_text = self.get( url_suffix=url_suffix, params=params, resp_type="text", ) return _parse_ndjson(response_text) def search_job_create( self, query: str, earliest: int | None = None, latest: int | None = None, sample_rate: int | None = None, num_events_before: int | None = None, num_events_after: int | None = None, target_event_time: int | None = None, is_private: bool | None = None, set_options: dict[str, Any] | None = None, expected_output_type: str | None = None, ) -> dict[str, Any]: """ Creates a new search job. Args: query (str): The search query string. earliest (int | None): The earliest time boundary. latest (int | None): The latest time boundary. sample_rate (int | None): The sample rate. num_events_before (int | None): Number of events before. num_events_after (int | None): Number of events after. target_event_time (int | None): Target event time. is_private (bool | None): Whether the job is private. set_options (dict[str, Any] | None): Additional options. expected_output_type (str | None): Expected output type. Returns: dict[str, Any]: The created search job. """ url_suffix = "/search/jobs" json_data: dict[str, Any] = assign_params( query=query, earliest=earliest, latest=latest, sampleRate=sample_rate, numEventsBefore=num_events_before, numEventsAfter=num_events_after, targetEventTime=target_event_time, isPrivate=is_private, setOptions=set_options, expectedOutputType=expected_output_type, ) demisto.debug(f"Sending a POST Request to {url_suffix}.") response = self.post( url_suffix=url_suffix, json_data=json_data, resp_type="json", ) items = response.get("items", []) return items[0] if items else {} def search_jobs_list(self, job_id: str | None = None) -> list[dict[str, Any]] | dict[str, Any]: """ Gets a list of all search jobs or a specific search job. Args: job_id (str | None): The ID of the search job to get. Returns: list[dict[str, Any]] | dict[str, Any]: A list of search jobs or a specific search job. """ url_suffix = "/search/jobs" if job_id: url_suffix += f"/{job_id}" demisto.debug(f"Sending a GET Request to {url_suffix}.") response = self.get( url_suffix=url_suffix, resp_type="json", ) items = response.get("items", []) if job_id: return items[0] if items else {} return items def search_job_update( self, job_id: str, status: str | None = None, is_private: bool | None = None, ) -> dict[str, Any]: """ Updates a search job. Args: job_id (str): The ID of the search job to update. status (str | None): The new status for the job. is_private (bool | None): Whether the job is private. Returns: dict[str, Any]: The updated search job. """ url_suffix = f"/search/jobs/{job_id}" json_data: dict[str, Any] = assign_params( status=status, isPrivate=is_private, ) demisto.debug(f"Sending a PATCH Request to {url_suffix}.") response = self.patch( url_suffix=url_suffix, json_data=json_data, resp_type="json", ) items = response.get("items", []) return items[0] if items else {} def search_job_delete(self, job_id: str) -> str: """ Deletes a search job. Args: job_id (str): The ID of the search job to delete. Returns: str: The deletion response text. """ url_suffix = f"/search/jobs/{job_id}" demisto.debug(f"Sending a DELETE Request to {url_suffix}.") return self.delete( url_suffix=url_suffix, resp_type="text", ) def search_datasets_list(self, dataset_id: str | None = None) -> list[dict[str, Any]] | dict[str, Any]: """ Gets a list of all datasets or a specific dataset. Args: dataset_id (str | None): The ID of the dataset to get. Returns: list[dict[str, Any]] | dict[str, Any]: A list of datasets or a specific dataset. """ url_suffix = "/search/datasets" if dataset_id: url_suffix += f"/{dataset_id}" demisto.debug(f"Sending a GET Request to {url_suffix}.") response = self.get( url_suffix=url_suffix, resp_type="json", ) items = response.get("items", []) if dataset_id: return items[0] if items else {} return items def saved_searches_list(self, search_id: str | None = None) -> list[dict[str, Any]] | dict[str, Any]: """ Gets a list of all saved searches or a specific saved search. Args: search_id (str | None): The ID of the saved search to get. Returns: list[dict[str, Any]] | dict[str, Any]: A list of saved searches or a specific saved search. """ url_suffix = "/search/saved" if search_id: url_suffix += f"/{search_id}" demisto.debug(f"Sending a GET Request to {url_suffix}.") response = self.get( url_suffix=url_suffix, resp_type="json", ) items = response.get("items", []) if search_id: return items[0] if items else {} return items # endregion # region test-module def test_module(client: CriblSearchClient) -> str: """ Verifies the connectivity with the Cribl Search API. This function attempts to list datasets to ensure that the provided credentials and Server URL are valid and reachable. Args: client (CriblSearchClient): The Cribl Search API client. Returns: str: Returns "ok" if the connection is successful, otherwise an error message. """ try: demisto.debug("[Testing] Testing API connectivity") client.search_datasets_list() demisto.debug("[Testing] API connectivity test passed") except Exception as e: demisto.error(traceback.format_exc()) return f"AuthenticationError: Connection failed. Make sure Server URL and credentials are correctly set. {str(e)}" demisto.debug("[Testing] All tests passed.") return "ok" # endregion # region cribl-search-query class SearchQueryArgs(ContentBaseModel): # NOTE: API enforces a oneOf on this endpoint - exactly one of: # (A) query_id alone, (B) job_id alone, or (C) query + earliest + latest (all three). # Combos (A)/(B)/(C) presence is enforced client-side via @root_validator below # (raises ValueError -> Pydantic ValidationError). Conflicting combos still bubble from the API. query_id: str | None = Field(None, alias="query_id") job_id: str | None = Field(None, alias="job_id") query: str | None = Field(None, alias="query") earliest: str | None = Field(None, alias="earliest") latest: str | None = Field(None, alias="latest") sample_rate: int | None = Field(None, alias="sample_rate") force: bool = Field(False, alias="force") page: int | None = Field(None, alias="page") limit: int | None = Field(50, alias="limit") @validator("sample_rate", pre=True, allow_reuse=True) @classmethod def validate_sample_rate(cls, v): return arg_to_number(v) @validator("force", pre=True, allow_reuse=True) @classmethod def validate_force(cls, v): return argToBoolean(v) @validator("page", pre=True, allow_reuse=True) @classmethod def validate_page(cls, v): return arg_to_number(v) @validator("limit", pre=True, allow_reuse=True) @classmethod def validate_limit(cls, v): return arg_to_number(v) @root_validator(allow_reuse=True) @classmethod def validate_oneof_combos(cls, values): query = values.get("query") query_id = values.get("query_id") job_id = values.get("job_id") earliest = values.get("earliest") latest = values.get("latest") if query is None and query_id is None and job_id is None: raise ValueError("At least one of 'query', 'query_id', or 'job_id' must be provided.") if query is not None and (earliest is None or latest is None): raise ValueError("When 'query' is provided, both 'earliest' and 'latest' must also be provided.") return values def search_query_command(client: CriblSearchClient, args: SearchQueryArgs) -> CommandResults: """ Executes the cribl-search-query command. Runs a search query against the Cribl Search API. Args: client (CriblSearchClient): The Cribl Search API client. args (SearchQueryArgs): The command arguments. Returns: CommandResults: The results of the command execution. """ offset = None if args.page and args.limit: offset = (args.page - 1) * args.limit results = client.search_query( query_id=args.query_id, job_id=args.job_id, query=args.query, earliest=args.earliest, latest=args.latest, sample_rate=args.sample_rate, force=args.force or None, offset=offset, limit=args.limit, ) # Flatten job info for display job_info = results.get("job", {}) display_data = { "isFinished": results.get("isFinished"), "id": job_info.get("id"), "status": job_info.get("status"), "query": job_info.get("query"), "earliest": job_info.get("earliest"), "latest": job_info.get("latest"), "totalEventCount": results.get("totalEventCount"), "persistedEventCount": results.get("persistedEventCount"), } readable_parts: list[str] = [] readable_parts.append( tableToMarkdown( "Search Query - Job Info", display_data, headers=["isFinished", "id", "status", "query", "earliest", "latest", "totalEventCount"], headerTransform=lambda x: { "isFinished": "Is Finished", "id": "Job ID", "status": "Status", "query": "Query", "earliest": "Earliest", "latest": "Latest", "totalEventCount": "Total Events", }.get(x, x), removeNull=True, ) ) events = results.get("events", []) if events: readable_parts.append( tableToMarkdown( "Search Query - Events", events, removeNull=True, ) ) readable_output = "\n".join(readable_parts) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchQuery", outputs_key_field="job.id", outputs=results, readable_output=readable_output, raw_response=results, ) # endregion # region cribl-search-status class SearchStatusArgs(ContentBaseModel): job_id: str = Field(alias="job_id") def search_status_command(client: CriblSearchClient, args: SearchStatusArgs) -> CommandResults: """ Executes the cribl-search-status command. Retrieves the status of a search job from the Cribl Search API. Args: client (CriblSearchClient): The Cribl Search API client. args (SearchStatusArgs): The command arguments including job_id. Returns: CommandResults: The results of the command execution. """ results = client.search_job_status(job_id=args.job_id) readable_output = tableToMarkdown( f"Search Job {args.job_id} Status", results, headers=["status", "timeStarted", "timeCreated", "timeCompleted"], headerTransform=lambda x: { "status": "Status", "timeStarted": "Time Started", "timeCreated": "Time Created", "timeCompleted": "Time Completed", }.get(x, x), removeNull=True, ) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchStatus", outputs=results, readable_output=readable_output, raw_response=results, ) # endregion # region cribl-search-result class SearchResultArgs(ContentBaseModel): job_id: str = Field(alias="job_id") lower_bound: int | None = Field(None, alias="lower_bound") upper_bound: int | None = Field(None, alias="upper_bound") page: int | None = Field(None, alias="page") limit: int | None = Field(50, alias="limit") all_results: bool = Field(False, alias="all_results") @validator("lower_bound", pre=True, allow_reuse=True) @classmethod def validate_lower_bound(cls, v): return arg_to_number(v) @validator("upper_bound", pre=True, allow_reuse=True) @classmethod def validate_upper_bound(cls, v): return arg_to_number(v) @validator("page", pre=True, allow_reuse=True) @classmethod def validate_page(cls, v): return arg_to_number(v) @validator("limit", pre=True, allow_reuse=True) @classmethod def validate_limit(cls, v): return arg_to_number(v) @validator("all_results", pre=True, allow_reuse=True) @classmethod def validate_all_results(cls, v): return argToBoolean(v) def search_result_command(client: CriblSearchClient, args: SearchResultArgs) -> CommandResults: """ Executes the cribl-search-result command. Retrieves the results of a search job from the Cribl Search API. Args: client (CriblSearchClient): The Cribl Search API client. args (SearchResultArgs): The command arguments including job_id and pagination options. Returns: CommandResults: The results of the command execution. """ offset = None if args.page and args.limit: offset = (args.page - 1) * args.limit results = client.search_job_results( job_id=args.job_id, lower_bound=args.lower_bound, upper_bound=args.upper_bound, offset=offset, limit=None if args.all_results else args.limit, ) # Flatten job info for display job_info = results.get("job", {}) display_data = { "isFinished": results.get("isFinished"), "id": job_info.get("id"), "status": job_info.get("status"), "query": job_info.get("query"), "earliest": job_info.get("earliest"), "latest": job_info.get("latest"), "totalEventCount": results.get("totalEventCount"), "persistedEventCount": results.get("persistedEventCount"), } readable_parts: list[str] = [] readable_parts.append( tableToMarkdown( f"Search Job {args.job_id} Results - Job Info", display_data, headers=["isFinished", "id", "status", "query", "earliest", "latest", "totalEventCount"], headerTransform=lambda x: { "isFinished": "Is Finished", "id": "Job ID", "status": "Status", "query": "Query", "earliest": "Earliest", "latest": "Latest", "totalEventCount": "Total Events", }.get(x, x), removeNull=True, ) ) events = results.get("events", []) if events: readable_parts.append( tableToMarkdown( f"Search Job {args.job_id} Results - Events", events, removeNull=True, ) ) readable_output = "\n".join(readable_parts) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchResult", outputs_key_field="job.id", outputs=results, readable_output=readable_output, raw_response=results, ) # endregion # region cribl-search-job-create class SearchJobCreateArgs(ContentBaseModel): query: str = Field(alias="query") earliest: int | None = Field(None, alias="earliest") latest: int | None = Field(None, alias="latest") sample_rate: int | None = Field(None, alias="sample_rate") num_events_before: int | None = Field(None, alias="num_events_before") num_events_after: int | None = Field(None, alias="num_events_after") target_event_time: int | None = Field(None, alias="target_event_time") is_private: bool = Field(True, alias="is_private") set_options: dict[str, Any] | None = Field(None, alias="set_options") expected_output_type: str | None = Field(None, alias="expected_output_type") @validator("earliest", pre=True, allow_reuse=True) @classmethod def validate_earliest(cls, v): return arg_to_number(v) @validator("latest", pre=True, allow_reuse=True) @classmethod def validate_latest(cls, v): return arg_to_number(v) @validator("sample_rate", pre=True, allow_reuse=True) @classmethod def validate_sample_rate(cls, v): return arg_to_number(v) @validator("num_events_before", pre=True, allow_reuse=True) @classmethod def validate_num_events_before(cls, v): return arg_to_number(v) @validator("num_events_after", pre=True, allow_reuse=True) @classmethod def validate_num_events_after(cls, v): return arg_to_number(v) @validator("target_event_time", pre=True, allow_reuse=True) @classmethod def validate_target_event_time(cls, v): return arg_to_number(v) @validator("is_private", pre=True, allow_reuse=True) @classmethod def validate_is_private(cls, v): return argToBoolean(v) @validator("set_options", pre=True, allow_reuse=True) @classmethod def validate_set_options(cls, v): return validate_json(v) def search_job_create_command(client: CriblSearchClient, args: SearchJobCreateArgs) -> CommandResults: """ Executes the cribl-search-job-create command. Creates a new search job in the Cribl Search API. Args: client (CriblSearchClient): The Cribl Search API client. args (SearchJobCreateArgs): The command arguments including query and optional parameters. Returns: CommandResults: The results of the command execution, including the created job details. """ results = client.search_job_create( query=args.query, earliest=args.earliest, latest=args.latest, sample_rate=args.sample_rate, num_events_before=args.num_events_before, num_events_after=args.num_events_after, target_event_time=args.target_event_time, is_private=args.is_private, set_options=args.set_options, expected_output_type=args.expected_output_type, ) readable_output = tableToMarkdown( "Search Job Created", results, headers=["user", "id", "isPrivate", "type", "status"], headerTransform=lambda x: { "user": "User", "id": "ID", "isPrivate": "Is Private", "type": "Type", "status": "Status", }.get(x, x), removeNull=True, ) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchJob", outputs_key_field="id", outputs=results, readable_output=readable_output, raw_response=results, ) # endregion # region cribl-search-job-list class SearchJobListArgs(ContentBaseModel): job_id: str | None = Field(None, alias="job_id") limit: int | None = Field(10, alias="limit") all_results: bool = Field(False, alias="all_results") @validator("limit", pre=True, allow_reuse=True) @classmethod def validate_limit(cls, v): return arg_to_number(v) @validator("all_results", pre=True, allow_reuse=True) @classmethod def validate_all_results(cls, v): return argToBoolean(v) def search_job_list_command(client: CriblSearchClient, args: SearchJobListArgs) -> CommandResults: """ Executes the cribl-search-job-list command. Retrieves a list of all search jobs or a specific search job from the Cribl Search API. Args: client (CriblSearchClient): The Cribl Search API client. args (SearchJobListArgs): The command arguments including optional job_id, limit, and all_results. Returns: CommandResults: The results of the command execution. """ results = client.search_jobs_list(job_id=args.job_id) if args.job_id: # Single job result job = results if isinstance(results, dict) else results[0] if results else {} readable_output = tableToMarkdown( f"Search Job {args.job_id} Details", job, headers=["user", "id", "isPrivate", "type", "status"], headerTransform=lambda x: { "user": "User", "id": "ID", "isPrivate": "Is Private", "type": "Type", "status": "Status", }.get(x, x), removeNull=True, ) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchJob", outputs_key_field="id", outputs=job, readable_output=readable_output, raw_response=results, ) # List of jobs paginated_results = truncate_results(results, limit=args.limit, all_results=args.all_results) # type: ignore[arg-type] readable_output = tableToMarkdown( "Search Jobs List", paginated_results, headers=["user", "id", "isPrivate", "type", "status"], headerTransform=lambda x: { "user": "User", "id": "ID", "isPrivate": "Is Private", "type": "Type", "status": "Status", }.get(x, x), removeNull=True, ) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchJob", outputs_key_field="id", outputs=paginated_results, readable_output=readable_output, raw_response=results, ) # endregion # region cribl-search-job-update class SearchJobUpdateArgs(ContentBaseModel): job_id: str = Field(alias="job_id") status: str | None = Field(None, alias="status") is_private: bool | None = Field(None, alias="is_private") @validator("is_private", pre=True, allow_reuse=True) @classmethod def validate_is_private(cls, v): if v is None: return None return argToBoolean(v) @root_validator(allow_reuse=True) @classmethod def validate_at_least_one_field(cls, values): status = values.get("status") is_private = values.get("is_private") if status is None and is_private is None: raise ValueError("At least one of 'status' or 'is_private' must be provided.") return values def search_job_update_command(client: CriblSearchClient, args: SearchJobUpdateArgs) -> CommandResults: """ Executes the cribl-search-job-update command. Updates an existing search job in the Cribl Search API. Args: client (CriblSearchClient): The Cribl Search API client. args (SearchJobUpdateArgs): The command arguments including job_id and fields to update. Returns: CommandResults: The results of the command execution. """ results = client.search_job_update( job_id=args.job_id, status=args.status, is_private=args.is_private, ) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchJob", outputs_key_field="id", outputs=results, readable_output=f"The job {args.job_id} has been successfully updated.", raw_response=results, ) # endregion # region cribl-search-job-delete class SearchJobDeleteArgs(ContentBaseModel): job_id: str = Field(alias="job_id") def search_job_delete_command(client: CriblSearchClient, args: SearchJobDeleteArgs) -> CommandResults: """ Executes the cribl-search-job-delete command. Deletes a search job from the Cribl Search API. Args: client (CriblSearchClient): The Cribl Search API client. args (SearchJobDeleteArgs): The command arguments including the job_id to delete. Returns: CommandResults: A message indicating the successful deletion of the job. """ client.search_job_delete(job_id=args.job_id) return CommandResults(readable_output=f"The job {args.job_id} has been successfully deleted.") # endregion # region cribl-search-dataset-list class SearchDatasetListArgs(ContentBaseModel): dataset_id: str | None = Field(None, alias="dataset_id") limit: int | None = Field(10, alias="limit") all_results: bool = Field(False, alias="all_results") @validator("limit", pre=True, allow_reuse=True) @classmethod def validate_limit(cls, v): return arg_to_number(v) @validator("all_results", pre=True, allow_reuse=True) @classmethod def validate_all_results(cls, v): return argToBoolean(v) def search_dataset_list_command(client: CriblSearchClient, args: SearchDatasetListArgs) -> CommandResults: """ Executes the cribl-search-dataset-list command. Retrieves a list of all datasets or a specific dataset from the Cribl Search API. Args: client (CriblSearchClient): The Cribl Search API client. args (SearchDatasetListArgs): The command arguments including optional dataset_id, limit, and all_results. Returns: CommandResults: The results of the command execution. """ results = client.search_datasets_list(dataset_id=args.dataset_id) if args.dataset_id: # Single dataset result dataset = results if isinstance(results, dict) else results[0] if results else {} readable_output = tableToMarkdown( f"Dataset {args.dataset_id} Details", dataset, headers=["id", "provider", "type", "region"], headerTransform=lambda x: { "id": "ID", "provider": "Provider", "type": "Type", "region": "Region", }.get(x, x), removeNull=True, ) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchDataset", outputs_key_field="id", outputs=dataset, readable_output=readable_output, raw_response=results, ) # List of datasets paginated_results = truncate_results(results, limit=args.limit, all_results=args.all_results) # type: ignore[arg-type] readable_output = tableToMarkdown( "Datasets List", paginated_results, headers=["id", "provider", "type", "region"], headerTransform=lambda x: { "id": "ID", "provider": "Provider", "type": "Type", "region": "Region", }.get(x, x), removeNull=True, ) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchDataset", outputs_key_field="id", outputs=paginated_results, readable_output=readable_output, raw_response=results, ) # endregion # region cribl-saved-search-list class SavedSearchListArgs(ContentBaseModel): search_id: str | None = Field(None, alias="search_id") limit: int | None = Field(10, alias="limit") all_results: bool = Field(False, alias="all_results") @validator("limit", pre=True, allow_reuse=True) @classmethod def validate_limit(cls, v): return arg_to_number(v) @validator("all_results", pre=True, allow_reuse=True) @classmethod def validate_all_results(cls, v): return argToBoolean(v) def saved_search_list_command(client: CriblSearchClient, args: SavedSearchListArgs) -> CommandResults: """ Executes the cribl-saved-search-list command. Retrieves a list of all saved searches or a specific saved search from the Cribl Search API. Args: client (CriblSearchClient): The Cribl Search API client. args (SavedSearchListArgs): The command arguments including optional search_id, limit, and all_results. Returns: CommandResults: The results of the command execution. """ results = client.saved_searches_list(search_id=args.search_id) if args.search_id: # Single saved search result saved_search = results if isinstance(results, dict) else results[0] if results else {} readable_output = tableToMarkdown( f"Saved Search {args.search_id} Details", saved_search, headers=["id", "description", "name", "query"], headerTransform=lambda x: { "id": "ID", "description": "Description", "name": "Name", "query": "Query", }.get(x, x), removeNull=True, ) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SavedSearch", outputs_key_field="id", outputs=saved_search, readable_output=readable_output, raw_response=results, ) # List of saved searches paginated_results = truncate_results(results, limit=args.limit, all_results=args.all_results) # type: ignore[arg-type] readable_output = tableToMarkdown( "Saved Searches List", paginated_results, headers=["id", "description", "name", "query"], headerTransform=lambda x: { "id": "ID", "description": "Description", "name": "Name", "query": "Query", }.get(x, x), removeNull=True, ) return CommandResults( outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SavedSearch", outputs_key_field="id", outputs=paginated_results, readable_output=readable_output, raw_response=results, ) # endregion # region ExecutionConfig class CriblSearchExecutionConfig(BaseExecutionConfig): """Execution configuration for Cribl Search.""" @property def params(self) -> CriblSearchParams: return CriblSearchParams(**self._raw_params) @property def search_query_args(self) -> SearchQueryArgs: return SearchQueryArgs(**self._raw_args) @property def search_status_args(self) -> SearchStatusArgs: return SearchStatusArgs(**self._raw_args) @property def search_result_args(self) -> SearchResultArgs: return SearchResultArgs(**self._raw_args) @property def search_job_create_args(self) -> SearchJobCreateArgs: return SearchJobCreateArgs(**self._raw_args) @property def search_job_list_args(self) -> SearchJobListArgs: return SearchJobListArgs(**self._raw_args) @property def search_job_update_args(self) -> SearchJobUpdateArgs: return SearchJobUpdateArgs(**self._raw_args) @property def search_job_delete_args(self) -> SearchJobDeleteArgs: return SearchJobDeleteArgs(**self._raw_args) @property def search_dataset_list_args(self) -> SearchDatasetListArgs: return SearchDatasetListArgs(**self._raw_args) @property def saved_search_list_args(self) -> SavedSearchListArgs: return SavedSearchListArgs(**self._raw_args) # endregion # region Main def main() -> None: """ Main entry point for the Cribl Search integration. Initializes the execution configuration, client, and dispatches the command to the appropriate command function. """ execution = CriblSearchExecutionConfig() command = execution.command demisto.debug(f"[Main] Starting to execute {command=}.") try: params = execution.params client = CriblSearchClient(params) match command: case "test-module": return_results(test_module(client)) case "cribl-search-query": return_results(search_query_command(client, execution.search_query_args)) case "cribl-search-status": return_results(search_status_command(client, execution.search_status_args)) case "cribl-search-result": return_results(search_result_command(client, execution.search_result_args)) case "cribl-search-job-create": return_results(search_job_create_command(client, execution.search_job_create_args)) case "cribl-search-job-list": return_results(search_job_list_command(client, execution.search_job_list_args)) case "cribl-search-job-update": return_results(search_job_update_command(client, execution.search_job_update_args)) case "cribl-search-job-delete": return_results(search_job_delete_command(client, execution.search_job_delete_args)) case "cribl-search-dataset-list": return_results(search_dataset_list_command(client, execution.search_dataset_list_args)) case "cribl-saved-search-list": return_results(saved_search_list_command(client, execution.saved_search_list_args)) case _: raise NotImplementedError(f"Command {command} is not implemented.") except Exception as e: demisto.error(traceback.format_exc()) return_error(f"Failed to execute {command} command.\nError:\n{str(e)}") if __name__ in ("__main__", "__builtin__", "builtins"): main() # endregion