Skip to content

Commit 607d3fa

Browse files
P1llusCAWilson94
authored andcommitted
[Rule Migration] Adding CIM to ECS mapping and ESQL validation (elastic#202331)
## Summary This PR adds the initial context to map CIM fields to ECS and two new nodes validation and a node to handle esql validation issues, fixing itself. This is how the graph looks compared to its old one: <img width="646" alt="image" src="https://github.com/user-attachments/assets/253e449c-ac6f-4913-8da4-eb36f4e7b982"> Validation always runs last, and if validation returns any errors it will run the appropriate node depending on what validation failed. Once it is resolved it will validate again and then END when its successful. Currently 5 error iterations is max, which is just an arbitrary number. The default Langgraph configuration is 25 nodes executed in total for a specific graph before it errors with a recursion limit (main and sub graphs are not combined in that count). A few things are included in this PR: - Moved ESQL KB caller to util(any better place?), as it is now used in multiple nodes. - New Validation node, where any sort of validation takes place, usually the last step before ending the graph (on success). - New ESQL Error node, to resolve any ESQL validation errors and trigger a re-validation. - Fix a small bug in the main graph on the conditional edges, added a map for the allowed return values.
1 parent c53d432 commit 607d3fa

File tree

17 files changed

+461
-24
lines changed

17 files changed

+461
-24
lines changed
18.6 KB
Loading

x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/graph.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,5 @@ const matchedPrebuiltRuleConditional = (state: MigrateRuleState) => {
4848
if (state.elastic_rule?.prebuilt_rule_id) {
4949
return END;
5050
}
51-
return 'translation';
51+
return 'translationSubGraph';
5252
};

x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/graph.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@
66
*/
77

88
import { END, START, StateGraph } from '@langchain/langgraph';
9+
import { isEmpty } from 'lodash/fp';
10+
import { SiemMigrationRuleTranslationResult } from '../../../../../../../../common/siem_migrations/constants';
11+
import { getFixQueryErrorsNode } from './nodes/fix_query_errors';
912
import { getProcessQueryNode } from './nodes/process_query';
1013
import { getRetrieveIntegrationsNode } from './nodes/retrieve_integrations';
1114
import { getTranslateRuleNode } from './nodes/translate_rule';
15+
import { getValidationNode } from './nodes/validation';
1216
import { translateRuleState } from './state';
13-
import type { TranslateRuleGraphParams } from './types';
17+
import type { TranslateRuleGraphParams, TranslateRuleState } from './types';
18+
19+
// How many times we will try to self-heal when validation fails, to prevent infinite graph recursions
20+
const MAX_VALIDATION_ITERATIONS = 3;
1421

1522
export function getTranslateRuleGraph({
1623
model,
@@ -35,19 +42,37 @@ export function getTranslateRuleGraph({
3542
model,
3643
integrationRetriever,
3744
});
45+
const validationNode = getValidationNode({ logger });
46+
const fixQueryErrorsNode = getFixQueryErrorsNode({ inferenceClient, connectorId, logger });
3847

3948
const translateRuleGraph = new StateGraph(translateRuleState)
4049
// Nodes
4150
.addNode('processQuery', processQueryNode)
4251
.addNode('retrieveIntegrations', retrieveIntegrationsNode)
4352
.addNode('translateRule', translateRuleNode)
53+
.addNode('validation', validationNode)
54+
.addNode('fixQueryErrors', fixQueryErrorsNode)
4455
// Edges
4556
.addEdge(START, 'processQuery')
4657
.addEdge('processQuery', 'retrieveIntegrations')
4758
.addEdge('retrieveIntegrations', 'translateRule')
48-
.addEdge('translateRule', END);
59+
.addEdge('translateRule', 'validation')
60+
.addEdge('fixQueryErrors', 'validation')
61+
.addConditionalEdges('validation', validationRouter);
4962

5063
const graph = translateRuleGraph.compile();
5164
graph.name = 'Translate Rule Graph';
5265
return graph;
5366
}
67+
68+
const validationRouter = (state: TranslateRuleState) => {
69+
if (
70+
state.validation_errors.iterations <= MAX_VALIDATION_ITERATIONS &&
71+
state.translation_result === SiemMigrationRuleTranslationResult.FULL
72+
) {
73+
if (!isEmpty(state.validation_errors?.esql_errors)) {
74+
return 'fixQueryErrors';
75+
}
76+
}
77+
return END;
78+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import type { Logger } from '@kbn/core/server';
9+
import type { InferenceClient } from '@kbn/inference-plugin/server';
10+
import { getEsqlKnowledgeBase } from '../../../../../util/esql_knowledge_base_caller';
11+
import type { GraphNode } from '../../types';
12+
import { RESOLVE_ESQL_ERRORS_TEMPLATE } from './prompts';
13+
14+
interface GetFixQueryErrorsNodeParams {
15+
inferenceClient: InferenceClient;
16+
connectorId: string;
17+
logger: Logger;
18+
}
19+
20+
export const getFixQueryErrorsNode = ({
21+
inferenceClient,
22+
connectorId,
23+
logger,
24+
}: GetFixQueryErrorsNodeParams): GraphNode => {
25+
const esqlKnowledgeBaseCaller = getEsqlKnowledgeBase({ inferenceClient, connectorId, logger });
26+
return async (state) => {
27+
const rule = state.elastic_rule;
28+
const prompt = await RESOLVE_ESQL_ERRORS_TEMPLATE.format({
29+
esql_errors: state.validation_errors.esql_errors,
30+
esql_query: rule.query,
31+
});
32+
const response = await esqlKnowledgeBaseCaller(prompt);
33+
34+
const esqlQuery = response.match(/```esql\n([\s\S]*?)\n```/)?.[1] ?? '';
35+
rule.query = esqlQuery;
36+
return { elastic_rule: rule };
37+
};
38+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
export { getFixQueryErrorsNode } from './fix_query_errors';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { ChatPromptTemplate } from '@langchain/core/prompts';
9+
10+
export const RESOLVE_ESQL_ERRORS_TEMPLATE =
11+
ChatPromptTemplate.fromTemplate(`You are a helpful cybersecurity (SIEM) expert agent. Your task is to resolve the errors in the Elasticsearch Query Language (ES|QL) query provided by the user.
12+
13+
Below is the relevant errors related to the ES|SQL query:
14+
15+
<context>
16+
<esql_errors>
17+
{esql_errors}
18+
</esql_errors>
19+
<esql_query>
20+
{esql_query}
21+
</esql_query>
22+
</context>
23+
24+
<guidelines>
25+
- You will be provided with the currentl ES|QL query and its related errors.
26+
- Try to resolve the errors in the ES|QL query as best as you can to make it work.
27+
- You must respond only with the modified query inside a \`\`\`esql code block, nothing else similar to the example response below.
28+
</guidelines>
29+
30+
<example_response>
31+
A: Please find the modified ES|QL query below:
32+
\`\`\`esql
33+
FROM logs-endpoint.events.process-*
34+
| WHERE process.executable LIKE \"%chown root%\"
35+
| STATS count = COUNT(*), firstTime = MIN(@timestamp), lastTime = MAX(@timestamp) BY process.executable,
36+
process.command_line,
37+
host.name
38+
| EVAL firstTime = TO_DATETIME(firstTime), lastTime = TO_DATETIME(lastTime)
39+
\`\`\`
40+
</example_response>
41+
42+
`);

x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/process_query/process_query.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,15 @@ export const getProcessQueryNode = ({
2929
const replaceQueryResourcePrompt =
3030
REPLACE_QUERY_RESOURCE_PROMPT.pipe(model).pipe(replaceQueryParser);
3131
const resourceContext = getResourcesContext(resources);
32-
query = await replaceQueryResourcePrompt.invoke({
32+
const response = await replaceQueryResourcePrompt.invoke({
3333
query: state.original_rule.query,
3434
macros: resourceContext.macros,
3535
lookup_tables: resourceContext.lists,
3636
});
37+
const splQuery = response.match(/```spl\n([\s\S]*?)\n```/)?.[1] ?? '';
38+
if (splQuery) {
39+
query = splQuery;
40+
}
3741
}
3842
return { inline_query: query };
3943
};

x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/process_query/prompts.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,14 @@ Divide the query up into separate section and go through each section one at a t
140140
141141
<example_response>
142142
A: Please find the modified SPL query below:
143-
\`\`\`json
144-
{{"match": "Linux User Account Creation"}}
143+
\`\`\`spl
144+
sourcetype="linux:audit" \`linux_auditd_normalized_proctitle_process\`
145+
| rename host as dest
146+
| where LIKE (process_exec, "%chown root%")
147+
| stats count min(_time) as firstTime max(_time) as lastTime by process_exec proctitle normalized_proctitle_delimiter dest
148+
| convert timeformat="%Y-%m-%dT%H:%M:%S" ctime(firstTime)
149+
| convert timeformat="%Y-%m-%dT%H:%M:%S" ctime(lastTime)
150+
| search *
145151
\`\`\`
146152
</example_response>
147153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
export const SIEM_RULE_MIGRATION_CIM_ECS_MAP = `
9+
datamodel,object,source_field,ecs_field,data_type
10+
Application_State,All_Application_State,dest,service.node.name,string
11+
Application_State,All_Application_State,process,process.title,string
12+
Application_State,All_Application_State,user,user.name,string
13+
Application_State,Ports,dest_port,destination.port,number
14+
Application_State,Ports,transport,network.transport,string
15+
Application_State,Ports,transport_dest_port,destination.port,string
16+
Application_State,Services,service,service.name,string
17+
Application_State,Services,service_id,service.id,string
18+
Application_State,Services,status,service.state,string
19+
Authentication,Authentication,action,event.action,string
20+
Authentication,Authentication,app,process.name,string
21+
Authentication,Authentication,dest,host.name,string
22+
Authentication,Authentication,duration,event.duration,number
23+
Authentication,Authentication,signature,event.code,string
24+
Authentication,Authentication,signature_id,event.reason,string
25+
Authentication,Authentication,src,source.address,string
26+
Authentication,Authentication,src_nt_domain,source.domain,string
27+
Authentication,Authentication,user,user.name,string
28+
Certificates,All_Certificates,dest_port,destination.port,number
29+
Certificates,All_Certificates,duration,event.duration,number
30+
Certificates,All_Certificates,src,source.address,string
31+
Certificates,All_Certificates,src_port,source.port,number
32+
Certificates,All_Certificates,transport,network.protocol,string
33+
Certificates,SSL,ssl_end_time,tls.server.not_after,time
34+
Certificates,SSL,ssl_hash,tls.server.hash,string
35+
Certificates,SSL,ssl_issuer_common_name,tls.server.issuer,string
36+
Certificates,SSL,ssl_issuer_locality,x509.issuer.locality,string
37+
Certificates,SSL,ssl_issuer_organization,x509.issuer.organization,string
38+
Certificates,SSL,ssl_issuer_state,x509.issuer.state_or_province,string
39+
Certificates,SSL,ssl_issuer_unit,x509.issuer.organizational_unit,string
40+
Certificates,SSL,ssl_publickey_algorithm,x509.public_key_algorithm,string
41+
Certificates,SSL,ssl_serial,x509.serial_number,string
42+
Certificates,SSL,ssl_signature_algorithm,x509.signature_algorithm,string
43+
Certificates,SSL,ssl_start_time,x509.not_before,time
44+
Certificates,SSL,ssl_subject,x509.subject.distinguished_name,string
45+
Certificates,SSL,ssl_subject_common_name,x509.subject.common_name,string
46+
Certificates,SSL,ssl_subject_locality,x509.subject.locality,string
47+
Certificates,SSL,ssl_subject_organization,x509.subject.organization,string
48+
Certificates,SSL,ssl_subject_state,x509.subject.state_or_province,string
49+
Certificates,SSL,ssl_subject_unit,x509.subject.organizational_unit,string
50+
Certificates,SSL,ssl_version,tls.version,string
51+
Change,All_Changes,action,event.action,string
52+
Change,Account_Management,dest_nt_domain,destination.domain,string
53+
Change,Account_Management,src_nt_domain,source.domain,string
54+
Change,Account_Management,src_user,source.user,string
55+
Intrusion_Detection,IDS_Attacks,action,event.action,string
56+
Intrusion_Detection,IDS_Attacks,dest,destination.address,string
57+
Intrusion_Detection,IDS_Attacks,dest_port,destination.port,number
58+
Intrusion_Detection,IDS_Attacks,dvc,observer.hostname,string
59+
Intrusion_Detection,IDS_Attacks,severity,event.severity,string
60+
Intrusion_Detection,IDS_Attacks,src,source.ip,string
61+
Intrusion_Detection,IDS_Attacks,user,source.user,string
62+
JVM,OS,os,host.os.name,string
63+
JVM,OS,os_architecture,host.architecture,string
64+
JVM,OS,os_version,host.os.version,string
65+
Malware,Malware_Attacks,action,event.action,string
66+
Malware,Malware_Attacks,date,event.created,string
67+
Malware,Malware_Attacks,dest,host.hostname,string
68+
Malware,Malware_Attacks,file_hash,file.hash.*,string
69+
Malware,Malware_Attacks,file_name,file.name,string
70+
Malware,Malware_Attacks,file_path,file.path,string
71+
Malware,Malware_Attacks,Sender,source.user.email,string
72+
Malware,Malware_Attacks,src,source.ip,string
73+
Malware,Malware_Attacks,user,related.user,string
74+
Malware,Malware_Attacks,url,rule.reference,string
75+
Network_Resolution,DNS,answer,dns.answers,string
76+
Network_Resolution,DNS,dest,destination.address,string
77+
Network_Resolution,DNS,dest_port,destination.port,number
78+
Network_Resolution,DNS,duration,event.duration,number
79+
Network_Resolution,DNS,message_type,dns.type,string
80+
Network_Resolution,DNS,name,dns.question.name,string
81+
Network_Resolution,DNS,query,dns.question.name,string
82+
Network_Resolution,DNS,query_type,dns.op_code,string
83+
Network_Resolution,DNS,record_type,dns.question.type,string
84+
Network_Resolution,DNS,reply_code,dns.response_code,string
85+
Network_Resolution,DNS,reply_code_id,dns.id,number
86+
Network_Resolution,DNS,response_time,event.duration,number
87+
Network_Resolution,DNS,src,source.address,string
88+
Network_Resolution,DNS,src_port,source.port,number
89+
Network_Resolution,DNS,transaction_id,dns.id,number
90+
Network_Resolution,DNS,transport,network.transport,string
91+
Network_Resolution,DNS,ttl,dns.answers.ttl,number
92+
Network_Sessions,All_Sessions,action,event.action,string
93+
Network_Sessions,All_Sessions,dest_ip,destination.ip,string
94+
Network_Sessions,All_Sessions,dest_mac,destination.mac,string
95+
Network_Sessions,All_Sessions,duration,event.duration,number
96+
Network_Sessions,All_Sessions,src_dns,source.registered_domain,string
97+
Network_Sessions,All_Sessions,src_ip,source.ip,string
98+
Network_Sessions,All_Sessions,src_mac,source.mac,string
99+
Network_Sessions,All_Sessions,user,user.name,string
100+
Network_Traffic,All_Traffic,action,event.action,string
101+
Network_Traffic,All_Traffic,app,network.protocol,string
102+
Network_Traffic,All_Traffic,bytes,network.bytes,number
103+
Network_Traffic,All_Traffic,dest,destination.ip,string
104+
Network_Traffic,All_Traffic,dest_ip,destination.ip,string
105+
Network_Traffic,All_Traffic,dest_mac,destination.mac,string
106+
Network_Traffic,All_Traffic,dest_port,destination.port,number
107+
Network_Traffic,All_Traffic,dest_translated_ip,destination.nat.ip,string
108+
Network_Traffic,All_Traffic,dest_translated_port,destination.nat.port,number
109+
Network_Traffic,All_Traffic,direction,network.direction,string
110+
Network_Traffic,All_Traffic,duration,event.duration,number
111+
Network_Traffic,All_Traffic,dvc,observer.name,string
112+
Network_Traffic,All_Traffic,dvc_ip,observer.ip,string
113+
Network_Traffic,All_Traffic,dvc_mac,observer.mac,string
114+
Network_Traffic,All_Traffic,dvc_zone,observer.egress.zone,string
115+
Network_Traffic,All_Traffic,packets,network.packets,number
116+
Network_Traffic,All_Traffic,packets_in,source.packets,number
117+
Network_Traffic,All_Traffic,packets_out,destination.packets,number
118+
Network_Traffic,All_Traffic,protocol,network.protocol,string
119+
Network_Traffic,All_Traffic,rule,rule.name,string
120+
Network_Traffic,All_Traffic,src,source.address,string
121+
Network_Traffic,All_Traffic,src_ip,source.ip,string
122+
Network_Traffic,All_Traffic,src_mac,source.mac,string
123+
Network_Traffic,All_Traffic,src_port,source.port,number
124+
Network_Traffic,All_Traffic,src_translated_ip,source.nat.ip,string
125+
Network_Traffic,All_Traffic,src_translated_port,source.nat.port,number
126+
Network_Traffic,All_Traffic,transport,network.transport,string
127+
Network_Traffic,All_Traffic,vlan,vlan.name,string
128+
Vulnerabilities,Vulnerabilities,category,vulnerability.category,string
129+
Vulnerabilities,Vulnerabilities,cve,vulnerability.id,string
130+
Vulnerabilities,Vulnerabilities,cvss,vulnerability.score.base,number
131+
Vulnerabilities,Vulnerabilities,dest,host.name,string
132+
Vulnerabilities,Vulnerabilities,dvc,vulnerability.scanner.vendor,string
133+
Vulnerabilities,Vulnerabilities,severity,vulnerability.severity,string
134+
Vulnerabilities,Vulnerabilities,url,vulnerability.reference,string
135+
Vulnerabilities,Vulnerabilities,user,related.user,string
136+
Vulnerabilities,Vulnerabilities,vendor_product,vulnerability.scanner.vendor,string
137+
Endpoint,Ports,creation_time,@timestamp,timestamp
138+
Endpoint,Ports,dest_port,destination.port,number
139+
Endpoint,Ports,process_id,process.pid,string
140+
Endpoint,Ports,transport,network.transport,string
141+
Endpoint,Ports,transport_dest_port,destination.port,string
142+
Endpoint,Processes,action,event.action,string
143+
Endpoint,Processes,os,os.full,string
144+
Endpoint,Processes,parent_process_exec,process.parent.name,string
145+
Endpoint,Processes,parent_process_id,process.ppid,number
146+
Endpoint,Processes,parent_process_guid,process.parent.entity_id,string
147+
Endpoint,Processes,parent_process_path,process.parent.executable,string
148+
Endpoint,Processes,process_current_directory,process.parent.working_directory,
149+
Endpoint,Processes,process_exec,process.name,string
150+
Endpoint,Processes,process_hash,process.hash.*,string
151+
Endpoint,Processes,process_guid,process.entity_id,string
152+
Endpoint,Processes,process_id,process.pid,number
153+
Endpoint,Processes,process_path,process.executable,string
154+
Endpoint,Processes,user_id,related.user,string
155+
Endpoint,Services,description,service.name,string
156+
Endpoint,Services,process_id,service.id,string
157+
Endpoint,Services,service_dll,dll.name,string
158+
Endpoint,Services,service_dll_path,dll.path,string
159+
Endpoint,Services,service_dll_hash,dll.hash.*,string
160+
Endpoint,Services,service_dll_signature_exists,dll.code_signature.exists,boolean
161+
Endpoint,Services,service_dll_signature_verified,dll.code_signature.valid,boolean
162+
Endpoint,Services,service_exec,service.name,string
163+
Endpoint,Services,service_hash,hash.*,string
164+
Endpoint,Filesystem,file_access_time,file.accessed,timestamp
165+
Endpoint,Filesystem,file_create_time,file.created,timestamp
166+
Endpoint,Filesystem,file_modify_time,file.mtime,timestamp
167+
Endpoint,Filesystem,process_id,process.pid,string
168+
Endpoint,Registry,process_id,process.id,string
169+
Web,Web,action,event.action,string
170+
Web,Web,app,observer.product,string
171+
Web,Web,bytes_in,http.request.bytes,number
172+
Web,Web,bytes_out,http.response.bytes,number
173+
Web,Web,dest,destination.ip,string
174+
Web,Web,duration,event.duration,number
175+
Web,Web,http_method,http.request.method,string
176+
Web,Web,http_referrer,http.request.referrer,string
177+
Web,Web,http_user_agent,user_agent.name,string
178+
Web,Web,status,http.response.status_code,string
179+
Web,Web,url,url.full,string
180+
Web,Web,user,url.username,string
181+
Web,Web,vendor_product,observer.product,string`;

0 commit comments

Comments
 (0)