diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index b8fe9d3d860b3..b5c213e422f2f 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -53918,6 +53918,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -58955,6 +58958,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -63545,6 +63551,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -68278,6 +68287,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -72870,6 +72882,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -82988,6 +83003,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -83447,6 +83465,9 @@ ], "type": "object" }, + "severity_score": { + "type": "number" + }, "title": { "minLength": 1, "type": "string" diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index 26e92325ff944..dff8c4e331b43 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -52997,6 +52997,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -58034,6 +58037,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -62624,6 +62630,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -67357,6 +67366,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -71949,6 +71961,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -82067,6 +82082,9 @@ "query" ], "type": "object" + }, + "severity_score": { + "type": "number" } }, "required": [ @@ -82526,6 +82544,9 @@ ], "type": "object" }, + "severity_score": { + "type": "number" + }, "title": { "minLength": 1, "type": "string" diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index f86f614c9962d..f957e6c600f37 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -56049,6 +56049,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql type: array @@ -58644,6 +58646,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql type: array @@ -61013,6 +61017,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql type: array @@ -63458,6 +63464,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql type: array @@ -65827,6 +65835,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql type: array @@ -71192,6 +71202,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql required: @@ -71452,6 +71464,8 @@ paths: type: string required: - query + severity_score: + type: number title: minLength: 1 type: string diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 3306063130265..a21f50b0f732b 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -60423,6 +60423,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql type: array @@ -63018,6 +63020,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql type: array @@ -65387,6 +65391,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql type: array @@ -67832,6 +67838,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql type: array @@ -70201,6 +70209,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql type: array @@ -75566,6 +75576,8 @@ paths: type: string required: - query + severity_score: + type: number required: - kql required: @@ -75826,6 +75838,8 @@ paths: type: string required: - query + severity_score: + type: number title: minLength: 1 type: string diff --git a/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/generate_significant_events.ts b/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/generate_significant_events.ts index d2e63eafeccff..f91e6204a6ee0 100644 --- a/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/generate_significant_events.ts +++ b/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/generate_significant_events.ts @@ -22,7 +22,9 @@ interface Query { kql: string; title: string; category: SignificantEventType; + severity_score: number; } + /** * Generate significant event definitions, based on: * - the description of the feature (or stream if feature is undefined) diff --git a/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/prompt.ts b/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/prompt.ts index c3b8bfe86b410..b63c3861b826d 100644 --- a/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/prompt.ts +++ b/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/prompt.ts @@ -63,8 +63,13 @@ export const GenerateSignificantEventsPrompt = createPrompt({ SIGNIFICANT_EVENT_TYPE_SECURITY, ], }, + severity_score: { + type: 'number', + minimum: 0, + maximum: 100, + }, }, - required: ['kql', 'title', 'category'], + required: ['kql', 'title', 'category', 'severity_score'], }, }, }, diff --git a/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/system_prompt.text b/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/system_prompt.text index 680e7e6983295..c221bacd3611a 100644 --- a/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/system_prompt.text +++ b/x-pack/platform/packages/shared/kbn-streams-ai/src/significant_events/system_prompt.text @@ -18,7 +18,7 @@ Your primary goal is to analyze the provided context about a user's system—inc | Tool | Function | Notes | | :--- | :--- | :--- | -| `add_queries` | Submits one or more KQL queries for the user. | Payload is a list of objects, each with `title`, `kql`, and `category`. | +| `add_queries` | Submits one or more KQL queries for the user. | Payload is a list of objects, each with `title`, `kql`, `category`, and `severity_score`. | | `reason()` | **Begin a Reasoning Monologue** | Outputs your private thoughts. Must use sentinel tags (`<<>>`...`<<>>`). | | `complete()` | Declare readiness to answer | Ends the loop and triggers the **Definitive Output**. | @@ -54,15 +54,15 @@ PLAN> Describe your next action in natural language. If you are ready to answer, ### Example 1: Calling `add_queries` with a single query -`>>> ACTION: add_queries(queries=[{"title": "View all errors", "kql": "error.message:*", "category": "error"}])` +`>>> ACTION: add_queries(queries=[{"title": "View all errors", "kql": "error.message:*", "category": "error", "severity_score": 60}])` ### Example 2: Calling `add_queries` with multiple queries ``` >>> ACTION: add_queries(queries=[ - {"title": "Application startup", "kql": "message:\"Started Application\"", "category": "operational"}, - {"title": "Failed login attempts", "kql": "body.text:\"Failed password for\"", "category": "security"}, - {"title": "Out of memory errors", "kql": "body.text:\"java.lang.OutOfMemoryError\"", "category": "error"} + {"title": "Application startup", "kql": "message:\"Started Application\"", "category": "operational", "severity_score": 25}, + {"title": "Failed login attempts", "kql": "body.text:\"Failed password for\"", "category": "security", "severity_score": 75}, + {"title": "Out of memory errors", "kql": "body.text:\"java.lang.OutOfMemoryError\"", "category": "error", "severity_score": 85} ]) ``` @@ -113,10 +113,10 @@ This is the final, user-facing response that follows the `complete()` call from **Scenario:** You attempt to add a query with invalid KQL syntax. **Initial Flawed Action:** -`>>> ACTION: add_queries(queries=[{"title": "Invalid KQL syntax", "kql": "body.text:value AND", "category": "error"}])` +`>>> ACTION: add_queries(queries=[{"title": "Invalid KQL syntax", "kql": "body.text:value AND", "category": "error", "severity_score": 60}])` **Tool Response (simulated):** -`Failed to add 1 of 1 queries. Invalid: [{"title": "Invalid KQL syntax", "kql": "body.text:value AND", "category": "error", "error": "KQL syntax error: trailing boolean operator"}]` +`Failed to add 1 of 1 queries. Invalid: [{"title": "Invalid KQL syntax", "kql": "body.text:value AND", "category": "error", "severity_score": 60, "error": "KQL syntax error: trailing boolean operator"}]` **Reasoning Monologue for Repair:** `<<>>` @@ -127,7 +127,7 @@ This is the final, user-facing response that follows the `complete()` call from `<<>>` **Corrected Action:** -`>>> ACTION: add_queries(queries=[{"title": "Corrected KQL", "kql": "body.text:value", "category": "error"}])` +`>>> ACTION: add_queries(queries=[{"title": "Corrected KQL", "kql": "body.text:value", "category": "error", "severity_score": 60}])` --- @@ -155,7 +155,23 @@ This is the final, user-facing response that follows the `complete()` call from --- -## 8. Tips & hints +## 8. Severity Scoring + +Assign a `severity_score` (0-100) based on category baseline + modifiers: + +| Category | Base | Modifiers | +|----------|------|-----------| +| `security` | 70 | +15 privilege escalation, +10 repeated failures | +| `error` | 60 | +25 crash/OOM/deadlock, +10 data integrity risk | +| `resource_health` | 50 | +15 exhaustion, +10 degradation warnings | +| `operational` | 30 | -10 expected lifecycle events | +| `configuration` | 25 | +10 security-related changes | + +**Score ranges:** 80-100 critical, 60-79 high, 40-59 medium, 0-39 low + +--- + +## 9. Tips & hints * **Focus on Actionable Insights:** Generate queries that a user would find genuinely helpful for debugging, monitoring, or security. Avoid trivial queries (e.g., `message:*`). * **Categorize Correctly:** Use the provided categories (`operational`, `configuration`, `resource_health`, `error`, `security`). If a query fits multiple, choose the most specific one. diff --git a/x-pack/platform/packages/shared/kbn-streams-schema/src/api/significant_events/index.ts b/x-pack/platform/packages/shared/kbn-streams-schema/src/api/significant_events/index.ts index 2fbc3761058d2..3266b3f8baea2 100644 --- a/x-pack/platform/packages/shared/kbn-streams-schema/src/api/significant_events/index.ts +++ b/x-pack/platform/packages/shared/kbn-streams-schema/src/api/significant_events/index.ts @@ -52,6 +52,7 @@ interface GeneratedSignificantEventQuery { filter: Condition; type: string; }; + severity_score: number; } type SignificantEventsGenerateResponse = Observable< diff --git a/x-pack/platform/packages/shared/kbn-streams-schema/src/queries/index.ts b/x-pack/platform/packages/shared/kbn-streams-schema/src/queries/index.ts index b473937d67d6f..22b12e9b0ba8d 100644 --- a/x-pack/platform/packages/shared/kbn-streams-schema/src/queries/index.ts +++ b/x-pack/platform/packages/shared/kbn-streams-schema/src/queries/index.ts @@ -26,6 +26,8 @@ export interface StreamQueryKql extends StreamQueryBase { kql: { query: string; }; + // from 0 to 100. aligned with anomaly detection scoring + severity_score?: number; } export type StreamQuery = StreamQueryKql; @@ -47,6 +49,7 @@ export const streamQueryKqlSchema: z.Schema = z.intersection( kql: z.object({ query: z.string(), }), + severity_score: z.number().optional(), }) ); @@ -67,6 +70,7 @@ export const upsertStreamQueryRequestSchema = z.object({ kql: z.object({ query: z.string(), }), + severity_score: z.number().optional(), }); export const isStreamQueryKql = createIsNarrowSchema(streamQuerySchema, streamQueryKqlSchema); diff --git a/x-pack/platform/plugins/shared/streams/server/lib/significant_events/generate_significant_events.ts b/x-pack/platform/plugins/shared/streams/server/lib/significant_events/generate_significant_events.ts index 8eb7ad8bb6137..db59031011f88 100644 --- a/x-pack/platform/plugins/shared/streams/server/lib/significant_events/generate_significant_events.ts +++ b/x-pack/platform/plugins/shared/streams/server/lib/significant_events/generate_significant_events.ts @@ -54,6 +54,7 @@ export async function generateSignificantEventDefinitions( feature: feature ? { name: feature.name, filter: feature?.filter, type: feature.type } : undefined, + severity_score: query.severity_score, })), tokensUsed, }; diff --git a/x-pack/platform/plugins/shared/streams/server/lib/significant_events/read_significant_events_from_alerts_indices.ts b/x-pack/platform/plugins/shared/streams/server/lib/significant_events/read_significant_events_from_alerts_indices.ts index a616e580f40e9..495abbed8a90d 100644 --- a/x-pack/platform/plugins/shared/streams/server/lib/significant_events/read_significant_events_from_alerts_indices.ts +++ b/x-pack/platform/plugins/shared/streams/server/lib/significant_events/read_significant_events_from_alerts_indices.ts @@ -147,6 +147,7 @@ export async function readSignificantEventsFromAlertsIndices( })) : [], change_points: changePoints, + severity_score: queryLink.query.severity_score, }; }); @@ -164,6 +165,7 @@ export async function readSignificantEventsFromAlertsIndices( stationary: { p_value: 0, change_point: 0 }, }, }, + severity_score: queryLink.query.severity_score, })); return [...significantEvents, ...notFoundSignificantEvents]; diff --git a/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/asset_client.ts b/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/asset_client.ts index bf723d5aba68e..78d4e6085db8c 100644 --- a/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/asset_client.ts +++ b/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/asset_client.ts @@ -16,7 +16,13 @@ import type { AssetType, QueryLink, } from '../../../../common/assets'; -import { QUERY_KQL_BODY, QUERY_FEATURE_FILTER, QUERY_FEATURE_NAME, QUERY_TITLE } from './fields'; +import { + QUERY_KQL_BODY, + QUERY_FEATURE_FILTER, + QUERY_FEATURE_NAME, + QUERY_TITLE, + QUERY_SEVERITY_SCORE, +} from './fields'; import { ASSET_ID, ASSET_TYPE, ASSET_UUID, STREAM_NAME } from './fields'; import type { AssetStorageSettings } from './storage_settings'; import { AssetNotFoundError } from '../errors/asset_not_found_error'; @@ -75,6 +81,7 @@ function toAssetLink( type StoredQueryLink = Omit & { [QUERY_TITLE]: string; [QUERY_KQL_BODY]: string; + [QUERY_SEVERITY_SCORE]?: number; }; export type StoredAssetLink = StoredQueryLink & { @@ -107,6 +114,7 @@ function fromStorage(link: StoredAssetLink): AssetLink { filter: JSON.parse(storedQueryLink[QUERY_FEATURE_FILTER]), } : undefined, + severity_score: storedQueryLink[QUERY_SEVERITY_SCORE], }, } satisfies QueryLink; } @@ -121,6 +129,7 @@ function toStorage(name: string, request: AssetLinkRequest): StoredAssetLink { [QUERY_KQL_BODY]: query.kql.query, [QUERY_FEATURE_NAME]: query.feature ? query.feature.name : '', [QUERY_FEATURE_FILTER]: query.feature ? JSON.stringify(query.feature.filter) : '', + [QUERY_SEVERITY_SCORE]: query.severity_score, } as unknown as StoredAssetLink; } diff --git a/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/fields.ts b/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/fields.ts index d0ddae1066456..ebf51b5fb3e82 100644 --- a/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/fields.ts +++ b/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/fields.ts @@ -12,6 +12,8 @@ export const ASSET_TYPE = 'asset.type'; export const QUERY_TITLE = 'query.title'; export const QUERY_KQL_BODY = 'query.kql.query'; +export const QUERY_SEVERITY_SCORE = 'query.severity_score'; + // Initially features were called systems, for backward compatibility we need to keep the same field names export const QUERY_FEATURE_NAME = 'experimental.query.system.name'; export const QUERY_FEATURE_FILTER = 'experimental.query.system.filter'; diff --git a/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/storage_settings.ts b/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/storage_settings.ts index 904356d690663..9dac665091ff5 100644 --- a/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/storage_settings.ts +++ b/x-pack/platform/plugins/shared/streams/server/lib/streams/assets/storage_settings.ts @@ -12,6 +12,7 @@ import { ASSET_TYPE, ASSET_UUID, QUERY_KQL_BODY, + QUERY_SEVERITY_SCORE, QUERY_TITLE, STREAM_NAME, } from './fields'; @@ -26,6 +27,7 @@ export const assetStorageSettings = { [STREAM_NAME]: types.keyword(), [QUERY_KQL_BODY]: types.match_only_text(), [QUERY_TITLE]: types.keyword(), + [QUERY_SEVERITY_SCORE]: types.long(), experimental: types.object({ enabled: false }), }, },